Skip to content

Commit

Permalink
feat: Linking Authorizers to Lambda functions using the invocation URI (
Browse files Browse the repository at this point in the history
aws#5196)

* Link authorizer to lambda function invoke URI

* Updated doc string

* Updated exception messages back

* Added check for one element in reference list

* Updated empty ref list check to not block

* Updated log message

* Fix long line lint error

---------

Co-authored-by: Mohamed Elasmar <[email protected]>
  • Loading branch information
2 people authored and Leonardo Gama committed Jun 22, 2023
1 parent a8f4b88 commit 9c4a0e2
Show file tree
Hide file tree
Showing 6 changed files with 248 additions and 43 deletions.
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 @@ -193,6 +193,20 @@ class GatewayResourceToApiGatewayIntegrationResponseLocalVariablesLinkingLimitat
"""


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


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


class InvalidSamMetadataPropertiesException(UserException):
pass

Expand Down
89 changes: 89 additions & 0 deletions samcli/hook_packages/terraform/hooks/prepare/resource_linking.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@

from samcli.hook_packages.terraform.hooks.prepare.exceptions import (
FunctionLayerLocalVariablesLinkingLimitationException,
GatewayAuthorizerToLambdaFunctionLocalVariablesLinkingLimitationException,
GatewayResourceToApiGatewayIntegrationLocalVariablesLinkingLimitationException,
GatewayResourceToApiGatewayIntegrationResponseLocalVariablesLinkingLimitationException,
GatewayResourceToApiGatewayMethodLocalVariablesLinkingLimitationException,
GatewayResourceToGatewayRestApiLocalVariablesLinkingLimitationException,
InvalidResourceLinkingException,
LambdaFunctionToApiGatewayIntegrationLocalVariablesLinkingLimitationException,
LocalVariablesLinkingLimitationException,
OneGatewayAuthorizerToLambdaFunctionLinkingLimitationException,
OneGatewayResourceToApiGatewayIntegrationLinkingLimitationException,
OneGatewayResourceToApiGatewayIntegrationResponseLinkingLimitationException,
OneGatewayResourceToApiGatewayMethodLinkingLimitationException,
Expand Down Expand Up @@ -1086,6 +1088,10 @@ def _link_gateway_resource_to_gateway_rest_apis_rest_api_id_call_back(
if len(referenced_rest_apis_values) > 1:
raise InvalidResourceLinkingException("Could not link multiple Rest APIs to one Gateway resource")

if not referenced_rest_apis_values:
LOG.info("Unable to find any references to Rest APIs, skip linking Gateway resources")
return

logical_id = referenced_rest_apis_values[0]
gateway_cfn_resource["Properties"]["RestApiId"] = (
{"Ref": logical_id.value} if isinstance(logical_id, LogicalIdReference) else logical_id.value
Expand All @@ -1111,6 +1117,10 @@ def _link_gateway_resource_to_gateway_resource_call_back(
if len(referenced_gateway_resource_values) > 1:
raise InvalidResourceLinkingException("Could not link multiple Gateway Resources to one Gateway resource")

if not referenced_gateway_resource_values:
LOG.info("Unable to find any references to the Gateway Resource, skip linking Gateway resources")
return

logical_id = referenced_gateway_resource_values[0]
gateway_resource_cfn_resource["Properties"]["ResourceId"] = (
{"Ref": logical_id.value} if isinstance(logical_id, LogicalIdReference) else logical_id.value
Expand All @@ -1137,6 +1147,10 @@ def _link_gateway_resource_to_gateway_rest_apis_parent_id_call_back(
if len(referenced_rest_apis_values) > 1:
raise InvalidResourceLinkingException("Could not link multiple Rest APIs to one Gateway resource")

if not referenced_rest_apis_values:
LOG.info("Unable to find any references to Rest APIs, skip linking Rest API to Gateway resource")
return

logical_id = referenced_rest_apis_values[0]
gateway_cfn_resource["Properties"]["ParentId"] = (
{"Fn::GetAtt": [logical_id.value, "RootResourceId"]}
Expand Down Expand Up @@ -1351,6 +1365,12 @@ def _link_gateway_integration_to_function_call_back(
"Could not link multiple Lambda functions to one Gateway integration resource"
)

if not referenced_gateway_resource_values:
LOG.info(
"Unable to find any references to Lambda functions, skip linking Lambda function to Gateway integration"
)
return

logical_id = referenced_gateway_resource_values[0]
gateway_integration_cfn_resource["Properties"]["Uri"] = (
{"Fn::Sub": INVOKE_ARN_FORMAT.format(function_logical_id=logical_id.value)}
Expand Down Expand Up @@ -1477,3 +1497,72 @@ def _link_gateway_integration_responses_to_gateway_resource(
linking_exceptions=exceptions,
)
ResourceLinker(resource_linking_pair).link_resources()


def _link_gateway_authorizer_to_lambda_function_call_back(
gateway_authorizer_cfn_resource: Dict, lambda_function_resource_values: List[ReferenceType]
) -> None:
"""
Callback function that is used by the linking algorithm to update a CFN Authorizer Resource with
a reference to the Lambda function's invocation URI
Parameters
----------
gateway_authorizer_cfn_resource: Dict
API Gateway Authorizer CFN resource
lambda_function_resource_values: List[ReferenceType]
List of referenced Lambda Functions either as the logical id of Lambda Function reosurces
defined in the customer project, or ARN values for actual Lambda Functions defined
in customer's account. This list should always contain one element only.
"""
if len(lambda_function_resource_values) > 1:
raise InvalidResourceLinkingException("Could not link multiple Lambda functions to one Gateway Authorizer")

if not lambda_function_resource_values:
LOG.info(
"Unable to find any references to Lambda functions, skip linking Lambda function to Gateway Authorizer"
)
return

logical_id = lambda_function_resource_values[0]
gateway_authorizer_cfn_resource["Properties"]["AuthorizerUri"] = (
{"Fn::Sub": INVOKE_ARN_FORMAT.format(function_logical_id=logical_id.value)}
if isinstance(logical_id, LogicalIdReference)
else logical_id.value
)


def _link_gateway_authorizer_to_lambda_function(
authorizer_config_resources: Dict[str, TFResource],
authorizer_cfn_resources: Dict[str, List],
authorizer_tf_resources: Dict[str, Dict],
) -> None:
"""
Iterate through all the resources and link the corresponding 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
lambda_layers_terraform_resources: Dict[str, Dict]
Dictionary of all actual terraform layers resources (not configuration resources). The dictionary's key is the
calculated logical id for each resource
"""
exceptions = ResourcePairExceptions(
multiple_resource_linking_exception=OneGatewayAuthorizerToLambdaFunctionLinkingLimitationException,
local_variable_linking_exception=GatewayAuthorizerToLambdaFunctionLocalVariablesLinkingLimitationException,
)
resource_linking_pair = ResourceLinkingPair(
source_resource_cfn_resource=authorizer_cfn_resources,
source_resource_tf_config=authorizer_config_resources,
destination_resource_tf=authorizer_tf_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()
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,15 @@ def __init__(self):
super(ApiGatewayStageProperties, self).__init__()


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

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


def add_integrations_to_methods(
gateway_methods_cfn: Dict[str, List], gateway_integrations_cfn: Dict[str, List]
) -> None:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import List

from samcli.hook_packages.terraform.hooks.prepare.property_builder import (
TF_AWS_API_GATEWAY_AUTHORIZER,
TF_AWS_API_GATEWAY_INTEGRATION,
TF_AWS_API_GATEWAY_INTEGRATION_RESPONSE,
TF_AWS_API_GATEWAY_METHOD,
Expand All @@ -11,6 +12,7 @@
TF_AWS_LAMBDA_LAYER_VERSION,
)
from samcli.hook_packages.terraform.hooks.prepare.resource_linking import (
_link_gateway_authorizer_to_lambda_function,
_link_gateway_integration_responses_to_gateway_resource,
_link_gateway_integration_responses_to_gateway_rest_apis,
_link_gateway_integrations_to_function_resource,
Expand Down Expand Up @@ -71,4 +73,9 @@
dest=TF_AWS_API_GATEWAY_RESOURCE,
linking_func=_link_gateway_integration_responses_to_gateway_resource,
),
LinkingPairCaller(
source=TF_AWS_API_GATEWAY_AUTHORIZER,
dest=TF_AWS_LAMBDA_FUNCTION,
linking_func=_link_gateway_authorizer_to_lambda_function,
),
]
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from typing import Dict

from samcli.hook_packages.terraform.hooks.prepare.property_builder import (
TF_AWS_API_GATEWAY_AUTHORIZER,
TF_AWS_API_GATEWAY_INTEGRATION,
TF_AWS_API_GATEWAY_INTEGRATION_RESPONSE,
TF_AWS_API_GATEWAY_METHOD,
Expand All @@ -12,6 +13,7 @@
TF_AWS_LAMBDA_LAYER_VERSION,
)
from samcli.hook_packages.terraform.hooks.prepare.resources.apigw import (
ApiGatewayAuthorizerProperties,
ApiGatewayMethodProperties,
ApiGatewayResourceProperties,
ApiGatewayRestApiProperties,
Expand Down Expand Up @@ -46,4 +48,5 @@ def get_resource_property_mapping() -> Dict[str, ResourceProperties]:
TF_AWS_API_GATEWAY_STAGE: ApiGatewayStageProperties(),
TF_AWS_API_GATEWAY_INTEGRATION: InternalApiGatewayIntegrationProperties(),
TF_AWS_API_GATEWAY_INTEGRATION_RESPONSE: InternalApiGatewayIntegrationResponseProperties(),
TF_AWS_API_GATEWAY_AUTHORIZER: ApiGatewayAuthorizerProperties(),
}
Loading

0 comments on commit 9c4a0e2

Please sign in to comment.