Skip to content
Merged
29 changes: 28 additions & 1 deletion samtranslator/model/api/api_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,34 @@ def _add_auth(self):
self._set_default_authorizer(swagger_editor, authorizers, auth_properties.DefaultAuthorizer)

# Assign the Swagger back to template
self.definition_body = swagger_editor.swagger

self.definition_body = self._openapi_auth_postprocess(swagger_editor.swagger)

def _openapi_auth_postprocess(self, definition_body):
"""
Convert auth components to openapi 3 in definition body if OpenApiVersion flag is specified.

To maintain backward compatibility with existing swagger documents that contain both swagger
and openapi defined in the definition body, we return if there is "swagger" defined in the
definition body.

If the definition body contains "openapi" and not "swagger", we enforce that the OpenApiVersion
flag is specified for cases with auth.
"""
if definition_body.get('swagger') is not None:
return definition_body

if definition_body.get('openapi') is not None:
if self.open_api_version is None:
raise InvalidResourceException(
self.logical_id, "OpenApiVersion must be specified")

if self.open_api_version and re.match(SwaggerEditor.get_openapi_version_3_regex(), self.open_api_version):
if definition_body.get('securityDefinitions'):
definition_body['components'] = {}
definition_body['components']['securitySchemes'] = definition_body['securityDefinitions']
del definition_body['securityDefinitions']
return definition_body
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Next question: should we error on an else statement here? (using openapi and not swagger, and the openapi version doesn't match a version we support).

Or is this already caught elsewhere?


def _add_gateway_responses(self):
"""
Expand Down
2 changes: 1 addition & 1 deletion samtranslator/plugins/globals/globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def fix_openapi_definitions(cls, template):
:param dict template: SAM template
:return: Modified SAM template with corrected swagger doc matching the OpenApiVersion.
"""
resources = template["Resources"]
resources = template.get("Resources") or {}

for _, resource in resources.items():
if ("Type" in resource) and (resource["Type"] == cls._API_TYPE):
Expand Down
12 changes: 11 additions & 1 deletion tests/plugins/globals/test_globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,17 @@ class TestGlobalsOpenApi(TestCase):
}
}
},

{
"name": "No Resources",
"input": {
"some": "other",
"swagger": "donottouch"
},
"expected": {
"some": "other",
"swagger": "donottouch"
}
}
]

def test_openapi_postprocess(self):
Expand Down
106 changes: 106 additions & 0 deletions tests/translator/input/api_with_auth_all_maximum_openapi_3.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
Resources:
MyApi:
Type: "AWS::Serverless::Api"
Properties:
StageName: Prod
OpenApiVersion: '3.0.1'
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
81 changes: 81 additions & 0 deletions tests/translator/input/api_with_auth_all_minimum_openapi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
Globals:
Api:
OpenApiVersion: '3.0.1'
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
MyAuthFn:
Type: AWS::Serverless::Function
Properties:
CodeUri: s3://bucketname/thumbnails.zip
Handler: index.handler
Runtime: nodejs8.10
MyFn:
Type: AWS::Serverless::Function
Properties:
CodeUri: s3://bucketname/thumbnails.zip
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
MyUserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: UserPoolName
Policies:
PasswordPolicy:
MinimumLength: 8
UsernameAttributes:
- email
Schema:
- AttributeDataType: String
Name: email
Required: false
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
Globals:
Api:
Name: "some api"
CacheClusterEnabled: True
CacheClusterSize: "1.6"
Auth:
DefaultAuthorizer: MyCognitoAuth
Authorizers:
MyCognitoAuth:
UserPoolArn: !GetAtt MyUserPool.Arn
Variables:
SomeVar: Value

Resources:
ImplicitApiFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: s3://sam-demo-bucket/member_portal.zip
Handler: index.gethtml
Runtime: nodejs4.3
Events:
GetHtml:
Type: Api
Properties:
Path: /
Method: get

ExplicitApi:
Type: AWS::Serverless::Api
Properties:
StageName: SomeStage
DefinitionBody:
openapi: 3.1.1
swagger: 2.0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a user wants to do this, we can let them, and allow them to deal with whatever ApiGateway thinks about it. I think this will be fine (but we definitely don't recommend doing anything of the sort).

info:
version: '1.0'
title: !Ref AWS::StackName
paths:
"/":
get:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations
responses: {}

MyUserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: UserPoolName
Policies:
PasswordPolicy:
MinimumLength: 8
UsernameAttributes:
- email
Schema:
- AttributeDataType: String
Name: email
Required: false
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
Globals:
Api:
Name: "some api"
CacheClusterEnabled: True
CacheClusterSize: "1.6"
Auth:
DefaultAuthorizer: MyCognitoAuth
Authorizers:
MyCognitoAuth:
UserPoolArn: !GetAtt MyUserPool.Arn
Variables:
SomeVar: Value

Resources:
ImplicitApiFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: s3://sam-demo-bucket/member_portal.zip
Handler: index.gethtml
Runtime: nodejs4.3
Events:
GetHtml:
Type: Api
Properties:
Path: /
Method: get

ExplicitApi:
Type: AWS::Serverless::Api
Properties:
StageName: SomeStage
DefinitionBody:
openapi: 3.1.1
info:
version: '1.0'
title: !Ref AWS::StackName
paths:
"/":
get:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations
responses: {}

MyUserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: UserPoolName
Policies:
PasswordPolicy:
MinimumLength: 8
UsernameAttributes:
- email
Schema:
- AttributeDataType: String
Name: email
Required: false
Loading