Skip to content

Commit 7f60a43

Browse files
authored
add APIGW NG workaround for AccessDenied exception (#11317)
1 parent 4e4a83b commit 7f60a43

File tree

2 files changed

+40
-14
lines changed

2 files changed

+40
-14
lines changed

localstack-core/localstack/services/apigateway/next_gen/execute_api/handlers/gateway_exception.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
)
1212
from localstack.services.apigateway.next_gen.execute_api.context import RestApiInvocationContext
1313
from localstack.services.apigateway.next_gen.execute_api.gateway_response import (
14+
AccessDeniedError,
1415
BaseGatewayException,
1516
get_gateway_response_or_default,
1617
)
@@ -81,6 +82,13 @@ def create_exception_response(
8182
def _build_response_content(exception: BaseGatewayException) -> str:
8283
# TODO apply responseTemplates to the content. We should also handle the default simply by managing the default
8384
# template body `{"message":$context.error.messageString}`
85+
86+
# TODO: remove this workaround by properly managing the responseTemplate for UnauthorizedError
87+
# on the CRUD level, it returns the same template as all other errors but in reality the message field is
88+
# capitalized
89+
if isinstance(exception, AccessDeniedError):
90+
return json.dumps({"Message": exception.message})
91+
8492
return json.dumps({"message": exception.message})
8593

8694
@staticmethod

tests/unit/services/apigateway/test_handler_exception.py

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from localstack.services.apigateway.next_gen.execute_api.gateway_response import (
99
AccessDeniedError,
1010
BaseGatewayException,
11+
UnauthorizedError,
1112
)
1213
from localstack.services.apigateway.next_gen.execute_api.handlers import GatewayExceptionHandler
1314
from localstack.services.apigateway.next_gen.execute_api.router import ApiGatewayEndpoint
@@ -69,27 +70,27 @@ def test_non_gateway_exception(self, get_context, apigw_response):
6970
def test_gateway_exception(self, get_context, apigw_response):
7071
exception_handler = GatewayExceptionHandler()
7172

72-
# Create an Access Denied exception with no Gateway Response configured
73-
exception = AccessDeniedError("Access Denied")
73+
# Create an UnauthorizedError exception with no Gateway Response configured
74+
exception = UnauthorizedError("Unauthorized")
7475
exception_handler(
7576
chain=RestApiGatewayHandlerChain(),
7677
exception=exception,
7778
context=get_context(),
7879
response=apigw_response,
7980
)
8081

81-
assert apigw_response.status_code == 403
82-
assert apigw_response.json == {"message": "Access Denied"}
83-
assert apigw_response.headers.get("x-amzn-errortype") == "AccessDeniedException"
82+
assert apigw_response.status_code == 401
83+
assert apigw_response.json == {"message": "Unauthorized"}
84+
assert apigw_response.headers.get("x-amzn-errortype") == "UnauthorizedException"
8485

8586
def test_gateway_exception_with_default_4xx(self, get_context, apigw_response):
8687
exception_handler = GatewayExceptionHandler()
8788

8889
# Configure DEFAULT_4XX response
8990
gateway_responses = {GatewayResponseType.DEFAULT_4XX: GatewayResponse(statusCode="400")}
9091

91-
# Create an Access Denied exception with DEFAULT_4xx configured
92-
exception = AccessDeniedError("Access Denied")
92+
# Create an UnauthorizedError exception with DEFAULT_4xx configured
93+
exception = UnauthorizedError("Unauthorized")
9394
exception_handler(
9495
chain=RestApiGatewayHandlerChain(),
9596
exception=exception,
@@ -98,24 +99,41 @@ def test_gateway_exception_with_default_4xx(self, get_context, apigw_response):
9899
)
99100

100101
assert apigw_response.status_code == 400
101-
assert apigw_response.json == {"message": "Access Denied"}
102-
assert apigw_response.headers.get("x-amzn-errortype") == "AccessDeniedException"
102+
assert apigw_response.json == {"message": "Unauthorized"}
103+
assert apigw_response.headers.get("x-amzn-errortype") == "UnauthorizedException"
103104

104105
def test_gateway_exception_with_gateway_response(self, get_context, apigw_response):
105106
exception_handler = GatewayExceptionHandler()
106107

107108
# Configure Access Denied response
108-
gateway_responses = {GatewayResponseType.ACCESS_DENIED: GatewayResponse(statusCode="400")}
109+
gateway_responses = {GatewayResponseType.UNAUTHORIZED: GatewayResponse(statusCode="405")}
109110

110-
# Create an Access Denied exception with ACCESS_DENIED configured
111-
exception = AccessDeniedError("Access Denied")
111+
# Create an UnauthorizedError exception with UNAUTHORIZED configured
112+
exception = UnauthorizedError("Unauthorized")
112113
exception_handler(
113114
chain=RestApiGatewayHandlerChain(),
114115
exception=exception,
115116
context=get_context(gateway_responses),
116117
response=apigw_response,
117118
)
118119

119-
assert apigw_response.status_code == 400
120-
assert apigw_response.json == {"message": "Access Denied"}
120+
assert apigw_response.status_code == 405
121+
assert apigw_response.json == {"message": "Unauthorized"}
122+
assert apigw_response.headers.get("x-amzn-errortype") == "UnauthorizedException"
123+
124+
def test_gateway_exception_access_denied(self, get_context, apigw_response):
125+
# special case where the `Message` field is capitalized
126+
exception_handler = GatewayExceptionHandler()
127+
128+
# Create an AccessDeniedError exception with no Gateway Response configured
129+
exception = AccessDeniedError("Access Denied")
130+
exception_handler(
131+
chain=RestApiGatewayHandlerChain(),
132+
exception=exception,
133+
context=get_context(),
134+
response=apigw_response,
135+
)
136+
137+
assert apigw_response.status_code == 403
138+
assert apigw_response.json == {"Message": "Access Denied"}
121139
assert apigw_response.headers.get("x-amzn-errortype") == "AccessDeniedException"

0 commit comments

Comments
 (0)