Skip to content

Commit bb5d1c3

Browse files
committed
Handle content type in lambda
1 parent 0b1bab1 commit bb5d1c3

File tree

6 files changed

+55
-2
lines changed

6 files changed

+55
-2
lines changed

backend/compact-connect/lambdas/python/common/cc_common/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ class CCNotFoundException(CCInvalidRequestException):
2020
"""Requested resource is not found, corresponds to a 404 response"""
2121

2222

23+
class CCUnsupportedMediaTypeException(CCInvalidRequestException):
24+
"""Unsupported media type, corresponds to a 415 response"""
25+
26+
2327
class CCRateLimitingException(CCInvalidRequestException):
2428
"""Client is rate limited, corresponds to a 429 response"""
2529

backend/compact-connect/lambdas/python/common/cc_common/utils.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
CCNotFoundException,
2424
CCRateLimitingException,
2525
CCUnauthorizedException,
26+
CCUnsupportedMediaTypeException,
2627
)
2728

2829

@@ -101,18 +102,25 @@ def caught_handler(event, context: LambdaContext):
101102
else:
102103
cors_origin = config.allowed_origins[0]
103104

105+
content_type = headers.get('Content-Type')
106+
104107
# Propagate these keys to all log messages in this with block
105108
with logger.append_context_keys(
106109
method=event['httpMethod'],
107110
origin=origin,
108111
path=event['requestContext']['resourcePath'],
112+
content_type=content_type,
109113
identity={'user': event['requestContext'].get('authorizer', {}).get('claims', {}).get('sub')},
110114
query_params=event['queryStringParameters'],
111115
username=event['requestContext'].get('authorizer', {}).get('claims', {}).get('cognito:username'),
112116
):
113117
logger.info('Incoming request')
114118

115119
try:
120+
# We'll enforce json-only content for the whole API, right here.
121+
if event.get('body') is not None and content_type != 'application/json':
122+
raise CCUnsupportedMediaTypeException(f'Unsupported media type: {content_type}')
123+
116124
return {
117125
'headers': {'Access-Control-Allow-Origin': cors_origin, 'Vary': 'Origin'},
118126
'statusCode': 200,
@@ -139,6 +147,13 @@ def caught_handler(event, context: LambdaContext):
139147
'statusCode': 404,
140148
'body': json.dumps({'message': f'{e.message}'}),
141149
}
150+
except CCUnsupportedMediaTypeException as e:
151+
logger.info('Unsupported media type', exc_info=e)
152+
return {
153+
'headers': {'Access-Control-Allow-Origin': cors_origin, 'Vary': 'Origin'},
154+
'statusCode': 415,
155+
'body': json.dumps({'message': 'Unsupported media type'}),
156+
}
142157
except CCRateLimitingException as e:
143158
logger.info('Rate limiting request', exc_info=e)
144159
return {

backend/compact-connect/lambdas/python/common/tests/resources/api-event.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"Accept-Encoding": "gzip, deflate, br",
88
"Authorization": "Bearer eyfoofoo",
99
"Cache-Control": "no-cache",
10+
"Content-Type": "application/json",
1011
"CloudFront-Forwarded-Proto": "https",
1112
"CloudFront-Is-Desktop-Viewer": "true",
1213
"CloudFront-Is-Mobile-Viewer": "false",

backend/compact-connect/lambdas/python/common/tests/unit/test_api_handler.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,36 @@ def lambda_handler(event: dict, context: LambdaContext): # noqa: ARG001 unused-
144144
resp = lambda_handler(event, self.mock_context)
145145
self.assertEqual(200, resp['statusCode'])
146146
self.assertEqual('https://example.org', resp['headers']['Access-Control-Allow-Origin'])
147+
148+
def test_unsupported_media_type(self):
149+
from cc_common.utils import api_handler
150+
151+
@api_handler
152+
def lambda_handler(event: dict, context: LambdaContext): # noqa: ARG001 unused-argument
153+
return {'message': 'OK'}
154+
155+
with open('tests/resources/api-event.json') as f:
156+
event = json.load(f)
157+
158+
# We only accept json
159+
event['headers']['Content-Type'] = 'text/plain'
160+
event['body'] = 'not json'
161+
162+
resp = lambda_handler(event, self.mock_context)
163+
self.assertEqual(415, resp['statusCode'])
164+
165+
def test_json_decode_error(self):
166+
from cc_common.utils import api_handler
167+
168+
@api_handler
169+
def lambda_handler(event: dict, context: LambdaContext): # noqa: ARG001 unused-argument
170+
return json.loads(event['body'])
171+
172+
with open('tests/resources/api-event.json') as f:
173+
event = json.load(f)
174+
175+
event['headers']['Content-Type'] = 'application/json'
176+
event['body'] = 'not json'
177+
178+
resp = lambda_handler(event, self.mock_context)
179+
self.assertEqual(400, resp['statusCode'])

backend/compact-connect/lambdas/python/staff-users/tests/resources/api-event.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"Accept-Encoding": "gzip, deflate, br",
88
"Authorization": "Bearer eyfoofoo",
99
"Cache-Control": "no-cache",
10+
"Content-Type": "application/json",
1011
"CloudFront-Forwarded-Proto": "https",
1112
"CloudFront-Is-Desktop-Viewer": "true",
1213
"CloudFront-Is-Mobile-Viewer": "false",

backend/compact-connect/stacks/api_stack/v1_api/provider_users.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import os
44

55
from aws_cdk import Duration
6-
from aws_cdk.aws_apigateway import LambdaIntegration, MethodResponse, PassthroughBehavior, Resource
6+
from aws_cdk.aws_apigateway import LambdaIntegration, MethodResponse, Resource
77
from aws_cdk.aws_cloudwatch import Alarm, ComparisonOperator, MathExpression, Metric, Stats, TreatMissingData
88
from aws_cdk.aws_cloudwatch_actions import SnsAction
99
from aws_cdk.aws_kms import IKey
@@ -350,7 +350,6 @@ def _add_provider_registration(
350350
integration=LambdaIntegration(
351351
self.provider_registration_handler,
352352
timeout=Duration.seconds(29),
353-
passthrough_behavior=PassthroughBehavior.NEVER,
354353
),
355354
)
356355

0 commit comments

Comments
 (0)