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: Added linking Gateway Method to Lambda Authorizer #5228

Merged
merged 3 commits into from
May 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -207,6 +207,20 @@ class GatewayAuthorizerToLambdaFunctionLocalVariablesLinkingLimitationException(
"""


class OneGatewayMethodToGatewayAuthorizerLinkingLimitationException(OneResourceLinkingLimitationException):
"""
Exception specific for Gateway Method linking to more than one Gateway Authorizer
"""


class GatewayMethodToGatewayAuthorizerLocalVariablesLinkingLimitationException(
LocalVariablesLinkingLimitationException
):
"""
Exception specific for Gateway Method linking to Gateway Authorizer using locals.
"""


class InvalidSamMetadataPropertiesException(UserException):
pass

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from samcli.hook_packages.terraform.hooks.prepare.exceptions import (
FunctionLayerLocalVariablesLinkingLimitationException,
GatewayAuthorizerToLambdaFunctionLocalVariablesLinkingLimitationException,
GatewayMethodToGatewayAuthorizerLocalVariablesLinkingLimitationException,
GatewayResourceToApiGatewayIntegrationLocalVariablesLinkingLimitationException,
GatewayResourceToApiGatewayIntegrationResponseLocalVariablesLinkingLimitationException,
GatewayResourceToApiGatewayMethodLocalVariablesLinkingLimitationException,
Expand All @@ -18,6 +19,7 @@
LambdaFunctionToApiGatewayIntegrationLocalVariablesLinkingLimitationException,
LocalVariablesLinkingLimitationException,
OneGatewayAuthorizerToLambdaFunctionLinkingLimitationException,
OneGatewayMethodToGatewayAuthorizerLinkingLimitationException,
OneGatewayResourceToApiGatewayIntegrationLinkingLimitationException,
OneGatewayResourceToApiGatewayIntegrationResponseLinkingLimitationException,
OneGatewayResourceToApiGatewayMethodLinkingLimitationException,
Expand Down Expand Up @@ -50,6 +52,7 @@
LAMBDA_LAYER_RESOURCE_ADDRESS_PREFIX = "aws_lambda_layer_version."
API_GATEWAY_REST_API_RESOURCE_ADDRESS_PREFIX = "aws_api_gateway_rest_api."
API_GATEWAY_RESOURCE_RESOURCE_ADDRESS_PREFIX = "aws_api_gateway_resource."
API_GATEWAY_AUTHORIZER_RESOURCE_ADDRESS_PREFIX = "aws_api_gateway_authorizer."
TERRAFORM_LOCAL_VARIABLES_ADDRESS_PREFIX = "local."
DATA_RESOURCE_ADDRESS_PREFIX = "data."

Expand Down Expand Up @@ -1566,3 +1569,69 @@ def _link_gateway_authorizer_to_lambda_function(
linking_exceptions=exceptions,
)
ResourceLinker(resource_linking_pair).link_resources()


def _link_gateway_method_to_gateway_authorizer_call_back(
gateway_method_cfn_resource: Dict, authorizer_resources: List[ReferenceType]
) -> None:
"""
Callback function that is used by the linking algorithm to update a CFN Method Resource with
a reference to the Lambda Authorizers's Id

Parameters
----------
gateway_method_cfn_resource: Dict
API Gateway Method CFN resource
authorizer_resources: List[ReferenceType]
List of referenced Authorizers either as the logical id of Authorizer resources
defined in the customer project, or ARN values for actual Authorizers defined
in customer's account. This list should always contain one element only.
"""
if len(authorizer_resources) > 1:
raise InvalidResourceLinkingException("Could not link multiple Lambda Authorizers to one Gateway Method")

if not authorizer_resources:
LOG.info("Unable to find any references to Authorizers, skip linking Gateway Method to Lambda Authorizer")
return

logical_id = authorizer_resources[0]
gateway_method_cfn_resource["Properties"]["AuthorizerId"] = (
{"Ref": logical_id.value} if isinstance(logical_id, LogicalIdReference) else logical_id.value
)


def _link_gateway_method_to_gateway_authorizer(
gateway_method_config_resources: Dict[str, TFResource],
gateway_method_config_address_cfn_resources_map: Dict[str, List],
authorizer_resources: Dict[str, Dict],
) -> None:
"""
Iterate through all the resources and link the corresponding
Gateway Method resources to each Gateway Authorizer

Parameters
----------
gateway_method_config_resources: Dict[str, TFResource]
Dictionary of configuration Gateway Methods
gateway_method_config_address_cfn_resources_map: Dict[str, List]
Dictionary containing resolved configuration addresses matched up to the cfn Gateway Stage
authorizer_resources: Dict[str, Dict]
Dictionary of all Terraform Authorizer resources (not configuration resources).
The dictionary's key is the calculated logical id for each resource.
"""
exceptions = ResourcePairExceptions(
multiple_resource_linking_exception=OneGatewayMethodToGatewayAuthorizerLinkingLimitationException,
local_variable_linking_exception=GatewayMethodToGatewayAuthorizerLocalVariablesLinkingLimitationException,
)
resource_linking_pair = ResourceLinkingPair(
source_resource_cfn_resource=gateway_method_config_address_cfn_resources_map,
source_resource_tf_config=gateway_method_config_resources,
destination_resource_tf=authorizer_resources,
tf_destination_attribute_name="id",
terraform_link_field_name="authorizer_id",
cfn_link_field_name="AuthorizerId",
terraform_resource_type_prefix=API_GATEWAY_AUTHORIZER_RESOURCE_ADDRESS_PREFIX,
cfn_resource_update_call_back_function=_link_gateway_method_to_gateway_authorizer_call_back,
linking_exceptions=exceptions,
)
ResourceLinker(resource_linking_pair).link_resources()
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
_link_gateway_integrations_to_function_resource,
_link_gateway_integrations_to_gateway_resource,
_link_gateway_integrations_to_gateway_rest_apis,
_link_gateway_method_to_gateway_authorizer,
_link_gateway_method_to_gateway_resource,
_link_gateway_methods_to_gateway_rest_apis,
_link_gateway_resources_to_gateway_rest_apis,
Expand Down Expand Up @@ -78,4 +79,9 @@
dest=TF_AWS_LAMBDA_FUNCTION,
linking_func=_link_gateway_authorizer_to_lambda_function,
),
LinkingPairCaller(
source=TF_AWS_API_GATEWAY_METHOD,
dest=TF_AWS_API_GATEWAY_AUTHORIZER,
linking_func=_link_gateway_method_to_gateway_authorizer,
),
]
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
from parameterized import parameterized
from samcli.hook_packages.terraform.hooks.prepare.exceptions import (
GatewayAuthorizerToLambdaFunctionLocalVariablesLinkingLimitationException,
GatewayMethodToGatewayAuthorizerLocalVariablesLinkingLimitationException,
InvalidResourceLinkingException,
LocalVariablesLinkingLimitationException,
ONE_LAMBDA_LAYER_LINKING_ISSUE_LINK,
LOCAL_VARIABLES_SUPPORT_ISSUE_LINK,
APPLY_WORK_AROUND_MESSAGE,
OneGatewayAuthorizerToLambdaFunctionLinkingLimitationException,
OneGatewayMethodToGatewayAuthorizerLinkingLimitationException,
OneLambdaLayerLinkingLimitationException,
FunctionLayerLocalVariablesLinkingLimitationException,
OneGatewayResourceToApiGatewayMethodLinkingLimitationException,
Expand All @@ -36,9 +38,12 @@
)

from samcli.hook_packages.terraform.hooks.prepare.resource_linking import (
API_GATEWAY_AUTHORIZER_RESOURCE_ADDRESS_PREFIX,
_clean_references_list,
_link_gateway_authorizer_to_lambda_function,
_link_gateway_authorizer_to_lambda_function_call_back,
_link_gateway_method_to_gateway_authorizer,
_link_gateway_method_to_gateway_authorizer_call_back,
_resolve_module_output,
_resolve_module_variable,
_build_module,
Expand Down Expand Up @@ -2081,6 +2086,10 @@ def test_link_gateway_integration_to_function_call_back(
_link_gateway_resource_to_gateway_rest_apis_rest_api_id_call_back,
"Could not link multiple Rest APIs to one Gateway resource",
),
(
_link_gateway_method_to_gateway_authorizer_call_back,
"Could not link multiple Lambda Authorizers to one Gateway Method",
),
]
)
def test_linking_callbacks_raises_multiple_reference_exception(self, linking_call_back_method, expected_message):
Expand All @@ -2094,6 +2103,7 @@ def test_linking_callbacks_raises_multiple_reference_exception(self, linking_cal
(_link_gateway_resource_to_gateway_rest_apis_parent_id_call_back,),
(_link_gateway_resource_to_gateway_resource_call_back,),
(_link_gateway_resource_to_gateway_rest_apis_rest_api_id_call_back,),
(_link_gateway_method_to_gateway_authorizer_call_back,),
]
)
def test_linking_callbacks_skips_empty_references(self, linking_call_back_method):
Expand Down Expand Up @@ -2245,3 +2255,67 @@ def test_link_gateway_authorizer_to_lambda_function(
)

mock_resource_linker.assert_called_once_with(mock_resource_linking_pair())

@parameterized.expand(
[
(
[LogicalIdReference("Authorizer")],
{"Ref": "Authorizer"},
),
(
[ExistingResourceReference("Existing123")],
"Existing123",
),
]
)
def test_link_gateway_method_to_gateway_authorizer_call_back(self, logical_ids, expected_reference):
original_method = {
"Type": "AWS::ApiGateway::Method",
"Properties": {"AuthorizerId": "id here"},
}
new_method = deepcopy(original_method)

_link_gateway_method_to_gateway_authorizer_call_back(new_method, logical_ids)

original_method["Properties"]["AuthorizerId"] = expected_reference
self.assertEqual(original_method, new_method)

@patch(
"samcli.hook_packages.terraform.hooks.prepare.resource_linking._link_gateway_method_to_gateway_authorizer_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_method_to_gateway_authorizer(
self,
mock_resource_linking_exceptions,
mock_resource_linking_pair,
mock_resource_linker,
mock_link_gateway_method_to_gateway_authorizer_call_back,
):
method_cfn_resources = Mock()
method_config_resources = Mock()
authorizer_tf_resources = Mock()

_link_gateway_method_to_gateway_authorizer(
method_config_resources, method_cfn_resources, authorizer_tf_resources
)

mock_resource_linking_exceptions.assert_called_once_with(
multiple_resource_linking_exception=OneGatewayMethodToGatewayAuthorizerLinkingLimitationException,
local_variable_linking_exception=GatewayMethodToGatewayAuthorizerLocalVariablesLinkingLimitationException,
)

mock_resource_linking_pair.assert_called_once_with(
source_resource_cfn_resource=method_cfn_resources,
source_resource_tf_config=method_config_resources,
destination_resource_tf=authorizer_tf_resources,
tf_destination_attribute_name="id",
terraform_link_field_name="authorizer_id",
cfn_link_field_name="AuthorizerId",
terraform_resource_type_prefix=API_GATEWAY_AUTHORIZER_RESOURCE_ADDRESS_PREFIX,
cfn_resource_update_call_back_function=mock_link_gateway_method_to_gateway_authorizer_call_back,
linking_exceptions=mock_resource_linking_exceptions(),
)

mock_resource_linker.assert_called_once_with(mock_resource_linking_pair())