Skip to content

Commit 22bb387

Browse files
authored
fix(integ-tests): Add IAM auth to API Gateway resources in integration tests (#3963)
1 parent c43bcbf commit 22bb387

56 files changed

Lines changed: 221 additions & 14 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

integration/combination/test_function_with_http_api.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@ def test_function_with_http_api(self):
1818

1919
stack_outputs = self.get_stack_outputs()
2020
base_url = stack_outputs["ApiUrl"]
21-
self.verify_get_request_response(base_url + "some/path", 200)
22-
self.verify_get_request_response(base_url + "something", 404)
23-
self.verify_get_request_response(base_url + "another/endpoint", 404)
21+
self.verify_get_request_response_sigv4(base_url + "some/path", 200)
22+
self.verify_get_request_response_sigv4(base_url + "something", 404)
23+
self.verify_get_request_response_sigv4(base_url + "another/endpoint", 404)
2424

2525
def test_function_with_http_api_default_path(self):
2626
self.create_and_verify_stack("combination/function_with_http_api_default_path")
2727

2828
stack_outputs = self.get_stack_outputs()
2929
base_url = stack_outputs["ApiUrl"]
3030
# The $default route catches requests that don't explicitly match other routes
31-
self.verify_get_request_response(base_url, 200)
32-
self.verify_get_request_response(base_url + "something", 200)
31+
self.verify_get_request_response_sigv4(base_url, 200)
32+
self.verify_get_request_response_sigv4(base_url + "something", 200)

integration/combination/test_function_with_implicit_http_api.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ def test_function_with_implicit_api(self):
1212

1313
stack_outputs = self.get_stack_outputs()
1414
base_url = stack_outputs["ApiUrl"]
15-
self.verify_get_request_response(base_url, 200)
16-
self.verify_get_request_response(base_url + "something", 200)
17-
self.verify_get_request_response(base_url + "another/endpoint", 200)
15+
self.verify_get_request_response_sigv4(base_url, 200)
16+
self.verify_get_request_response_sigv4(base_url + "something", 200)
17+
self.verify_get_request_response_sigv4(base_url + "another/endpoint", 200)

integration/helpers/base_test.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@
44
import shutil
55
from pathlib import Path
66
from unittest.case import TestCase
7+
from urllib.parse import urlparse
78

89
import boto3
910
import botocore
1011
import pytest
1112
import requests
13+
from botocore.auth import SigV4Auth
14+
from botocore.awsrequest import AWSRequest
1215
from samtranslator.translator.arn_generator import ArnGenerator
1316
from samtranslator.yaml_helper import yaml_parse
1417
from tenacity import (
@@ -538,6 +541,25 @@ def verify_get_request_response(self, url, expected_status_code, headers=None):
538541
)
539542
return response
540543

544+
@retry(
545+
stop=stop_after_attempt(6),
546+
wait=wait_exponential(multiplier=1, min=16, max=64) + wait_random(0, 1),
547+
retry=retry_if_exception_type(StatusCodeError),
548+
after=after_log(LOG, logging.WARNING),
549+
reraise=True,
550+
)
551+
def verify_get_request_response_sigv4(self, url, expected_status_code, headers=None):
552+
"""
553+
Verify if a SigV4-signed get request to a certain url returns the expected status code.
554+
Use this for APIs with IAM authorization.
555+
"""
556+
response = self.do_get_request_with_sigv4(url, headers)
557+
if response.status_code != expected_status_code:
558+
raise StatusCodeError(
559+
f"SigV4 request to {url} failed with status: {response.status_code}, expected status: {expected_status_code}"
560+
)
561+
return response
562+
541563
@retry(
542564
stop=stop_after_attempt(6),
543565
wait=wait_exponential(multiplier=1, min=16, max=64) + wait_random(0, 1),
@@ -581,6 +603,22 @@ def verify_post_request(self, url: str, body_obj, expected_status_code: int, hea
581603
)
582604
return response
583605

606+
@retry(
607+
stop=stop_after_attempt(6),
608+
wait=wait_exponential(multiplier=1, min=16, max=64) + wait_random(0, 1),
609+
retry=retry_if_exception_type(StatusCodeError),
610+
after=after_log(LOG, logging.WARNING),
611+
reraise=True,
612+
)
613+
def verify_post_request_sigv4(self, url: str, body_obj, expected_status_code: int, headers=None):
614+
"""Return response to SigV4-signed POST request and verify matches expected status code."""
615+
response = self.do_post_request_with_sigv4(url, body_obj, headers)
616+
if response.status_code != expected_status_code:
617+
raise StatusCodeError(
618+
f"SigV4 POST request to {url} failed with status: {response.status_code}, expected status: {expected_status_code}"
619+
)
620+
return response
621+
584622
def get_default_test_template_parameters(self):
585623
"""
586624
get the default template parameters
@@ -636,6 +674,29 @@ def do_get_request_with_logging(self, url, headers=None):
636674
)
637675
return response
638676

677+
def do_get_request_with_sigv4(self, url, headers=None):
678+
"""
679+
Perform a SigV4-signed GET request to an APIGW endpoint with IAM auth.
680+
"""
681+
parsed = urlparse(url)
682+
request_headers = {"host": parsed.hostname}
683+
if headers:
684+
request_headers.update(headers)
685+
686+
aws_request = AWSRequest(method="GET", url=url, headers=request_headers)
687+
session = botocore.session.Session()
688+
credentials = session.get_credentials().get_frozen_credentials()
689+
SigV4Auth(credentials, "execute-api", self.my_region).add_auth(aws_request)
690+
691+
response = requests.get(url, headers=dict(aws_request.headers))
692+
amazon_headers = RequestUtils(response).get_amazon_headers()
693+
if self.internal:
694+
REQUEST_LOGGER.info(
695+
"SigV4 request made to " + url,
696+
extra={"test": self.testcase, "status": response.status_code, "headers": amazon_headers},
697+
)
698+
return response
699+
639700
def do_options_request_with_logging(self, url, headers=None):
640701
"""
641702
Perform a options request to an APIGW endpoint and log relevant info
@@ -669,3 +730,25 @@ def do_post_request_with_logging(self, url: str, body_obj, requestHeaders=None):
669730
extra={"test": self.testcase, "status": response.status_code, "headers": amazon_headers},
670731
)
671732
return response
733+
734+
def do_post_request_with_sigv4(self, url: str, body_obj, headers=None):
735+
"""Perform a SigV4-signed POST request to an APIGW endpoint with IAM auth."""
736+
parsed = urlparse(url)
737+
body = json.dumps(body_obj)
738+
request_headers = {"host": parsed.hostname, "content-type": "application/json"}
739+
if headers:
740+
request_headers.update(headers)
741+
742+
aws_request = AWSRequest(method="POST", url=url, headers=request_headers, data=body)
743+
session = botocore.session.Session()
744+
credentials = session.get_credentials().get_frozen_credentials()
745+
SigV4Auth(credentials, "execute-api", self.my_region).add_auth(aws_request)
746+
747+
response = requests.post(url, data=body, headers=dict(aws_request.headers))
748+
amazon_headers = RequestUtils(response).get_amazon_headers()
749+
if self.internal:
750+
REQUEST_LOGGER.info(
751+
"SigV4 POST request made to " + url,
752+
extra={"test": self.testcase, "status": response.status_code, "headers": amazon_headers},
753+
)
754+
return response

integration/resources/templates/combination/api_with_binary_media_types.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Resources:
1717
MyApi:
1818
Type: AWS::Serverless::Api
1919
Properties:
20+
Auth:
21+
DefaultAuthorizer: AWS_IAM
2022
StageName: Prod
2123
DefinitionUri: ${definitionuri}
2224
BinaryMediaTypes:

integration/resources/templates/combination/api_with_binary_media_types_with_definition_body.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ Resources:
2222
MyApi:
2323
Type: AWS::Serverless::Api
2424
Properties:
25+
Auth:
26+
DefaultAuthorizer: AWS_IAM
2527
StageName: Prod
2628
DefinitionBody:
2729
# Simple HTTP Proxy API

integration/resources/templates/combination/api_with_binary_media_types_with_definition_body_openapi.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ Resources:
2626
MyApi:
2727
Type: AWS::Serverless::Api
2828
Properties:
29+
Auth:
30+
DefaultAuthorizer: AWS_IAM
2931
StageName: Prod
3032
DefinitionBody:
3133
# Simple HTTP Proxy API

integration/resources/templates/combination/api_with_custom_domains_edge.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ Resources:
3232
MyApi:
3333
Type: AWS::Serverless::Api
3434
Properties:
35+
Auth:
36+
DefaultAuthorizer: AWS_IAM
3537
OpenApiVersion: 3.0.1
3638
StageName: Prod
3739
Domain:

integration/resources/templates/combination/api_with_disable_execute_api_endpoint.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ Resources:
1212
RestApiGateway:
1313
Type: AWS::Serverless::Api
1414
Properties:
15+
Auth:
16+
DefaultAuthorizer: AWS_IAM
1517
StageName: Prod
1618
DisableExecuteApiEndpoint:
1719
Ref: DisableExecuteApiEndpointValue

integration/resources/templates/combination/api_with_disable_execute_api_endpoint_openapi_3.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ Resources:
1212
RestApiGateway:
1313
Type: AWS::Serverless::Api
1414
Properties:
15+
Auth:
16+
DefaultAuthorizer: AWS_IAM
1517
StageName: Prod
1618
OpenApiVersion: 3.0
1719
DisableExecuteApiEndpoint:

integration/resources/templates/combination/api_with_endpoint_configuration.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ Resources:
1313
MyApi:
1414
Type: AWS::Serverless::Api
1515
Properties:
16+
Auth:
17+
DefaultAuthorizer: AWS_IAM
1618
StageName: Prod
1719
EndpointConfiguration: {Ref: Config}
1820
DefinitionUri: ${definitionuri}

0 commit comments

Comments
 (0)