From 955e38551979205fdf5f0570086753a6d0fc9894 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Tue, 9 Jul 2024 17:27:14 +0100 Subject: [PATCH] feat(parser): add support for API Gateway Lambda authorizer events (#4718) * Adding Authorizer parser * Make pydantic v2 happy --- .../utilities/parser/models/__init__.py | 6 +++++ .../utilities/parser/models/apigw.py | 13 ++++++++++- .../utilities/parser/models/apigwv2.py | 8 ++++++- docs/utilities/parser.md | 3 +++ .../apiGatewayAuthorizerRequestEvent.json | 14 ++++++++++- tests/events/apiGatewayAuthorizerV2Event.json | 2 +- tests/unit/parser/_pydantic/test_apigw.py | 23 ++++++++++++++++++- tests/unit/parser/_pydantic/test_apigwv2.py | 10 ++++++++ 8 files changed, 74 insertions(+), 5 deletions(-) diff --git a/aws_lambda_powertools/utilities/parser/models/__init__.py b/aws_lambda_powertools/utilities/parser/models/__init__.py index fc78f938c16..7b0146f7d30 100644 --- a/aws_lambda_powertools/utilities/parser/models/__init__.py +++ b/aws_lambda_powertools/utilities/parser/models/__init__.py @@ -4,12 +4,15 @@ from .alb import AlbModel, AlbRequestContext, AlbRequestContextData from .apigw import ( + ApiGatewayAuthorizerRequest, + ApiGatewayAuthorizerToken, APIGatewayEventAuthorizer, APIGatewayEventIdentity, APIGatewayEventRequestContext, APIGatewayProxyEventModel, ) from .apigwv2 import ( + ApiGatewayAuthorizerRequestV2, APIGatewayProxyEventV2Model, RequestContextV2, RequestContextV2Authorizer, @@ -105,6 +108,7 @@ __all__ = [ "APIGatewayProxyEventV2Model", + "ApiGatewayAuthorizerRequestV2", "RequestContextV2", "RequestContextV2Http", "RequestContextV2Authorizer", @@ -165,6 +169,8 @@ "APIGatewayEventRequestContext", "APIGatewayEventAuthorizer", "APIGatewayEventIdentity", + "ApiGatewayAuthorizerRequest", + "ApiGatewayAuthorizerToken", "KafkaSelfManagedEventModel", "KafkaRecordModel", "KafkaMskEventModel", diff --git a/aws_lambda_powertools/utilities/parser/models/apigw.py b/aws_lambda_powertools/utilities/parser/models/apigw.py index c17b094d0c0..0865d02b98d 100644 --- a/aws_lambda_powertools/utilities/parser/models/apigw.py +++ b/aws_lambda_powertools/utilities/parser/models/apigw.py @@ -90,5 +90,16 @@ class APIGatewayProxyEventModel(BaseModel): requestContext: APIGatewayEventRequestContext pathParameters: Optional[Dict[str, str]] = None stageVariables: Optional[Dict[str, str]] = None - isBase64Encoded: bool + isBase64Encoded: Optional[bool] = None body: Optional[Union[str, Type[BaseModel]]] = None + + +class ApiGatewayAuthorizerToken(BaseModel): + type: Literal["TOKEN"] + methodArn: str + authorizationToken: str + + +class ApiGatewayAuthorizerRequest(APIGatewayProxyEventModel): + type: Literal["REQUEST"] + methodArn: str diff --git a/aws_lambda_powertools/utilities/parser/models/apigwv2.py b/aws_lambda_powertools/utilities/parser/models/apigwv2.py index 8f0f8dbf50c..4761b44a53c 100644 --- a/aws_lambda_powertools/utilities/parser/models/apigwv2.py +++ b/aws_lambda_powertools/utilities/parser/models/apigwv2.py @@ -68,4 +68,10 @@ class APIGatewayProxyEventV2Model(BaseModel): stageVariables: Optional[Dict[str, str]] = None requestContext: RequestContextV2 body: Optional[Union[str, Type[BaseModel]]] = None - isBase64Encoded: bool + isBase64Encoded: Optional[bool] = None + + +class ApiGatewayAuthorizerRequestV2(APIGatewayProxyEventV2Model): + type: Literal["REQUEST"] + routeArn: str + identitySource: List[str] diff --git a/docs/utilities/parser.md b/docs/utilities/parser.md index 4a91d5aa13c..95b70f7cd41 100644 --- a/docs/utilities/parser.md +++ b/docs/utilities/parser.md @@ -177,7 +177,10 @@ Parser comes with the following built-in models: | ------------------------------------------- | ------------------------------------------------------------------------------------- | | **AlbModel** | Lambda Event Source payload for Amazon Application Load Balancer | | **APIGatewayProxyEventModel** | Lambda Event Source payload for Amazon API Gateway | +| **ApiGatewayAuthorizerToken** | Lambda Event Source payload for Amazon API Gateway Lambda Authorizer with Token | +| **ApiGatewayAuthorizerRequest** | Lambda Event Source payload for Amazon API Gateway Lambda Authorizer with Request | | **APIGatewayProxyEventV2Model** | Lambda Event Source payload for Amazon API Gateway v2 payload | +| **ApiGatewayAuthorizerRequestV2** | Lambda Event Source payload for Amazon API Gateway v2 Lambda Authorizer | | **BedrockAgentEventModel** | Lambda Event Source payload for Bedrock Agents | | **CloudFormationCustomResourceCreateModel** | Lambda Event Source payload for AWS CloudFormation `CREATE` operation | | **CloudFormationCustomResourceUpdateModel** | Lambda Event Source payload for AWS CloudFormation `UPDATE` operation | diff --git a/tests/events/apiGatewayAuthorizerRequestEvent.json b/tests/events/apiGatewayAuthorizerRequestEvent.json index d8dfe3fecf9..908c7118c06 100644 --- a/tests/events/apiGatewayAuthorizerRequestEvent.json +++ b/tests/events/apiGatewayAuthorizerRequestEvent.json @@ -17,6 +17,18 @@ "CloudFront-Is-Mobile-Viewer": "false", "User-Agent": "..." }, + "multiValueHeaders": { + "Header1": [ + "value1" + ], + "Origin": [ + "https://aws.amazon.com" + ], + "Header2": [ + "value1", + "value2" + ] + }, "queryStringParameters": { "QueryString1": "queryValue1" }, @@ -42,7 +54,7 @@ "cognitoIdentityPoolId": null, "principalOrgId": null, "apiKey": "...", - "sourceIp": "...", + "sourceIp": "test-invoke-source-ip", "user": null, "userAgent": "PostmanRuntime/7.28.3", "userArn": null, diff --git a/tests/events/apiGatewayAuthorizerV2Event.json b/tests/events/apiGatewayAuthorizerV2Event.json index f0528080c90..83c3c9d8d61 100644 --- a/tests/events/apiGatewayAuthorizerV2Event.json +++ b/tests/events/apiGatewayAuthorizerV2Event.json @@ -38,7 +38,7 @@ "method": "POST", "path": "/merchants", "protocol": "HTTP/1.1", - "sourceIp": "IP", + "sourceIp": "10.10.10.10", "userAgent": "agent" }, "requestId": "id", diff --git a/tests/unit/parser/_pydantic/test_apigw.py b/tests/unit/parser/_pydantic/test_apigw.py index 7b4b2528373..9fdf623bcf9 100644 --- a/tests/unit/parser/_pydantic/test_apigw.py +++ b/tests/unit/parser/_pydantic/test_apigw.py @@ -2,7 +2,11 @@ from pydantic import ValidationError from aws_lambda_powertools.utilities.parser import envelopes, parse -from aws_lambda_powertools.utilities.parser.models import APIGatewayProxyEventModel +from aws_lambda_powertools.utilities.parser.models import ( + ApiGatewayAuthorizerRequest, + ApiGatewayAuthorizerToken, + APIGatewayProxyEventModel, +) from tests.functional.utils import load_event from tests.unit.parser._pydantic.schemas import MyApiGatewayBusiness @@ -148,3 +152,20 @@ def test_apigw_event_empty_body(): event = load_event("apiGatewayProxyEvent.json") event["body"] = None parse(event=event, model=APIGatewayProxyEventModel) + + +def test_apigw_event_authorizer_token(): + raw_event = load_event("apiGatewayAuthorizerTokenEvent.json") + parsed_event: ApiGatewayAuthorizerToken = ApiGatewayAuthorizerToken(**raw_event) + + assert parsed_event.type == raw_event["type"] + assert parsed_event.methodArn == raw_event["methodArn"] + assert parsed_event.authorizationToken == raw_event["authorizationToken"] + + +def test_apigw_event_authorizer_event(): + raw_event = load_event("apiGatewayAuthorizerRequestEvent.json") + parsed_event: ApiGatewayAuthorizerRequest = ApiGatewayAuthorizerRequest(**raw_event) + + assert parsed_event.type == raw_event["type"] + assert parsed_event.methodArn == raw_event["methodArn"] diff --git a/tests/unit/parser/_pydantic/test_apigwv2.py b/tests/unit/parser/_pydantic/test_apigwv2.py index 47e79cbaa36..cec9e05bccd 100644 --- a/tests/unit/parser/_pydantic/test_apigwv2.py +++ b/tests/unit/parser/_pydantic/test_apigwv2.py @@ -1,5 +1,6 @@ from aws_lambda_powertools.utilities.parser import envelopes, parse from aws_lambda_powertools.utilities.parser.models import ( + ApiGatewayAuthorizerRequestV2, APIGatewayProxyEventV2Model, RequestContextV2, RequestContextV2Authorizer, @@ -120,3 +121,12 @@ def test_apigw_event_empty_query_strings(): raw_event["rawQueryString"] = "" raw_event.pop("queryStringParameters") # API GW v2 removes certain keys when no data is passed parse(event=raw_event, model=APIGatewayProxyEventV2Model) + + +def test_apigw_v2_request_authorizer(): + raw_event = load_event("apiGatewayAuthorizerV2Event.json") + parsed_event: ApiGatewayAuthorizerRequestV2 = ApiGatewayAuthorizerRequestV2(**raw_event) + + assert parsed_event.type == raw_event["type"] + assert parsed_event.identitySource == raw_event["identitySource"] + assert parsed_event.routeArn == raw_event["routeArn"]