Skip to content

Commit

Permalink
feat: sam support to add resource policies to api properties (#1045)
Browse files Browse the repository at this point in the history
  • Loading branch information
praneetap committed Aug 1, 2019
1 parent 05eae60 commit 5090228
Show file tree
Hide file tree
Showing 18 changed files with 1,045 additions and 4 deletions.
11 changes: 11 additions & 0 deletions examples/2016-10-31/api_resource_policy/README.md
@@ -0,0 +1,11 @@
# Api Resource Policy Event Source Example

Example SAM template for adding Custom Resource Policy to Api.

## Running the example

```bash
# Replace YOUR_S3_ARTIFACTS_BUCKET
aws cloudformation package --template-file template.yaml --output-template-file cfn-transformed-template.yaml --s3-bucket YOUR_S3_ARTIFACTS_BUCKET
aws cloudformation deploy --template-file ./cfn-transformed-template.yaml --stack-name example-resource-policy --capabilities CAPABILITY_IAM
```
37 changes: 37 additions & 0 deletions examples/2016-10-31/api_resource_policy/template.yaml
@@ -0,0 +1,37 @@
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Globals:
Api:
Auth:
ResourcePolicy:
CustomStatements: [{
"Effect": "Allow",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": "execute-api:*/*/*",
"Condition": {
"IpAddress": {
"aws:SourceIp": "1.2.3.4"
}
}
}]
Resources:
MyFunction:
Type: AWS::Serverless::Function
Properties:
InlineCode: |
exports.handler = async (event) => {
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
Handler: index.handler
Runtime: nodejs8.10
Events:
Api:
Type: Api
Properties:
Method: Put
Path: /get
7 changes: 5 additions & 2 deletions samtranslator/model/api/api_generator.py
Expand Up @@ -23,8 +23,8 @@

AuthProperties = namedtuple("_AuthProperties",
["Authorizers", "DefaultAuthorizer", "InvokeRole", "AddDefaultAuthorizerToCorsPreflight",
"ApiKeyRequired"])
AuthProperties.__new__.__defaults__ = (None, None, None, True, None)
"ApiKeyRequired", "ResourcePolicy"])
AuthProperties.__new__.__defaults__ = (None, None, None, True, None, None)

GatewayResponseProperties = ["ResponseParameters", "ResponseTemplates", "StatusCode"]

Expand Down Expand Up @@ -325,6 +325,9 @@ def _add_auth(self):
swagger_editor.add_apikey_security_definition()
self._set_default_apikey_required(swagger_editor)

if auth_properties.ResourcePolicy:
swagger_editor.add_resource_policy(auth_properties.ResourcePolicy)

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

def _add_gateway_responses(self):
Expand Down
2 changes: 1 addition & 1 deletion samtranslator/plugins/globals/globals.py
Expand Up @@ -44,7 +44,7 @@ class Globals(object):
# StageName: Because StageName cannot be overridden for Implicit APIs because of the current plugin
# architecture
SamResourceType.Api.value: [
'Auth',
"Auth",
"Name",
"DefinitionUri",
"CacheClusterEnabled",
Expand Down
34 changes: 33 additions & 1 deletion samtranslator/swagger/swagger.py
Expand Up @@ -21,6 +21,7 @@ class SwaggerEditor(object):
_X_APIGW_BINARY_MEDIA_TYPES = 'x-amazon-apigateway-binary-media-types'
_CONDITIONAL_IF = "Fn::If"
_X_APIGW_GATEWAY_RESPONSES = 'x-amazon-apigateway-gateway-responses'
_X_APIGW_POLICY = 'x-amazon-apigateway-policy'
_X_ANY_METHOD = 'x-amazon-apigateway-any-method'

def __init__(self, doc):
Expand All @@ -39,6 +40,7 @@ def __init__(self, doc):
self.paths = self._doc["paths"]
self.security_definitions = self._doc.get("securityDefinitions", {})
self.gateway_responses = self._doc.get(self._X_APIGW_GATEWAY_RESPONSES, {})
self.resource_policy = self._doc.get(self._X_APIGW_POLICY, {})
self.definitions = self._doc.get('definitions', {})

def get_path(self, path):
Expand Down Expand Up @@ -614,7 +616,6 @@ def add_auth_to_method(self, path, method_name, auth, api):
:param string path: Path name
:param string method_name: Method name
:param dict auth: Auth configuration such as Authorizers, ApiKeyRequired, ResourcePolicy
(Authorizers and ApiKeyRequired supported currently)
:param dict api: Reference to the related Api's properties as defined in the template.
"""
method_authorizer = auth and auth.get('Authorizer')
Expand Down Expand Up @@ -788,6 +789,37 @@ def add_models(self, models):

self.definitions[model_name.lower()] = schema

def add_resource_policy(self, resource_policy):
"""
Add resource policy definition to Swagger.
:param dict resource_policy: Dictionary of resource_policy statements which gets translated
:return:
"""
if resource_policy is None:
return

custom_statements = resource_policy.get('CustomStatements')

if custom_statements is not None:
if not isinstance(custom_statements, list):
custom_statements = [custom_statements]

self.resource_policy['Version'] = '2012-10-17'
if self.resource_policy.get('Statement') is None:
self.resource_policy['Statement'] = custom_statements
else:
statement = self.resource_policy['Statement']
if isinstance(statement, list):
statement.extend(custom_statements)
else:
statement = [statement]
statement.extend(custom_statements)

self.resource_policy['Statement'] = statement

self._doc[self._X_APIGW_POLICY] = self.resource_policy

@property
def swagger(self):
"""
Expand Down
12 changes: 12 additions & 0 deletions tests/translator/input/api_with_resource_policy.yaml
@@ -0,0 +1,12 @@
Resources:
ExplicitApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
Auth:
ResourcePolicy:
CustomStatements: {
Action: 'execute-api:Invoke',
Resource: ['execute-api:/*/*/*']
}

38 changes: 38 additions & 0 deletions tests/translator/input/api_with_resource_policy_global.yaml
@@ -0,0 +1,38 @@
Globals:
Api:
Auth:
ResourcePolicy:
CustomStatements: [{
Action: 'execute-api:Invoke',
Resource: ['execute-api:/*/*/*']
},
{
Action: 'execute-api:blah',
Resource: ['execute-api:/*/*/*']
}]

Resources:
ExplicitApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
DefinitionBody: {
"info": {
"version": "1.0",
"title": {
"Ref": "AWS::StackName"
}
},
"paths": {},
"swagger": "2.0",
"x-amazon-apigateway-policy": {
"Version": "2012-10-17",
"Statement": {
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
}
}
}
}
@@ -0,0 +1,26 @@
Globals:
Api:
Auth:
ResourcePolicy:
CustomStatements: [{
Action: 'execute-api:Invoke',
Resource: ['execute-api:/*/*/*']
},
{
Action: 'execute-api:blah',
Resource: ['execute-api:/*/*/*']
}]
Resources:
MinimalFunction:
Type: 'AWS::Serverless::Function'
Properties:
CodeUri: s3://sam-demo-bucket/hello.zip
Handler: hello.handler
Runtime: python2.7
Events:
AddItem:
Type: Api
Properties:
Path: /add
Method: post

52 changes: 52 additions & 0 deletions tests/translator/output/api_with_resource_policy.json
@@ -0,0 +1,52 @@
{
"Resources": {
"ExplicitApiDeploymente11dac9531": {
"Type": "AWS::ApiGateway::Deployment",
"Properties": {
"RestApiId": {
"Ref": "ExplicitApi"
},
"Description": "RestApi deployment id: e11dac9531e1328d9249c42ac3e40044b4159d60",
"StageName": "Stage"
}
},
"ExplicitApiProdStage": {
"Type": "AWS::ApiGateway::Stage",
"Properties": {
"DeploymentId": {
"Ref": "ExplicitApiDeploymente11dac9531"
},
"RestApiId": {
"Ref": "ExplicitApi"
},
"StageName": "Prod"
}
},
"ExplicitApi": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"Body": {
"info": {
"version": "1.0",
"title": {
"Ref": "AWS::StackName"
}
},
"paths": {},
"swagger": "2.0",
"x-amazon-apigateway-policy": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Resource": [
"execute-api:/*/*/*"
]
}
]
}
}
}
}
}
}
65 changes: 65 additions & 0 deletions tests/translator/output/api_with_resource_policy_global.json
@@ -0,0 +1,65 @@
{
"Resources": {
"ExplicitApiProdStage": {
"Type": "AWS::ApiGateway::Stage",
"Properties": {
"DeploymentId": {
"Ref": "ExplicitApiDeployment8d22456d58"
},
"RestApiId": {
"Ref": "ExplicitApi"
},
"StageName": "Prod"
}
},
"ExplicitApiDeployment8d22456d58": {
"Type": "AWS::ApiGateway::Deployment",
"Properties": {
"RestApiId": {
"Ref": "ExplicitApi"
},
"Description": "RestApi deployment id: 8d22456d5883ad51c72f5d9be988d14f0a41182e",
"StageName": "Stage"
}
},
"ExplicitApi": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"Body": {
"info": {
"version": "1.0",
"title": {
"Ref": "AWS::StackName"
}
},
"paths": {},
"swagger": "2.0",
"x-amazon-apigateway-policy": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
}
},
{
"Action": "execute-api:Invoke",
"Resource": [
"execute-api:/*/*/*"
]
},
{
"Action": "execute-api:blah",
"Resource": [
"execute-api:/*/*/*"
]
}
]
}
}
}
}
}
}

0 comments on commit 5090228

Please sign in to comment.