Skip to content

Commit 70f942d

Browse files
iphbnusunny
andauthored
feat: support RestApi Security Policy in Serverless API (#3872)
Co-authored-by: Harold Sun <bnusunny@gmail.com>
1 parent b95c828 commit 70f942d

15 files changed

Lines changed: 406 additions & 3 deletions

File tree

docs/globals.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ Currently, the following resources and properties are being supported:
9898
TracingEnabled:
9999
OpenApiVersion:
100100
Domain:
101+
SecurityPolicy:
101102
102103
HttpApi:
103104
# Properties of AWS::Serverless::HttpApi

samtranslator/internal/schema_source/aws_serverless_api.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,11 @@ class Properties(BaseModel):
326326
Tags: Optional[DictStrAny] = properties("Tags")
327327
Policy: Optional[PassThroughProp] # TODO: add docs
328328
PropagateTags: Optional[bool] # TODO: add docs
329+
SecurityPolicy: Optional[PassThroughProp] = passthrough_prop(
330+
PROPERTIES_STEM,
331+
"SecurityPolicy",
332+
["AWS::ApiGateway::RestApi", "Properties", "SecurityPolicy"],
333+
)
329334
TracingEnabled: Optional[TracingEnabled] = passthrough_prop(
330335
PROPERTIES_STEM,
331336
"TracingEnabled",
@@ -392,6 +397,11 @@ class Globals(BaseModel):
392397
Domain: Optional[Domain] = properties("Domain")
393398
AlwaysDeploy: Optional[AlwaysDeploy] = properties("AlwaysDeploy")
394399
PropagateTags: Optional[bool] # TODO: add docs
400+
SecurityPolicy: Optional[PassThroughProp] = passthrough_prop(
401+
PROPERTIES_STEM,
402+
"SecurityPolicy",
403+
["AWS::ApiGateway::RestApi", "Properties", "SecurityPolicy"],
404+
)
395405

396406

397407
class Resource(ResourceAttributes):

samtranslator/internal/schema_source/sam-docs.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,7 @@
705705
"Name": "A name for the API Gateway RestApi resource \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`Name`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html#cfn-apigateway-restapi-name) property of an `AWS::ApiGateway::RestApi` resource\\.",
706706
"OpenApiVersion": "Version of OpenApi to use\\. This can either be `2.0` for the Swagger specification, or one of the OpenApi 3\\.0 versions, like `3.0.1`\\. For more information about OpenAPI, see the [OpenAPI Specification](https://swagger.io/specification/)\\. \n AWS SAM creates a stage called `Stage` by default\\. Setting this property to any valid value will prevent the creation of the stage `Stage`\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.",
707707
"PropagateTags": "Indicate whether or not to pass tags from the `Tags` property to your [AWS::Serverless::Api](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-generated-resources-api.html) generated resources\\. Specify `True` to propagate tags in your generated resources\\. \n*Type*: Boolean \n*Required*: No \n*Default*: `False` \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.",
708+
"SecurityPolicy": "The Transport Layer Security (TLS) version + cipher suite for this RestApi. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`SecurityPolicy`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html#cfn-apigateway-restapi-securitypolicy) property of an `AWS::ApiGateway::RestApi` resource\\.",
708709
"StageName": "The name of the stage, which API Gateway uses as the first path segment in the invoke Uniform Resource Identifier \\(URI\\)\\. \nTo reference the stage resource, use `<api-logical-id>.Stage`\\. For more information about referencing resources generated when an [AWS::Serverless::Api](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/#sam-resource-api.html#sam-resource-api) resource is specified, see [AWS CloudFormation resources generated when AWS::Serverless::Api is specified](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-generated-resources-api.html)\\. For general information about generated AWS CloudFormation resources, see [Generated AWS CloudFormation resources for AWS SAM](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-generated-resources.html)\\. \n*Type*: String \n*Required*: Yes \n*AWS CloudFormation compatibility*: This property is similar to the [`StageName`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-stage.html#cfn-apigateway-stage-stagename) property of an `AWS::ApiGateway::Stage` resource\\. It is required in SAM, but not required in API Gateway \n*Additional notes*: The Implicit API has a stage name of \"Prod\"\\.",
709710
"Tags": "A map \\(string to string\\) that specifies the tags to be added to this API Gateway stage\\. For details about valid keys and values for tags, see [Resource tag](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html) in the *AWS CloudFormation User Guide*\\. \n*Type*: Map \n*Required*: No \n*AWS CloudFormation compatibility*: This property is similar to the [`Tags`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-stage.html#cfn-apigateway-stage-tags) property of an `AWS::ApiGateway::Stage` resource\\. The Tags property in SAM consists of Key:Value pairs; in CloudFormation it consists of a list of Tag objects\\.",
710711
"TracingEnabled": "Indicates whether active tracing with X\\-Ray is enabled for the stage\\. For more information about X\\-Ray, see [Tracing user requests to REST APIs using X\\-Ray](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-xray.html) in the *API Gateway Developer Guide*\\. \n*Type*: Boolean \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`TracingEnabled`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-stage.html#cfn-apigateway-stage-tracingenabled) property of an `AWS::ApiGateway::Stage` resource\\.",
@@ -841,4 +842,4 @@
841842
"UseAliasAsEventTarget": "Indicate whether or not to pass the alias, created by using the `AutoPublishAlias` property, to the events source's target defined with [Events](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/#sam-statemachine-events.html#sam-statemachine-events)\\. \nSpecify `True` to use the alias as the events' target\\. \n*Type*: Boolean \n*Required*: No \n*Default*: `False` \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\."
842843
}
843844
}
844-
}
845+
}

samtranslator/model/api/api_generator.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ def __init__( # noqa: PLR0913
221221
always_deploy: Optional[bool] = False,
222222
feature_toggle: Optional[FeatureToggle] = None,
223223
policy: Optional[Union[Dict[str, Any], Intrinsicable[str]]] = None,
224+
security_policy: Optional[Intrinsicable[str]] = None,
224225
):
225226
"""Constructs an API Generator class that generates API Gateway resources
226227
@@ -279,6 +280,7 @@ def __init__( # noqa: PLR0913
279280
self.always_deploy = always_deploy
280281
self.feature_toggle = feature_toggle
281282
self.policy = policy
283+
self.security_policy = security_policy
282284

283285
def _construct_rest_api(self) -> ApiGatewayRestApi:
284286
"""Constructs and returns the ApiGateway RestApi.
@@ -335,6 +337,9 @@ def _construct_rest_api(self) -> ApiGatewayRestApi:
335337
if self.policy:
336338
rest_api.Policy = self.policy
337339

340+
if self.security_policy:
341+
rest_api.SecurityPolicy = self.security_policy
342+
338343
return rest_api
339344

340345
def _validate_properties(self) -> None:

samtranslator/model/apigateway.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class ApiGatewayRestApi(Resource):
3030
"ApiKeySourceType": GeneratedProperty(),
3131
"Tags": GeneratedProperty(),
3232
"Policy": GeneratedProperty(),
33+
"SecurityPolicy": GeneratedProperty(),
3334
}
3435

3536
Body: Optional[Dict[str, Any]]
@@ -46,6 +47,7 @@ class ApiGatewayRestApi(Resource):
4647
ApiKeySourceType: Optional[PassThrough]
4748
Tags: Optional[PassThrough]
4849
Policy: Optional[PassThrough]
50+
SecurityPolicy: Optional[PassThrough]
4951

5052
runtime_attrs = {"rest_api_id": lambda self: ref(self.logical_id)}
5153

samtranslator/model/sam_resources.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1645,6 +1645,7 @@ class SamApi(SamResourceMacro):
16451645
"ApiKeySourceType": PropertyType(False, IS_STR),
16461646
"AlwaysDeploy": Property(False, IS_BOOL),
16471647
"Policy": PropertyType(False, one_of(IS_STR, IS_DICT)),
1648+
"SecurityPolicy": PropertyType(False, IS_STR),
16481649
}
16491650

16501651
Name: Optional[Intrinsicable[str]]
@@ -1677,6 +1678,7 @@ class SamApi(SamResourceMacro):
16771678
ApiKeySourceType: Optional[Intrinsicable[str]]
16781679
AlwaysDeploy: Optional[bool]
16791680
Policy: Optional[Union[Dict[str, Any], Intrinsicable[str]]]
1681+
SecurityPolicy: Optional[Intrinsicable[str]]
16801682

16811683
referable_properties = {
16821684
"Stage": ApiGatewayStage.resource_type,
@@ -1745,6 +1747,7 @@ def to_cloudformation(self, **kwargs) -> List[Resource]: # type: ignore[no-unty
17451747
always_deploy=self.AlwaysDeploy,
17461748
feature_toggle=feature_toggle,
17471749
policy=self.Policy,
1750+
security_policy=self.SecurityPolicy,
17481751
)
17491752

17501753
generated_resources = api_generator.to_cloudformation(redeploy_restapi_parameters, route53_record_set_groups)

samtranslator/plugins/globals/globals.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ class Globals:
8888
"Domain",
8989
"AlwaysDeploy",
9090
"PropagateTags",
91+
"SecurityPolicy",
9192
],
9293
SamResourceType.HttpApi.value: [
9394
"Auth",

samtranslator/schema/schema.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355541,6 +355541,11 @@
355541355541
"title": "Propagatetags",
355542355542
"type": "boolean"
355543355543
},
355544+
"SecurityPolicy": {
355545+
"markdownDescription": "The Transport Layer Security (TLS) version + cipher suite for this RestApi. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`SecurityPolicy`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html#cfn-apigateway-restapi-securitypolicy) property of an `AWS::ApiGateway::RestApi` resource\\.",
355546+
"title": "SecurityPolicy",
355547+
"type": "string"
355548+
},
355544355549
"TracingEnabled": {
355545355550
"markdownDescription": "Indicates whether active tracing with X\\-Ray is enabled for the stage\\. For more information about X\\-Ray, see [Tracing user requests to REST APIs using X\\-Ray](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-xray.html) in the *API Gateway Developer Guide*\\. \n*Type*: Boolean \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`TracingEnabled`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-stage.html#cfn-apigateway-stage-tracingenabled) property of an `AWS::ApiGateway::Stage` resource\\.",
355546355551
"title": "TracingEnabled",
@@ -355741,6 +355746,11 @@
355741355746
"title": "Propagatetags",
355742355747
"type": "boolean"
355743355748
},
355749+
"SecurityPolicy": {
355750+
"markdownDescription": "The Transport Layer Security (TLS) version + cipher suite for this RestApi. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`SecurityPolicy`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html#cfn-apigateway-restapi-securitypolicy) property of an `AWS::ApiGateway::RestApi` resource\\.",
355751+
"title": "SecurityPolicy",
355752+
"type": "string"
355753+
},
355744355754
"StageName": {
355745355755
"anyOf": [
355746355756
{

schema_source/sam.schema.json

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4267,6 +4267,25 @@
42674267
"title": "Propagatetags",
42684268
"type": "boolean"
42694269
},
4270+
"SecurityPolicy": {
4271+
"__samPassThrough": {
4272+
"markdownDescriptionOverride": "The Transport Layer Security (TLS) version + cipher suite for this RestApi. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`SecurityPolicy`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html#cfn-apigateway-restapi-securitypolicy) property of an `AWS::ApiGateway::RestApi` resource\\.",
4273+
"schemaPath": [
4274+
"definitions",
4275+
"AWS::ApiGateway::RestApi",
4276+
"properties",
4277+
"Properties",
4278+
"properties",
4279+
"SecurityPolicy"
4280+
]
4281+
},
4282+
"allOf": [
4283+
{
4284+
"$ref": "#/definitions/PassThroughProp"
4285+
}
4286+
],
4287+
"title": "SecurityPolicy"
4288+
},
42704289
"TracingEnabled": {
42714290
"__samPassThrough": {
42724291
"markdownDescriptionOverride": "Indicates whether active tracing with X\\-Ray is enabled for the stage\\. For more information about X\\-Ray, see [Tracing user requests to REST APIs using X\\-Ray](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-xray.html) in the *API Gateway Developer Guide*\\. \n*Type*: Boolean \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`TracingEnabled`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-stage.html#cfn-apigateway-stage-tracingenabled) property of an `AWS::ApiGateway::Stage` resource\\.",
@@ -4640,6 +4659,25 @@
46404659
"title": "Propagatetags",
46414660
"type": "boolean"
46424661
},
4662+
"SecurityPolicy": {
4663+
"__samPassThrough": {
4664+
"markdownDescriptionOverride": "The Transport Layer Security (TLS) version + cipher suite for this RestApi. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`SecurityPolicy`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-restapi.html#cfn-apigateway-restapi-securitypolicy) property of an `AWS::ApiGateway::RestApi` resource\\.",
4665+
"schemaPath": [
4666+
"definitions",
4667+
"AWS::ApiGateway::RestApi",
4668+
"properties",
4669+
"Properties",
4670+
"properties",
4671+
"SecurityPolicy"
4672+
]
4673+
},
4674+
"allOf": [
4675+
{
4676+
"$ref": "#/definitions/PassThroughProp"
4677+
}
4678+
],
4679+
"title": "SecurityPolicy"
4680+
},
46434681
"StageName": {
46444682
"anyOf": [
46454683
{
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from unittest import TestCase
2+
from unittest.mock import Mock
3+
4+
from samtranslator.model.api.api_generator import ApiGenerator
5+
6+
7+
class TestApiGeneratorSecurityPolicy(TestCase):
8+
def setUp(self):
9+
self.logical_id = "MyApi"
10+
self.default_args = {
11+
"logical_id": self.logical_id,
12+
"cache_cluster_enabled": None,
13+
"cache_cluster_size": None,
14+
"variables": None,
15+
"depends_on": None,
16+
"definition_body": {"swagger": "2.0"},
17+
"definition_uri": None,
18+
"name": None,
19+
"stage_name": "Prod",
20+
"shared_api_usage_plan": Mock(),
21+
"template_conditions": Mock(),
22+
"method_settings": None,
23+
"endpoint_configuration": {"Type": "REGIONAL"},
24+
"access_log_setting": None,
25+
"canary_setting": None,
26+
"tracing_enabled": None,
27+
"open_api_version": None,
28+
"always_deploy": None,
29+
}
30+
31+
def test_security_policy_tls_1_3(self):
32+
api_generator = ApiGenerator(**self.default_args, security_policy="SecurityPolicy_TLS13_1_3_2025_09")
33+
34+
rest_api = api_generator._construct_rest_api()
35+
36+
self.assertEqual(rest_api.SecurityPolicy, "SecurityPolicy_TLS13_1_3_2025_09")
37+
38+
def test_no_security_policy(self):
39+
api_generator = ApiGenerator(**self.default_args, security_policy=None)
40+
41+
rest_api = api_generator._construct_rest_api()
42+
43+
self.assertIsNone(rest_api.SecurityPolicy)

0 commit comments

Comments
 (0)