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 _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
Ejemplo n.º 3
0
    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_role(self, managed_policy_map):
        """Constructs a Lambda execution role based on this SAM function's Policies property.

        :returns: the generated IAM Role
        :rtype: model.iam.IAMRole
        """
        execution_role = IAMRole(self.logical_id + 'Role')
        execution_role.AssumeRolePolicyDocument = IAMRolePolicies.lambda_assume_role_policy()

        managed_policy_arns = [ArnGenerator.generate_aws_managed_policy_arn('service-role/AWSLambdaBasicExecutionRole')]
        if self.Tracing:
            managed_policy_arns.append(ArnGenerator.generate_aws_managed_policy_arn('AWSXrayWriteOnlyAccess'))

        function_policies = FunctionPolicies({"Policies": self.Policies},
                                             # No support for policy templates in the "core"
                                             policy_template_processor=None)
        policy_documents = []

        if self.DeadLetterQueue:
            policy_documents.append(IAMRolePolicies.dead_letter_queue_policy(
                self.dead_letter_queue_policy_actions[self.DeadLetterQueue['Type']],
                self.DeadLetterQueue['TargetArn']))

        for index, policy_entry in enumerate(function_policies.get()):

            if policy_entry.type is PolicyTypes.POLICY_STATEMENT:

                policy_documents.append({
                    'PolicyName': execution_role.logical_id + 'Policy' + str(index),
                    'PolicyDocument': policy_entry.data
                })
            elif policy_entry.type is PolicyTypes.MANAGED_POLICY:

                # There are three options:
                #   Managed Policy Name (string): Try to convert to Managed Policy ARN
                #   Managed Policy Arn (string): Insert it directly into the list
                #   Intrinsic Function (dict): Insert it directly into the list
                #
                # When you insert into managed_policy_arns list, de-dupe to prevent same ARN from showing up twice
                #

                policy_arn = policy_entry.data
                if isinstance(policy_entry.data, string_types) and policy_entry.data in managed_policy_map:
                    policy_arn = managed_policy_map[policy_entry.data]

                # De-Duplicate managed policy arns before inserting. Mainly useful
                # when customer specifies a managed policy which is already inserted
                # by SAM, such as AWSLambdaBasicExecutionRole
                if policy_arn not in managed_policy_arns:
                    managed_policy_arns.append(policy_arn)
            else:
                # Policy Templates are not supported here in the "core"
                raise InvalidResourceException(
                    self.logical_id,
                    "Policy at index {} in the 'Policies' property is not valid".format(index))

        execution_role.ManagedPolicyArns = list(managed_policy_arns)
        execution_role.Policies = policy_documents or None

        return execution_role
Ejemplo n.º 5
0
    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
Ejemplo n.º 6
0
    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)
Ejemplo n.º 7
0
    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 _construct_role(self, managed_policy_map):
        """Constructs a Lambda execution role based on this SAM function's Policies property.

        :returns: the generated IAM Role
        :rtype: model.iam.IAMRole
        """
        execution_role = IAMRole(self.logical_id + 'Role')
        execution_role.AssumeRolePolicyDocument = IAMRolePolicies.lambda_assume_role_policy()

        managed_policy_arns = [ArnGenerator.generate_aws_managed_policy_arn('service-role/AWSLambdaBasicExecutionRole')]
        if self.Tracing:
            managed_policy_arns.append(ArnGenerator.generate_aws_managed_policy_arn('AWSXrayWriteOnlyAccess'))

        function_policies = FunctionPolicies({"Policies": self.Policies},
                                             # No support for policy templates in the "core"
                                             policy_template_processor=None)
        policy_documents = []

        if self.DeadLetterQueue:
            policy_documents.append(IAMRolePolicies.dead_letter_queue_policy(
                self.dead_letter_queue_policy_actions[self.DeadLetterQueue['Type']],
                self.DeadLetterQueue['TargetArn']))

        for index, policy_entry in enumerate(function_policies.get()):

            if policy_entry.type is PolicyTypes.POLICY_STATEMENT:

                policy_documents.append({
                    'PolicyName': execution_role.logical_id + 'Policy' + str(index),
                    'PolicyDocument': policy_entry.data
                })
            elif policy_entry.type is PolicyTypes.MANAGED_POLICY:

                # There are three options:
                #   Managed Policy Name (string): Try to convert to Managed Policy ARN
                #   Managed Policy Arn (string): Insert it directly into the list
                #   Intrinsic Function (dict): Insert it directly into the list
                #
                # When you insert into managed_policy_arns list, de-dupe to prevent same ARN from showing up twice
                #

                policy_arn = policy_entry.data
                if isinstance(policy_entry.data, string_types) and policy_entry.data in managed_policy_map:
                    policy_arn = managed_policy_map[policy_entry.data]

                # De-Duplicate managed policy arns before inserting. Mainly useful
                # when customer specifies a managed policy which is already inserted
                # by SAM, such as AWSLambdaBasicExecutionRole
                if policy_arn not in managed_policy_arns:
                    managed_policy_arns.append(policy_arn)
            else:
                # Policy Templates are not supported here in the "core"
                raise InvalidResourceException(
                    self.logical_id,
                    "Policy at index {} in the 'Policies' property is not valid".format(index))

        execution_role.ManagedPolicyArns = list(managed_policy_arns)
        execution_role.Policies = policy_documents or None

        return execution_role
    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 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
Ejemplo n.º 12
0
    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_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"
Ejemplo n.º 14
0
    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
Ejemplo n.º 15
0
    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 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
Ejemplo n.º 17
0
    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
Ejemplo n.º 18
0
    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
Ejemplo n.º 19
0
    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, 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, 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
Ejemplo n.º 22
0
    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 _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']}
            }]
        }
        iam_role.ManagedPolicyArns = [
            ArnGenerator.generate_aws_managed_policy_arn('service-role/AWSCodeDeployRoleForLambda')
        ]

        return iam_role
    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 _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"]
                },
            }],
        }
        iam_role.ManagedPolicyArns = [
            ArnGenerator.generate_aws_managed_policy_arn(
                "service-role/AWSCodeDeployRoleForLambda")
        ]

        return iam_role
    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']
                }
            }]
        }
        iam_role.ManagedPolicyArns = [
            ArnGenerator.generate_aws_managed_policy_arn(
                'service-role/AWSCodeDeployRoleForLambda')
        ]

        return iam_role
Ejemplo n.º 27
0
    def _get_permission(self, resources_to_link, stage):
        # 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)

        editor = None
        if resources_to_link["explicit_api"].get("DefinitionBody"):
            try:
                editor = OpenApiEditor(resources_to_link["explicit_api"].get("DefinitionBody"))
            except ValueError as e:
                api_logical_id = self.ApiId.get("Ref") if isinstance(self.ApiId, dict) else self.ApiId
                raise InvalidResourceException(api_logical_id, e)

        # If this is using the new $default path, keep path blank and add a * permission
        if path == OpenApiEditor._DEFAULT_PATH:
            path = ""
        elif editor and resources_to_link.get("function").logical_id == editor.get_integration_function_logical_id(
            OpenApiEditor._DEFAULT_PATH, OpenApiEditor._X_ANY_METHOD
        ):
            # Case where default exists for this function, and so the permissions for that will apply here as well
            # This can save us several CFN resources (not duplicating permissions)
            return
        else:
            path = OpenApiEditor.get_path_without_trailing_slash(path)

        # Handle case where Method is already the ANY ApiGateway extension
        if self.Method.lower() == "any" or self.Method.lower() == OpenApiEditor._X_ANY_METHOD:
            method = "*"
        else:
            method = self.Method.upper()

        api_id = self.ApiId

        # ApiId can be a simple string or intrinsic function like !Ref. Using Fn::Sub will handle both cases
        resource = "${__ApiId__}/" + "${__Stage__}/" + method + path
        source_arn = fnSub(
            ArnGenerator.generate_arn(partition="${AWS::Partition}", service="execute-api", resource=resource),
            {"__ApiId__": api_id, "__Stage__": stage},
        )

        return self._construct_permission(resources_to_link["function"], source_arn=source_arn)
Ejemplo n.º 28
0
    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
Ejemplo n.º 29
0
 def get_policy_arn(self):
     return ArnGenerator.generate_aws_managed_policy_arn(
         "service-role/AWSLambdaSQSQueueExecutionRole")
Ejemplo n.º 30
0
 def get_policy_arn(self):
     return ArnGenerator.generate_aws_managed_policy_arn('service-role/AWSLambdaDynamoDBExecutionRole')
 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})
Ejemplo n.º 32
0
 def test_get_partition_name_raise_NoRegionFound(self):
     with self.assertRaises(NoRegionFound):
         ArnGenerator.get_partition_name(None)
 def get_policy_arn(self):
     return ArnGenerator.generate_aws_managed_policy_arn('service-role/AWSLambdaDynamoDBExecutionRole')
Ejemplo n.º 34
0
    def test_get_partition_name(self, region, expected):
        actual = ArnGenerator.get_partition_name(region)

        self.assertEqual(actual, expected)
Ejemplo n.º 35
0
 def test_get_partition_name(self, region, expected_partition):
     self.assertEqual(expected_partition,
                      ArnGenerator.get_partition_name(region=region))
Ejemplo n.º 36
0
    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
Ejemplo n.º 37
0
 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())
Ejemplo n.º 38
0
    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
Ejemplo n.º 39
0
    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