diff --git a/DEVELOPMENT_GUIDE.rst b/DEVELOPMENT_GUIDE.rst index 034acc25b7..2c6913d597 100755 --- a/DEVELOPMENT_GUIDE.rst +++ b/DEVELOPMENT_GUIDE.rst @@ -81,4 +81,23 @@ Install snakeviz `pip install snakeviz` ``` python -m cProfile -o sam_profile_results bin/sam-translate.py translate --input-file=tests/translator/input/alexa_skill.yaml --output-file=cfn-template.json snakeviz sam_profile_results +``` + +Verifying transforms +-------------------- + +If you make changes to the transformer and want to verify the resulting CloudFormation template works as expected, you can transform your SAM template into a CloudFormation template using the following process: + +```shell +# Optional: You only need to run the package command in certain cases; e.g. when your CodeUri specifies a local path +# Replace MY_TEMPLATE_PATH with the path to your template and MY_S3_BUCKET with an existing S3 bucket +aws cloudformation package --template-file MY_TEMPLATE_PATH/template.yaml --output-template-file output-template.yaml --s3-bucket MY_S3_BUCKET + +# Transform your SAM template into a CloudFormation template +# Replace "output-template.yaml" if you didn't run the package command above or specified a different path for --output-template-file +bin/sam-translate.py --input-file=output-template.yaml + +# Deploy your transformed CloudFormation template +# Replace MY_STACK_NAME with a unique name each time you deploy +aws cloudformation deploy --template-file cfn-template.json --capabilities CAPABILITY_NAMED_IAM --stack-name MY_STACK_NAME ``` \ No newline at end of file diff --git a/bin/sam-translate.py b/bin/sam-translate.py index 5c67b0552b..4b0df09739 100755 --- a/bin/sam-translate.py +++ b/bin/sam-translate.py @@ -57,7 +57,7 @@ def main(): except InvalidDocumentException as e: errorMessage = reduce(lambda message, error: message + ' ' + error.message, e.causes, e.message) print(errorMessage) - errors = map(lambda cause: {'errorMessage': cause.message}, e.causes) + errors = map(lambda cause: cause.message, e.causes) print(errors) diff --git a/examples/.eslintrc.yml b/examples/.eslintrc.yml new file mode 100644 index 0000000000..f13cc3238a --- /dev/null +++ b/examples/.eslintrc.yml @@ -0,0 +1,3 @@ +extends: standard +rules: + prefer-promise-reject-errors: off # API Gateway expects string response from Lamdba (when using async + Promise.reject) diff --git a/examples/2016-10-31/api_cognito_auth/README.md b/examples/2016-10-31/api_cognito_auth/README.md new file mode 100644 index 0000000000..5eae27aa22 --- /dev/null +++ b/examples/2016-10-31/api_cognito_auth/README.md @@ -0,0 +1,45 @@ +# API Gateway + Cognito Auth + Cognito Hosted Auth Example + +This example shows you how to create an API Gateway API with a Cognito Authorizer using SAM. + +## Running the example + +Install the Node.js/NPM dependencies for your API's Lambda logic into the `src/` directory. This is necessary so that the dependencies get packaged up along with your Lambda function. + +```bash +npm install . --prefix ./src +``` + +Deploy the example into your account (replace `YOUR_S3_ARTIFACTS_BUCKET` with an existing S3 bucket to store your app assets): + +```bash +# The following default values are also allowed: STACK_NAME, COGNITO_USER_POOL_CLIENT_NAME, COGNITO_USER_POOL_DOMAIN_PREFIX +S3_BUCKET_NAME=YOUR_S3_ARTIFACTS_BUCKET \ +npm run package-deploy +``` + +Cognito User Pools doesn't currently have CloudFormation support for configuring their Hosted Register/Signin UI. For now we will create these via the AWS CLI: + +```bash +npm run configure-cognito-user-pool +``` + +Open the registration page created and hosted for you by Cognito in your browser. After the page loads, enter a Username and Password and click the Sign Up button. + +```bash +npm run open-signup-page + +# Alternatively, you can open the login page by running `npm run open-login-page` +``` + +After clicking Sign Up, you will be redirected to the UI client for your API. + +To access the API UI directly as an unauthorized user (who has access to `GET /users` and `GET /users/{userId}`) you can run `npm run open-api-ui`. + +## Additional resources + +- https://aws.amazon.com/blogs/aws/launch-amazon-cognito-user-pools-general-availability-app-integration-and-federation/ +- https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-integrate-with-cognito.html +- https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-app-idp-settings.html +- https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-invoke-api-integrated-with-cognito-user-pool.html +- https://docs.aws.amazon.com/cognito/latest/developerguide/token-endpoint.html \ No newline at end of file diff --git a/examples/2016-10-31/api_cognito_auth/package.json b/examples/2016-10-31/api_cognito_auth/package.json new file mode 100644 index 0000000000..997222c19a --- /dev/null +++ b/examples/2016-10-31/api_cognito_auth/package.json @@ -0,0 +1,30 @@ +{ + "name": "api_cognito_auth", + "version": "1.0.0", + "description": "Example using API Gateway with Cognito Authorizer.", + "main": "lambda.js", + "license": "Apache-2.0", + "dependencies": { + "aws-serverless-express": "^3.3.3", + "body-parser": "^1.17.1", + "cors": "^2.8.3", + "express": "^4.15.2", + "pug": "^2.0.0-rc.1" + }, + "scripts": { + "package-deploy": "npm run set-config && npm run package && npm run deploy", + "set-config": "npm config set STACK_NAME ${STACK_NAME:-sam-example-api-cognito-auth}", + "package": "aws cloudformation package --template-file template.yaml --output-template-file template.packaged.yaml --s3-bucket $S3_BUCKET_NAME", + "deploy": "aws cloudformation deploy --template-file ./template.packaged.yaml --stack-name $STACK_NAME --capabilities CAPABILITY_IAM", + "configure-cognito-user-pool": "npm run set-cognito-user-pool-id && npm run set-cognito-user-pool-client-id && npm run set-api-id && npm run set-api-url && npm run update-user-pool-client && npm run create-user-pool-domain", + "set-cognito-user-pool-id": "npm config set COGNITO_USER_POOL_ID $(aws cloudformation describe-stacks --stack-name $(npm config get STACK_NAME) --query 'Stacks[].Outputs[?OutputKey==`CognitoUserPoolId`].OutputValue' --output text)", + "set-cognito-user-pool-client-id": "npm config set COGNITO_USER_POOL_CLIENT_ID $(aws cloudformation describe-stacks --stack-name $(npm config get STACK_NAME) --query 'Stacks[].Outputs[?OutputKey==`CognitoUserPoolClientId`].OutputValue' --output text)", + "set-api-url": "npm config set API_URL $(aws cloudformation describe-stacks --stack-name sam-example-api-cognito-auth --query 'Stacks[].Outputs[?OutputKey==`ApiUrl`].OutputValue' --output text)", + "set-api-id": "npm config set API_ID $(aws cloudformation describe-stacks --stack-name sam-example-api-cognito-auth --query 'Stacks[].Outputs[?OutputKey==`ApiId`].OutputValue' --output text)", + "update-user-pool-client": "aws cognito-idp update-user-pool-client --user-pool-id $(npm config get COGNITO_USER_POOL_ID) --client-id $(npm config get COGNITO_USER_POOL_CLIENT_ID) --supported-identity-providers COGNITO --callback-urls \"[\\\"$(npm config get API_URL)\\\"]\" --allowed-o-auth-flows code implicit --allowed-o-auth-scopes openid email --allowed-o-auth-flows-user-pool-client", + "create-user-pool-domain": "aws cognito-idp create-user-pool-domain --domain $(npm config get API_ID) --user-pool-id $(npm config get COGNITO_USER_POOL_ID)", + "open-signup-page": "open \"https://$(npm config get API_ID).auth.us-east-1.amazoncognito.com/signup?response_type=code&client_id=$(npm config get COGNITO_USER_POOL_CLIENT_ID)&redirect_uri=$(npm config get API_URL)\"", + "open-login-page": "open \"https://$(npm config get API_ID).auth.us-east-1.amazoncognito.com/login?response_type=code&client_id=$(npm config get COGNITO_USER_POOL_CLIENT_ID)&redirect_uri=$(npm config get API_URL)\"", + "open-api-ui": "open \"$(npm config get API_URL)\"" + } +} diff --git a/examples/2016-10-31/api_cognito_auth/src/app.js b/examples/2016-10-31/api_cognito_auth/src/app.js new file mode 100644 index 0000000000..b37b1497b2 --- /dev/null +++ b/examples/2016-10-31/api_cognito_auth/src/app.js @@ -0,0 +1,79 @@ +'use strict' +const express = require('express') +const bodyParser = require('body-parser') +const cors = require('cors') +const awsServerlessExpressMiddleware = require('aws-serverless-express/middleware') +const app = express() +const router = express.Router() + +app.set('view engine', 'pug') + +router.use(cors()) +router.use(bodyParser.json()) +router.use(bodyParser.urlencoded({ extended: true })) +router.use(awsServerlessExpressMiddleware.eventContext()) + +router.get('/', (req, res) => { + res.render('index', { + apiId: req.apiGateway ? req.apiGateway.event.requestContext.apiId : null, + apiUrl: req.apiGateway ? `https://${req.apiGateway.event.headers.Host}/${req.apiGateway.event.requestContext.stage}` : 'http://localhost:3000', + cognitoUserPoolClientId: process.env.COGNITO_USER_POOL_CLIENT_ID + }) +}) + +router.get('/users', (req, res) => { + res.json(users) +}) + +router.get('/users/:userId', (req, res) => { + const user = getUser(req.params.userId) + + if (!user) return res.status(404).json({}) + + return res.json(user) +}) + +router.post('/users', (req, res) => { + const user = { + id: ++userIdCounter, + name: req.body.name + } + users.push(user) + res.status(201).json(user) +}) + +router.put('/users/:userId', (req, res) => { + const user = getUser(req.params.userId) + + if (!user) return res.status(404).json({}) + + user.name = req.body.name + res.json(user) +}) + +router.delete('/users/:userId', (req, res) => { + const userIndex = getUserIndex(req.params.userId) + + if (userIndex === -1) return res.status(404).json({}) + + users.splice(userIndex, 1) + res.json(users) +}) + +const getUser = (userId) => users.find(u => u.id === parseInt(userId)) +const getUserIndex = (userId) => users.findIndex(u => u.id === parseInt(userId)) + +// Ephemeral in-memory data store +const users = [{ + id: 1, + name: 'Joe' +}, { + id: 2, + name: 'Jane' +}] +let userIdCounter = users.length + +app.use('/', router) + +// Export your express server so you can import it in the lambda function. +module.exports = app diff --git a/examples/2016-10-31/api_cognito_auth/src/lambda.js b/examples/2016-10-31/api_cognito_auth/src/lambda.js new file mode 100644 index 0000000000..4f82597e70 --- /dev/null +++ b/examples/2016-10-31/api_cognito_auth/src/lambda.js @@ -0,0 +1,7 @@ +'use strict' +const awsServerlessExpress = require('aws-serverless-express') +const app = require('./app') + +const server = awsServerlessExpress.createServer(app) + +exports.handler = (event, context) => awsServerlessExpress.proxy(server, event, context) diff --git a/examples/2016-10-31/api_cognito_auth/src/views/index.pug b/examples/2016-10-31/api_cognito_auth/src/views/index.pug new file mode 100644 index 0000000000..7634e6a9cf --- /dev/null +++ b/examples/2016-10-31/api_cognito_auth/src/views/index.pug @@ -0,0 +1,159 @@ +doctype html +html + head + title My Serverless Application + style. + body { + width: 650px; + margin: auto; + } + h1 { + text-align: center; + } + .response > code { + display: block; + background-color: #eff0f1; + color: #393318; + padding: 5px; + font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; + white-space: pre; + overflow-x: auto; + } + form { + margin-bottom: 1rem; + } + .form-group { + padding-bottom: 1rem; + } + label { + display: block; + } + body + h1 My Serverless Application + p + | Public endpoints: GET /, GET /users, GET /users/{userId} + p + | Authorized endpoints: POST /users, PUT /users/{userId}, DELETE /users/{userId} + + section.form + h2 Invoke API + p Experiment with the `users` resource with the form below. + form + div.form-group + label(for='methodField') Method + select(name='method' id='methodField') + option(value='GET') GET + option(value='POST') POST + option(value='PUT') PUT + option(value='DELETE') DELETE + div.form-group + label(for='idField') user id + input(type='text' name='id' id='idField') + div.form-group + label(for='nameField') name + input(type='text' name='name' id='nameField') + input(type='submit') + + section + h2 Response + p.request + span.request__method GET + span   + spand.request__endpoint /users + section.response + code + + script. + + const apiId = '#{apiId}' + const apiUrl = '#{apiUrl}/' + const cognitoUserPoolClientId = '#{cognitoUserPoolClientId}' + + const queryStringParams = new URLSearchParams(window.location.search) + const cognitoCode = queryStringParams.get('code') + let cognitoIdentityToken = localStorage.getItem('cognitoIdentityToken') + + const form = document.querySelector('form') + form.addEventListener('submit', onApiInvokeFormSubmit) + + fetch('users') + .then(setResponseText) + .catch(setResponseText) + + if (cognitoCode) { + exchangeCodeForAccessToken() + .then(response => response.json()) + .then(json => { + if (json.id_token) { + cognitoIdentityToken = json.id_token + localStorage.setItem('cognitoIdentityToken', cognitoIdentityToken) + } + }) + } + + function convertJsonToFormUrlEncoded(json) { + const oAuthTokenBodyArray = Object.entries(json).map(([key, value]) => { + const encodedKey = encodeURIComponent(key) + const encodedValue = encodeURIComponent(value) + + return `${encodedKey}=${encodedValue}` + }) + + return oAuthTokenBodyArray.join('&') + } + + function exchangeCodeForAccessToken() { + const oauthTokenBodyJson = { + grant_type: 'authorization_code', + client_id: cognitoUserPoolClientId, + code: cognitoCode, + redirect_uri: apiUrl + } + const oauthTokenBody = convertJsonToFormUrlEncoded(oauthTokenBodyJson) + + return fetch(`https://${apiId}.auth.us-east-1.amazoncognito.com/oauth2/token`, + { + method: 'POST', + headers: { + ['Content-Type']: 'application/x-www-form-urlencoded' + }, + body: oauthTokenBody + }) + } + + function onApiInvokeFormSubmit (event) { + event.preventDefault() + const method = document.getElementById('methodField').value + const id = document.getElementById('idField').value + const name = document.getElementById('nameField').value + const endpoint = id ? 'users/' + id : 'users' + const body = ['POST', 'PUT'].includes(method) ? JSON.stringify({ name: name }) : undefined + const headers = { + 'content-type': 'application/json', + 'Authorization': cognitoIdentityToken + } + + document.querySelector('.request__method').innerText = method + document.querySelector('.request__endpoint').innerText = `/${endpoint}` + + return fetch(endpoint, { + method, + headers, + body + }) + .then(setResponseText) + .catch(setResponseText) + } + + function setResponseText(response) { + const contentType = response.headers.get('content-type') + if (contentType.includes('application/json')) { + return response.json().then(json => { + document.querySelector('code').innerText = JSON.stringify(json, null, 4) + }) + } + + return response.text().then(text => { + document.querySelector('code').innerText = text + }) + } \ No newline at end of file diff --git a/examples/2016-10-31/api_cognito_auth/template.yaml b/examples/2016-10-31/api_cognito_auth/template.yaml new file mode 100644 index 0000000000..6c701f33e5 --- /dev/null +++ b/examples/2016-10-31/api_cognito_auth/template.yaml @@ -0,0 +1,153 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: API Gateway + Cognito User Pools Auth + +Parameters: + CognitoUserPoolName: + Type: String + Default: MyUserPool + + CognitoUserPoolClientName: + Type: String + Default: MyUserPoolClient + +Resources: + MyApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + Cors: "'*'" + Auth: + DefaultAuthorizer: MyCognitoAuthorizer + Authorizers: + MyCognitoAuthorizer: + UserPoolArn: !GetAtt MyCognitoUserPool.Arn + + MyFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./src + Handler: lambda.handler + MemorySize: 1024 + Runtime: nodejs8.10 + Environment: + Variables: + COGNITO_USER_POOL_CLIENT_ID: !Ref MyCognitoUserPoolClient + Events: + Root: + Type: Api + Properties: + RestApiId: !Ref MyApi + Path: / + Method: GET + Auth: + Authorizer: NONE + ProxyAny: + Type: Api + Properties: + RestApiId: !Ref MyApi + Path: /{proxy+} + Method: ANY + Auth: + Authorizer: NONE + GetUsers: + Type: Api + Properties: + RestApiId: !Ref MyApi + Path: /users + Method: GET + Auth: + Authorizer: NONE + GetUser: + Type: Api + Properties: + RestApiId: !Ref MyApi + Path: /users/{userId} + Method: GET + Auth: + Authorizer: NONE + CreateUser: + Type: Api + Properties: + RestApiId: !Ref MyApi + Path: /users + Method: POST + DeleteUser: + Type: Api + Properties: + RestApiId: !Ref MyApi + Path: /users/{userId} + Method: DELETE + UpdateUser: + Type: Api + Properties: + RestApiId: !Ref MyApi + Path: /users/{userId} + Method: PUT + + MyCognitoUserPool: + Type: AWS::Cognito::UserPool + Properties: + UserPoolName: !Ref CognitoUserPoolName + LambdaConfig: + PreSignUp: !GetAtt PreSignupLambdaFunction.Arn + Policies: + PasswordPolicy: + MinimumLength: 8 + UsernameAttributes: + - email + Schema: + - AttributeDataType: String + Name: email + Required: false + + MyCognitoUserPoolClient: + Type: AWS::Cognito::UserPoolClient + Properties: + UserPoolId: !Ref MyCognitoUserPool + ClientName: !Ref CognitoUserPoolClientName + GenerateSecret: false + + PreSignupLambdaFunction: + Type: AWS::Serverless::Function + Properties: + InlineCode: | + exports.handler = async (event, context, callback) => { + event.response = { autoConfirmUser: true } + return event + } + Handler: index.handler + MemorySize: 128 + Runtime: nodejs8.10 + Timeout: 3 + + LambdaCognitoUserPoolExecutionPermission: + Type: AWS::Lambda::Permission + Properties: + Action: lambda:InvokeFunction + FunctionName: !GetAtt PreSignupLambdaFunction.Arn + Principal: cognito-idp.amazonaws.com + SourceArn: !Sub 'arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/${MyCognitoUserPool}' + # TODO: Add a CognitoUserPool Event Source to SAM to create this permission for you. + # Events: + # CognitoUserPoolPreSignup: + # Type: CognitoUserPool + # Properties: + # UserPool: !Ref MyCognitoUserPool + +Outputs: + ApiId: + Description: "API ID" + Value: !Ref MyApi + + ApiUrl: + Description: "API endpoint URL for Prod environment" + Value: !Sub 'https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/' + + CognitoUserPoolId: + Description: "Cognito User Pool Id" + Value: !Ref MyCognitoUserPool + + CognitoUserPoolClientId: + Description: "Cognito User Pool Client Id" + Value: !Ref MyCognitoUserPoolClient \ No newline at end of file diff --git a/examples/2016-10-31/api_lambda_request_auth/README.md b/examples/2016-10-31/api_lambda_request_auth/README.md new file mode 100644 index 0000000000..61b36b5ce1 --- /dev/null +++ b/examples/2016-10-31/api_lambda_request_auth/README.md @@ -0,0 +1,40 @@ +# API Gateway + Lambda REQUEST Authorizer Example + +This example shows you how to create an API Gateway API with a Lambda REQUEST Authorizer using SAM. + +The Authorizer Lambda Function in this example simply accepts an `auth` query string with valid values `"allow"` and `"deny"`. See [Introducing custom authorizers in Amazon API Gateway](https://aws.amazon.com/blogs/compute/introducing-custom-authorizers-in-amazon-api-gateway/) for a more detailed example using JWT. + +## Running the example + +Deploy the example into your account: + +```bash +# Replace YOUR_S3_ARTIFACTS_BUCKET with the name of a bucket which already exists in your account +aws cloudformation package --template-file template.yaml --output-template-file template.packaged.yaml --s3-bucket YOUR_S3_ARTIFACTS_BUCKET + +aws cloudformation deploy --template-file ./template.packaged.yaml --stack-name sam-example-api-lambda-request-auth --capabilities CAPABILITY_IAM +``` + +Invoke the API's root endpoint `/` without an `auth` query string to see the API respond with a 200. In the SAM template, we explicitly state `Authorizer: NONE` to make this a public/open endpoint (the Authorizer Lambda Function is not invoked). + +```bash +api_url=$(aws cloudformation describe-stacks --stack-name sam-example-api-lambda-request-auth --query 'Stacks[].Outputs[?OutputKey==`ApiURL`].OutputValue' --output text) +curl $api_url +``` + +Invoke the API's `/users` endpoint without an `auth` query string to see a `401 {"message":"Unauthorized"}` response. Since we didn't specify an `auth` query string, API Gateway didn't even attempt to invoke our Authorizer Lambda Function. + +```bash +curl $api_url"users" +``` + +Invoke the API's `/users` endpoint with an `auth=allow` query string to see a `200` response. Try changing the query string value to `"deny"` to see a `403` response and `"gibberish"` to see a `500` response. + +```bash +curl $api_url"users?auth=allow" +``` + +## Additional resources + +- https://aws.amazon.com/blogs/compute/introducing-custom-authorizers-in-amazon-api-gateway/ +- https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html diff --git a/examples/2016-10-31/api_lambda_request_auth/src/authorizer.js b/examples/2016-10-31/api_lambda_request_auth/src/authorizer.js new file mode 100644 index 0000000000..8661771921 --- /dev/null +++ b/examples/2016-10-31/api_lambda_request_auth/src/authorizer.js @@ -0,0 +1,58 @@ +/* +** Adapted from https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html#api-gateway-lambda-authorizer-request-lambda-function-create +** A simple REQUEST authorizer example to demonstrate how to use request parameters to allow or deny a request. +** In this example, a request is authorized if the client provided an `auth=allow` in the query string. +*/ +exports.handler = async function (event) { + const token = event.queryStringParameters.auth.toLowerCase() + const methodArn = event.methodArn + + /* + ** headers, queryStringParameters, stageVariables, requestContext and more are available on the `event` object + ** event.headers: A map/object containing HTTP request headers + ** event.queryStringParameters: A map/object containing query strings supplied in the URL + ** event.stageVariables: A map/object containing variables defined on the API Gateway Stage + ** event.requestContext: A map/object containing additional request context + */ + + switch (token) { + case 'allow': + return generateAuthResponse('user', 'Allow', methodArn) + case 'deny': + return generateAuthResponse('user', 'Deny', methodArn) + default: + return Promise.reject('Error: Invalid token') // Returns 500 Internal Server Error + } +} + +function generateAuthResponse (principalId, effect, methodArn) { + // If you need to provide additional information to your integration + // endpoint (e.g. your Lambda Function), you can add it to `context` + const context = { + 'stringKey': 'stringval', + 'numberKey': 123, + 'booleanKey': true + } + const policyDocument = generatePolicyDocument(effect, methodArn) + + return { + principalId, + context, + policyDocument + } +} + +function generatePolicyDocument (effect, methodArn) { + if (!effect || !methodArn) return null + + const policyDocument = { + Version: '2012-10-17', + Statement: [{ + Action: 'execute-api:Invoke', + Effect: effect, + Resource: methodArn + }] + } + + return policyDocument +} diff --git a/examples/2016-10-31/api_lambda_request_auth/src/index.js b/examples/2016-10-31/api_lambda_request_auth/src/index.js new file mode 100644 index 0000000000..472369c6c6 --- /dev/null +++ b/examples/2016-10-31/api_lambda_request_auth/src/index.js @@ -0,0 +1,7 @@ +exports.handler = async (event) => { + return { + statusCode: 200, + body: JSON.stringify(event), + headers: {} + } +} diff --git a/examples/2016-10-31/api_lambda_request_auth/template.yaml b/examples/2016-10-31/api_lambda_request_auth/template.yaml new file mode 100644 index 0000000000..0bf1da16a0 --- /dev/null +++ b/examples/2016-10-31/api_lambda_request_auth/template.yaml @@ -0,0 +1,60 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: API Gateway with Lambda Token Authorizer +Resources: + MyApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + Auth: + DefaultAuthorizer: MyLambdaRequestAuthorizer + Authorizers: + MyLambdaRequestAuthorizer: + FunctionPayloadType: REQUEST + FunctionArn: !GetAtt MyAuthFunction.Arn + # FunctionInvokeRole: !Ref MyRole + Identity: + QueryStrings: + - auth + # NOTE: Additional options: + # Headers: + # - Authorization + # StageVariables: + # - AUTHORIZATION + # Context: + # - authorization + # ReauthorizeEvery: 100 # seconds + + MyFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./src + Handler: index.handler + Runtime: nodejs8.10 + Events: + GetRoot: + Type: Api + Properties: + RestApiId: !Ref MyApi + Path: / + Method: get + Auth: + Authorizer: NONE + GetUsers: + Type: Api + Properties: + RestApiId: !Ref MyApi + Path: /users + Method: get + + MyAuthFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./src + Handler: authorizer.handler + Runtime: nodejs8.10 + +Outputs: + ApiURL: + Description: "API URL" + Value: !Sub 'https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/' \ No newline at end of file diff --git a/examples/2016-10-31/api_lambda_token_auth/README.md b/examples/2016-10-31/api_lambda_token_auth/README.md new file mode 100644 index 0000000000..c693a2a6b5 --- /dev/null +++ b/examples/2016-10-31/api_lambda_token_auth/README.md @@ -0,0 +1,39 @@ +# API Gateway + Lambda TOKEN Authorizer Example + +This example shows you how to create an API Gateway API with a Lambda TOKEN Authorizer using SAM. + +The Authorizer Lambda Function in this example simply accepts an `Authorization` header with valid values `"allow"` and `"deny"`. See [Introducing custom authorizers in Amazon API Gateway](https://aws.amazon.com/blogs/compute/introducing-custom-authorizers-in-amazon-api-gateway/) for a more detailed example using JWT. + +## Running the example + +Deploy the example into your account: + +```bash +# Replace YOUR_S3_ARTIFACTS_BUCKET with the name of a bucket which already exists in your account +aws cloudformation package --template-file template.yaml --output-template-file template.packaged.yaml --s3-bucket YOUR_S3_ARTIFACTS_BUCKET + +aws cloudformation deploy --template-file ./template.packaged.yaml --stack-name sam-example-api-lambda-token-auth --capabilities CAPABILITY_IAM +``` + +Invoke the API's root endpoint `/` without an `Authorization` header to see the API respond with a 200. In the SAM template, we explicitly state `Authorizer: NONE` to make this a public/open endpoint (the Authorizer Lambda Function is not invoked). + +```bash +curl "$(aws cloudformation describe-stacks --stack-name sam-example-api-lambda-token-auth --query 'Stacks[].Outputs[?OutputKey==`ApiURL`].OutputValue' --output text)" +``` + +Invoke the API's `/users` endpoint without an `Authorization` header to see a `401 {"message":"Unauthorized"}` response. Since we didn't specify an `Authorization` header, API Gateway didn't even attempt to invoke our Authorizer Lambda Function. If you specify a `ValidationExpression` on the Authorizer, it will also not invoke the Authorizer Lambda Function if the header was provided but does not match the pattern. + +```bash +curl "$(aws cloudformation describe-stacks --stack-name sam-example-api-lambda-token-auth --query 'Stacks[].Outputs[?OutputKey==`ApiURL`].OutputValue' --output text)users" +``` + +Invoke the API's `/users` endpoint with an `Authorization: allow` header to see a `200` response. Try changing the header value to `"deny"` to see a `403` response and `"gibberish"` to see a `500` response. + +```bash +curl "$(aws cloudformation describe-stacks --stack-name sam-example-api-lambda-token-auth --query 'Stacks[].Outputs[?OutputKey==`ApiURL`].OutputValue' --output text)users" -H 'Authorization: allow' +``` + +## Additional resources + +- https://aws.amazon.com/blogs/compute/introducing-custom-authorizers-in-amazon-api-gateway/ +- https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html diff --git a/examples/2016-10-31/api_lambda_token_auth/src/authorizer.js b/examples/2016-10-31/api_lambda_token_auth/src/authorizer.js new file mode 100644 index 0000000000..b5bceb1be8 --- /dev/null +++ b/examples/2016-10-31/api_lambda_token_auth/src/authorizer.js @@ -0,0 +1,55 @@ +/* +** Adapted from https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html#api-gateway-lambda-authorizer-token-lambda-function-create +** A simple TOKEN authorizer example to demonstrate how to use an authorization token +** to allow or deny a request.In this example, the caller named 'user'is allowed to invoke +** a request if the client - supplied token value is 'allow'.The caller is not allowed to +** invoke the request if the token value is 'deny'.If the token value is 'Unauthorized', +** the function returns the 'Unauthorized' error with an HTTP status code of 401. For any +** other token value, the authorizer returns 'Error: Invalid token' as a 500 error. +*/ + +exports.handler = async function (event) { + const token = event.authorizationToken.toLowerCase() + const methodArn = event.methodArn + + switch (token) { + case 'allow': + return generateAuthResponse('user', 'Allow', methodArn) + case 'deny': + return generateAuthResponse('user', 'Deny', methodArn) + default: + return Promise.reject('Error: Invalid token') // Returns 500 Internal Server Error + } +} + +function generateAuthResponse (principalId, effect, methodArn) { + // If you need to provide additional information to your integration + // endpoint (e.g. your Lambda Function), you can add it to `context` + const context = { + 'stringKey': 'stringval', + 'numberKey': 123, + 'booleanKey': true + } + const policyDocument = generatePolicyDocument(effect, methodArn) + + return { + principalId, + context, + policyDocument + } +} + +function generatePolicyDocument (effect, methodArn) { + if (!effect || !methodArn) return null + + const policyDocument = { + Version: '2012-10-17', + Statement: [{ + Action: 'execute-api:Invoke', + Effect: effect, + Resource: methodArn + }] + } + + return policyDocument +} diff --git a/examples/2016-10-31/api_lambda_token_auth/src/index.js b/examples/2016-10-31/api_lambda_token_auth/src/index.js new file mode 100644 index 0000000000..d33f8abad6 --- /dev/null +++ b/examples/2016-10-31/api_lambda_token_auth/src/index.js @@ -0,0 +1,7 @@ +exports.handler = async (event) => { + return { + statusCode: 200, + body: JSON.stringify(event), + headers: {} + } +} \ No newline at end of file diff --git a/examples/2016-10-31/api_lambda_token_auth/template.yaml b/examples/2016-10-31/api_lambda_token_auth/template.yaml new file mode 100644 index 0000000000..50d18a3195 --- /dev/null +++ b/examples/2016-10-31/api_lambda_token_auth/template.yaml @@ -0,0 +1,53 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: API Gateway with Lambda Token Authorizer +Resources: + MyApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + Auth: + DefaultAuthorizer: MyLambdaTokenAuthorizer + Authorizers: + MyLambdaTokenAuthorizer: + FunctionArn: !GetAtt MyAuthFunction.Arn + # NOTE: Additional options: + # FunctionInvokeRole: !Ref MyRole + # Identity: + # Header: Auth + # ValidationExpression: Bearer.* + # ReauthorizeEvery: 30 # seconds + + MyFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./src + Handler: index.handler + Runtime: nodejs8.10 + Events: + GetRoot: + Type: Api + Properties: + RestApiId: !Ref MyApi + Path: / + Method: get + Auth: + Authorizer: NONE + GetUsers: + Type: Api + Properties: + RestApiId: !Ref MyApi + Path: /users + Method: get + + MyAuthFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./src + Handler: authorizer.handler + Runtime: nodejs8.10 + +Outputs: + ApiURL: + Description: "API URL" + Value: !Sub 'https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/' \ No newline at end of file diff --git a/examples/package.json b/examples/package.json new file mode 100644 index 0000000000..dc8bd46939 --- /dev/null +++ b/examples/package.json @@ -0,0 +1,19 @@ +{ + "name": "examples", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "Apache 2.0", + "devDependencies": { + "eslint": "^5.3.0", + "eslint-config-standard": "^11.0.0", + "eslint-plugin-import": "^2.14.0", + "eslint-plugin-node": "^7.0.1", + "eslint-plugin-promise": "^3.8.0", + "eslint-plugin-standard": "^3.1.0" + } +} diff --git a/samtranslator/model/api/api_generator.py b/samtranslator/model/api/api_generator.py index 99cc72d47d..5625cab747 100644 --- a/samtranslator/model/api/api_generator.py +++ b/samtranslator/model/api/api_generator.py @@ -3,21 +3,27 @@ from samtranslator.model.intrinsics import ref from samtranslator.model.apigateway import (ApiGatewayDeployment, ApiGatewayRestApi, - ApiGatewayStage) + ApiGatewayStage, ApiGatewayAuthorizer) from samtranslator.model.exceptions import InvalidResourceException from samtranslator.model.s3_utils.uri_parser import parse_s3_uri from samtranslator.region_configuration import RegionConfiguration from samtranslator.swagger.swagger import SwaggerEditor -from samtranslator.model.intrinsics import is_instrinsic +from samtranslator.model.intrinsics import is_instrinsic, fnSub +from samtranslator.model.lambda_ import LambdaPermission +from samtranslator.translator.arn_generator import ArnGenerator _CORS_WILDCARD = "'*'" CorsProperties = namedtuple("_CorsProperties", ["AllowMethods", "AllowHeaders", "AllowOrigin", "MaxAge"]) # Default the Cors Properties to '*' wildcard. Other properties are actually Optional CorsProperties.__new__.__defaults__ = (None, None, _CORS_WILDCARD, None) +AuthProperties = namedtuple("_AuthProperties", ["Authorizers", "DefaultAuthorizer"]) +AuthProperties.__new__.__defaults__ = (None, None) + + class ApiGenerator(object): - def __init__(self, logical_id, cache_cluster_enabled, cache_cluster_size, variables, depends_on, definition_body, definition_uri, name, stage_name, endpoint_configuration=None, method_settings=None, binary_media=None, cors=None): + def __init__(self, logical_id, cache_cluster_enabled, cache_cluster_size, variables, depends_on, definition_body, definition_uri, name, stage_name, endpoint_configuration=None, method_settings=None, binary_media=None, cors=None, auth=None): """Constructs an API Generator class that generates API Gateway resources :param logical_id: Logical id of the SAM API Resource @@ -43,6 +49,7 @@ def __init__(self, logical_id, cache_cluster_enabled, cache_cluster_size, variab self.method_settings = method_settings self.binary_media = binary_media self.cors = cors + self.auth = auth def _construct_rest_api(self): """Constructs and returns the ApiGateway RestApi. @@ -61,12 +68,12 @@ def _construct_rest_api(self): # to Regional which is the only supported config. self._set_endpoint_configuration(rest_api, "REGIONAL") - if self.definition_uri and self.definition_body: raise InvalidResourceException(self.logical_id, "Specify either 'DefinitionUri' or 'DefinitionBody' property and not both") self._add_cors() + self._add_auth() if self.definition_uri: rest_api.BodyS3Location = self._construct_body_s3_dict() @@ -164,8 +171,9 @@ def to_cloudformation(self): swagger = rest_api.BodyS3Location stage = self._construct_stage(deployment, swagger) + permissions = self._construct_authorizer_lambda_permission() - return rest_api, deployment, stage + return rest_api, deployment, stage, permissions def _add_cors(self): """ @@ -208,6 +216,115 @@ def _add_cors(self): # Assign the Swagger back to template self.definition_body = editor.swagger + def _add_auth(self): + """ + Add Auth configuration to the Swagger file, if necessary + """ + + if not self.auth: + return + + if self.auth and not self.definition_body: + raise InvalidResourceException(self.logical_id, + "Auth works only with inline Swagger specified in " + "'DefinitionBody' property") + + # Make sure keys in the dict are recognized + if not all(key in AuthProperties._fields for key in self.auth.keys()): + raise InvalidResourceException( + self.logical_id, "Invalid value for 'Auth' property") + + if not SwaggerEditor.is_valid(self.definition_body): + raise InvalidResourceException(self.logical_id, "Unable to add Auth configuration because " + "'DefinitionBody' does not contain a valid Swagger") + swagger_editor = SwaggerEditor(self.definition_body) + auth_properties = AuthProperties(**self.auth) + authorizers = self._get_authorizers(auth_properties.Authorizers) + + if authorizers: + swagger_editor.add_authorizers(authorizers) + self._set_default_authorizer(swagger_editor, authorizers, auth_properties.DefaultAuthorizer) + + # Assign the Swagger back to template + self.definition_body = swagger_editor.swagger + + def _get_authorizers(self, authorizers_config): + if not authorizers_config: + return None + + if not isinstance(authorizers_config, dict): + raise InvalidResourceException(self.logical_id, + "Authorizers must be a dictionary") + authorizers = {} + + for authorizer_name, authorizer in authorizers_config.items(): + authorizers[authorizer_name] = ApiGatewayAuthorizer( + api_logical_id=self.logical_id, + name=authorizer_name, + user_pool_arn=authorizer.get('UserPoolArn'), + function_arn=authorizer.get('FunctionArn'), + identity=authorizer.get('Identity'), + function_payload_type=authorizer.get('FunctionPayloadType'), + function_invoke_role=authorizer.get('FunctionInvokeRole') + ) + + return authorizers + + def _get_permission(self, authorizer_name, authorizer_lambda_function_arn): + """Constructs and returns the Lambda Permission resource allowing the Authorizer to invoke the function. + + :returns: the permission resource + :rtype: model.lambda_.LambdaPermission + """ + rest_api = ApiGatewayRestApi(self.logical_id, depends_on=self.depends_on) + api_id = rest_api.get_runtime_attr('rest_api_id') + + partition = ArnGenerator.get_partition_name() + resource = '${__ApiId__}/authorizers/*' + source_arn = fnSub(ArnGenerator.generate_arn(partition=partition, service='execute-api', resource=resource), + {"__ApiId__": api_id}) + + lambda_permission = LambdaPermission(self.logical_id + authorizer_name + 'AuthorizerPermission') + lambda_permission.Action = 'lambda:invokeFunction' + lambda_permission.FunctionName = authorizer_lambda_function_arn + lambda_permission.Principal = 'apigateway.amazonaws.com' + lambda_permission.SourceArn = source_arn + + return lambda_permission + + def _construct_authorizer_lambda_permission(self): + if not self.auth: + return [] + + auth_properties = AuthProperties(**self.auth) + authorizers = self._get_authorizers(auth_properties.Authorizers) + + if not authorizers: + return [] + + permissions = [] + + for authorizer_name, authorizer in authorizers.items(): + # Construct permissions for Lambda Authorizers only + if not authorizer.function_arn: + continue + + permission = self._get_permission(authorizer_name, authorizer.function_arn) + permissions.append(permission) + + return permissions + + def _set_default_authorizer(self, swagger_editor, authorizers, default_authorizer): + if not default_authorizer: + return + + if not authorizers.get(default_authorizer): + raise InvalidResourceException(self.logical_id, "Unable to set DefaultAuthorizer because '" + + default_authorizer + "' was not defined in 'Authorizers'") + + for path in swagger_editor.iter_on_path(): + swagger_editor.set_path_default_authorizer(path, default_authorizer, authorizers=authorizers) + def _set_endpoint_configuration(self, rest_api, value): """ Sets endpoint configuration property of AWS::ApiGateway::RestApi resource diff --git a/samtranslator/model/apigateway.py b/samtranslator/model/apigateway.py index 8c2ff8b30b..3d2e3dcb59 100644 --- a/samtranslator/model/apigateway.py +++ b/samtranslator/model/apigateway.py @@ -1,7 +1,9 @@ from samtranslator.model import PropertyType, Resource +from samtranslator.model.exceptions import InvalidResourceException from samtranslator.model.types import is_type, one_of, is_str -from samtranslator.model.intrinsics import ref +from samtranslator.model.intrinsics import ref, fnSub from samtranslator.translator import logical_id_generator +from samtranslator.translator.arn_generator import ArnGenerator class ApiGatewayRestApi(Resource): @@ -85,3 +87,170 @@ def make_auto_deployable(self, stage, swagger=None): hash = generator.get_hash(length=40) # Get the full hash self.Description = "RestApi deployment id: {}".format(hash) stage.update_deployment_ref(self.logical_id) + + +class ApiGatewayAuthorizer(object): + _VALID_FUNCTION_PAYLOAD_TYPES = [None, 'TOKEN', 'REQUEST'] + + def __init__(self, api_logical_id=None, name=None, user_pool_arn=None, function_arn=None, identity=None, + function_payload_type=None, function_invoke_role=None): + if function_payload_type not in ApiGatewayAuthorizer._VALID_FUNCTION_PAYLOAD_TYPES: + raise InvalidResourceException(api_logical_id, name + " Authorizer has invalid " + "'FunctionPayloadType': " + function_payload_type) + + if function_payload_type == 'REQUEST' and self._is_missing_identity_source(identity): + raise InvalidResourceException(api_logical_id, name + " Authorizer must specify Identity with at least one " + "of Headers, QueryStrings, StageVariables, or Context.") + + self.api_logical_id = api_logical_id + self.name = name + self.user_pool_arn = user_pool_arn + self.function_arn = function_arn + self.identity = identity + self.function_payload_type = function_payload_type + self.function_invoke_role = function_invoke_role + + def _is_missing_identity_source(self, identity): + if not identity: + return True + + headers = identity.get('Headers') + query_strings = identity.get('QueryStrings') + stage_variables = identity.get('StageVariables') + context = identity.get('Context') + + if not headers and not query_strings and not stage_variables and not context: + return True + + return False + + def generate_swagger(self): + authorizer_type = self._get_type() + APIGATEWAY_AUTHORIZER_KEY = 'x-amazon-apigateway-authorizer' + swagger = { + "type": "apiKey", + "name": self._get_swagger_header_name(), + "in": "header", + "x-amazon-apigateway-authtype": self._get_swagger_authtype(), + "x-amazon-apigateway-authorizer": { + "type": self._get_swagger_authorizer_type() + } + } + + if authorizer_type == 'COGNITO_USER_POOLS': + swagger[APIGATEWAY_AUTHORIZER_KEY]['providerARNs'] = self._get_user_pool_arn_array() + + elif authorizer_type == 'LAMBDA': + partition = ArnGenerator.get_partition_name() + resource = 'lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations' + authorizer_uri = fnSub(ArnGenerator.generate_arn(partition=partition, service='apigateway', + resource=resource, include_account_id=False), + {'__FunctionArn__': self.function_arn}) + + swagger[APIGATEWAY_AUTHORIZER_KEY]['authorizerUri'] = authorizer_uri + reauthorize_every = self._get_reauthorize_every() + function_invoke_role = self._get_function_invoke_role() + + if reauthorize_every is not None: + swagger[APIGATEWAY_AUTHORIZER_KEY]['authorizerResultTtlInSeconds'] = reauthorize_every + + if function_invoke_role: + swagger[APIGATEWAY_AUTHORIZER_KEY]['authorizerCredentials'] = function_invoke_role + + if self._get_function_payload_type() == 'REQUEST': + swagger[APIGATEWAY_AUTHORIZER_KEY]['identitySource'] = self._get_identity_source() + + # Authorizer Validation Expression is only allowed on COGNITO_USER_POOLS and LAMBDA_TOKEN + is_lambda_token_authorizer = authorizer_type == 'LAMBDA' and self._get_function_payload_type() == 'TOKEN' + + if authorizer_type == 'COGNITO_USER_POOLS' or is_lambda_token_authorizer: + identity_validation_expression = self._get_identity_validation_expression() + + if identity_validation_expression: + swagger[APIGATEWAY_AUTHORIZER_KEY]['identityValidationExpression'] = identity_validation_expression + + return swagger + + def _get_identity_validation_expression(self): + return self.identity and self.identity.get('ValidationExpression') + + def _get_identity_source(self): + identity_source_headers = [] + identity_source_query_strings = [] + identity_source_stage_variables = [] + identity_source_context = [] + + if self.identity.get('Headers'): + identity_source_headers = list(map(lambda h: 'method.request.header.' + h, self.identity.get('Headers'))) + + if self.identity.get('QueryStrings'): + identity_source_query_strings = list(map(lambda qs: 'method.request.querystring.' + qs, self.identity.get('QueryStrings'))) + + if self.identity.get('StageVariables'): + identity_source_stage_variables = list(map(lambda sv: 'stageVariables.' + sv, self.identity.get('StageVariables'))) + + if self.identity.get('Context'): + identity_source_context = list(map(lambda c: 'context.' + c, self.identity.get('Context'))) + + identity_source_array = identity_source_headers + identity_source_query_strings + identity_source_stage_variables + identity_source_context + identity_source = ', '.join(identity_source_array) + + return identity_source + + def _get_user_pool_arn_array(self): + return self.user_pool_arn if isinstance(self.user_pool_arn, list) else [self.user_pool_arn] + + def _get_swagger_header_name(self): + authorizer_type = self._get_type() + payload_type = self._get_function_payload_type() + + if authorizer_type == 'LAMBDA' and payload_type == 'REQUEST': + return 'Unused' + + return self._get_identity_header() + + def _get_type(self): + if self.user_pool_arn: + return 'COGNITO_USER_POOLS' + + return 'LAMBDA' + + def _get_identity_header(self): + if not self.identity or not self.identity.get('Header'): + return 'Authorization' + + return self.identity.get('Header') + + def _get_reauthorize_every(self): + if not self.identity: + return None + + return self.identity.get('ReauthorizeEvery') + + def _get_function_invoke_role(self): + if not self.function_invoke_role or self.function_invoke_role == 'NONE': + return None + + return self.function_invoke_role + + def _get_swagger_authtype(self): + authorizer_type = self._get_type() + + return 'cognito_user_pools' if authorizer_type == 'COGNITO_USER_POOLS' else 'custom' + + def _get_function_payload_type(self): + return 'TOKEN' if not self.function_payload_type else self.function_payload_type + + def _get_swagger_authorizer_type(self): + authorizer_type = self._get_type() + + if authorizer_type == 'COGNITO_USER_POOLS': + return 'cognito_user_pools' + + payload_type = self._get_function_payload_type() + + if payload_type == 'REQUEST': + return 'request' + + if payload_type == 'TOKEN': + return 'token' diff --git a/samtranslator/model/eventsources/push.py b/samtranslator/model/eventsources/push.py index 096aa3e696..007096d3bc 100644 --- a/samtranslator/model/eventsources/push.py +++ b/samtranslator/model/eventsources/push.py @@ -335,7 +335,8 @@ class Api(PushEventSource): 'Method': PropertyType(True, is_str()), # Api Event sources must "always" be paired with a Serverless::Api - 'RestApiId': PropertyType(True, is_str()) + 'RestApiId': PropertyType(True, is_str()), + 'Auth': PropertyType(False, is_type(dict)) } def resources_to_link(self, resources): @@ -471,6 +472,37 @@ def _add_swagger_integration(self, api, function): method=self.Method, path=self.Path)) editor.add_lambda_integration(self.Path, self.Method, uri) + + if self.Auth: + method_authorizer = self.Auth.get('Authorizer') + + if method_authorizer: + api_auth = api.get('Auth') + api_authorizers = api_auth and api_auth.get('Authorizers') + + if not api_authorizers: + raise InvalidEventException( + self.relative_id, + 'Unable to set Authorizer [{authorizer}] on API method [{method}] for path [{path}] because ' + 'the related API does not define any Authorizers.'.format( + authorizer=method_authorizer, method=self.Method, path=self.Path)) + + if method_authorizer != 'NONE' and not api_authorizers.get(method_authorizer): + raise InvalidEventException( + self.relative_id, + 'Unable to set Authorizer [{authorizer}] on API method [{method}] for path [{path}] because it ' + 'wasn\'t defined in the API\'s Authorizers.'.format( + authorizer=method_authorizer, method=self.Method, path=self.Path)) + + if method_authorizer == 'NONE' and not api_auth.get('DefaultAuthorizer'): + raise InvalidEventException( + self.relative_id, + 'Unable to set Authorizer on API method [{method}] for path [{path}] because \'NONE\' ' + 'is only a valid value when a DefaultAuthorizer on the API is specified.'.format( + method=self.Method, path=self.Path)) + + editor.add_auth_to_method(api=api, path=self.Path, method_name=self.Method, auth=self.Auth) + api["DefinitionBody"] = editor.swagger diff --git a/samtranslator/model/sam_resources.py b/samtranslator/model/sam_resources.py index c9f4c2c3d9..4e8c537a3d 100644 --- a/samtranslator/model/sam_resources.py +++ b/samtranslator/model/sam_resources.py @@ -315,12 +315,12 @@ def _generate_event_resources(self, lambda_function, execution_role, event_resou return resources def _construct_code_dict(self): - if self.CodeUri: - return self._construct_code_dict_code_uri() - elif self.InlineCode: + if self.InlineCode: return { "ZipFile": self.InlineCode } + elif self.CodeUri: + return self._construct_code_dict_code_uri() else: raise InvalidResourceException(self.logical_id, "Either 'InlineCode' or 'CodeUri' must be set") @@ -477,7 +477,8 @@ class SamApi(SamResourceMacro): 'EndpointConfiguration': PropertyType(False, is_str()), 'MethodSettings': PropertyType(False, is_type(list)), 'BinaryMediaTypes': PropertyType(False, is_type(list)), - 'Cors': PropertyType(False, one_of(is_str(), is_type(dict))) + 'Cors': PropertyType(False, one_of(is_str(), is_type(dict))), + 'Auth': PropertyType(False, is_type(dict)) } referable_properties = { @@ -507,11 +508,13 @@ def to_cloudformation(self, **kwargs): endpoint_configuration=self.EndpointConfiguration, method_settings=self.MethodSettings, binary_media=self.BinaryMediaTypes, - cors=self.Cors) + cors=self.Cors, + auth=self.Auth) - rest_api, deployment, stage = api_generator.to_cloudformation() + rest_api, deployment, stage, permissions = api_generator.to_cloudformation() resources.extend([rest_api, deployment, stage]) + resources.extend(permissions) return resources diff --git a/samtranslator/plugins/api/implicit_api_plugin.py b/samtranslator/plugins/api/implicit_api_plugin.py index 807bc867bc..c4a1dc528c 100644 --- a/samtranslator/plugins/api/implicit_api_plugin.py +++ b/samtranslator/plugins/api/implicit_api_plugin.py @@ -87,8 +87,8 @@ def _get_api_events(self, function): :param SamResource function: Function Resource object :return dict: Dictionary of API events along with any other configuration passed to it. Example: { - FooEvent: {Path: "/foo", Method: "post", RestApiId: blah, MethodSettings: {}, Cors: {}}, - BarEvent: {Path: "/bar", Method: "any", MethodSettings: {}, Cors: {}}" + FooEvent: {Path: "/foo", Method: "post", RestApiId: blah, MethodSettings: {}, Cors: {}, Auth: {}}, + BarEvent: {Path: "/bar", Method: "any", MethodSettings: {}, Cors: {}, Auth: {}}" } """ diff --git a/samtranslator/swagger/swagger.py b/samtranslator/swagger/swagger.py index bce74c55a9..c90f57a7a7 100644 --- a/samtranslator/swagger/swagger.py +++ b/samtranslator/swagger/swagger.py @@ -30,6 +30,7 @@ def __init__(self, doc): self._doc = copy.deepcopy(doc) self.paths = self._doc["paths"] + self.security_definitions = self._doc.get("securityDefinitions", {}) def has_path(self, path, method=None): """ @@ -285,6 +286,108 @@ def _make_cors_allowed_methods_for_path(self, path): # Allow-Methods is comma separated string return ','.join(allow_methods) + def add_authorizers(self, authorizers): + """ + Add Authorizer definitions to the securityDefinitions part of Swagger. + + :param list authorizers: List of Authorizer configurations which get translated to securityDefinitions. + """ + self.security_definitions = self.security_definitions or {} + + for authorizerName, authorizer in authorizers.items(): + self.security_definitions[authorizerName] = authorizer.generate_swagger() + + def set_path_default_authorizer(self, path, default_authorizer, authorizers): + """ + Sets the DefaultAuthorizer for each method on this path. The DefaultAuthorizer won't be set if an Authorizer + was defined at the Function/Path/Method level + + :param string path: Path name + :param string default_authorizer: Name of the authorizer to use as the default. Must be a key in the authorizers param. + :param list authorizers: List of Authorizer configurations defined on the related Api. + """ + for method_name, method in self.paths[path].items(): + self.set_method_authorizer(path, method_name, default_authorizer, authorizers, default_authorizer=default_authorizer, is_default=True) + + def add_auth_to_method(self, path, method_name, auth, api): + """ + Adds auth settings for this path/method. Auth settings currently consist solely of Authorizers + but this method will eventually include setting other auth settings such as API Key, + Resource Policy, etc. + + :param string path: Path name + :param string method_name: Method name + :param dict auth: Auth configuration such as Authorizers, ApiKey, ResourcePolicy (only Authorizers supported + currently) + :param dict api: Reference to the related Api's properties as defined in the template. + """ + method_authorizer = auth and auth.get('Authorizer') + if method_authorizer: + api_auth = api.get('Auth') + api_authorizers = api_auth and api_auth.get('Authorizers') + default_authorizer = api_auth and api_auth.get('DefaultAuthorizer') + + self.set_method_authorizer(path, method_name, method_authorizer, api_authorizers, default_authorizer) + + def set_method_authorizer(self, path, method_name, authorizer_name, authorizers, default_authorizer, + is_default=False): + normalized_method_name = self._normalize_method_name(method_name) + existing_security = self.paths[path][normalized_method_name].get('security', []) # TEST: [{'sigv4': []}, {'api_key': []}]) + authorizer_names = set(authorizers.keys()) + existing_non_authorizer_security = [] + existing_authorizer_security = [] + + # Split existing security into Authorizers and everything else + # (e.g. sigv4 (AWS_IAM), api_key (API Key/Usage Plans), NONE (marker for ignoring default)) + # We want to ensure only a single Authorizer security entry exists while keeping everything else + for security in existing_security: + if authorizer_names.isdisjoint(security.keys()): + existing_non_authorizer_security.append(security) + else: + existing_authorizer_security.append(security) + + none_idx = -1 + authorizer_security = [] + + # If this is the Api-level DefaultAuthorizer we need to check for an + # existing Authorizer before applying the default. It would be simpler + # if instead we applied the DefaultAuthorizer first and then simply + # overwrote it if necessary, however, the order in which things get + # applied (Function Api Events first; then Api Resource) complicates it. + if is_default: + # Check if Function/Path/Method specified 'NONE' for Authorizer + for idx, security in enumerate(existing_non_authorizer_security): + is_none = any(key == 'NONE' for key in security.keys()) + + if is_none: + none_idx = idx + break + + # NONE was found; remove it and don't add the DefaultAuthorizer + if none_idx > -1: + del existing_non_authorizer_security[none_idx] + + # Existing Authorizer found (defined at Function/Path/Method); use that instead of default + elif existing_authorizer_security: + authorizer_security = existing_authorizer_security + + # No existing Authorizer found; use default + else: + security_dict = {} + security_dict[authorizer_name] = [] + authorizer_security = [security_dict] + + # This is a Function/Path/Method level Authorizer; simply set it + else: + security_dict = {} + security_dict[authorizer_name] = [] + authorizer_security = [security_dict] + + security = existing_non_authorizer_security + authorizer_security + + if security: + self.paths[path][normalized_method_name]['security'] = security + @property def swagger(self): """ @@ -295,6 +398,10 @@ def swagger(self): # Make sure any changes to the paths are reflected back in output self._doc["paths"] = self.paths + + if self.security_definitions: + self._doc["securityDefinitions"] = self.security_definitions + return copy.deepcopy(self._doc) @staticmethod diff --git a/samtranslator/translator/arn_generator.py b/samtranslator/translator/arn_generator.py index 032ade3172..0769520d4a 100644 --- a/samtranslator/translator/arn_generator.py +++ b/samtranslator/translator/arn_generator.py @@ -3,12 +3,18 @@ class ArnGenerator(object): @classmethod - def generate_arn(cls, partition, service, resource): + def generate_arn(cls, partition, service, resource, include_account_id=True): if not service or not resource: raise RuntimeError("Could not construct ARN for resource.") - return 'arn:{0}:{1}:${{AWS::Region}}:${{AWS::AccountId}}:{2}'.format(partition, service, resource) + arn = 'arn:{0}:{1}:${{AWS::Region}}:' + if include_account_id: + arn += '${{AWS::AccountId}}:' + + arn += '{2}' + + return arn.format(partition, service, resource) @classmethod def generate_aws_managed_policy_arn(cls, policy_name): diff --git a/tests/translator/input/api_with_auth_all_maximum.yaml b/tests/translator/input/api_with_auth_all_maximum.yaml new file mode 100644 index 0000000000..89c94fdc7e --- /dev/null +++ b/tests/translator/input/api_with_auth_all_maximum.yaml @@ -0,0 +1,105 @@ +Resources: + MyApi: + Type: "AWS::Serverless::Api" + Properties: + StageName: Prod + Auth: + DefaultAuthorizer: MyCognitoAuth + Authorizers: + MyCognitoAuth: + UserPoolArn: arn:aws:1 + Identity: + Header: MyAuthorizationHeader + ValidationExpression: myauthvalidationexpression + + MyCognitoAuthMultipleUserPools: + UserPoolArn: + - arn:aws:2 + - arn:aws:3 + Identity: + Header: MyAuthorizationHeader2 + ValidationExpression: myauthvalidationexpression2 + + MyLambdaTokenAuth: + FunctionPayloadType: TOKEN + FunctionArn: arn:aws + FunctionInvokeRole: arn:aws:iam::123456789012:role/S3Access + Identity: + Header: MyCustomAuthHeader + ValidationExpression: mycustomauthexpression + ReauthorizeEvery: 20 + + MyLambdaTokenAuthNoneFunctionInvokeRole: + FunctionArn: arn:aws + FunctionInvokeRole: NONE + Identity: + ReauthorizeEvery: 0 + + MyLambdaRequestAuth: + FunctionPayloadType: REQUEST + FunctionArn: arn:aws + FunctionInvokeRole: arn:aws:iam::123456789012:role/S3Access + Identity: + Headers: + - Authorization1 + QueryStrings: + - Authorization2 + StageVariables: + - Authorization3 + Context: + - Authorization4 + ReauthorizeEvery: 0 + + MyFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/thumbnails.zip + Handler: index.handler + Runtime: nodejs8.10 + Events: + WithNoAuthorizer: + Type: Api + Properties: + RestApiId: !Ref MyApi + Path: / + Method: get + Auth: + Authorizer: NONE + WithCognitoMultipleUserPoolsAuthorizer: + Type: Api + Properties: + RestApiId: !Ref MyApi + Path: /users + Method: post + Auth: + Authorizer: MyCognitoAuthMultipleUserPools + WithLambdaTokenAuthorizer: + Type: Api + Properties: + RestApiId: !Ref MyApi + Path: /users + Method: get + Auth: + Authorizer: MyLambdaTokenAuth + WithLambdaTokenAuthorizer: + Type: Api + Properties: + RestApiId: !Ref MyApi + Path: /users + Method: patch + Auth: + Authorizer: MyLambdaTokenAuthNoneFunctionInvokeRole + WithLambdaRequestAuthorizer: + Type: Api + Properties: + RestApiId: !Ref MyApi + Path: /users + Method: delete + Auth: + Authorizer: MyLambdaRequestAuth + WithDefaultAuthorizer: + Type: Api + Properties: + RestApiId: !Ref MyApi + Path: /users + Method: put \ No newline at end of file diff --git a/tests/translator/input/api_with_auth_all_minimum.yaml b/tests/translator/input/api_with_auth_all_minimum.yaml new file mode 100644 index 0000000000..2c1d03ad43 --- /dev/null +++ b/tests/translator/input/api_with_auth_all_minimum.yaml @@ -0,0 +1,59 @@ +Resources: + MyApiWithCognitoAuth: + Type: "AWS::Serverless::Api" + Properties: + StageName: Prod + Auth: + DefaultAuthorizer: MyCognitoAuth + Authorizers: + MyCognitoAuth: + UserPoolArn: !GetAtt MyUserPool.Arn + + MyApiWithLambdaTokenAuth: + Type: "AWS::Serverless::Api" + Properties: + StageName: Prod + Auth: + DefaultAuthorizer: MyLambdaTokenAuth + Authorizers: + MyLambdaTokenAuth: + FunctionArn: !GetAtt MyAuthFn.Arn + + MyApiWithLambdaRequestAuth: + Type: "AWS::Serverless::Api" + Properties: + StageName: Prod + Auth: + DefaultAuthorizer: MyLambdaRequestAuth + Authorizers: + MyLambdaRequestAuth: + FunctionPayloadType: REQUEST + FunctionArn: !GetAtt MyAuthFn.Arn + Identity: + Headers: + - Authorization1 + MyFn: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://bucket/key + Handler: index.handler + Runtime: nodejs8.10 + Events: + Cognito: + Type: Api + Properties: + RestApiId: !Ref MyApiWithCognitoAuth + Method: get + Path: /cognito + LambdaToken: + Type: Api + Properties: + RestApiId: !Ref MyApiWithLambdaTokenAuth + Method: get + Path: /lambda-token + LambdaRequest: + Type: Api + Properties: + RestApiId: !Ref MyApiWithLambdaRequestAuth + Method: get + Path: /lambda-request \ No newline at end of file diff --git a/tests/translator/input/api_with_auth_no_default.yaml b/tests/translator/input/api_with_auth_no_default.yaml new file mode 100644 index 0000000000..f3d3b81d88 --- /dev/null +++ b/tests/translator/input/api_with_auth_no_default.yaml @@ -0,0 +1,56 @@ +Resources: + MyApiWithCognitoAuth: + Type: "AWS::Serverless::Api" + Properties: + StageName: Prod + Auth: + Authorizers: + MyCognitoAuth: + UserPoolArn: !GetAtt MyUserPool.Arn + + MyApiWithLambdaTokenAuth: + Type: "AWS::Serverless::Api" + Properties: + StageName: Prod + Auth: + Authorizers: + MyLambdaTokenAuth: + FunctionArn: !GetAtt MyAuthFn.Arn + + MyApiWithLambdaRequestAuth: + Type: "AWS::Serverless::Api" + Properties: + StageName: Prod + Auth: + Authorizers: + MyLambdaRequestAuth: + FunctionPayloadType: REQUEST + FunctionArn: !GetAtt MyAuthFn.Arn + Identity: + Headers: + - Authorization1 + MyFn: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://bucket/key + Handler: index.handler + Runtime: nodejs8.10 + Events: + Cognito: + Type: Api + Properties: + RestApiId: !Ref MyApiWithCognitoAuth + Method: get + Path: /cognito + LambdaToken: + Type: Api + Properties: + RestApiId: !Ref MyApiWithLambdaTokenAuth + Method: get + Path: /lambda-token + LambdaRequest: + Type: Api + Properties: + RestApiId: !Ref MyApiWithLambdaRequestAuth + Method: get + Path: /lambda-request \ No newline at end of file diff --git a/tests/translator/input/error_api_invalid_auth.yaml b/tests/translator/input/error_api_invalid_auth.yaml new file mode 100644 index 0000000000..0d8e7f3b29 --- /dev/null +++ b/tests/translator/input/error_api_invalid_auth.yaml @@ -0,0 +1,188 @@ +Resources: + NoAuthApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + + NoAuthFn: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://bucket/key + Handler: index.handler + Runtime: nodejs8.10 + Events: + GetRoot: + Type: Api + Properties: + RestApiId: !Ref NoAuthApi + Path: / + Method: get + Auth: + Authorizer: MyAuth + + NoAuthorizersApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + Auth: + DefaultAuthorizer: Foo + + NoAuthorizersFn: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://bucket/key + Handler: index.handler + Runtime: nodejs8.10 + Events: + GetRoot: + Type: Api + Properties: + RestApiId: !Ref NoAuthorizersApi + Path: / + Method: get + Auth: + Authorizer: MyAuth + + MissingAuthorizerApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + Auth: + Authorizers: + MyCognitoAuthorizer: + UserPoolArn: 'arn:aws' + + MissingAuthorizerFn: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://bucket/key + Handler: index.handler + Runtime: nodejs8.10 + Events: + GetRoot: + Type: Api + Properties: + RestApiId: !Ref MissingAuthorizerApi + Path: / + Method: get + Auth: + Authorizer: UnspecifiedAuthorizer + + NoDefaultAuthorizerWithNoneApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + Auth: + Authorizers: + MyCognitoAuthorizer: + UserPoolArn: 'arn:aws' + + NoDefaultAuthorizerWithNoneFn: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://bucket/key + Handler: index.handler + Runtime: nodejs8.10 + Events: + GetRoot: + Type: Api + Properties: + RestApiId: !Ref NoDefaultAuthorizerWithNoneApi + Path: / + Method: get + Auth: + Authorizer: NONE + + AuthNotDictApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + Auth: notadict + + AuthorizersNotDictApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + Auth: + Authorizers: notadict + + AuthWithDefinitionUriApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + DefinitionUri: s3://bucket/key + Auth: + DefaultAuthorizer: Foo + + AuthWithAdditionalPropertyApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + Auth: + MyBad: Foo + + AuthWithInvalidDefinitionBodyApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + DefinitionBody: { invalid: true } + Auth: + DefaultAuthorizer: Foo + + AuthWithMissingDefaultAuthorizerApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + Auth: + DefaultAuthorizer: NotThere + Authorizers: + MyCognitoAuthorizer: + UserPoolArn: 'arn:aws' + + NoApiAuthorizerFn: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://bucket/key + Handler: index.handler + Runtime: nodejs8.10 + Events: + GetRoot: + Type: Api + Properties: + Path: / + Method: get + Auth: + Authorizer: MyAuth + + + InvalidFunctionPayloadTypeApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + Auth: + Authorizers: + MyLambdaAuthorizer: + FunctionArn: 'arn:aws' + FunctionPayloadType: INVALID + + NoIdentityOnRequestAuthorizer: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + Auth: + Authorizers: + MyLambdaRequestAuthorizer: + FunctionArn: 'arn:aws' + FunctionPayloadType: REQUEST + + NoIdentitySourceOnRequestAuthorizer: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + Auth: + Authorizers: + MyLambdaRequestAuthorizer: + FunctionArn: 'arn:aws' + FunctionPayloadType: REQUEST + Identity: + ReauthorizeEvery: 10 \ No newline at end of file diff --git a/tests/translator/output/api_with_auth_all_maximum.json b/tests/translator/output/api_with_auth_all_maximum.json new file mode 100644 index 0000000000..61b4a6b398 --- /dev/null +++ b/tests/translator/output/api_with_auth_all_maximum.json @@ -0,0 +1,505 @@ +{ + "Resources": { + "MyFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "thumbnails.zip" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "Tags": [{ + "Value": "SAM", + "Key": "lambda:createdBy" + }] + } + }, + "MyFunctionWithLambdaTokenAuthorizerPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/PATCH/users", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyApiMyLambdaRequestAuthAuthorizerPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": "arn:aws", + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/authorizers/*", + { + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyFunctionWithLambdaTokenAuthorizerPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/PATCH/users", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyFunctionWithDefaultAuthorizerPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/PUT/users", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyFunctionWithCognitoMultipleUserPoolsAuthorizerPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/users", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyFunctionWithNoAuthorizerPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [{ + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + }] + } + } + }, + "MyApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + } + }, + "security": [{ + "NONE": [] + }], + "responses": {} + } + }, + "/users": { + "put": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + } + }, + "security": [{ + "MyCognitoAuth": [] + }], + "responses": {} + }, + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + } + }, + "security": [{ + "MyCognitoAuthMultipleUserPools": [] + }], + "responses": {} + }, + "patch": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + } + }, + "security": [{ + "MyLambdaTokenAuthNoneFunctionInvokeRole": [] + }], + "responses": {} + }, + "delete": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + } + }, + "security": [{ + "MyLambdaRequestAuth": [] + }], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "MyCognitoAuth": { + "in": "header", + "type": "apiKey", + "name": "MyAuthorizationHeader", + "x-amazon-apigateway-authorizer": { + "identityValidationExpression": "myauthvalidationexpression", + "providerARNs": [ + "arn:aws:1" + ], + "type": "cognito_user_pools" + }, + "x-amazon-apigateway-authtype": "cognito_user_pools" + }, + "MyLambdaTokenAuthNoneFunctionInvokeRole": { + "in": "header", + "type": "apiKey", + "name": "Authorization", + "x-amazon-apigateway-authorizer": { + "type": "token", + "authorizerResultTtlInSeconds": 0, + "authorizerUri": { + "Fn::Sub": [ + "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations", + { + "__FunctionArn__": "arn:aws" + } + ] + } + }, + "x-amazon-apigateway-authtype": "custom" + }, + "MyCognitoAuthMultipleUserPools": { + "in": "header", + "type": "apiKey", + "name": "MyAuthorizationHeader2", + "x-amazon-apigateway-authorizer": { + "identityValidationExpression": "myauthvalidationexpression2", + "providerARNs": [ + "arn:aws:2", + "arn:aws:3" + ], + "type": "cognito_user_pools" + }, + "x-amazon-apigateway-authtype": "cognito_user_pools" + }, + "MyLambdaTokenAuth": { + "in": "header", + "type": "apiKey", + "name": "MyCustomAuthHeader", + "x-amazon-apigateway-authorizer": { + "type": "token", + "authorizerResultTtlInSeconds": 20, + "authorizerUri": { + "Fn::Sub": [ + "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations", + { + "__FunctionArn__": "arn:aws" + } + ] + }, + "authorizerCredentials": "arn:aws:iam::123456789012:role/S3Access", + "identityValidationExpression": "mycustomauthexpression" + }, + "x-amazon-apigateway-authtype": "custom" + }, + "MyLambdaRequestAuth": { + "in": "header", + "type": "apiKey", + "name": "Unused", + "x-amazon-apigateway-authorizer": { + "type": "request", + "authorizerResultTtlInSeconds": 0, + "identitySource": "method.request.header.Authorization1, method.request.querystring.Authorization2, stageVariables.Authorization3, context.Authorization4", + "authorizerUri": { + "Fn::Sub": [ + "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations", + { + "__FunctionArn__": "arn:aws" + } + ] + }, + "authorizerCredentials": "arn:aws:iam::123456789012:role/S3Access" + }, + "x-amazon-apigateway-authtype": "custom" + } + } + } + } + }, + "MyApiMyLambdaTokenAuthAuthorizerPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": "arn:aws", + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/authorizers/*", + { + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyFunctionWithLambdaRequestAuthorizerPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/DELETE/users", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyFunctionWithLambdaRequestAuthorizerPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/DELETE/users", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyApiDeployment22f365e69f": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApi" + }, + "Description": "RestApi deployment id: 22f365e69fd10c36975a60c0b715eb1340f4b5e6", + "StageName": "Stage" + } + }, + "MyFunctionWithCognitoMultipleUserPoolsAuthorizerPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/users", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyApiMyLambdaTokenAuthNoneFunctionInvokeRoleAuthorizerPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": "arn:aws", + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/authorizers/*", + { + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiDeployment22f365e69f" + }, + "RestApiId": { + "Ref": "MyApi" + }, + "StageName": "Prod" + } + }, + "MyFunctionWithNoAuthorizerPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyFunctionWithDefaultAuthorizerPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/PUT/users", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/api_with_auth_all_minimum.json b/tests/translator/output/api_with_auth_all_minimum.json new file mode 100644 index 0000000000..b3c4c4fd95 --- /dev/null +++ b/tests/translator/output/api_with_auth_all_minimum.json @@ -0,0 +1,444 @@ +{ + "Resources": { + "MyFnCognitoPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/cognito", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApiWithCognitoAuth" + } + } + ] + } + } + }, + "MyApiWithCognitoAuth": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/cognito": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFn.Arn}/invocations" + } + }, + "security": [{ + "MyCognitoAuth": [] + }], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "MyCognitoAuth": { + "in": "header", + "type": "apiKey", + "name": "Authorization", + "x-amazon-apigateway-authorizer": { + "providerARNs": [{ + "Fn::GetAtt": [ + "MyUserPool", + "Arn" + ] + }], + "type": "cognito_user_pools" + }, + "x-amazon-apigateway-authtype": "cognito_user_pools" + } + } + } + } + }, + "MyApiWithLambdaRequestAuthMyLambdaRequestAuthAuthorizerPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Fn::GetAtt": [ + "MyAuthFn", + "Arn" + ] + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/authorizers/*", + { + "__ApiId__": { + "Ref": "MyApiWithLambdaRequestAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaRequestAuthProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiWithLambdaRequestAuthDeployment6e52add211" + }, + "RestApiId": { + "Ref": "MyApiWithLambdaRequestAuth" + }, + "StageName": "Prod" + } + }, + "MyFnLambdaTokenPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/lambda-token", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApiWithLambdaTokenAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaTokenAuthMyLambdaTokenAuthAuthorizerPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Fn::GetAtt": [ + "MyAuthFn", + "Arn" + ] + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/authorizers/*", + { + "__ApiId__": { + "Ref": "MyApiWithLambdaTokenAuth" + } + } + ] + } + } + }, + "MyFnLambdaRequestPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/lambda-request", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApiWithLambdaRequestAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaTokenAuth": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/lambda-token": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFn.Arn}/invocations" + } + }, + "security": [{ + "MyLambdaTokenAuth": [] + }], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "MyLambdaTokenAuth": { + "in": "header", + "type": "apiKey", + "name": "Authorization", + "x-amazon-apigateway-authorizer": { + "type": "token", + "authorizerUri": { + "Fn::Sub": [ + "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations", + { + "__FunctionArn__": { + "Fn::GetAtt": [ + "MyAuthFn", + "Arn" + ] + } + } + ] + } + }, + "x-amazon-apigateway-authtype": "custom" + } + } + } + } + }, + "MyApiWithCognitoAuthDeployment62312fa971": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiWithCognitoAuth" + }, + "Description": "RestApi deployment id: 62312fa9711ad898b40e76b7a4ae1358305b0bcd", + "StageName": "Stage" + } + }, + "MyFnLambdaRequestPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/lambda-request", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiWithLambdaRequestAuth" + } + } + ] + } + } + }, + "MyFnLambdaTokenPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/lambda-token", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiWithLambdaTokenAuth" + } + } + ] + } + } + }, + "MyFnCognitoPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/cognito", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiWithCognitoAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaRequestAuth": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/lambda-request": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFn.Arn}/invocations" + } + }, + "security": [{ + "MyLambdaRequestAuth": [] + }], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "MyLambdaRequestAuth": { + "in": "header", + "type": "apiKey", + "name": "Unused", + "x-amazon-apigateway-authorizer": { + "type": "request", + "identitySource": "method.request.header.Authorization1", + "authorizerUri": { + "Fn::Sub": [ + "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations", + { + "__FunctionArn__": { + "Fn::GetAtt": [ + "MyAuthFn", + "Arn" + ] + } + } + ] + } + }, + "x-amazon-apigateway-authtype": "custom" + } + } + } + } + }, + "MyApiWithLambdaTokenAuthDeployment445c6c96e7": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiWithLambdaTokenAuth" + }, + "Description": "RestApi deployment id: 445c6c96e7f43bd49f83bd67ae0d6813c517348f", + "StageName": "Stage" + } + }, + "MyApiWithLambdaTokenAuthProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiWithLambdaTokenAuthDeployment445c6c96e7" + }, + "RestApiId": { + "Ref": "MyApiWithLambdaTokenAuth" + }, + "StageName": "Prod" + } + }, + "MyFnRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [{ + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + }] + } + } + }, + "MyApiWithLambdaRequestAuthDeployment6e52add211": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiWithLambdaRequestAuth" + }, + "Description": "RestApi deployment id: 6e52add211cda52ae10a7cc0e0afcf4afc682f9f", + "StageName": "Stage" + } + }, + "MyApiWithCognitoAuthProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiWithCognitoAuthDeployment62312fa971" + }, + "RestApiId": { + "Ref": "MyApiWithCognitoAuth" + }, + "StageName": "Prod" + } + }, + "MyFn": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "bucket", + "S3Key": "key" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFnRole", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "Tags": [{ + "Value": "SAM", + "Key": "lambda:createdBy" + }] + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/api_with_auth_no_default.json b/tests/translator/output/api_with_auth_no_default.json new file mode 100644 index 0000000000..2a981e6ed6 --- /dev/null +++ b/tests/translator/output/api_with_auth_no_default.json @@ -0,0 +1,435 @@ +{ + "Resources": { + "MyFnCognitoPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/cognito", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApiWithCognitoAuth" + } + } + ] + } + } + }, + "MyApiWithCognitoAuth": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/cognito": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFn.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "MyCognitoAuth": { + "in": "header", + "type": "apiKey", + "name": "Authorization", + "x-amazon-apigateway-authorizer": { + "providerARNs": [{ + "Fn::GetAtt": [ + "MyUserPool", + "Arn" + ] + }], + "type": "cognito_user_pools" + }, + "x-amazon-apigateway-authtype": "cognito_user_pools" + } + } + } + } + }, + "MyApiWithCognitoAuthDeployment039b508d89": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiWithCognitoAuth" + }, + "Description": "RestApi deployment id: 039b508d8974255326ad180948c0f232635032d8", + "StageName": "Stage" + } + }, + "MyApiWithLambdaRequestAuthMyLambdaRequestAuthAuthorizerPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Fn::GetAtt": [ + "MyAuthFn", + "Arn" + ] + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/authorizers/*", + { + "__ApiId__": { + "Ref": "MyApiWithLambdaRequestAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaRequestAuthDeployment7d0d103fdf": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiWithLambdaRequestAuth" + }, + "Description": "RestApi deployment id: 7d0d103fdf357021c9e3f88a03f27a766045308f", + "StageName": "Stage" + } + }, + "MyFnLambdaTokenPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/lambda-token", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApiWithLambdaTokenAuth" + } + } + ] + } + } + }, + "MyFnLambdaRequestPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/lambda-request", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApiWithLambdaRequestAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaTokenAuth": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/lambda-token": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFn.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "MyLambdaTokenAuth": { + "in": "header", + "type": "apiKey", + "name": "Authorization", + "x-amazon-apigateway-authorizer": { + "type": "token", + "authorizerUri": { + "Fn::Sub": [ + "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations", + { + "__FunctionArn__": { + "Fn::GetAtt": [ + "MyAuthFn", + "Arn" + ] + } + } + ] + } + }, + "x-amazon-apigateway-authtype": "custom" + } + } + } + } + }, + "MyApiWithLambdaTokenAuthDeployment50695ee60b": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiWithLambdaTokenAuth" + }, + "Description": "RestApi deployment id: 50695ee60b97eeade77bcc6137fa5dabc526938d", + "StageName": "Stage" + } + }, + "MyFnLambdaRequestPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/lambda-request", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiWithLambdaRequestAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaRequestAuthProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiWithLambdaRequestAuthDeployment7d0d103fdf" + }, + "RestApiId": { + "Ref": "MyApiWithLambdaRequestAuth" + }, + "StageName": "Prod" + } + }, + "MyFnLambdaTokenPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/lambda-token", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiWithLambdaTokenAuth" + } + } + ] + } + } + }, + "MyFnCognitoPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/cognito", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiWithCognitoAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaRequestAuth": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/lambda-request": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFn.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "MyLambdaRequestAuth": { + "in": "header", + "type": "apiKey", + "name": "Unused", + "x-amazon-apigateway-authorizer": { + "type": "request", + "identitySource": "method.request.header.Authorization1", + "authorizerUri": { + "Fn::Sub": [ + "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations", + { + "__FunctionArn__": { + "Fn::GetAtt": [ + "MyAuthFn", + "Arn" + ] + } + } + ] + } + }, + "x-amazon-apigateway-authtype": "custom" + } + } + } + } + }, + "MyApiWithLambdaTokenAuthMyLambdaTokenAuthAuthorizerPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Fn::GetAtt": [ + "MyAuthFn", + "Arn" + ] + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/authorizers/*", + { + "__ApiId__": { + "Ref": "MyApiWithLambdaTokenAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaTokenAuthProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiWithLambdaTokenAuthDeployment50695ee60b" + }, + "RestApiId": { + "Ref": "MyApiWithLambdaTokenAuth" + }, + "StageName": "Prod" + } + }, + "MyFnRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [{ + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + }] + } + } + }, + "MyApiWithCognitoAuthProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiWithCognitoAuthDeployment039b508d89" + }, + "RestApiId": { + "Ref": "MyApiWithCognitoAuth" + }, + "StageName": "Prod" + } + }, + "MyFn": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "bucket", + "S3Key": "key" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFnRole", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "Tags": [{ + "Value": "SAM", + "Key": "lambda:createdBy" + }] + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/api_with_auth_all_maximum.json b/tests/translator/output/aws-cn/api_with_auth_all_maximum.json new file mode 100644 index 0000000000..781c9b8bef --- /dev/null +++ b/tests/translator/output/aws-cn/api_with_auth_all_maximum.json @@ -0,0 +1,513 @@ +{ + "Resources": { + "MyFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "thumbnails.zip" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "Tags": [{ + "Value": "SAM", + "Key": "lambda:createdBy" + }] + } + }, + "MyFunctionWithLambdaTokenAuthorizerPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/PATCH/users", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyApiMyLambdaRequestAuthAuthorizerPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": "arn:aws", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/authorizers/*", + { + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyFunctionWithLambdaTokenAuthorizerPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/PATCH/users", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyFunctionWithDefaultAuthorizerPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/PUT/users", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyApiDeployment8401165542": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApi" + }, + "Description": "RestApi deployment id: 8401165542e94cea0086026b427b10c966b84f1d", + "StageName": "Stage" + } + }, + "MyFunctionWithCognitoMultipleUserPoolsAuthorizerPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/users", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyFunctionWithNoAuthorizerPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [{ + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + }] + } + } + }, + "MyApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + } + }, + "security": [{ + "NONE": [] + }], + "responses": {} + } + }, + "/users": { + "put": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + } + }, + "security": [{ + "MyCognitoAuth": [] + }], + "responses": {} + }, + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + } + }, + "security": [{ + "MyCognitoAuthMultipleUserPools": [] + }], + "responses": {} + }, + "patch": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + } + }, + "security": [{ + "MyLambdaTokenAuthNoneFunctionInvokeRole": [] + }], + "responses": {} + }, + "delete": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + } + }, + "security": [{ + "MyLambdaRequestAuth": [] + }], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "MyCognitoAuth": { + "in": "header", + "type": "apiKey", + "name": "MyAuthorizationHeader", + "x-amazon-apigateway-authorizer": { + "identityValidationExpression": "myauthvalidationexpression", + "providerARNs": [ + "arn:aws:1" + ], + "type": "cognito_user_pools" + }, + "x-amazon-apigateway-authtype": "cognito_user_pools" + }, + "MyLambdaTokenAuthNoneFunctionInvokeRole": { + "in": "header", + "type": "apiKey", + "name": "Authorization", + "x-amazon-apigateway-authorizer": { + "type": "token", + "authorizerResultTtlInSeconds": 0, + "authorizerUri": { + "Fn::Sub": [ + "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations", + { + "__FunctionArn__": "arn:aws" + } + ] + } + }, + "x-amazon-apigateway-authtype": "custom" + }, + "MyCognitoAuthMultipleUserPools": { + "in": "header", + "type": "apiKey", + "name": "MyAuthorizationHeader2", + "x-amazon-apigateway-authorizer": { + "identityValidationExpression": "myauthvalidationexpression2", + "providerARNs": [ + "arn:aws:2", + "arn:aws:3" + ], + "type": "cognito_user_pools" + }, + "x-amazon-apigateway-authtype": "cognito_user_pools" + }, + "MyLambdaTokenAuth": { + "in": "header", + "type": "apiKey", + "name": "MyCustomAuthHeader", + "x-amazon-apigateway-authorizer": { + "type": "token", + "authorizerResultTtlInSeconds": 20, + "authorizerUri": { + "Fn::Sub": [ + "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations", + { + "__FunctionArn__": "arn:aws" + } + ] + }, + "authorizerCredentials": "arn:aws:iam::123456789012:role/S3Access", + "identityValidationExpression": "mycustomauthexpression" + }, + "x-amazon-apigateway-authtype": "custom" + }, + "MyLambdaRequestAuth": { + "in": "header", + "type": "apiKey", + "name": "Unused", + "x-amazon-apigateway-authorizer": { + "type": "request", + "authorizerResultTtlInSeconds": 0, + "identitySource": "method.request.header.Authorization1, method.request.querystring.Authorization2, stageVariables.Authorization3, context.Authorization4", + "authorizerUri": { + "Fn::Sub": [ + "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations", + { + "__FunctionArn__": "arn:aws" + } + ] + }, + "authorizerCredentials": "arn:aws:iam::123456789012:role/S3Access" + }, + "x-amazon-apigateway-authtype": "custom" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "MyApiMyLambdaTokenAuthAuthorizerPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": "arn:aws", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/authorizers/*", + { + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyFunctionWithLambdaRequestAuthorizerPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/DELETE/users", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyFunctionWithLambdaRequestAuthorizerPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/DELETE/users", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyFunctionWithCognitoMultipleUserPoolsAuthorizerPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/users", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyApiMyLambdaTokenAuthNoneFunctionInvokeRoleAuthorizerPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": "arn:aws", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/authorizers/*", + { + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiDeployment8401165542" + }, + "RestApiId": { + "Ref": "MyApi" + }, + "StageName": "Prod" + } + }, + "MyFunctionWithNoAuthorizerPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyFunctionWithDefaultAuthorizerPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/PUT/users", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/api_with_auth_all_minimum.json b/tests/translator/output/aws-cn/api_with_auth_all_minimum.json new file mode 100644 index 0000000000..4ee847bdbb --- /dev/null +++ b/tests/translator/output/aws-cn/api_with_auth_all_minimum.json @@ -0,0 +1,468 @@ +{ + "Resources": { + "MyFnCognitoPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/cognito", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApiWithCognitoAuth" + } + } + ] + } + } + }, + "MyApiWithCognitoAuth": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/cognito": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFn.Arn}/invocations" + } + }, + "security": [{ + "MyCognitoAuth": [] + }], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "MyCognitoAuth": { + "in": "header", + "type": "apiKey", + "name": "Authorization", + "x-amazon-apigateway-authorizer": { + "providerARNs": [{ + "Fn::GetAtt": [ + "MyUserPool", + "Arn" + ] + }], + "type": "cognito_user_pools" + }, + "x-amazon-apigateway-authtype": "cognito_user_pools" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "MyApiWithLambdaRequestAuthMyLambdaRequestAuthAuthorizerPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Fn::GetAtt": [ + "MyAuthFn", + "Arn" + ] + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/authorizers/*", + { + "__ApiId__": { + "Ref": "MyApiWithLambdaRequestAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaTokenAuthDeploymenta48b731095": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiWithLambdaTokenAuth" + }, + "Description": "RestApi deployment id: a48b7310952ed029bd212c380e89a1bd39c74eae", + "StageName": "Stage" + } + }, + "MyApiWithLambdaRequestAuthProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiWithLambdaRequestAuthDeploymentd3ee2721bc" + }, + "RestApiId": { + "Ref": "MyApiWithLambdaRequestAuth" + }, + "StageName": "Prod" + } + }, + "MyFnLambdaTokenPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/lambda-token", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApiWithLambdaTokenAuth" + } + } + ] + } + } + }, + "MyFnLambdaRequestPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/lambda-request", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApiWithLambdaRequestAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaTokenAuth": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/lambda-token": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFn.Arn}/invocations" + } + }, + "security": [{ + "MyLambdaTokenAuth": [] + }], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "MyLambdaTokenAuth": { + "in": "header", + "type": "apiKey", + "name": "Authorization", + "x-amazon-apigateway-authorizer": { + "type": "token", + "authorizerUri": { + "Fn::Sub": [ + "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations", + { + "__FunctionArn__": { + "Fn::GetAtt": [ + "MyAuthFn", + "Arn" + ] + } + } + ] + } + }, + "x-amazon-apigateway-authtype": "custom" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "MyApiWithCognitoAuthDeploymenta9cf768eaa": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiWithCognitoAuth" + }, + "Description": "RestApi deployment id: a9cf768eaa1ac6804c7a7b05b79d7ee79d369fcf", + "StageName": "Stage" + } + }, + "MyFnLambdaRequestPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/lambda-request", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiWithLambdaRequestAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaRequestAuthDeploymentd3ee2721bc": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiWithLambdaRequestAuth" + }, + "Description": "RestApi deployment id: d3ee2721bcff60c4d00d26138ccf8007434bb862", + "StageName": "Stage" + } + }, + "MyFnLambdaTokenPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/lambda-token", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiWithLambdaTokenAuth" + } + } + ] + } + } + }, + "MyFnCognitoPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/cognito", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiWithCognitoAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaRequestAuth": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/lambda-request": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFn.Arn}/invocations" + } + }, + "security": [{ + "MyLambdaRequestAuth": [] + }], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "MyLambdaRequestAuth": { + "in": "header", + "type": "apiKey", + "name": "Unused", + "x-amazon-apigateway-authorizer": { + "type": "request", + "identitySource": "method.request.header.Authorization1", + "authorizerUri": { + "Fn::Sub": [ + "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations", + { + "__FunctionArn__": { + "Fn::GetAtt": [ + "MyAuthFn", + "Arn" + ] + } + } + ] + } + }, + "x-amazon-apigateway-authtype": "custom" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "MyApiWithLambdaTokenAuthMyLambdaTokenAuthAuthorizerPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Fn::GetAtt": [ + "MyAuthFn", + "Arn" + ] + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/authorizers/*", + { + "__ApiId__": { + "Ref": "MyApiWithLambdaTokenAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaTokenAuthProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiWithLambdaTokenAuthDeploymenta48b731095" + }, + "RestApiId": { + "Ref": "MyApiWithLambdaTokenAuth" + }, + "StageName": "Prod" + } + }, + "MyFnRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [{ + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + }] + } + } + }, + "MyApiWithCognitoAuthProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiWithCognitoAuthDeploymenta9cf768eaa" + }, + "RestApiId": { + "Ref": "MyApiWithCognitoAuth" + }, + "StageName": "Prod" + } + }, + "MyFn": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "bucket", + "S3Key": "key" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFnRole", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "Tags": [{ + "Value": "SAM", + "Key": "lambda:createdBy" + }] + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/api_with_auth_no_default.json b/tests/translator/output/aws-cn/api_with_auth_no_default.json new file mode 100644 index 0000000000..b8df0978df --- /dev/null +++ b/tests/translator/output/aws-cn/api_with_auth_no_default.json @@ -0,0 +1,459 @@ +{ + "Resources": { + "MyFnCognitoPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/cognito", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApiWithCognitoAuth" + } + } + ] + } + } + }, + "MyApiWithCognitoAuth": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/cognito": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFn.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "MyCognitoAuth": { + "in": "header", + "type": "apiKey", + "name": "Authorization", + "x-amazon-apigateway-authorizer": { + "providerARNs": [{ + "Fn::GetAtt": [ + "MyUserPool", + "Arn" + ] + }], + "type": "cognito_user_pools" + }, + "x-amazon-apigateway-authtype": "cognito_user_pools" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "MyApiWithLambdaRequestAuthDeployment93e0147508": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiWithLambdaRequestAuth" + }, + "Description": "RestApi deployment id: 93e01475088ff4675852021a99279d60fc93cd6a", + "StageName": "Stage" + } + }, + "MyApiWithCognitoAuthDeployment6a169547ee": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiWithCognitoAuth" + }, + "Description": "RestApi deployment id: 6a169547eef02f4a0cd9fdc97aca9d1e8a106b11", + "StageName": "Stage" + } + }, + "MyApiWithLambdaRequestAuthProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiWithLambdaRequestAuthDeployment93e0147508" + }, + "RestApiId": { + "Ref": "MyApiWithLambdaRequestAuth" + }, + "StageName": "Prod" + } + }, + "MyFnLambdaTokenPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/lambda-token", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApiWithLambdaTokenAuth" + } + } + ] + } + } + }, + "MyFnLambdaRequestPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/lambda-request", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApiWithLambdaRequestAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaTokenAuth": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/lambda-token": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFn.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "MyLambdaTokenAuth": { + "in": "header", + "type": "apiKey", + "name": "Authorization", + "x-amazon-apigateway-authorizer": { + "type": "token", + "authorizerUri": { + "Fn::Sub": [ + "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations", + { + "__FunctionArn__": { + "Fn::GetAtt": [ + "MyAuthFn", + "Arn" + ] + } + } + ] + } + }, + "x-amazon-apigateway-authtype": "custom" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "MyApiWithLambdaTokenAuthProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiWithLambdaTokenAuthDeploymente838608f2f" + }, + "RestApiId": { + "Ref": "MyApiWithLambdaTokenAuth" + }, + "StageName": "Prod" + } + }, + "MyFnLambdaRequestPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/lambda-request", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiWithLambdaRequestAuth" + } + } + ] + } + } + }, + "MyFnLambdaTokenPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/lambda-token", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiWithLambdaTokenAuth" + } + } + ] + } + } + }, + "MyFnCognitoPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/cognito", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiWithCognitoAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaRequestAuth": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/lambda-request": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFn.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "MyLambdaRequestAuth": { + "in": "header", + "type": "apiKey", + "name": "Unused", + "x-amazon-apigateway-authorizer": { + "type": "request", + "identitySource": "method.request.header.Authorization1", + "authorizerUri": { + "Fn::Sub": [ + "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations", + { + "__FunctionArn__": { + "Fn::GetAtt": [ + "MyAuthFn", + "Arn" + ] + } + } + ] + } + }, + "x-amazon-apigateway-authtype": "custom" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "MyApiWithLambdaTokenAuthMyLambdaTokenAuthAuthorizerPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Fn::GetAtt": [ + "MyAuthFn", + "Arn" + ] + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/authorizers/*", + { + "__ApiId__": { + "Ref": "MyApiWithLambdaTokenAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaRequestAuthMyLambdaRequestAuthAuthorizerPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Fn::GetAtt": [ + "MyAuthFn", + "Arn" + ] + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/authorizers/*", + { + "__ApiId__": { + "Ref": "MyApiWithLambdaRequestAuth" + } + } + ] + } + } + }, + "MyFnRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [{ + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + }] + } + } + }, + "MyApiWithCognitoAuthProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiWithCognitoAuthDeployment6a169547ee" + }, + "RestApiId": { + "Ref": "MyApiWithCognitoAuth" + }, + "StageName": "Prod" + } + }, + "MyFn": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "bucket", + "S3Key": "key" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFnRole", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "Tags": [{ + "Value": "SAM", + "Key": "lambda:createdBy" + }] + } + }, + "MyApiWithLambdaTokenAuthDeploymente838608f2f": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiWithLambdaTokenAuth" + }, + "Description": "RestApi deployment id: e838608f2f6897932f7883ba5afaa855145e38f5", + "StageName": "Stage" + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/api_with_auth_all_maximum.json b/tests/translator/output/aws-us-gov/api_with_auth_all_maximum.json new file mode 100644 index 0000000000..7faead9d31 --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_with_auth_all_maximum.json @@ -0,0 +1,513 @@ +{ + "Resources": { + "MyFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "thumbnails.zip" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "Tags": [{ + "Value": "SAM", + "Key": "lambda:createdBy" + }] + } + }, + "MyFunctionWithLambdaTokenAuthorizerPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/PATCH/users", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyApiMyLambdaRequestAuthAuthorizerPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": "arn:aws", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/authorizers/*", + { + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyFunctionWithLambdaTokenAuthorizerPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/PATCH/users", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyFunctionWithDefaultAuthorizerPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/PUT/users", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyFunctionWithCognitoMultipleUserPoolsAuthorizerPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/users", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyFunctionWithNoAuthorizerPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [{ + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + }] + } + } + }, + "MyApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + } + }, + "security": [{ + "NONE": [] + }], + "responses": {} + } + }, + "/users": { + "put": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + } + }, + "security": [{ + "MyCognitoAuth": [] + }], + "responses": {} + }, + "post": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + } + }, + "security": [{ + "MyCognitoAuthMultipleUserPools": [] + }], + "responses": {} + }, + "patch": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + } + }, + "security": [{ + "MyLambdaTokenAuthNoneFunctionInvokeRole": [] + }], + "responses": {} + }, + "delete": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunction.Arn}/invocations" + } + }, + "security": [{ + "MyLambdaRequestAuth": [] + }], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "MyCognitoAuth": { + "in": "header", + "type": "apiKey", + "name": "MyAuthorizationHeader", + "x-amazon-apigateway-authorizer": { + "identityValidationExpression": "myauthvalidationexpression", + "providerARNs": [ + "arn:aws:1" + ], + "type": "cognito_user_pools" + }, + "x-amazon-apigateway-authtype": "cognito_user_pools" + }, + "MyLambdaTokenAuthNoneFunctionInvokeRole": { + "in": "header", + "type": "apiKey", + "name": "Authorization", + "x-amazon-apigateway-authorizer": { + "type": "token", + "authorizerResultTtlInSeconds": 0, + "authorizerUri": { + "Fn::Sub": [ + "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations", + { + "__FunctionArn__": "arn:aws" + } + ] + } + }, + "x-amazon-apigateway-authtype": "custom" + }, + "MyCognitoAuthMultipleUserPools": { + "in": "header", + "type": "apiKey", + "name": "MyAuthorizationHeader2", + "x-amazon-apigateway-authorizer": { + "identityValidationExpression": "myauthvalidationexpression2", + "providerARNs": [ + "arn:aws:2", + "arn:aws:3" + ], + "type": "cognito_user_pools" + }, + "x-amazon-apigateway-authtype": "cognito_user_pools" + }, + "MyLambdaTokenAuth": { + "in": "header", + "type": "apiKey", + "name": "MyCustomAuthHeader", + "x-amazon-apigateway-authorizer": { + "type": "token", + "authorizerResultTtlInSeconds": 20, + "authorizerUri": { + "Fn::Sub": [ + "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations", + { + "__FunctionArn__": "arn:aws" + } + ] + }, + "authorizerCredentials": "arn:aws:iam::123456789012:role/S3Access", + "identityValidationExpression": "mycustomauthexpression" + }, + "x-amazon-apigateway-authtype": "custom" + }, + "MyLambdaRequestAuth": { + "in": "header", + "type": "apiKey", + "name": "Unused", + "x-amazon-apigateway-authorizer": { + "type": "request", + "authorizerResultTtlInSeconds": 0, + "identitySource": "method.request.header.Authorization1, method.request.querystring.Authorization2, stageVariables.Authorization3, context.Authorization4", + "authorizerUri": { + "Fn::Sub": [ + "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations", + { + "__FunctionArn__": "arn:aws" + } + ] + }, + "authorizerCredentials": "arn:aws:iam::123456789012:role/S3Access" + }, + "x-amazon-apigateway-authtype": "custom" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "MyApiMyLambdaTokenAuthAuthorizerPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": "arn:aws", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/authorizers/*", + { + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyFunctionWithLambdaRequestAuthorizerPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/DELETE/users", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyFunctionWithLambdaRequestAuthorizerPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/DELETE/users", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyApiDeploymentf9e0be23dc": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApi" + }, + "Description": "RestApi deployment id: f9e0be23dccfaabdc2729c4d2221a3eeaa8e87db", + "StageName": "Stage" + } + }, + "MyFunctionWithCognitoMultipleUserPoolsAuthorizerPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/users", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyApiMyLambdaTokenAuthNoneFunctionInvokeRoleAuthorizerPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": "arn:aws", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/authorizers/*", + { + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiDeploymentf9e0be23dc" + }, + "RestApiId": { + "Ref": "MyApi" + }, + "StageName": "Prod" + } + }, + "MyFunctionWithNoAuthorizerPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + }, + "MyFunctionWithDefaultAuthorizerPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/PUT/users", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApi" + } + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/api_with_auth_all_minimum.json b/tests/translator/output/aws-us-gov/api_with_auth_all_minimum.json new file mode 100644 index 0000000000..999138fa0f --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_with_auth_all_minimum.json @@ -0,0 +1,468 @@ +{ + "Resources": { + "MyFnCognitoPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/cognito", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApiWithCognitoAuth" + } + } + ] + } + } + }, + "MyApiWithCognitoAuth": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/cognito": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFn.Arn}/invocations" + } + }, + "security": [{ + "MyCognitoAuth": [] + }], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "MyCognitoAuth": { + "in": "header", + "type": "apiKey", + "name": "Authorization", + "x-amazon-apigateway-authorizer": { + "providerARNs": [{ + "Fn::GetAtt": [ + "MyUserPool", + "Arn" + ] + }], + "type": "cognito_user_pools" + }, + "x-amazon-apigateway-authtype": "cognito_user_pools" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "MyApiWithLambdaRequestAuthMyLambdaRequestAuthAuthorizerPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Fn::GetAtt": [ + "MyAuthFn", + "Arn" + ] + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/authorizers/*", + { + "__ApiId__": { + "Ref": "MyApiWithLambdaRequestAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaRequestAuthProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiWithLambdaRequestAuthDeploymentca86749bcd" + }, + "RestApiId": { + "Ref": "MyApiWithLambdaRequestAuth" + }, + "StageName": "Prod" + } + }, + "MyFnLambdaTokenPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/lambda-token", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApiWithLambdaTokenAuth" + } + } + ] + } + } + }, + "MyFnLambdaRequestPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/lambda-request", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApiWithLambdaRequestAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaTokenAuth": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/lambda-token": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFn.Arn}/invocations" + } + }, + "security": [{ + "MyLambdaTokenAuth": [] + }], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "MyLambdaTokenAuth": { + "in": "header", + "type": "apiKey", + "name": "Authorization", + "x-amazon-apigateway-authorizer": { + "type": "token", + "authorizerUri": { + "Fn::Sub": [ + "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations", + { + "__FunctionArn__": { + "Fn::GetAtt": [ + "MyAuthFn", + "Arn" + ] + } + } + ] + } + }, + "x-amazon-apigateway-authtype": "custom" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "MyApiWithLambdaTokenAuthProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiWithLambdaTokenAuthDeployment5192789870" + }, + "RestApiId": { + "Ref": "MyApiWithLambdaTokenAuth" + }, + "StageName": "Prod" + } + }, + "MyFnLambdaRequestPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/lambda-request", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiWithLambdaRequestAuth" + } + } + ] + } + } + }, + "MyFnLambdaTokenPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/lambda-token", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiWithLambdaTokenAuth" + } + } + ] + } + } + }, + "MyFnCognitoPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/cognito", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiWithCognitoAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaRequestAuth": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/lambda-request": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFn.Arn}/invocations" + } + }, + "security": [{ + "MyLambdaRequestAuth": [] + }], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "MyLambdaRequestAuth": { + "in": "header", + "type": "apiKey", + "name": "Unused", + "x-amazon-apigateway-authorizer": { + "type": "request", + "identitySource": "method.request.header.Authorization1", + "authorizerUri": { + "Fn::Sub": [ + "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations", + { + "__FunctionArn__": { + "Fn::GetAtt": [ + "MyAuthFn", + "Arn" + ] + } + } + ] + } + }, + "x-amazon-apigateway-authtype": "custom" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "MyApiWithLambdaRequestAuthDeploymentca86749bcd": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiWithLambdaRequestAuth" + }, + "Description": "RestApi deployment id: ca86749bcd339b4d6564954e2e12b20ebf9fb2ff", + "StageName": "Stage" + } + }, + "MyApiWithLambdaTokenAuthMyLambdaTokenAuthAuthorizerPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Fn::GetAtt": [ + "MyAuthFn", + "Arn" + ] + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/authorizers/*", + { + "__ApiId__": { + "Ref": "MyApiWithLambdaTokenAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaTokenAuthDeployment5192789870": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiWithLambdaTokenAuth" + }, + "Description": "RestApi deployment id: 5192789870157c12b0fb5a78c7e570d22c4e46f5", + "StageName": "Stage" + } + }, + "MyFnRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [{ + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + }] + } + } + }, + "MyApiWithCognitoAuthDeployment9b695a6dd5": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiWithCognitoAuth" + }, + "Description": "RestApi deployment id: 9b695a6dd5c12bb346b4163227f398c34128a49a", + "StageName": "Stage" + } + }, + "MyApiWithCognitoAuthProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiWithCognitoAuthDeployment9b695a6dd5" + }, + "RestApiId": { + "Ref": "MyApiWithCognitoAuth" + }, + "StageName": "Prod" + } + }, + "MyFn": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "bucket", + "S3Key": "key" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFnRole", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "Tags": [{ + "Value": "SAM", + "Key": "lambda:createdBy" + }] + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/api_with_auth_no_default.json b/tests/translator/output/aws-us-gov/api_with_auth_no_default.json new file mode 100644 index 0000000000..2f19c769ac --- /dev/null +++ b/tests/translator/output/aws-us-gov/api_with_auth_no_default.json @@ -0,0 +1,459 @@ +{ + "Resources": { + "MyFnCognitoPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/cognito", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApiWithCognitoAuth" + } + } + ] + } + } + }, + "MyApiWithCognitoAuth": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/cognito": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFn.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "MyCognitoAuth": { + "in": "header", + "type": "apiKey", + "name": "Authorization", + "x-amazon-apigateway-authorizer": { + "providerARNs": [{ + "Fn::GetAtt": [ + "MyUserPool", + "Arn" + ] + }], + "type": "cognito_user_pools" + }, + "x-amazon-apigateway-authtype": "cognito_user_pools" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "MyApiWithCognitoAuthDeployment2da3114321": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiWithCognitoAuth" + }, + "Description": "RestApi deployment id: 2da3114321f3a31e83ea7029e5b167e14f36e7fb", + "StageName": "Stage" + } + }, + "MyApiWithLambdaRequestAuthMyLambdaRequestAuthAuthorizerPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Fn::GetAtt": [ + "MyAuthFn", + "Arn" + ] + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/authorizers/*", + { + "__ApiId__": { + "Ref": "MyApiWithLambdaRequestAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaTokenAuthDeployment613e605d96": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiWithLambdaTokenAuth" + }, + "Description": "RestApi deployment id: 613e605d962dfc3e8ac8e456357d807af4264223", + "StageName": "Stage" + } + }, + "MyApiWithLambdaRequestAuthProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiWithLambdaRequestAuthDeployment9a21d88fe2" + }, + "RestApiId": { + "Ref": "MyApiWithLambdaRequestAuth" + }, + "StageName": "Prod" + } + }, + "MyFnLambdaTokenPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/lambda-token", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApiWithLambdaTokenAuth" + } + } + ] + } + } + }, + "MyFnLambdaRequestPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/lambda-request", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "MyApiWithLambdaRequestAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaTokenAuth": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/lambda-token": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFn.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "MyLambdaTokenAuth": { + "in": "header", + "type": "apiKey", + "name": "Authorization", + "x-amazon-apigateway-authorizer": { + "type": "token", + "authorizerUri": { + "Fn::Sub": [ + "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations", + { + "__FunctionArn__": { + "Fn::GetAtt": [ + "MyAuthFn", + "Arn" + ] + } + } + ] + } + }, + "x-amazon-apigateway-authtype": "custom" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "MyApiWithLambdaTokenAuthProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiWithLambdaTokenAuthDeployment613e605d96" + }, + "RestApiId": { + "Ref": "MyApiWithLambdaTokenAuth" + }, + "StageName": "Prod" + } + }, + "MyFnLambdaRequestPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/lambda-request", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiWithLambdaRequestAuth" + } + } + ] + } + } + }, + "MyFnLambdaTokenPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/lambda-token", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiWithLambdaTokenAuth" + } + } + ] + } + } + }, + "MyFnCognitoPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "MyFn" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/cognito", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "MyApiWithCognitoAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaRequestAuth": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/lambda-request": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFn.Arn}/invocations" + } + }, + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "MyLambdaRequestAuth": { + "in": "header", + "type": "apiKey", + "name": "Unused", + "x-amazon-apigateway-authorizer": { + "type": "request", + "identitySource": "method.request.header.Authorization1", + "authorizerUri": { + "Fn::Sub": [ + "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${__FunctionArn__}/invocations", + { + "__FunctionArn__": { + "Fn::GetAtt": [ + "MyAuthFn", + "Arn" + ] + } + } + ] + } + }, + "x-amazon-apigateway-authtype": "custom" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "MyApiWithLambdaTokenAuthMyLambdaTokenAuthAuthorizerPermission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Fn::GetAtt": [ + "MyAuthFn", + "Arn" + ] + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/authorizers/*", + { + "__ApiId__": { + "Ref": "MyApiWithLambdaTokenAuth" + } + } + ] + } + } + }, + "MyApiWithLambdaRequestAuthDeployment9a21d88fe2": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "MyApiWithLambdaRequestAuth" + }, + "Description": "RestApi deployment id: 9a21d88fe25a74e9f2ca61175f4dd4d281b61d12", + "StageName": "Stage" + } + }, + "MyFnRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [{ + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + }] + } + } + }, + "MyApiWithCognitoAuthProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "DeploymentId": { + "Ref": "MyApiWithCognitoAuthDeployment2da3114321" + }, + "RestApiId": { + "Ref": "MyApiWithCognitoAuth" + }, + "StageName": "Prod" + } + }, + "MyFn": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "bucket", + "S3Key": "key" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFnRole", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "Tags": [{ + "Value": "SAM", + "Key": "lambda:createdBy" + }] + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/error_api_invalid_auth.json b/tests/translator/output/error_api_invalid_auth.json new file mode 100644 index 0000000000..bf11bdb6e9 --- /dev/null +++ b/tests/translator/output/error_api_invalid_auth.json @@ -0,0 +1,3 @@ +{ + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 14. Resource with id [AuthNotDictApi] is invalid. Type of property 'Auth' is invalid. Resource with id [AuthWithAdditionalPropertyApi] is invalid. Invalid value for 'Auth' property Resource with id [AuthWithDefinitionUriApi] is invalid. Auth works only with inline Swagger specified in 'DefinitionBody' property Resource with id [AuthWithInvalidDefinitionBodyApi] is invalid. Unable to add Auth configuration because 'DefinitionBody' does not contain a valid Swagger Resource with id [AuthWithMissingDefaultAuthorizerApi] is invalid. Unable to set DefaultAuthorizer because 'NotThere' was not defined in 'Authorizers' Resource with id [AuthorizersNotDictApi] is invalid. Authorizers must be a dictionary Resource with id [InvalidFunctionPayloadTypeApi] is invalid. MyLambdaAuthorizer Authorizer has invalid 'FunctionPayloadType': INVALID Resource with id [MissingAuthorizerFn] is invalid. Event with id [GetRoot] is invalid. Unable to set Authorizer [UnspecifiedAuthorizer] on API method [get] for path [/] because it wasn't defined in the API's Authorizers. Resource with id [NoApiAuthorizerFn] is invalid. Event with id [GetRoot] is invalid. Unable to set Authorizer [MyAuth] on API method [get] for path [/] because the related API does not define any Authorizers. Resource with id [NoAuthFn] is invalid. Event with id [GetRoot] is invalid. Unable to set Authorizer [MyAuth] on API method [get] for path [/] because the related API does not define any Authorizers. Resource with id [NoAuthorizersFn] is invalid. Event with id [GetRoot] is invalid. Unable to set Authorizer [MyAuth] on API method [get] for path [/] because the related API does not define any Authorizers. Resource with id [NoDefaultAuthorizerWithNoneFn] is invalid. Event with id [GetRoot] is invalid. Unable to set Authorizer on API method [get] for path [/] because 'NONE' is only a valid value when a DefaultAuthorizer on the API is specified. Resource with id [NoIdentityOnRequestAuthorizer] is invalid. MyLambdaRequestAuthorizer Authorizer must specify Identity with at least one of Headers, QueryStrings, StageVariables, or Context. Resource with id [NoIdentitySourceOnRequestAuthorizer] is invalid. MyLambdaRequestAuthorizer Authorizer must specify Identity with at least one of Headers, QueryStrings, StageVariables, or Context." +} \ No newline at end of file diff --git a/tests/translator/test_translator.py b/tests/translator/test_translator.py index 51704033a6..5145669453 100644 --- a/tests/translator/test_translator.py +++ b/tests/translator/test_translator.py @@ -104,6 +104,9 @@ class TestTranslatorEndToEnd(TestCase): 'implicit_api', 'explicit_api', 'api_endpoint_configuration', + 'api_with_auth_all_maximum', + 'api_with_auth_all_minimum', + 'api_with_auth_no_default', 'api_with_method_settings', 'api_with_binary_media_types', 'api_with_resource_refs', @@ -269,6 +272,7 @@ def _generate_new_deployment_hash(self, logical_id, dict_to_hash, rest_api_to_sw @pytest.mark.parametrize('testcase', [ 'error_api_duplicate_methods_same_path', + 'error_api_invalid_auth', 'error_api_invalid_definitionuri', 'error_api_invalid_definitionbody', 'error_api_invalid_restapiid', @@ -304,7 +308,7 @@ def _generate_new_deployment_hash(self, logical_id, dict_to_hash, rest_api_to_sw ]) @patch('boto3.session.Session.region_name', 'ap-southeast-1') def test_transform_invalid_document(testcase): - manifest = yaml.load(open(os.path.join(INPUT_FOLDER, testcase + '.yaml'), 'r')) + manifest = yaml_parse(open(os.path.join(INPUT_FOLDER, testcase + '.yaml'), 'r')) expected = json.load(open(os.path.join(OUTPUT_FOLDER, testcase + '.json'), 'r')) mock_policy_loader = MagicMock() diff --git a/versions/2016-10-31.md b/versions/2016-10-31.md index f7189794e0..497f0d773f 100644 --- a/versions/2016-10-31.md +++ b/versions/2016-10-31.md @@ -208,8 +208,8 @@ Property Name | Type | Description ---|:---:|--- Name | `string` | A name for the API Gateway RestApi resource. StageName | `string` | **Required** The name of the stage, which API Gateway uses as the first path segment in the invoke Uniform Resource Identifier (URI). -DefinitionUri | `string` | [S3 Location Object](#s3-location-object) | S3 URI or location to the Swagger document describing the API. Either one of `DefinitionUri` or `DefinitionBody` must be specified. -DefinitionBody | `JSON or YAML Object` | Swagger specification that describes your API. Either one of `DefinitionUri` or `DefinitionBody` must be specified. +DefinitionUri | `string` | [S3 Location Object](#s3-location-object) | S3 URI or location to the Swagger document describing the API. If neither `DefinitionUri` nor `DefinitionBody` are specified, SAM will generate a `DefinitionBody` for you based on your template configuration. +DefinitionBody | `JSON or YAML Object` | Swagger specification that describes your API. If neither `DefinitionUri` nor `DefinitionBody` are specified, SAM will generate a `DefinitionBody` for you based on your template configuration. CacheClusterEnabled | `boolean` | Indicates whether cache clustering is enabled for the stage. CacheClusterSize | `string` | The stage's cache cluster size. Variables | Map of `string` to `string` | A map (string to string map) that defines the stage variables, where the variable name is the key and the variable value is the value. Variable names are limited to alphanumeric characters. Values must match the following regular expression: `[A-Za-z0-9._~:/?#&=,-]+`. @@ -217,6 +217,7 @@ MethodSettings | [CloudFormation MethodSettings property](https://docs.aws.amazo EndpointConfiguration | `string` | Specify the type of endpoint for API endpoint. Value is either `REGIONAL`, `EDGE`, or `PRIVATE`. BinaryMediaTypes | List of `string` | List of MIME types that your API could return. Use this to enable binary support for APIs. Use `~1` instead of `/` in the mime types (See examples in [template.yaml](../examples/2016-10-31/implicit_api_settings/template.yaml)). Cors | `string` or [Cors Configuration](#cors-configuration) | Enable CORS for all your APIs. Specify the domain to allow as a string or specify a dictionary with additional [Cors Configuration](#cors-configuration). NOTE: Cors requires SAM to modify your Swagger definition. Hence it works only inline swagger defined with `DefinitionBody`. +Auth | [API Auth Object](#api-auth-object) | Auth configuration for this API. Define Lambda and Cognito `Authorizers` and specify a `DefaultAuthorizer` for this API. ##### Return values @@ -405,6 +406,7 @@ Property Name | Type | Description Path | `string` | **Required.** Uri path for which this function is invoked. MUST start with `/`. Method | `string` | **Required.** HTTP method for which this function is invoked. RestApiId | `string` | Identifier of a RestApi resource which MUST contain an operation with the given path and method. Typically, this is set to [reference](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-ref.html) an `AWS::Serverless::Api` resource defined in this template. If not defined, a default `AWS::Serverless::Api` resource is created using a generated Swagger document contains a union of all paths and methods defined by `Api` events defined in this template that do not specify a RestApiId. +Auth | [Function Auth Object](#function-auth-object) | Auth configuration for this specific Api+Path+Method. Useful for overriding the API's `DefaultAuthorizer` or setting auth config on an individual path when no `DefaultAuthorizer` is specified. ##### Example: Api event source object @@ -569,11 +571,20 @@ Properties: ``` ### Data Types + +- [S3 Location Object](#s3-location-object) +- [DeadLetterQueue Object](#deadletterqueue-object) +- [Cors Configuration](#cors-configuration) +- [API Auth Object](#api-auth-object) +- [Function Auth Object](#function-auth-object) + #### S3 Location Object + Specifies the location of an S3 object as a dictionary containing `Bucket`, `Key`, and optional `Version` properties. Example: -``` + +```yaml CodeUri: Bucket: mybucket-name Key: code.zip @@ -584,7 +595,8 @@ CodeUri: Specifies an SQS queue or SNS topic that AWS Lambda (Lambda) sends events to when it can't process them. For more information about DLQ functionality, refer to the officiall documentation at http://docs.aws.amazon.com/lambda/latest/dg/dlq.html. SAM will automatically add appropriate permission to your Lambda function execution role to give Lambda service access to the resource. `sqs:SendMessage` will be added for SQS queues and `sns:Publish` for SNS topics. Syntax: -``` + +```yaml DeadLetterQueue: Type: `SQS` or `SNS` TargetArn: ARN of the SQS queue or SNS topic to use as DLQ. @@ -611,7 +623,6 @@ DeploymentPreference: Enable and configure CORS for the APIs. Enabling CORS will allow your API to be called from other domains. Assume your API is served from 'www.example.com' and you want to allow. ```yaml - Cors: AllowMethods: Optional. String containing the HTTP methods to allow. # For example, "'GET,POST,DELETE'". If you omit this property, then SAM will automatically allow all the methods configured for each API. @@ -628,3 +639,52 @@ Cors: ``` > NOTE: HTTP spec requires the value of Allow properties to be a quoted string. So don't forget the additional quotes in the value. ie. "'www.example.com'" is correct whereas "www.example.com" is wrong + +#### API Auth Object + +Configure Auth on APIs. Define Lambda and Cognito `Authorizers` and specify a `DefaultAuthorizer`. For more information, see the documentation on [Lambda Authorizers](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html) and [Amazon Cognito User Pool Authorizers](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-integrate-with-cognito.html). + +```yaml +Auth: + DefaultAuthorizer: MyCognitoAuth # OPTIONAL + Authorizers: + MyCognitoAuth: + UserPoolArn: !GetAtt MyCognitoUserPool.Arn # Can also accept an array + Identity: # OPTIONAL + Header: MyAuthorizationHeader # OPTIONAL; Default: 'Authorization' + ValidationExpression: myauthvalidationexpression # OPTIONAL + + MyLambdaTokenAuth: + FunctionPayloadType: TOKEN # OPTIONAL; Defaults to 'TOKEN' when `FunctionArn` is specified + FunctionArn: !GetAtt MyAuthFunction.Arn + FunctionInvokeRole: arn:aws:iam::123456789012:role/S3Access # OPTIONAL + Identity: + Header: MyCustomAuthHeader # OPTIONAL; Default: 'Authorization' + ValidationExpression: mycustomauthexpression # OPTIONAL + ReauthorizeEvery: 20 # OPTIONAL; Service Default: 300 + + MyLambdaRequestAuth: + FunctionPayloadType: REQUEST + FunctionArn: !GetAtt MyAuthFunction.Arn + FunctionInvokeRole: arn:aws:iam::123456789012:role/S3Access # OPTIONAL + Identity: + # Must specify at least one of Headers, QueryStrings, StageVariables, or Context + Headers: # OPTIONAL + - Authorization1 + QueryStrings: # OPTIONAL + - Authorization2 + StageVariables: # OPTIONAL + - Authorization3 + Context: # OPTIONAL + - Authorization4 + ReauthorizeEvery: 0 # OPTIONAL; Service Default: 300 +``` + +#### Function Auth Object + +Configure Auth for a specific Api+Path+Method. + +```yaml +Auth: + Authorizer: MyCognitoAuth # OPTIONAL +``` \ No newline at end of file