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 _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, attributes=self.resource_attributes) 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", attributes=self.passthrough_resource_attributes ) 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 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(), } if authorizer_type == "COGNITO_USER_POOLS": swagger[APIGATEWAY_AUTHORIZER_KEY] = { "type": self._get_swagger_authorizer_type(), "providerARNs": self._get_user_pool_arn_array(), } elif authorizer_type == "LAMBDA": swagger[APIGATEWAY_AUTHORIZER_KEY] = { "type": self._get_swagger_authorizer_type() } 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_permission(self, resources_to_link, stage, suffix): # It turns out that APIGW doesn't like trailing slashes in paths (#665) # and removes as a part of their behaviour, but this isn't documented. # The regex removes the tailing slash to ensure the permission works as intended path = re.sub(r'^(.+)/$', r'\1', self.Path) if not stage or not suffix: raise RuntimeError("Could not add permission to lambda function.") path = re.sub(r'{([a-zA-Z0-9._-]+|proxy\+)}', '*', path) method = '*' if self.Method.lower() == 'any' else self.Method.upper() api_id = self.RestApiId # RestApiId can be a simple string or intrinsic function like !Ref. Using Fn::Sub will handle both cases resource = '${__ApiId__}/' + '${__Stage__}/' + method + path partition = ArnGenerator.get_partition_name() source_arn = fnSub( ArnGenerator.generate_arn(partition=partition, service='execute-api', resource=resource), { "__ApiId__": api_id, "__Stage__": stage }) return self._construct_permission(resources_to_link['function'], source_arn=source_arn, suffix=suffix)
def get_xray_managed_policy_name(): # use previous (old) policy name for regular regions # for china and gov regions, use the newer policy name partition_name = ArnGenerator.get_partition_name() if partition_name == "aws": return "AWSXrayWriteOnlyAccess" return "AWSXRayDaemonWriteAccess"
def to_cloudformation(self, **kwargs): function = kwargs.get('function') if not function: raise TypeError("Missing required keyword argument: function") resources = [] resource = 'rule/${RuleName}' partition = ArnGenerator.get_partition_name() source_arn = fnSub( ArnGenerator.generate_arn(partition=partition, service='iot', resource=resource), {'RuleName': ref(self.logical_id)}) source_account = fnSub('${AWS::AccountId}') resources.append( self._construct_permission(function, source_arn=source_arn, source_account=source_account)) resources.append(self._construct_iot_rule(function)) return resources
def _add_swagger_integration(self, api, function): """Adds the path and method for this Api event source to the Swagger body for the provided RestApi. :param model.apigateway.ApiGatewayRestApi rest_api: the RestApi to which the path and method should be added. """ swagger_body = api.get("DefinitionBody") if swagger_body is None: return function_arn = function.get_runtime_attr('arn') partition = ArnGenerator.get_partition_name() uri = fnSub( 'arn:' + partition + ':apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/' + make_shorthand(function_arn) + '/invocations') editor = SwaggerEditor(swagger_body) if editor.has_integration(self.Path, self.Method): # Cannot add the Lambda Integration, if it is already present raise InvalidEventException( self.relative_id, 'API method "{method}" defined multiple times for path "{path}".' .format(method=self.Method, path=self.Path)) editor.add_lambda_integration(self.Path, self.Method, uri) api["DefinitionBody"] = editor.swagger
def _codedeploy_iam_role(self): iam_role = IAMRole(CODE_DEPLOY_SERVICE_ROLE_LOGICAL_ID) iam_role.AssumeRolePolicyDocument = { "Version": "2012-10-17", "Statement": [ { "Action": ["sts:AssumeRole"], "Effect": "Allow", "Principal": {"Service": ["codedeploy.amazonaws.com"]}, } ], } # CodeDeploy has a new managed policy. We cannot update any existing partitions, without customer reach out # that support AWSCodeDeployRoleForLambda since this could regress stacks that are currently deployed. if ArnGenerator.get_partition_name() in ["aws-iso", "aws-iso-b"]: iam_role.ManagedPolicyArns = [ ArnGenerator.generate_aws_managed_policy_arn("service-role/AWSCodeDeployRoleForLambdaLimited") ] else: iam_role.ManagedPolicyArns = [ ArnGenerator.generate_aws_managed_policy_arn("service-role/AWSCodeDeployRoleForLambda") ] return iam_role
def test_get_partition_name_from_boto_session(self): ArnGenerator.BOTO_SESSION_REGION_NAME = "us-east-1" actual = ArnGenerator.get_partition_name() self.assertEqual(actual, "aws") ArnGenerator.BOTO_SESSION_REGION_NAME = None
def get_source_arn(self): resource = "log-group:${__LogGroupName__}:*" partition = ArnGenerator.get_partition_name() return fnSub( ArnGenerator.generate_arn(partition=partition, service="logs", resource=resource), {"__LogGroupName__": self.LogGroupName}, )
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 _add_swagger_integration(self, api, function): """Adds the path and method for this Api event source to the Swagger body for the provided RestApi. :param model.apigateway.ApiGatewayRestApi rest_api: the RestApi to which the path and method should be added. """ swagger_body = api.get("DefinitionBody") if swagger_body is None: return function_arn = function.get_runtime_attr('arn') partition = ArnGenerator.get_partition_name() uri = fnSub('arn:'+partition+':apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/' + make_shorthand(function_arn) + '/invocations') editor = SwaggerEditor(swagger_body) if editor.has_integration(self.Path, self.Method): # Cannot add the Lambda Integration, if it is already present raise InvalidEventException( self.relative_id, 'API method "{method}" defined multiple times for path "{path}".'.format( 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
def test_get_partition_name_from_boto_session(self): boto_session_mock = Mock() boto_session_mock.region_name = "us-east-1" ArnGenerator.class_boto_session = boto_session_mock actual = ArnGenerator.get_partition_name() self.assertEqual(actual, "aws") ArnGenerator.class_boto_session = None
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() } if authorizer_type == 'COGNITO_USER_POOLS': swagger[APIGATEWAY_AUTHORIZER_KEY] = { 'type': self._get_swagger_authorizer_type(), 'providerARNs': self._get_user_pool_arn_array() } elif authorizer_type == 'LAMBDA': swagger[APIGATEWAY_AUTHORIZER_KEY] = { 'type': self._get_swagger_authorizer_type() } 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_permission(self, resources_to_link, stage, suffix): if not stage or not suffix: raise RuntimeError("Could not add permission to lambda function.") path = self.Path.replace('{proxy+}', '*') api_id = self.RestApiId # RestApiId can be a simple string or intrinsic function like !Ref. Using Fn::Sub will handle both cases resource = '${__ApiId__}/' + '${__Stage__}/' + self.Method.upper() + path partition = ArnGenerator.get_partition_name() source_arn = fnSub(ArnGenerator.generate_arn(partition=partition, service='execute-api', resource=resource), {"__ApiId__": api_id, "__Stage__": stage}) return self._construct_permission(resources_to_link['function'], source_arn=source_arn, suffix=suffix)
def _get_permission(self, resources_to_link, stage, suffix): if not stage or not suffix: raise RuntimeError("Could not add permission to lambda function.") path = self.Path.replace('{proxy+}', '*') method = '*' if self.Method.lower() == 'any' else self.Method.upper() api_id = self.RestApiId # RestApiId can be a simple string or intrinsic function like !Ref. Using Fn::Sub will handle both cases resource = '${__ApiId__}/' + '${__Stage__}/' + method + path partition = ArnGenerator.get_partition_name() source_arn = fnSub(ArnGenerator.generate_arn(partition=partition, service='execute-api', resource=resource), {"__ApiId__": api_id, "__Stage__": stage}) return self._construct_permission(resources_to_link['function'], source_arn=source_arn, suffix=suffix)
def add_pseudo_parameter_values(self, session=None): """ Add pseudo parameter values :return: parameter values that have pseudo parameter in it """ if session is None: session = boto3.session.Session() if not session.region_name: raise NoRegionFound("AWS Region cannot be found") if "AWS::Region" not in self.parameter_values: self.parameter_values["AWS::Region"] = session.region_name if "AWS::Partition" not in self.parameter_values: self.parameter_values[ "AWS::Partition"] = ArnGenerator.get_partition_name( session.region_name)
def to_cloudformation(self, **kwargs): function = kwargs.get('function') if not function: raise TypeError("Missing required keyword argument: function") resources = [] resource = 'rule/${RuleName}' partition = ArnGenerator.get_partition_name() source_arn = fnSub(ArnGenerator.generate_arn(partition=partition, service='iot', resource=resource), {'RuleName': ref(self.logical_id)}) source_account = fnSub('${AWS::AccountId}') resources.append(self._construct_permission(function, source_arn=source_arn, source_account=source_account)) resources.append(self._construct_iot_rule(function)) return resources
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 _get_permission(self, resources_to_link, stage, suffix): # It turns out that APIGW doesn't like trailing slashes in paths (#665) # and removes as a part of their behaviour, but this isn't documented. # The regex removes the tailing slash to ensure the permission works as intended path = re.sub(r'^(.+)/$', r'\1', self.Path) if not stage or not suffix: raise RuntimeError("Could not add permission to lambda function.") path = path.replace('{proxy+}', '*') method = '*' if self.Method.lower() == 'any' else self.Method.upper() api_id = self.RestApiId # RestApiId can be a simple string or intrinsic function like !Ref. Using Fn::Sub will handle both cases resource = '${__ApiId__}/' + '${__Stage__}/' + method + path partition = ArnGenerator.get_partition_name() source_arn = fnSub(ArnGenerator.generate_arn(partition=partition, service='execute-api', resource=resource), {"__ApiId__": api_id, "__Stage__": stage}) return self._construct_permission(resources_to_link['function'], source_arn=source_arn, suffix=suffix)
def _get_permission(self, resources_to_link, stage, suffix): # It turns out that APIGW doesn't like trailing slashes in paths (#665) # and removes as a part of their behaviour, but this isn't documented. # The regex removes the tailing slash to ensure the permission works as intended path = re.sub(r"^(.+)/$", r"\1", self.Path) if not stage or not suffix: raise RuntimeError("Could not add permission to lambda function.") path = SwaggerEditor.get_path_without_trailing_slash(path) method = "*" if self.Method.lower() == "any" else self.Method.upper() api_id = self.RestApiId # RestApiId can be a simple string or intrinsic function like !Ref. Using Fn::Sub will handle both cases resource = "${__ApiId__}/" + "${__Stage__}/" + method + path partition = ArnGenerator.get_partition_name() source_arn = fnSub( ArnGenerator.generate_arn(partition=partition, service="execute-api", resource=resource), {"__ApiId__": api_id, "__Stage__": stage}, ) return self._construct_permission(resources_to_link["function"], source_arn=source_arn, suffix=suffix)
def get_source_arn(self): resource = "log-group:${__LogGroupName__}:*" partition = ArnGenerator.get_partition_name() return fnSub(ArnGenerator.generate_arn(partition=partition, service='logs', resource=resource), {'__LogGroupName__': self.LogGroupName})
def test_get_partition_name(self, region, expected_partition): self.assertEqual(expected_partition, ArnGenerator.get_partition_name(region=region))
def test_get_partition_name_raise_NoRegionFound(self): with self.assertRaises(NoRegionFound): ArnGenerator.get_partition_name(None)
def generate_openapi(self): """ Generates OAS for the securitySchemes section """ authorizer_type = self._get_auth_type() if authorizer_type == "JWT": openapi = {"type": "oauth2"} openapi[APIGATEWAY_AUTHORIZER_KEY] = { "jwtConfiguration": self.jwt_configuration, "identitySource": self.id_source, "type": "jwt", } if authorizer_type == "REQUEST": openapi = { "type": "apiKey", "name": "Unused", "in": "header", } openapi[APIGATEWAY_AUTHORIZER_KEY] = {"type": "request"} # Generate the lambda arn 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}, ) openapi[APIGATEWAY_AUTHORIZER_KEY][ "authorizerUri"] = authorizer_uri # Set authorizerCredentials if present function_invoke_role = self._get_function_invoke_role() if function_invoke_role: openapi[APIGATEWAY_AUTHORIZER_KEY][ "authorizerCredentials"] = function_invoke_role # Set authorizerResultTtlInSeconds if present reauthorize_every = self._get_reauthorize_every() if reauthorize_every is not None: openapi[APIGATEWAY_AUTHORIZER_KEY][ "authorizerResultTtlInSeconds"] = reauthorize_every # Set identitySource if present if self.identity: openapi[APIGATEWAY_AUTHORIZER_KEY][ "identitySource"] = self._get_identity_source() # Set authorizerPayloadFormatVersion. It's a required parameter openapi[APIGATEWAY_AUTHORIZER_KEY][ "authorizerPayloadFormatVersion"] = self.authorizer_payload_format_version # Set authorizerPayloadFormatVersion. It's a required parameter if self.enable_simple_responses: openapi[APIGATEWAY_AUTHORIZER_KEY][ "enableSimpleResponses"] = self.enable_simple_responses return openapi
def test_get_partition_name(self, region, expected): actual = ArnGenerator.get_partition_name(region) self.assertEqual(actual, expected)
def test_get_partition_name_when_region_not_provided( self, region, expected_partition): with patch("boto3.session.Session.region_name", region): self.assertEqual(expected_partition, ArnGenerator.get_partition_name())
def _add_swagger_integration(self, api, function): """Adds the path and method for this Api event source to the Swagger body for the provided RestApi. :param model.apigateway.ApiGatewayRestApi rest_api: the RestApi to which the path and method should be added. """ swagger_body = api.get("DefinitionBody") if swagger_body is None: return function_arn = function.get_runtime_attr('arn') partition = ArnGenerator.get_partition_name() uri = fnSub('arn:' + partition + ':apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/' + make_shorthand(function_arn) + '/invocations') editor = SwaggerEditor(swagger_body) if editor.has_integration(self.Path, self.Method): # Cannot add the Lambda Integration, if it is already present raise InvalidEventException( self.relative_id, 'API method "{method}" defined multiple times for path "{path}".'.format( method=self.Method, path=self.Path)) condition = None if CONDITION in function.resource_attributes: condition = function.resource_attributes[CONDITION] editor.add_lambda_integration(self.Path, self.Method, uri, self.Auth, api.get('Auth'), condition=condition) if self.Auth: method_authorizer = self.Auth.get('Authorizer') api_auth = api.get('Auth') if method_authorizer: api_authorizers = api_auth and api_auth.get('Authorizers') if method_authorizer != 'AWS_IAM': if method_authorizer != 'NONE' and 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)) apikey_required_setting = self.Auth.get('ApiKeyRequired') apikey_required_setting_is_false = apikey_required_setting is not None and not apikey_required_setting if apikey_required_setting_is_false and not api_auth.get('ApiKeyRequired'): raise InvalidEventException( self.relative_id, 'Unable to set ApiKeyRequired [False] on API method [{method}] for path [{path}] ' 'because the related API does not specify any ApiKeyRequired.'.format( method=self.Method, path=self.Path)) if method_authorizer or apikey_required_setting is not None: editor.add_auth_to_method(api=api, path=self.Path, method_name=self.Method, auth=self.Auth) if self.Auth.get('ResourcePolicy'): resource_policy = self.Auth.get('ResourcePolicy') editor.add_resource_policy(resource_policy=resource_policy, path=self.Path, api_id=self.RestApiId.get('Ref'), stage=self.Stage) if self.RequestModel: method_model = self.RequestModel.get('Model') if method_model: api_models = api.get('Models') if not api_models: raise InvalidEventException( self.relative_id, 'Unable to set RequestModel [{model}] on API method [{method}] for path [{path}] ' 'because the related API does not define any Models.'.format( model=method_model, method=self.Method, path=self.Path)) if not api_models.get(method_model): raise InvalidEventException( self.relative_id, 'Unable to set RequestModel [{model}] on API method [{method}] for path [{path}] ' 'because it wasn\'t defined in the API\'s Models.'.format( model=method_model, method=self.Method, path=self.Path)) editor.add_request_model_to_method(path=self.Path, method_name=self.Method, request_model=self.RequestModel) if self.RequestParameters: default_value = { 'Required': False, 'Caching': False } parameters = [] for parameter in self.RequestParameters: if isinstance(parameter, dict): parameter_name, parameter_value = next(iter(parameter.items())) if not re.match('method\.request\.(querystring|path|header)\.', parameter_name): raise InvalidEventException( self.relative_id, "Invalid value for 'RequestParameters' property. Keys must be in the format " "'method.request.[querystring|path|header].{value}', " "e.g 'method.request.header.Authorization'.") if not isinstance(parameter_value, dict) or not all(key in REQUEST_PARAMETER_PROPERTIES for key in parameter_value.keys()): raise InvalidEventException( self.relative_id, "Invalid value for 'RequestParameters' property. Values must be an object, " "e.g { Required: true, Caching: false }") settings = default_value.copy() settings.update(parameter_value) settings.update({'Name': parameter_name}) parameters.append(settings) elif isinstance(parameter, string_types): if not re.match('method\.request\.(querystring|path|header)\.', parameter): raise InvalidEventException( self.relative_id, "Invalid value for 'RequestParameters' property. Keys must be in the format " "'method.request.[querystring|path|header].{value}', " "e.g 'method.request.header.Authorization'.") settings = default_value.copy() settings.update({'Name': parameter}) parameters.append(settings) else: raise InvalidEventException( self.relative_id, "Invalid value for 'RequestParameters' property. Property must be either a string or an object") editor.add_request_parameters_to_method(path=self.Path, method_name=self.Method, request_parameters=parameters) api["DefinitionBody"] = editor.swagger
def _add_swagger_integration(self, api, function): """Adds the path and method for this Api event source to the Swagger body for the provided RestApi. :param model.apigateway.ApiGatewayRestApi rest_api: the RestApi to which the path and method should be added. """ swagger_body = api.get("DefinitionBody") if swagger_body is None: return function_arn = function.get_runtime_attr('arn') partition = ArnGenerator.get_partition_name() uri = fnSub( 'arn:' + partition + ':apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/' + make_shorthand(function_arn) + '/invocations') editor = SwaggerEditor(swagger_body) if editor.has_integration(self.Path, self.Method): # Cannot add the Lambda Integration, if it is already present raise InvalidEventException( self.relative_id, 'API method "{method}" defined multiple times for path "{path}".' .format(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
def _add_swagger_integration(self, api, function): """Adds the path and method for this Api event source to the Swagger body for the provided RestApi. :param model.apigateway.ApiGatewayRestApi rest_api: the RestApi to which the path and method should be added. """ swagger_body = api.get("DefinitionBody") if swagger_body is None: return function_arn = function.get_runtime_attr('arn') partition = ArnGenerator.get_partition_name() uri = fnSub( 'arn:' + partition + ':apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/' + make_shorthand(function_arn) + '/invocations') editor = SwaggerEditor(swagger_body) if editor.has_integration(self.Path, self.Method): # Cannot add the Lambda Integration, if it is already present raise InvalidEventException( self.relative_id, 'API method "{method}" defined multiple times for path "{path}".' .format(method=self.Method, path=self.Path)) condition = None if CONDITION in function.resource_attributes: condition = function.resource_attributes[CONDITION] editor.add_lambda_integration(self.Path, self.Method, uri, self.Auth, api.get('Auth'), condition=condition) if self.Auth: method_authorizer = self.Auth.get('Authorizer') api_auth = api.get('Auth') if method_authorizer: api_authorizers = api_auth and api_auth.get('Authorizers') if method_authorizer != 'AWS_IAM': 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)) apikey_required_setting = self.Auth.get('ApiKeyRequired') apikey_required_setting_is_false = apikey_required_setting is not None and not apikey_required_setting if apikey_required_setting_is_false and not api_auth.get( 'ApiKeyRequired'): raise InvalidEventException( self.relative_id, 'Unable to set ApiKeyRequired [False] on API method [{method}] for path [{path}] ' 'because the related API does not specify any ApiKeyRequired.' .format(method=self.Method, path=self.Path)) if method_authorizer or apikey_required_setting is not None: editor.add_auth_to_method(api=api, path=self.Path, method_name=self.Method, auth=self.Auth) if self.RequestModel: method_model = self.RequestModel.get('Model') if method_model: api_models = api.get('Models') if not api_models: raise InvalidEventException( self.relative_id, 'Unable to set RequestModel [{model}] on API method [{method}] for path [{path}] ' 'because the related API does not define any Models.'. format(model=method_model, method=self.Method, path=self.Path)) if not api_models.get(method_model): raise InvalidEventException( self.relative_id, 'Unable to set RequestModel [{model}] on API method [{method}] for path [{path}] ' 'because it wasn\'t defined in the API\'s Models.'. format(model=method_model, method=self.Method, path=self.Path)) editor.add_request_model_to_method( path=self.Path, method_name=self.Method, request_model=self.RequestModel) api["DefinitionBody"] = editor.swagger