Skip to content

Commit f7a5eb8

Browse files
mndevecisattigar
andauthored
feat: adding OwnershipVerificationCertificateArn and DisableExecuteApiEndpoint to APIs. (#93) (#2239)
Co-authored-by: sattigar <67429403+sattigar@users.noreply.github.com>
1 parent 1e96b09 commit f7a5eb8

File tree

9 files changed

+136
-0
lines changed

9 files changed

+136
-0
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from parameterized import parameterized
2+
3+
from integration.helpers.base_test import BaseTest
4+
5+
6+
class TestApiWithDisableExecuteApiEndpoint(BaseTest):
7+
@parameterized.expand(
8+
[
9+
("combination/api_with_disable_execute_api_endpoint", True),
10+
("combination/api_with_disable_execute_api_endpoint", False),
11+
]
12+
)
13+
def test_end_point_configuration(self, file_name, disable_value):
14+
parameters = [
15+
{
16+
"ParameterKey": "DisableExecuteApiEndpointValue",
17+
"ParameterValue": "true" if disable_value else "false",
18+
"UsePreviousValue": False,
19+
"ResolvedValue": "string",
20+
}
21+
]
22+
23+
self.create_and_verify_stack(file_name, parameters)
24+
25+
rest_api_id = self.get_physical_id_by_type("AWS::ApiGateway::RestApi")
26+
apigw_client = self.client_provider.api_client
27+
28+
response = apigw_client.get_rest_api(restApiId=rest_api_id)
29+
api_result = response["disableExecuteApiEndpoint"]
30+
self.assertEqual(api_result, disable_value)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[
2+
{"LogicalResourceId": "RestApiGateway", "ResourceType": "AWS::ApiGateway::RestApi"},
3+
{"LogicalResourceId": "RestApiGatewayDeployment", "ResourceType": "AWS::ApiGateway::Deployment"},
4+
{"LogicalResourceId": "RestApiGatewayProdStage", "ResourceType": "AWS::ApiGateway::Stage"},
5+
{"LogicalResourceId": "RestApiFunction", "ResourceType": "AWS::Lambda::Function"},
6+
{"LogicalResourceId": "RestApiFunctionIamPermissionProd", "ResourceType": "AWS::Lambda::Permission"},
7+
{"LogicalResourceId": "RestApiFunctionRole", "ResourceType": "AWS::IAM::Role"}
8+
]
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
Parameters:
2+
DisableExecuteApiEndpointValue:
3+
Description: Variable to define if client can access default API endpoint.
4+
Type: String
5+
AllowedValues: [true, false]
6+
7+
Resources:
8+
RestApiGateway:
9+
Type: AWS::Serverless::Api
10+
Properties:
11+
StageName: Prod
12+
DisableExecuteApiEndpoint:
13+
Ref: DisableExecuteApiEndpointValue
14+
15+
RestApiFunction:
16+
Type: AWS::Serverless::Function
17+
Properties:
18+
InlineCode: |
19+
exports.handler = async (event) => {
20+
const response = {
21+
statusCode: 200,
22+
body: JSON.stringify('Hello from Lambda!'),
23+
};
24+
return response;
25+
};
26+
Handler: index.handler
27+
Runtime: nodejs12.x
28+
Events:
29+
Iam:
30+
Type: Api
31+
Properties:
32+
RestApiId: !Ref RestApiGateway
33+
Method: GET
34+
Path: /
35+
Outputs:
36+
ApiUrl:
37+
Description: "API endpoint URL for Prod environment"
38+
Value:
39+
Fn::Sub: 'https://${RestApiGateway}.execute-api.${AWS::Region}.${AWS::URLSuffix}/Prod/'

samtranslator/model/api/api_generator.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ def __init__(
170170
method_settings=None,
171171
binary_media=None,
172172
minimum_compression_size=None,
173+
disable_execute_api_endpoint=None,
173174
cors=None,
174175
auth=None,
175176
gateway_responses=None,
@@ -218,6 +219,7 @@ def __init__(
218219
self.method_settings = method_settings
219220
self.binary_media = binary_media
220221
self.minimum_compression_size = minimum_compression_size
222+
self.disable_execute_api_endpoint = disable_execute_api_endpoint
221223
self.cors = cors
222224
self.auth = auth
223225
self.gateway_responses = gateway_responses
@@ -290,8 +292,27 @@ def _construct_rest_api(self):
290292
if self.mode:
291293
rest_api.Mode = self.mode
292294

295+
if self.disable_execute_api_endpoint is not None:
296+
self._add_endpoint_extension()
297+
293298
return rest_api
294299

300+
def _add_endpoint_extension(self):
301+
"""Add disableExecuteApiEndpoint if it is set in SAM
302+
Note:
303+
If neither DefinitionUri nor DefinitionBody are specified,
304+
SAM will generate a openapi definition body based on template configuration.
305+
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-api.html#sam-api-definitionbody
306+
For this reason, we always put DisableExecuteApiEndpoint into openapi object irrespective of origin of DefinitionBody.
307+
"""
308+
if self.disable_execute_api_endpoint and not self.definition_body:
309+
raise InvalidResourceException(
310+
self.logical_id, "DisableExecuteApiEndpoint works only within 'DefinitionBody' property."
311+
)
312+
editor = SwaggerEditor(self.definition_body)
313+
editor.add_disable_execute_api_endpoint_extension(self.disable_execute_api_endpoint)
314+
self.definition_body = editor.swagger
315+
295316
def _construct_body_s3_dict(self):
296317
"""Constructs the RestApi's `BodyS3Location property`_, from the SAM Api's DefinitionUri property.
297318
@@ -444,6 +465,9 @@ def _construct_api_domain(self, rest_api):
444465
if self.domain.get("SecurityPolicy", None):
445466
domain.SecurityPolicy = self.domain["SecurityPolicy"]
446467

468+
if self.domain.get("OwnershipVerificationCertificateArn", None):
469+
domain.OwnershipVerificationCertificateArn = self.domain["OwnershipVerificationCertificateArn"]
470+
447471
# Create BasepathMappings
448472
if self.domain.get("BasePath") and isinstance(self.domain.get("BasePath"), string_types):
449473
basepaths = [self.domain.get("BasePath")]

samtranslator/model/api/http_api_generator.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,12 @@ def _construct_api_domain(self, http_api):
253253
"EndpointConfiguration for Custom Domains must be one of {}.".format(["REGIONAL"]),
254254
)
255255
domain_config["EndpointType"] = endpoint
256+
257+
if self.domain.get("OwnershipVerificationCertificateArn", None):
258+
domain_config["OwnershipVerificationCertificateArn"] = self.domain.get(
259+
"OwnershipVerificationCertificateArn"
260+
)
261+
256262
domain_config["CertificateArn"] = self.domain.get("CertificateArn")
257263
if self.domain.get("SecurityPolicy", None):
258264
domain_config["SecurityPolicy"] = self.domain.get("SecurityPolicy")

samtranslator/model/apigateway.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ class ApiGatewayDomainName(Resource):
169169
"MutualTlsAuthentication": PropertyType(False, is_type(dict)),
170170
"SecurityPolicy": PropertyType(False, is_str()),
171171
"CertificateArn": PropertyType(False, is_str()),
172+
"OwnershipVerificationCertificateArn": PropertyType(False, is_str()),
172173
}
173174

174175

samtranslator/model/sam_resources.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -878,6 +878,7 @@ class SamApi(SamResourceMacro):
878878
"Domain": PropertyType(False, is_type(dict)),
879879
"Description": PropertyType(False, is_str()),
880880
"Mode": PropertyType(False, is_str()),
881+
"DisableExecuteApiEndpoint": PropertyType(False, is_type(bool)),
881882
}
882883

883884
referable_properties = {
@@ -925,6 +926,7 @@ def to_cloudformation(self, **kwargs):
925926
method_settings=self.MethodSettings,
926927
binary_media=self.BinaryMediaTypes,
927928
minimum_compression_size=self.MinimumCompressionSize,
929+
disable_execute_api_endpoint=self.DisableExecuteApiEndpoint,
928930
cors=self.Cors,
929931
auth=self.Auth,
930932
gateway_responses=self.GatewayResponses,

samtranslator/swagger/swagger.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class SwaggerEditor(object):
2525
_X_ANY_METHOD = "x-amazon-apigateway-any-method"
2626
_X_APIGW_REQUEST_VALIDATORS = "x-amazon-apigateway-request-validators"
2727
_X_APIGW_REQUEST_VALIDATOR = "x-amazon-apigateway-request-validator"
28+
_X_ENDPOINT_CONFIG = "x-amazon-apigateway-endpoint-configuration"
2829
_CACHE_KEY_PARAMETERS = "cacheKeyParameters"
2930
# https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
3031
_ALL_HTTP_METHODS = ["OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "PATCH"]
@@ -111,6 +112,19 @@ def get_method_contents(self, method):
111112
return method[self._CONDITIONAL_IF][1:]
112113
return [method]
113114

115+
def add_disable_execute_api_endpoint_extension(self, disable_execute_api_endpoint):
116+
"""Add endpoint configuration to _X_APIGW_ENDPOINT_CONFIG in open api definition as extension
117+
Following this guide:
118+
https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-swagger-extensions-endpoint-configuration.html
119+
:param boolean disable_execute_api_endpoint: Specifies whether clients can invoke your API by using the default execute-api endpoint.
120+
"""
121+
if not self._doc.get(self._X_ENDPOINT_CONFIG):
122+
self._doc[self._X_ENDPOINT_CONFIG] = {}
123+
124+
DISABLE_EXECUTE_API_ENDPOINT = "disableExecuteApiEndpoint"
125+
set_disable_api_endpoint = {DISABLE_EXECUTE_API_ENDPOINT: disable_execute_api_endpoint}
126+
self._doc[self._X_ENDPOINT_CONFIG].update(set_disable_api_endpoint)
127+
114128
def has_integration(self, path, method):
115129
"""
116130
Checks if an API Gateway integration is already present at the given path/method

samtranslator/validator/sam_schema/definitions/api.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,12 @@
9898
"intrinsic"
9999
]
100100
},
101+
"DisableExecuteApiEndpoint": {
102+
"type": [
103+
"boolean",
104+
"intrinsic"
105+
]
106+
},
101107
"Domain": {
102108
"$ref": "#definitions/AWS::Serverless::Api.DomainConfiguration"
103109
},
@@ -443,6 +449,12 @@
443449
"string",
444450
"intrinsic"
445451
]
452+
},
453+
"OwnershipVerificationCertificateArn": {
454+
"type": [
455+
"string",
456+
"intrinsic"
457+
]
446458
}
447459
},
448460
"required": [

0 commit comments

Comments
 (0)