├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── THIRD-PARTY-LICENSES.txt
├── cdk.json
├── files
├── helloworld.html
└── web
│ └── reauth.html
├── images
├── Generate.jpg
├── HelloWorldPage.jpg
├── InvalidFile.jpg
└── singleusesignedurl.jpg
├── lambda
├── CloudFrontViewRequest.js
├── CreateSignedURL.js
└── generate.html
├── pom.xml
└── src
├── main
└── java
│ └── com
│ └── amazonaws
│ └── singleusesignedurl
│ ├── SingleUseSignedUrlApp.java
│ └── SingleUseSignedUrlStack.java
└── test
└── java
└── com
└── amazonaws
└── singleusesignedurl
└── SingleUseSignedUrlTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | .classpath.txt
2 | target
3 | .classpath
4 | .project
5 | .idea
6 | .settings
7 | .vscode
8 | *.iml
9 | .DS_Store
10 | lambda/uuid.txt
11 | lambda/region.txt
12 |
13 | # CDK asset staging directory
14 | .cdk.staging
15 | cdk.out
16 |
17 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ## Code of Conduct
2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
4 | opensource-codeofconduct@amazon.com with any additional questions or comments.
5 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
4 | documentation, we greatly value feedback and contributions from our community.
5 |
6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
7 | information to effectively respond to your bug report or contribution.
8 |
9 |
10 | ## Reporting Bugs/Feature Requests
11 |
12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features.
13 |
14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already
15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:
16 |
17 | * A reproducible test case or series of steps
18 | * The version of our code being used
19 | * Any modifications you've made relevant to the bug
20 | * Anything unusual about your environment or deployment
21 |
22 |
23 | ## Contributing via Pull Requests
24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
25 |
26 | 1. You are working against the latest source on the *main* branch.
27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted.
29 |
30 | To send us a pull request, please:
31 |
32 | 1. Fork the repository.
33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change.
34 | 3. Ensure local tests pass.
35 | 4. Commit to your fork using clear commit messages.
36 | 5. Send us a pull request, answering any default questions in the pull request interface.
37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.
38 |
39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
41 |
42 |
43 | ## Finding contributions to work on
44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start.
45 |
46 |
47 | ## Code of Conduct
48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
50 | opensource-codeofconduct@amazon.com with any additional questions or comments.
51 |
52 |
53 | ## Security issue notifications
54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue.
55 |
56 |
57 | ## Licensing
58 |
59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
60 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7 | the Software, and to permit persons to whom the Software is furnished to do so.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15 |
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Single Use SignedURL
2 | AWS CDK to create a CloudFront distribution with a request Lambda to allow single use signed URL file downloads. Each file is tracked by an identifier which is stored in a DynamoDB database.
3 | Each request will check the identifier against values stored in the database.
4 | If the identifier is found the file process continues and the files is received, the id is then removed from the database.
5 | If the identifier is not found the system will perform a 302 redirect to a specified URL.
6 |
7 | ### Architecture
8 |
9 |
10 | ### Requirements
11 | * A CloudFront Key Pair
12 | * The CloudFront Key Pair private key PEM file
13 | * AWS CDK Toolkit
14 | * CloudFront Triggers for Lambda Functions must execute in US East (N. Virginia) Region see requirements doc
15 |
16 | ### Setup
17 | 1. Create a CloudFront Key Pair (**Root Account required**).
18 | You can configure your CloudFront key pair through the Security Credentials page in the IAM console.
19 | Make sure you download your private key, and make a note of the key pair ID listed in the AWS Management Console.
20 | 1. Next we will store the private key file (PEM) in Secrets Manager.
21 | * First store a new secret
22 | * Select "Other type of secrets"
23 | * Select "Plaintext"
24 | * Replace the entire contents of the edit box with the entire contents of the private key PEM file
25 | * Enter a secret name (SignedURLPem is used in this sample)
26 | * Save the secret
27 | 1. Edit the cdk.json file and update the following values:
28 | * UUID - A unique string value used in bucket creation and service linking. This value must be unique across all AWS customers. It is suggested to generate a UUID for this value.
29 | * keyPairId - The Id of the CloudFront Key Pair
30 | * secretName - The name of the secrets manager value that holds the PEM file used to sign URLs
31 | * region - The region your DynamoDB and parameter store are located in. Due to CloudFront Edge Lambda requirement to execute in us-east-1 this value is required to execute the calls to other services in another region.
32 | 1. From a terminal window at the root directory of this project do ```cdk synth```
33 | 1. From a terminal window at the root directory of this project do ```cdk deploy```
34 | 1. Once the deployment is complete the terminal window will display outputs of the deployment. One of the outputs will be ```CreateSignedURLEndpoint```, navigating to this endpoint will display a web page used to generate single use signed URLS.
35 | * Click the **Generate Single SignedURL** button on this page to generate a signed url with the given sample helloworld.html sample file.
36 | * Click the **Open URL** button to display the file
37 | * Once the file is displayed try refreshing to the page and notice **Invalid File** is now displayed.
38 |
39 | ### Resource Cleanup
40 | 1. From a terminal window at the root directory of this project do ```cdk destroy```
41 | * The ```cdk destroy``` command will sometimes fail due to the ```CloudFrontViewRequest``` function currently being use by CloudFront. There can be a long wait period while the CloudFront resources are cleaned up.
42 | * If a failure occurs log into the AWS console and goto the CloudFormation console and manually delete the stack. It is recommended to check the option to retain the ```CloudFrontViewRequest``` function and manually remove it later.
43 | 1. Manually remove the two S3 buckets created which are given as outputs when you deploy.
44 | * The bucket names will begin with ```singleusesingedurl-```
45 |
--------------------------------------------------------------------------------
/THIRD-PARTY-LICENSES.txt:
--------------------------------------------------------------------------------
1 | ** jquery.min 3.5.1; version 3.5.1 -- https://jquery.com/
2 | Copyright OpenJS Foundation and other contributors, https://openjsf.org/
3 |
4 | Copyright OpenJS Foundation and other contributors, https://openjsf.org/
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining
7 | a copy of this software and associated documentation files (the
8 | "Software"), to deal in the Software without restriction, including
9 | without limitation the rights to use, copy, modify, merge, publish,
10 | distribute, sublicense, and/or sell copies of the Software, and to
11 | permit persons to whom the Software is furnished to do so, subject to
12 | the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be
15 | included in all copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/cdk.json:
--------------------------------------------------------------------------------
1 | {
2 | "app": "mvn -e -q compile exec:java",
3 | "context": {
4 | "aws-cdk:enableDiffNoFail": "true",
5 | "@aws-cdk/core:stackRelativeExports": "true",
6 | "keyPairId": "ABCD1234567890",
7 | "secretName": "SignedURLPem",
8 | "region": "us-east-1",
9 | "UUID": ""
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/files/helloworld.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
This file can be access a single time, secondary accesses will be redirected to an auth page.
8 | 9 | 10 | -------------------------------------------------------------------------------- /files/web/reauth.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |The requested file is invalid or expired, please request a new url.
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /images/Generate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/single-use-signed-url/7a3e8a5698da5a409e8ee7ff8500cde65e1393ce/images/Generate.jpg -------------------------------------------------------------------------------- /images/HelloWorldPage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/single-use-signed-url/7a3e8a5698da5a409e8ee7ff8500cde65e1393ce/images/HelloWorldPage.jpg -------------------------------------------------------------------------------- /images/InvalidFile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/single-use-signed-url/7a3e8a5698da5a409e8ee7ff8500cde65e1393ce/images/InvalidFile.jpg -------------------------------------------------------------------------------- /images/singleusesignedurl.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/single-use-signed-url/7a3e8a5698da5a409e8ee7ff8500cde65e1393ce/images/singleusesignedurl.jpg -------------------------------------------------------------------------------- /lambda/CloudFrontViewRequest.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: MIT-0 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 6 | * software and associated documentation files (the "Software"), to deal in the Software 7 | * without restriction, including without limitation the rights to use, copy, modify, 8 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 13 | * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 14 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 16 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | 19 | 'use strict'; 20 | const AWS = require('aws-sdk'); 21 | const fs = require('fs'); 22 | const uuid = fs.readFileSync('uuid.txt'); 23 | const dynamoDBRegion = fs.readFileSync('region.txt'); 24 | let dynamoDB; 25 | const cfDomainParamName = "singleusesignedurl-domain-" + uuid, 26 | activeKeysTableParamName = "singleusesignedurl-activekeys-" + uuid; 27 | const paramQuery = { 28 | "Names": [cfDomainParamName, activeKeysTableParamName], 29 | "WithDecryption": true 30 | } 31 | let dynamoDBTableName = '', 32 | domain = '', 33 | redirectURL = ''; 34 | 35 | function redirectReponse(err, callback) { 36 | const response = { 37 | status: '302', 38 | statusDescription: 'Not Found', 39 | headers: { 40 | location: [{ 41 | key: 'Location', 42 | value: redirectURL + '?err=' + err, 43 | }], 44 | }, 45 | }; 46 | callback(null, response); 47 | } 48 | 49 | function notFoundResponse(callback) { 50 | const response = { 51 | status: '404', 52 | statusDescription: 'Not Found' 53 | }; 54 | callback(null, response); 55 | } 56 | 57 | function badRequestReponse(err, callback) { 58 | const response = { 59 | status: '400', 60 | statusDescription: 'Bad Request: ' + err 61 | }; 62 | callback(null, response); 63 | } 64 | 65 | const getSystemsManagerValues = (query) => { 66 | return new Promise((resolve, reject) => { 67 | const ssm = new AWS.SSM({'region': '' + dynamoDBRegion}); 68 | ssm.getParameters(query, function (err, data) { 69 | if (err) { 70 | return reject(err); 71 | } 72 | for (const i of data.Parameters) { 73 | if (i.Name === activeKeysTableParamName) { 74 | dynamoDBTableName = i.Value 75 | } else if (i.Name === cfDomainParamName) { 76 | domain = i.Value 77 | } 78 | } 79 | resolve({}); 80 | }) 81 | }); 82 | } 83 | 84 | exports.handler = (event, context, callback) => { 85 | AWS.config.update({'region': '' + dynamoDBRegion}); 86 | console.info("Event:" + JSON.stringify(event)); 87 | if (typeof event == "undefined" 88 | || typeof event.Records == "undefined" 89 | || event.Records.length === 0 90 | || typeof event.Records[0].cf == "undefined" 91 | || typeof event.Records[0].cf.request == "undefined" 92 | || typeof event.Records[0].cf.request.querystring == "undefined") { 93 | badRequestReponse('Invalid parameters', callback); 94 | return; 95 | } 96 | let querystring = event.Records[0].cf.request.querystring; 97 | let vars = querystring.split('&'); 98 | let id = ''; 99 | for (let i = 0; i < vars.length; i++) { 100 | let pair = vars[i].split('='); 101 | if (decodeURIComponent(pair[0]) === 'id') { 102 | id = decodeURIComponent(pair[1]); 103 | break; 104 | } 105 | } 106 | 107 | if (id === '') { 108 | notFoundResponse(callback); 109 | return; 110 | } 111 | 112 | getSystemsManagerValues(paramQuery).then(() => { 113 | redirectURL = "https://" + domain + "/web/reauth.html"; 114 | dynamoDB = new AWS.DynamoDB({maxRetries: 0, endpoint: "https://dynamodb." + dynamoDBRegion + ".amazonaws.com"}); 115 | let dbQuery = { 116 | TableName: dynamoDBTableName, 117 | Key: { 118 | "id": { 119 | "S": id 120 | }, 121 | } 122 | }; 123 | dynamoDB 124 | .getItem(dbQuery) 125 | .promise() 126 | .then(res => { 127 | if (res.Item) { // we found the item so allow access and remove key 128 | dynamoDB 129 | .deleteItem(dbQuery) 130 | .promise() 131 | .then(() => { 132 | callback(null, event.Records[0].cf.request); 133 | }) 134 | .catch(err => { 135 | console.error("DynamoDB Delete Error: " + JSON.stringify(err)); 136 | badRequestReponse(JSON.stringify(err), callback); 137 | }); 138 | } else { // item not found so redirect to fallback page 139 | redirectReponse('Item not found', callback); 140 | } 141 | }) 142 | .catch(err => { 143 | console.error("Error: " + JSON.stringify(err)) 144 | badRequestReponse(JSON.stringify(err), callback); 145 | }); 146 | }).catch(err => { 147 | console.error("Error getting parameter: " + JSON.stringify(err)); 148 | badRequestReponse(JSON.stringify(err), callback); 149 | }); 150 | }; 151 | -------------------------------------------------------------------------------- /lambda/CreateSignedURL.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: MIT-0 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 6 | * software and associated documentation files (the "Software"), to deal in the Software 7 | * without restriction, including without limitation the rights to use, copy, modify, 8 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 13 | * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 14 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 16 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | 19 | 'use strict'; 20 | const AWS = require('aws-sdk'); 21 | const fs = require('fs'); 22 | const uuid = fs.readFileSync('uuid.txt'); 23 | const ssm = new AWS.SSM(); 24 | const dynamoDB = new AWS.DynamoDB.DocumentClient(); 25 | const secretsManager = new AWS.SecretsManager(); 26 | const cfDomainParamName = "singleusesignedurl-domain-" + uuid, 27 | activeKeysTableParamName = "singleusesignedurl-activekeys-" + uuid, 28 | keyPairIdParamName = "singleusesignedurl-keyPairId-" + uuid, 29 | secretNameParamName = "singleusesignedurl-secretName-" + uuid, 30 | apiendpointParamName = "singleusesignedurl-api-endpoint-" + uuid; 31 | const paramQuery = { 32 | "Names": [cfDomainParamName, activeKeysTableParamName, keyPairIdParamName, secretNameParamName, apiendpointParamName], 33 | "WithDecryption": true 34 | } 35 | let dynamoDBTableName = '', 36 | domain = '', 37 | cloudFrontURL = '', 38 | secretName = '', 39 | keyPairId = '', 40 | apiendpoint = ''; 41 | 42 | /* Use the Secrets Manager to get the PEM file from secret variable 'secretName' which was previously 43 | created by CloudFront 44 | see: 45 | https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-trusted-signers.html#private-content-creating-cloudfront-key-pairs 46 | */ 47 | const getSecurePEM = (secretName) => { 48 | return new Promise((resolve, reject) => { 49 | secretsManager.getSecretValue({SecretId: secretName}, function (err, data) { 50 | if (err) { 51 | return reject(err); 52 | } else { 53 | var secretData; 54 | if ('SecretString' in data) { 55 | secretData = data.SecretString; 56 | } else { 57 | let buff = new Buffer(data.SecretBinary, 'base64'); 58 | secretData = buff.toString('ascii'); 59 | } 60 | //console.log("PEM: " + secretData); 61 | resolve(secretData); 62 | } 63 | }) 64 | }); 65 | } 66 | 67 | const getSystemsManagerValues = (query) => { 68 | return new Promise((resolve, reject) => { 69 | ssm.getParameters(query, function (err, data) { 70 | if (err) { 71 | return reject(err); 72 | } 73 | for (const i of data.Parameters) { 74 | if (i.Name === activeKeysTableParamName) { 75 | dynamoDBTableName = i.Value 76 | } else if (i.Name === cfDomainParamName) { 77 | domain = i.Value 78 | } else if (i.Name === secretNameParamName) { 79 | secretName = i.Value 80 | } else if (i.Name === keyPairIdParamName) { 81 | keyPairId = i.Value 82 | } else if (i.Name === apiendpointParamName) { 83 | apiendpoint = i.Value 84 | } 85 | } 86 | resolve({}); 87 | }) 88 | }); 89 | } 90 | 91 | /* Create a signed URL 92 | */ 93 | const getSignedURL = (signer, options) => { 94 | return new Promise((resolve, reject) => { 95 | signer.getSignedUrl(options, function (err, data) { 96 | if (err) { 97 | reject(err); 98 | } else { 99 | resolve(data); 100 | } 101 | }); 102 | }) 103 | } 104 | 105 | /* Write the UUID, SignedURL, and valid until date to the database, 106 | Pass along the url as the result if successful 107 | */ 108 | const writeRecordToDynamoDB = (url, uuid, file, validuntil) => { 109 | return new Promise((resolve, reject) => { 110 | let params = { 111 | TableName: dynamoDBTableName, 112 | Item: { 113 | id: uuid, 114 | file: file, 115 | validuntil: validuntil 116 | } 117 | }; 118 | dynamoDB 119 | .put(params) 120 | .promise() 121 | .then(res => { 122 | console.info("Sent data to DynamoDB"); 123 | resolve(url); // pass the data along 124 | }).catch(err => { 125 | reject(err); 126 | }); 127 | }) 128 | } 129 | exports.handler = function (event, context) { 130 | console.info("event: " + JSON.stringify(event)); 131 | console.info("context: " + JSON.stringify(context)); 132 | 133 | let timeout = 0; 134 | let file = ''; 135 | let epoch = 0; 136 | 137 | getSystemsManagerValues(paramQuery).then(smParams => { 138 | cloudFrontURL = "https://" + domain + "/"; 139 | if (!event.queryStringParameters) { 140 | let html = fs.readFileSync('generate.html', 'utf8'); 141 | html = html.replace('{API_ENDPOINT}', apiendpoint) 142 | context.succeed({ 143 | statusCode: 200, 144 | headers: { 145 | 'Content-Type': 'text/html' 146 | }, 147 | body: html 148 | }); 149 | } 150 | timeout = parseInt(event.queryStringParameters.timeout); 151 | file = event.queryStringParameters.file; 152 | epoch = parseInt(((Date.now() + 0) / 1000) + timeout); 153 | console.info("Timeout: " + timeout + " Expires: " + epoch + " File: " + file); 154 | 155 | return getSecurePEM(secretName); 156 | }).then(pem => { 157 | return getSignedURL(new AWS.CloudFront.Signer(keyPairId, pem), { 158 | "url": cloudFrontURL + file + "?id=" + context.awsRequestId, 159 | expires: epoch 160 | }); 161 | }).then(signedURL => { 162 | console.info("URL: " + signedURL); 163 | return writeRecordToDynamoDB(signedURL, context.awsRequestId, file, epoch); 164 | }).then(data => { 165 | // singed url created and store in dynamodb at this point so return a success code with detailed body. 166 | context.succeed({ 167 | statusCode: 200, 168 | headers: { 169 | "Access-Control-Allow-Headers": "Content-Type", 170 | "Access-Control-Allow-Origin": "*", 171 | "Access-Control-Allow-Methods": "OPTIONS,GET" 172 | }, 173 | body: JSON.stringify({ 174 | id: context.awsRequestId, 175 | url: data, 176 | validuntil: '' + epoch 177 | }) 178 | }); 179 | }).catch(err => { 180 | context.fail(err); 181 | }); 182 | }; 183 | -------------------------------------------------------------------------------- /lambda/generate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |