Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Link a APIGW V2 Authorizer to a Lambda Function #5624

Closed
14 changes: 14 additions & 0 deletions samcli/hook_packages/terraform/hooks/prepare/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,20 @@ class GatewayV2RouteToGatewayV2ApiLocalVariablesLinkingLimitationException(Local
"""


class OneGatewayV2AuthorizerToLambdaFunctionLinkingLimitationException(OneResourceLinkingLimitationException):
"""
Exception specific for Gateway V2 Authorizer linking to more than one Lambda Function
"""


class GatewayV2AuthorizerToLambdaFunctionLocalVariablesLinkingLimitationException(
LocalVariablesLinkingLimitationException
):
"""
Exception specific for Gateway V2 Authorizer linking to Lambda Function using locals.
"""


class OneGatewayV2ApiToLambdaFunctionLinkingLimitationException(OneResourceLinkingLimitationException):
"""
Exception specific for Gateway V2 API linking to more than one Lambda Function
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from samcli.lib.utils.resources import AWS_APIGATEWAY_RESTAPI as CFN_AWS_APIGATEWAY_RESTAPI
from samcli.lib.utils.resources import AWS_APIGATEWAY_STAGE as CFN_AWS_APIGATEWAY_STAGE
from samcli.lib.utils.resources import AWS_APIGATEWAY_V2_API as CFN_AWS_APIGATEWAY_V2_API
from samcli.lib.utils.resources import AWS_APIGATEWAY_V2_AUTHORIZER as CFN_AWS_APIGATEWAY_V2_AUTHORIZER
from samcli.lib.utils.resources import AWS_APIGATEWAY_V2_INTEGRATION as CFN_AWS_APIGATEWAY_V2_INTEGRATION
from samcli.lib.utils.resources import AWS_APIGATEWAY_V2_ROUTE as CFN_AWS_APIGATEWAY_V2_ROUTE
from samcli.lib.utils.resources import AWS_APIGATEWAY_V2_STAGE as CFN_AWS_APIGATEWAY_V2_STAGE
Expand All @@ -49,6 +50,7 @@
TF_AWS_API_GATEWAY_V2_ROUTE = "aws_apigatewayv2_route"
TF_AWS_API_GATEWAY_V2_STAGE = "aws_apigatewayv2_stage"
TF_AWS_API_GATEWAY_V2_INTEGRATION = "aws_apigatewayv2_integration"
TF_AWS_API_GATEWAY_V2_AUTHORIZER = "aws_apigatewayv2_authorizer"


def _build_code_property(tf_properties: dict, resource: TFResource) -> Any:
Expand Down Expand Up @@ -412,6 +414,16 @@ def _add_property(cfn_prop, tf_prop):
"PayloadFormatVersion": _get_property_extractor("payload_format_version"),
}

AWS_API_GATEWAY_V2_AUTHORIZER_PROPERTY_BUILDER_MAPPING: PropertyBuilderMapping = {
"ApiId": _get_property_extractor("api_id"),
"AuthorizerType": _get_property_extractor("authorizer_type"),
"AuthorizerUri": _get_property_extractor("authorizer_uri"),
"Name": _get_property_extractor("name"),
"AuthorizerPayloadFormatVersion": _get_property_extractor("authorizer_payload_format_version"),
"IdentitySource": _get_property_extractor("identity_sources"),
"EnableSimpleResponses": _get_property_extractor("enable_simple_responses"),
}

RESOURCE_TRANSLATOR_MAPPING: Dict[str, ResourceTranslator] = {
TF_AWS_LAMBDA_FUNCTION: ResourceTranslator(CFN_AWS_LAMBDA_FUNCTION, AWS_LAMBDA_FUNCTION_PROPERTY_BUILDER_MAPPING),
TF_AWS_LAMBDA_LAYER_VERSION: ResourceTranslator(
Expand Down Expand Up @@ -450,4 +462,7 @@ def _add_property(cfn_prop, tf_prop):
TF_AWS_API_GATEWAY_V2_INTEGRATION: ResourceTranslator(
CFN_AWS_APIGATEWAY_V2_INTEGRATION, AWS_API_GATEWAY_V2_INTEGRATION_PROPERTY_BUILDER_MAPPING
),
TF_AWS_API_GATEWAY_V2_AUTHORIZER: ResourceTranslator(
CFN_AWS_APIGATEWAY_V2_AUTHORIZER, AWS_API_GATEWAY_V2_AUTHORIZER_PROPERTY_BUILDER_MAPPING
),
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
GatewayResourceToApiGatewayMethodLocalVariablesLinkingLimitationException,
GatewayResourceToGatewayRestApiLocalVariablesLinkingLimitationException,
GatewayV2ApiToLambdaFunctionLocalVariablesLinkingLimitationException,
GatewayV2AuthorizerToLambdaFunctionLocalVariablesLinkingLimitationException,
GatewayV2IntegrationToGatewayV2ApiLocalVariablesLinkingLimitationException,
GatewayV2IntegrationToLambdaFunctionLocalVariablesLinkingLimitationException,
GatewayV2RouteToGatewayV2ApiLocalVariablesLinkingLimitationException,
Expand All @@ -33,6 +34,7 @@
OneGatewayResourceToApiGatewayMethodLinkingLimitationException,
OneGatewayResourceToRestApiLinkingLimitationException,
OneGatewayV2ApiToLambdaFunctionLinkingLimitationException,
OneGatewayV2AuthorizerToLambdaFunctionLinkingLimitationException,
OneGatewayV2IntegrationToGatewayV2ApiLinkingLimitationException,
OneGatewayV2IntegrationToLambdaFunctionLinkingLimitationException,
OneGatewayV2RouteToGatewayV2ApiLinkingLimitationException,
Expand Down Expand Up @@ -2008,6 +2010,42 @@ def _link_gateway_v2_route_to_api(
ResourceLinker(resource_linking_pair).link_resources()


def _link_gateway_v2_authorizer_to_lambda_function(
authorizer_config_resources: Dict[str, TFResource],
authorizer_cfn_resources: Dict[str, List],
lamda_function_resources: Dict[str, Dict],
) -> None:
"""
Iterate through all the resources and link the corresponding V2 Authorizer to each Lambda Function

Parameters
----------
authorizer_config_resources: Dict[str, TFResource]
Dictionary of configuration Authorizer resources
authorizer_cfn_resources: Dict[str, List]
Dictionary containing resolved configuration address of CFN Authorizer resources
lamda_function_resources: Dict[str, Dict]
Dictionary of Terraform Lambda Function resources (not configuration resources). The dictionary's key is the
calculated logical id for each resource
"""
exceptions = ResourcePairExceptions(
multiple_resource_linking_exception=OneGatewayV2AuthorizerToLambdaFunctionLinkingLimitationException,
local_variable_linking_exception=GatewayV2AuthorizerToLambdaFunctionLocalVariablesLinkingLimitationException,
)
resource_linking_pair = ResourceLinkingPair(
source_resource_cfn_resource=authorizer_cfn_resources,
source_resource_tf_config=authorizer_config_resources,
destination_resource_tf=lamda_function_resources,
tf_destination_attribute_name="invoke_arn",
terraform_link_field_name="authorizer_uri",
cfn_link_field_name="AuthorizerUri",
terraform_resource_type_prefix=LAMBDA_FUNCTION_RESOURCE_ADDRESS_PREFIX,
cfn_resource_update_call_back_function=_link_gateway_authorizer_to_lambda_function_call_back,
linking_exceptions=exceptions,
)
ResourceLinker(resource_linking_pair).link_resources()


def _link_gateway_v2_api_to_function_callback(
gateway_v2_api_cfn_resource: Dict, referenced_function_resource_values: List[ReferenceType]
) -> None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,22 @@ def __init__(self):

class ApiGatewayV2IntegrationProperties(ResourceProperties):
"""
Contains the collection logic of the required properties for linking the aws_api_gateway_v2_authorizer resources.
Contains the collection logic of the required properties for linking the aws_api_gateway_v2_integration resources.
"""

def __init__(self):
super(ApiGatewayV2IntegrationProperties, self).__init__()


class ApiGatewayV2AuthorizerProperties(ResourceProperties):
"""
Contains the collection logic of the required properties for linking the aws_api_gateway_v2_authorizer resources.
"""

def __init__(self):
super(ApiGatewayV2AuthorizerProperties, self).__init__()


class ApiGatewayV2StageProperties(ResourceProperties):
"""
Contains the collection logic of the required properties for linking the aws_api_gateway_v2_stage resources.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
TF_AWS_API_GATEWAY_REST_API,
TF_AWS_API_GATEWAY_STAGE,
TF_AWS_API_GATEWAY_V2_API,
TF_AWS_API_GATEWAY_V2_AUTHORIZER,
TF_AWS_API_GATEWAY_V2_INTEGRATION,
TF_AWS_API_GATEWAY_V2_ROUTE,
TF_AWS_API_GATEWAY_V2_STAGE,
Expand All @@ -29,6 +30,7 @@
_link_gateway_resources_to_gateway_rest_apis,
_link_gateway_stage_to_rest_api,
_link_gateway_v2_api_to_function,
_link_gateway_v2_authorizer_to_lambda_function,
_link_gateway_v2_integration_to_api,
_link_gateway_v2_integration_to_lambda_function,
_link_gateway_v2_route_to_api,
Expand Down Expand Up @@ -120,6 +122,11 @@
dest=TF_AWS_API_GATEWAY_V2_API,
linking_func=_link_gateway_v2_route_to_api,
),
LinkingPairCaller(
source=TF_AWS_API_GATEWAY_V2_AUTHORIZER,
dest=TF_AWS_LAMBDA_FUNCTION,
linking_func=_link_gateway_v2_authorizer_to_lambda_function,
),
LinkingPairCaller(
source=TF_AWS_API_GATEWAY_V2_API,
dest=TF_AWS_LAMBDA_FUNCTION,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
TF_AWS_API_GATEWAY_REST_API,
TF_AWS_API_GATEWAY_STAGE,
TF_AWS_API_GATEWAY_V2_API,
TF_AWS_API_GATEWAY_V2_AUTHORIZER,
TF_AWS_API_GATEWAY_V2_INTEGRATION,
TF_AWS_API_GATEWAY_V2_ROUTE,
TF_AWS_API_GATEWAY_V2_STAGE,
Expand All @@ -23,6 +24,7 @@
ApiGatewayRestApiProperties,
ApiGatewayStageProperties,
ApiGatewayV2ApiProperties,
ApiGatewayV2AuthorizerProperties,
ApiGatewayV2IntegrationProperties,
ApiGatewayV2RouteProperties,
ApiGatewayV2StageProperties,
Expand Down Expand Up @@ -60,5 +62,6 @@ def get_resource_property_mapping() -> Dict[str, ResourceProperties]:
TF_AWS_API_GATEWAY_V2_ROUTE: ApiGatewayV2RouteProperties(),
TF_AWS_API_GATEWAY_V2_INTEGRATION: ApiGatewayV2IntegrationProperties(),
TF_AWS_API_GATEWAY_V2_API: ApiGatewayV2ApiProperties(),
TF_AWS_API_GATEWAY_V2_AUTHORIZER: ApiGatewayV2AuthorizerProperties(),
TF_AWS_API_GATEWAY_V2_STAGE: ApiGatewayV2StageProperties(),
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from samcli.hook_packages.terraform.hooks.prepare.translate import AWS_PROVIDER_NAME, NULL_RESOURCE_PROVIDER_NAME
from samcli.lib.utils.resources import (
AWS_APIGATEWAY_V2_AUTHORIZER,
AWS_LAMBDA_FUNCTION as CFN_AWS_LAMBDA_FUNCTION,
AWS_LAMBDA_LAYERVERSION,
AWS_APIGATEWAY_RESOURCE,
Expand Down Expand Up @@ -61,6 +62,7 @@ def setUp(self) -> None:
self.apigwv2_route_name = "my_apigwv2_route"
self.apigwv2_stage_name = "my_apigwv2_stage"
self.apigwv2_integration_name = "my_apigwv2_integration"
self.apigwv2_authorizer_name = "my_authorizer_v2"

self.tf_function_common_properties: dict = {
"function_name": self.zip_function_name,
Expand Down Expand Up @@ -941,6 +943,44 @@ def setUp(self) -> None:
"Metadata": {"SamResourceId": f"aws_apigatewayv2_integration.{self.apigwv2_integration_name}"},
}

self.tf_apigwv2_authorizer_common_attributes: dict = {
"type": "aws_apigatewayv2_authorizer",
"provider_name": AWS_PROVIDER_NAME,
}

self.tf_apigwv2_authorizer_properties: dict = {
"api_id": "aws_apigatewayv2_api.my_api.id",
"authorizer_type": "REQUEST",
"authorizer_uri": "aws_lambda_function.authorizerv2.invoke_arn",
"name": self.apigwv2_authorizer_name,
"authorizer_payload_format_version": "2.0",
"identity_sources": ["$request.header.hello"],
"enable_simple_responses": False,
}

self.expected_cfn_apigwv2_authorizer_properties: dict = {
"ApiId": "aws_apigatewayv2_api.my_api.id",
"AuthorizerType": "REQUEST",
"AuthorizerUri": "aws_lambda_function.authorizerv2.invoke_arn",
"Name": self.apigwv2_authorizer_name,
"AuthorizerPayloadFormatVersion": "2.0",
"IdentitySource": ["$request.header.hello"],
"EnableSimpleResponses": False,
}

self.tf_apigwv2_authorizer_resource: dict = {
**self.tf_apigwv2_authorizer_common_attributes,
"values": self.tf_apigwv2_authorizer_properties,
"address": f"aws_apigatewayv2_authorizer.{self.apigwv2_authorizer_name}",
"name": self.apigwv2_authorizer_name,
}

self.expected_cfn_apigwv2_authorizer: dict = {
"Type": AWS_APIGATEWAY_V2_AUTHORIZER,
"Properties": self.expected_cfn_apigwv2_authorizer_properties,
"Metadata": {"SamResourceId": f"aws_apigatewayv2_authorizer.{self.apigwv2_authorizer_name}"},
}

self.tf_json_with_root_module_only: dict = {
"planned_values": {
"root_module": {
Expand All @@ -961,6 +1001,7 @@ def setUp(self) -> None:
self.tf_apigwv2_route_resource,
self.tf_apigwv2_stage_resource,
self.tf_apigwv2_integration_resource,
self.tf_apigwv2_authorizer_resource,
]
}
}
Expand All @@ -982,9 +1023,9 @@ def setUp(self) -> None:
f"AwsApigatewayv2RouteMyApigwv2Route{self.mock_logical_id_hash}": self.expected_cfn_apigwv2_route,
f"AwsApigatewayv2StageMyApigwv2Stage{self.mock_logical_id_hash}": self.expected_cfn_apigwv2_stage,
f"AwsApigatewayv2IntegrationMyApigwv2Integration{self.mock_logical_id_hash}": self.expected_cfn_apigwv2_integration,
f"AwsApigatewayv2AuthorizerMyAuthorizerV2{self.mock_logical_id_hash}": self.expected_cfn_apigwv2_authorizer,
},
}

self.tf_json_with_root_module_with_sam_metadata_resources: dict = {
"planned_values": {
"root_module": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
GatewayAuthorizerToLambdaFunctionLocalVariablesLinkingLimitationException,
GatewayAuthorizerToRestApiLocalVariablesLinkingLimitationException,
GatewayMethodToGatewayAuthorizerLocalVariablesLinkingLimitationException,
GatewayV2AuthorizerToLambdaFunctionLocalVariablesLinkingLimitationException,
InvalidResourceLinkingException,
LocalVariablesLinkingLimitationException,
ONE_LAMBDA_LAYER_LINKING_ISSUE_LINK,
Expand All @@ -17,6 +18,7 @@
OneGatewayAuthorizerToLambdaFunctionLinkingLimitationException,
OneGatewayAuthorizerToRestApiLinkingLimitationException,
OneGatewayMethodToGatewayAuthorizerLinkingLimitationException,
OneGatewayV2AuthorizerToLambdaFunctionLinkingLimitationException,
OneLambdaLayerLinkingLimitationException,
FunctionLayerLocalVariablesLinkingLimitationException,
OneGatewayResourceToApiGatewayMethodLinkingLimitationException,
Expand Down Expand Up @@ -59,6 +61,7 @@
_link_gateway_authorizer_to_rest_api,
_link_gateway_method_to_gateway_authorizer,
_link_gateway_method_to_gateway_authorizer_call_back,
_link_gateway_v2_authorizer_to_lambda_function,
_resolve_module_output,
_resolve_module_variable,
_build_module,
Expand Down Expand Up @@ -2737,6 +2740,46 @@ def test_link_gateway_v2_integration_to_api_callback(
input_gateway_v2_integration["Properties"]["ApiId"] = expected_api_reference
self.assertEqual(gateway_resource, input_gateway_v2_integration)

@patch(
"samcli.hook_packages.terraform.hooks.prepare.resource_linking._link_gateway_authorizer_to_lambda_function_call_back"
)
@patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinker")
@patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinkingPair")
@patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourcePairExceptions")
def test_link_gateway_v2_authorizer_to_lambda_function(
self,
mock_resource_linking_exceptions,
mock_resource_linking_pair,
mock_resource_linker,
mock_link_gateway_authorizer_to_lambda_function_call_back,
):
v2_authorizer_cfn_resources = Mock()
v2_authorizer_config_resources = Mock()
lambda_function_resources = Mock()

_link_gateway_v2_authorizer_to_lambda_function(
v2_authorizer_config_resources, v2_authorizer_cfn_resources, lambda_function_resources
)

mock_resource_linking_exceptions.assert_called_once_with(
multiple_resource_linking_exception=OneGatewayV2AuthorizerToLambdaFunctionLinkingLimitationException,
local_variable_linking_exception=GatewayV2AuthorizerToLambdaFunctionLocalVariablesLinkingLimitationException,
)

mock_resource_linking_pair.assert_called_once_with(
source_resource_cfn_resource=v2_authorizer_cfn_resources,
source_resource_tf_config=v2_authorizer_config_resources,
destination_resource_tf=lambda_function_resources,
tf_destination_attribute_name="invoke_arn",
terraform_link_field_name="authorizer_uri",
cfn_link_field_name="AuthorizerUri",
terraform_resource_type_prefix=LAMBDA_FUNCTION_RESOURCE_ADDRESS_PREFIX,
cfn_resource_update_call_back_function=mock_link_gateway_authorizer_to_lambda_function_call_back,
linking_exceptions=mock_resource_linking_exceptions(),
)

mock_resource_linker.assert_called_once_with(mock_resource_linking_pair())

@patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking._link_gateway_v2_api_to_function_callback")
@patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinker")
@patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinkingPair")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from tests.unit.hook_packages.terraform.hooks.prepare.prepare_base import PrepareHookUnitBase
from samcli.hook_packages.terraform.hooks.prepare.property_builder import (
AWS_API_GATEWAY_V2_AUTHORIZER_PROPERTY_BUILDER_MAPPING,
AWS_LAMBDA_FUNCTION_PROPERTY_BUILDER_MAPPING,
REMOTE_DUMMY_VALUE,
AWS_API_GATEWAY_RESOURCE_PROPERTY_BUILDER_MAPPING,
Expand Down Expand Up @@ -1146,6 +1147,12 @@ def test_translating_apigwv2_integration(self):
)
self.assertEqual(translated_cfn_properties, self.expected_cfn_apigwv2_integration_properties)

def test_translating_apigwv2_authorizer(self):
translated_cfn_properties = _translate_properties(
self.tf_apigwv2_authorizer_properties, AWS_API_GATEWAY_V2_AUTHORIZER_PROPERTY_BUILDER_MAPPING, Mock()
)
self.assertEqual(translated_cfn_properties, self.expected_cfn_apigwv2_authorizer_properties)


class TestUnresolvableAttributeCheck(TestCase):
@patch("samcli.hook_packages.terraform.hooks.prepare.translate.RESOURCE_TRANSLATOR_MAPPING")
Expand Down
Loading