-
Notifications
You must be signed in to change notification settings - Fork 0
/
private-docs.sh
executable file
·233 lines (189 loc) · 8.17 KB
/
private-docs.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
#!/usr/bin/env bash
set -euf -o pipefail
REPO_NAME="dedalusj/private-docs"
SCRIPT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PROMPT_PREFIX=">>"
TMP_DIR="$(pwd)/.tmp"
LAMBDA_CODE_PATH="lambda_code"
INDEX_TEMPLATE="index.js.template"
INDEX_FILE="${INDEX_TEMPLATE%.*}"
SOURCE_INDEX_TEMPLATE="${SCRIPT_PATH}/${LAMBDA_CODE_PATH}/${INDEX_TEMPLATE}"
OUTPUT_INDEX_TEMPLATE="${TMP_DIR}/${INDEX_FILE}"
NODE_MODULES_DIR="node_modules"
SOURCE_NODE_MODULES_DIR="${SCRIPT_PATH}/${LAMBDA_CODE_PATH}/${NODE_MODULES_DIR}"
OUTPUT_NODE_MODULES_DIR="${TMP_DIR}/${NODE_MODULES_DIR}"
DATE=$(date +%Y%m%d%H%M)
LAMBDA_ARCHIVE_FILE_PATH="${TMP_DIR}/${DATE}.zip"
CORS_CONFIGURATION_FILE_NAME="cors_configuration.json"
CORS_CONFIGURATION_FILE_PATH="${TMP_DIR}/${CORS_CONFIGURATION_FILE_NAME}"
CORS_CONFIGURATION_CONTENT='
{
"CORSRules": [
{
"AllowedOrigins": ["http*"],
"AllowedHeaders": ["*"],
"AllowedMethods": ["HEAD", "GET", "PUT", "POST", "DELETE"],
"ExposeHeaders": ["x-amz-server-side-encryption", "ETag", "x-amz-meta-custom-header"]
}
]
}
'
AWS_REGION="us-east-1"
STACK_PREFIX="private-docs"
CF_TEMPLATE_FILE_NAME="cf_template.yml"
CF_TEMPLATE_FILE_PATH="${SCRIPT_PATH}/${CF_TEMPLATE_FILE_NAME}"
function prompt {
echo -n "${PROMPT_PREFIX} $@ " 1>&2
}
function info {
echo "INFO | $1" 1>&2
}
function die {
EXIT_CODE=$?
if [ "$EXIT_CODE" -eq "0" ]; then
EXIT_CODE=1
fi
echo "ERROR | $1" 1>&2
exit ${EXIT_CODE}
}
function validate_tools {
which ssh-keygen > /dev/null || die "Required ssh-keygen not found."
which openssl > /dev/null || die "Required openssl not found."
which zip > /dev/null || die "Required zip not found."
which unzip > /dev/null || die "Required unzip not found."
which aws > /dev/null || die "Required aws cli not found. See https://docs.aws.amazon.com/cli/latest/userguide/installing.html to install."
}
function gather_input {
prompt "Client ID:"
read CLIENT_ID
prompt "Client Secret:"
read CLIENT_SECRET
prompt "Redirect URI:"
read REDIRECT_URI
prompt "Hosted domain:"
read HOSTED_DOMAIN
prompt "Endpoint URL for json lookup (leave blank if not used):"
read JSON_EMAIL_LOOKUP
prompt "Session duration (seconds):"
read SESSION_DURATION
echo "$PROMPT_PREFIX Authorization methods:" 1>&2
echo "$PROMPT_PREFIX (1) Hosted Domain - verify email's domain matches that of the given hosted domain" 1>&2
echo "$PROMPT_PREFIX (2) HTTP Email Lookup - verify email exists in JSON array located at given HTTP endpoint" 1>&2
prompt "Select an authorization method: " 1>&2
read AUTH_METHOD_INDEX
case "${AUTH_METHOD_INDEX}" in
1)
AUTH_METHOD="domain"
;;
2)
AUTH_METHOD="json-lookup"
;;
*)
die "Invalid auth method selected"
esac
echo "${CLIENT_ID}|${CLIENT_SECRET}|${REDIRECT_URI}|${HOSTED_DOMAIN}|${JSON_EMAIL_LOOKUP}|${SESSION_DURATION}|${AUTH_METHOD}"
}
function reset_temp_dir {
rm -rf ${TMP_DIR}
mkdir -p ${TMP_DIR}
}
function extract_callback_path {
local URL=$1
local URL_NOPRO=$(echo $URL | sed -e 's/^http:\/\///g' -e 's/^https:\/\///g')
local URL_REL=${URL_NOPRO#*/}
echo "/${URL_REL%%\?*}"
}
function create_keys {
ssh-keygen -t rsa -b 4096 -f ${TMP_DIR}/id_rsa -q -N '' || die "Failed to generate private key"
local PRIVATE_KEY=$(cat ${TMP_DIR}/id_rsa | sed 's/$/\\\\n/' | tr -d '\n')
openssl rsa -in ${TMP_DIR}/id_rsa -pubout -outform PEM -out ${TMP_DIR}/id_rsa.pub 2> /dev/null || die "Failed to generate public key"
local PUBLIC_KEY=$(cat ${TMP_DIR}/id_rsa.pub | sed 's/$/\\\\n/' | tr -d '\n' )
echo "${PRIVATE_KEY}|${PUBLIC_KEY}"
}
function get_s3_bucket {
local PROMPT_MSG="$@"
prompt ${PROMPT_MSG}
read S3_BUCKET
echo "${S3_BUCKET}"
}
function validate_s3_bucket {
local S3_BUCKET=$1
if ! aws s3api head-bucket --bucket "${S3_BUCKET}" 2> /dev/null; then
info "Creating S3 bucket: ${S3_BUCKET}"
aws s3 mb s3://${S3_BUCKET}/ --region ${AWS_REGION} || die "Failed to create S3 bucket ${S3_BUCKET}"
fi
}
function run {
source ${SCRIPT_PATH}/mo
validate_tools
reset_temp_dir || die "Failed to recreate temporary directory ${TMP_DIR}"
info "Gathering necessary details:"
echo "" 1>&2
IFS='|' read PRIVATE_KEY PUBLIC_KEY < <(create_keys)
IFS='|' read CLIENT_ID CLIENT_SECRET REDIRECT_URI HOSTED_DOMAIN JSON_EMAIL_LOOKUP SESSION_DURATION AUTH_METHOD < <(gather_input)
S3_BUCKET=$(get_s3_bucket "Enter the S3 bucket to expose (if it doesn't exist it will be created): ")
LAMBDA_S3_BUCKET=$(get_s3_bucket "Enter the S3 bucket for the AWS Lambda zip bundle (if it doesn't exist it will be created): ")
CALLBACK_PATH=$(extract_callback_path $REDIRECT_URI)
echo "" 1>&2
validate_s3_bucket ${S3_BUCKET}
validate_s3_bucket ${LAMBDA_S3_BUCKET}
info "Creating temporary files in: ${TMP_DIR}"
mo ${SOURCE_INDEX_TEMPLATE} > ${OUTPUT_INDEX_TEMPLATE} || die "Failed to generate index file"
cp -r ${SOURCE_NODE_MODULES_DIR} ${OUTPUT_NODE_MODULES_DIR} || die "Failed to copy node modules"
info "Creating zip file: ${LAMBDA_ARCHIVE_FILE_PATH}"
pushd ${TMP_DIR} > /dev/null || die "Failed to cd into temporary directory"
zip -q ${LAMBDA_ARCHIVE_FILE_PATH} ${INDEX_FILE} -r ${NODE_MODULES_DIR} || die "Failed to create zip file"
popd > /dev/null || die "Failed to return to original directory"
LAMBDA_ARCHIVE_S3_PATH="s3://${LAMBDA_S3_BUCKET}/${DATE}.zip"
info "Uploading AWS Lambda zip archive to: ${LAMBDA_ARCHIVE_S3_PATH}"
aws s3 cp ${LAMBDA_ARCHIVE_FILE_PATH} ${LAMBDA_ARCHIVE_S3_PATH} \
--region ${AWS_REGION} > /dev/null || die "Uploading AWS Lambda zip archive"
info "Updating cors configuration for bucket: ${S3_BUCKET}"
echo ${CORS_CONFIGURATION_CONTENT} > ${CORS_CONFIGURATION_FILE_PATH} || die "Writing temporary cors configuration"
aws s3api put-bucket-cors \
--bucket ${S3_BUCKET} \
--cors-configuration file://${CORS_CONFIGURATION_FILE_PATH} \
--region ${AWS_REGION} || die "Updating cors configuration"
STACK_NAME="${STACK_PREFIX}-${S3_BUCKET}"
info "Creating AWS CloudFormation stack with name: ${STACK_NAME}"
aws cloudformation deploy \
--stack-name ${STACK_NAME} \
--template-file file://${CF_TEMPLATE_FILE_PATH} \
--parameter-overrides DomainName=${HOSTED_DOMAIN} \
S3Bucket=${S3_BUCKET} \
LambdaArchiveS3Bucket=${LAMBDA_S3_BUCKET} \
LambdaArchiveName=${DATE}.zip \
--capabilities CAPABILITY_IAM \
--region ${AWS_REGION} > /dev/null || die "Failed to create CloudFormation stack"
CLOUDFRONT_AWS_URL=$(aws cloudformation describe-stacks \
--stack-name ${STACK_NAME} \
--query 'Stacks[0].Outputs[?OutputKey==`CloudFrontDistribution`].OutputValue' \
--output text \
--region ${AWS_REGION} || die "Failed to retrieve CloudFront AWS URL")
SITE_URL=$(aws cloudformation describe-stacks \
--stack-name ${STACK_NAME} \
--query 'Stacks[0].Outputs[?OutputKey==`SiteURL`].OutputValue' \
--output text \
--region ${AWS_REGION} || die "Failed to retrieve Site URL")
info "Waiting for CloudFormation stack completion"
aws cloudformation wait stack-create-complete --stack-name ${STACK_NAME} --region ${AWS_REGION}
info "The website is available at: ${SITE_URL}"
info "If you require HTTPS access to the site change the SSL certificate for the CloudFront distribution here: ${CLOUDFRONT_AWS_URL}"
}
function get_latest_release {
info "Retrieving private-docs last version"
curl --silent "https://api.github.com/repos/${REPO_NAME}/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/' || die "Failed to retrieve last version"
}
function install {
local OUTPUT_DIR=$1
local VERSION=$(get_latest_release)
local REPO_ARCHIVE_URL="https://github.com/${REPO_NAME}/archive/${VERSION}.zip"
info "Downloading private-docs version: ${VERSION}"
curl --silent -L -o /tmp/private-docs.zip "${REPO_ARCHIVE_URL}" || die "Failed to download private-docs zip archive"
unzip -q /tmp/private-docs.zip || die "Failed to unzip private-docs archive"
}
function deploy {
install $(pwd)
$(pwd)/privaste-docs/private-docs.sh run
}
"$@"