From 076aa9f6c1285b85b9cc1c1abe67f789148cbd83 Mon Sep 17 00:00:00 2001 From: Ike Nefcy Date: Thu, 23 Jan 2025 09:09:26 -0700 Subject: [PATCH 1/2] fix(aws-apigatewayv2): incorrect arn function causing unwanted behavior in websocket iam auth --- .../aws-cdk-aws-apigatewayv2.assets.json | 19 ++ .../aws-cdk-aws-apigatewayv2.template.json | 136 +++++++++ .../cdk.out | 1 + .../integ.json | 12 + .../manifest.json | 125 +++++++++ .../tree.json | 261 ++++++++++++++++++ ...efaultTestDeployAssert230DE1C6.assets.json | 19 ++ ...aultTestDeployAssert230DE1C6.template.json | 36 +++ .../test/websocket/integ.api-grant-invoke.ts | 38 +++ .../aws-cdk-lib/aws-apigatewayv2/README.md | 2 +- .../aws-apigatewayv2/lib/websocket/api.ts | 22 +- .../test/websocket/api.test.ts | 33 +-- packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md | 2 +- 13 files changed, 658 insertions(+), 48 deletions(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/aws-cdk-aws-apigatewayv2.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/aws-cdk-aws-apigatewayv2.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/websocketapiDefaultTestDeployAssert230DE1C6.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/websocketapiDefaultTestDeployAssert230DE1C6.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/aws-cdk-aws-apigatewayv2.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/aws-cdk-aws-apigatewayv2.assets.json new file mode 100644 index 0000000000000..f2a90865c9ea1 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/aws-cdk-aws-apigatewayv2.assets.json @@ -0,0 +1,19 @@ +{ + "version": "39.0.0", + "files": { + "b5ced6ed6fa91dfe6ccd4f8ad8a40ef5d932304d05e556c8d43e54212f8a1b14": { + "source": { + "path": "aws-cdk-aws-apigatewayv2.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "b5ced6ed6fa91dfe6ccd4f8ad8a40ef5d932304d05e556c8d43e54212f8a1b14.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/aws-cdk-aws-apigatewayv2.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/aws-cdk-aws-apigatewayv2.template.json new file mode 100644 index 0000000000000..ded89531a26e1 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/aws-cdk-aws-apigatewayv2.template.json @@ -0,0 +1,136 @@ +{ + "Resources": { + "webocketapiD5DB5DB0": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Name": "webocket-api", + "ProtocolType": "WEBSOCKET", + "RouteSelectionExpression": "$request.body.action" + } + }, + "websocketstageA39DAC37": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "webocketapiD5DB5DB0" + }, + "StageName": "prod" + } + }, + "testiamrole05EFDD08": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": "execute-api:Invoke", + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "webocketapiD5DB5DB0" + }, + "/*/*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "webocketapiD5DB5DB0" + }, + "/prod/$connect" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "webSocketAccess" + } + ] + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/cdk.out new file mode 100644 index 0000000000000..91e1a8b9901d5 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"39.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/integ.json new file mode 100644 index 0000000000000..99dced49b5c9e --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "39.0.0", + "testCases": { + "web-socket-api/DefaultTest": { + "stacks": [ + "aws-cdk-aws-apigatewayv2" + ], + "assertionStack": "web-socket-api/DefaultTest/DeployAssert", + "assertionStackName": "websocketapiDefaultTestDeployAssert230DE1C6" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/manifest.json new file mode 100644 index 0000000000000..eae6f7f13ae67 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/manifest.json @@ -0,0 +1,125 @@ +{ + "version": "39.0.0", + "artifacts": { + "aws-cdk-aws-apigatewayv2.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-cdk-aws-apigatewayv2.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-cdk-aws-apigatewayv2": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-cdk-aws-apigatewayv2.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/b5ced6ed6fa91dfe6ccd4f8ad8a40ef5d932304d05e556c8d43e54212f8a1b14.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-cdk-aws-apigatewayv2.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "aws-cdk-aws-apigatewayv2.assets" + ], + "metadata": { + "/aws-cdk-aws-apigatewayv2/webocket-api/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "webocketapiD5DB5DB0" + } + ], + "/aws-cdk-aws-apigatewayv2/websocket-stage/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "websocketstageA39DAC37" + } + ], + "/aws-cdk-aws-apigatewayv2/test-iam-role/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "testiamrole05EFDD08" + } + ], + "/aws-cdk-aws-apigatewayv2/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-cdk-aws-apigatewayv2/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-cdk-aws-apigatewayv2" + }, + "websocketapiDefaultTestDeployAssert230DE1C6.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "websocketapiDefaultTestDeployAssert230DE1C6.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "websocketapiDefaultTestDeployAssert230DE1C6": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "websocketapiDefaultTestDeployAssert230DE1C6.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "websocketapiDefaultTestDeployAssert230DE1C6.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "websocketapiDefaultTestDeployAssert230DE1C6.assets" + ], + "metadata": { + "/web-socket-api/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/web-socket-api/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "web-socket-api/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/tree.json new file mode 100644 index 0000000000000..3046bfebbf79c --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/tree.json @@ -0,0 +1,261 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "aws-cdk-aws-apigatewayv2": { + "id": "aws-cdk-aws-apigatewayv2", + "path": "aws-cdk-aws-apigatewayv2", + "children": { + "webocket-api": { + "id": "webocket-api", + "path": "aws-cdk-aws-apigatewayv2/webocket-api", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-apigatewayv2/webocket-api/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ApiGatewayV2::Api", + "aws:cdk:cloudformation:props": { + "name": "webocket-api", + "protocolType": "WEBSOCKET", + "routeSelectionExpression": "$request.body.action" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apigatewayv2.CfnApi", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apigatewayv2.WebSocketApi", + "version": "0.0.0" + } + }, + "websocket-stage": { + "id": "websocket-stage", + "path": "aws-cdk-aws-apigatewayv2/websocket-stage", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-apigatewayv2/websocket-stage/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::ApiGatewayV2::Stage", + "aws:cdk:cloudformation:props": { + "apiId": { + "Ref": "webocketapiD5DB5DB0" + }, + "stageName": "prod" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apigatewayv2.CfnStage", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apigatewayv2.WebSocketStage", + "version": "0.0.0" + } + }, + "test-iam-role": { + "id": "test-iam-role", + "path": "aws-cdk-aws-apigatewayv2/test-iam-role", + "children": { + "Importtest-iam-role": { + "id": "Importtest-iam-role", + "path": "aws-cdk-aws-apigatewayv2/test-iam-role/Importtest-iam-role", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-aws-apigatewayv2/test-iam-role/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "policies": [ + { + "policyName": "webSocketAccess", + "policyDocument": { + "Statement": [ + { + "Action": "execute-api:Invoke", + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "webocketapiD5DB5DB0" + }, + "/*/*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "webocketapiD5DB5DB0" + }, + "/prod/$connect" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + } + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-cdk-aws-apigatewayv2/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-cdk-aws-apigatewayv2/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "web-socket-api": { + "id": "web-socket-api", + "path": "web-socket-api", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "web-socket-api/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "web-socket-api/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.4.2" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "web-socket-api/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "web-socket-api/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "web-socket-api/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.4.2" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/websocketapiDefaultTestDeployAssert230DE1C6.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/websocketapiDefaultTestDeployAssert230DE1C6.assets.json new file mode 100644 index 0000000000000..6c0629e7c5c94 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/websocketapiDefaultTestDeployAssert230DE1C6.assets.json @@ -0,0 +1,19 @@ +{ + "version": "39.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "websocketapiDefaultTestDeployAssert230DE1C6.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/websocketapiDefaultTestDeployAssert230DE1C6.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/websocketapiDefaultTestDeployAssert230DE1C6.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.js.snapshot/websocketapiDefaultTestDeployAssert230DE1C6.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.ts new file mode 100644 index 0000000000000..61c6ae7530d86 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.ts @@ -0,0 +1,38 @@ +#!/usr/bin/env node +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; +import * as cdk from 'aws-cdk-lib'; +import * as apigw from 'aws-cdk-lib/aws-apigatewayv2'; +import * as iam from 'aws-cdk-lib/aws-iam'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'aws-cdk-aws-apigatewayv2'); + +const websocketApi = new apigw.WebSocketApi(stack, 'webocket-api'); + +new apigw.WebSocketStage(stack, 'websocket-stage', { + stageName: 'prod', + webSocketApi: websocketApi, +}); + +new iam.Role(stack, 'test-iam-role', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), + inlinePolicies: { + webSocketAccess: new iam.PolicyDocument({ + statements: [ + new iam.PolicyStatement({ + actions: ['execute-api:Invoke'], + resources: [ + websocketApi.arnForExecuteApi(), + websocketApi.arnForExecuteApi('$connect', 'prod'), + ], + }), + ], + }), + }, +}); + +new IntegTest(app, 'web-socket-api', { + testCases: [stack], +}); + +app.synth(); diff --git a/packages/aws-cdk-lib/aws-apigatewayv2/README.md b/packages/aws-cdk-lib/aws-apigatewayv2/README.md index fdde2f476c216..278e5bd1f89d8 100644 --- a/packages/aws-cdk-lib/aws-apigatewayv2/README.md +++ b/packages/aws-cdk-lib/aws-apigatewayv2/README.md @@ -433,7 +433,7 @@ To generate an ARN for Execute API: ```ts const api = new apigwv2.WebSocketApi(this, 'mywsapi'); -const arn = api.arnForExecuteApi('GET', '/myApiPath', 'dev'); +const arn = api.arnForExecuteApi('$connect', 'dev'); ``` For a detailed explanation of this function, including usage and examples, please refer to the [Generating ARN for Execute API](#generating-arn-for-execute-api) section under HTTP API. diff --git a/packages/aws-cdk-lib/aws-apigatewayv2/lib/websocket/api.ts b/packages/aws-cdk-lib/aws-apigatewayv2/lib/websocket/api.ts index 5a8f0a90e13b3..1996f1a8b9d4e 100644 --- a/packages/aws-cdk-lib/aws-apigatewayv2/lib/websocket/api.ts +++ b/packages/aws-cdk-lib/aws-apigatewayv2/lib/websocket/api.ts @@ -2,7 +2,7 @@ import { Construct } from 'constructs'; import { WebSocketRoute, WebSocketRouteOptions } from './route'; import { CfnApi } from '.././index'; import { Grant, IGrantable } from '../../../aws-iam'; -import { ArnFormat, Stack, Token } from '../../../core'; +import { ArnFormat, Stack } from '../../../core'; import { IApi } from '../common/api'; import { ApiBase } from '../common/base'; @@ -190,28 +190,18 @@ export class WebSocketApi extends ApiBase implements IWebSocketApi { /** * Get the "execute-api" ARN. - * When 'ANY' is passed to the method, an ARN with the method set to '*' is obtained. * - * @default - The default behavior applies when no specific method, path, or stage is provided. - * In this case, the ARN will cover all methods, all resources, and all stages of this API. - * Specifically, if 'method' is not specified, it defaults to '*', representing all methods. - * If 'path' is not specified, it defaults to '/*', representing all paths. + * @default - The default behavior applies when no specific route, or stage is provided. + * In this case, the ARN will cover all routes, and all stages of this API. + * Specifically, if 'route' is not specified, it defaults to '*', representing all routes. * If 'stage' is not specified, it also defaults to '*', representing all stages. */ - public arnForExecuteApi(method?: string, path?: string, stage?: string): string { - if (path && !Token.isUnresolved(path) && !path.startsWith('/')) { - throw new Error(`Path must start with '/': ${path}`); - } - - if (method && method.toUpperCase() === 'ANY') { - method = '*'; - } - + public arnForExecuteApi(route?: string, stage?: string): string { return Stack.of(this).formatArn({ service: 'execute-api', resource: this.apiId, arnFormat: ArnFormat.SLASH_RESOURCE_NAME, - resourceName: `${stage ?? '*'}/${method ?? '*'}${path ?? '/*'}`, + resourceName: `${stage ?? '*'}/${route ?? '*'}`, }); } } diff --git a/packages/aws-cdk-lib/aws-apigatewayv2/test/websocket/api.test.ts b/packages/aws-cdk-lib/aws-apigatewayv2/test/websocket/api.test.ts index 8abe6d44c1340..b6def87203400 100644 --- a/packages/aws-cdk-lib/aws-apigatewayv2/test/websocket/api.test.ts +++ b/packages/aws-cdk-lib/aws-apigatewayv2/test/websocket/api.test.ts @@ -152,7 +152,7 @@ describe('WebSocketApi', () => { const stack = new Stack(); const api = new WebSocketApi(stack, 'api'); - expect(stack.resolve(api.arnForExecuteApi('method', '/path', 'stage'))).toEqual({ + expect(stack.resolve(api.arnForExecuteApi('route', 'stage'))).toEqual({ 'Fn::Join': ['', [ 'arn:', { Ref: 'AWS::Partition' }, @@ -162,7 +162,7 @@ describe('WebSocketApi', () => { { Ref: 'AWS::AccountId' }, ':', stack.resolve(api.apiId), - '/stage/method/path', + '/stage/route', ]], }); }); @@ -181,38 +181,11 @@ describe('WebSocketApi', () => { { Ref: 'AWS::AccountId' }, ':', stack.resolve(api.apiId), - '/*/*/*', + '/*/*', ]], }); }); - test('get arnForExecuteApi with ANY method', () => { - const stack = new Stack(); - const api = new WebSocketApi(stack, 'api'); - - expect(stack.resolve(api.arnForExecuteApi('ANY', '/path', 'stage'))).toEqual({ - 'Fn::Join': ['', [ - 'arn:', - { Ref: 'AWS::Partition' }, - ':execute-api:', - { Ref: 'AWS::Region' }, - ':', - { Ref: 'AWS::AccountId' }, - ':', - stack.resolve(api.apiId), - '/stage/*/path', - ]], - }); - }); - - test('throws when call arnForExecuteApi method with specifing a string that does not start with / for the path argument.', () => { - const stack = new Stack(); - const api = new WebSocketApi(stack, 'api'); - - expect(() => api.arnForExecuteApi('method', 'path', 'stage')) - .toThrow("Path must start with '/': path"); - }); - describe('grantManageConnections', () => { test('adds an IAM policy to the principal', () => { // GIVEN diff --git a/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md b/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md index 955542ef2b803..3255976d583d1 100644 --- a/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md +++ b/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md @@ -86,7 +86,7 @@ Flags come in three types: | [@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource](#aws-cdkaws-route53-targetsuserpooldomainnamemethodwithoutcustomresource) | When enabled, use a new method for DNS Name of user pool domain target without creating a custom resource. | 2.174.0 | (fix) | | [@aws-cdk/aws-ecs:disableEcsImdsBlocking](#aws-cdkaws-ecsdisableecsimdsblocking) | When set to true, CDK synth will throw exception if canContainersAccessInstanceRole is false. **IMPORTANT: See [details.](#aws-cdkaws-ecsdisableEcsImdsBlocking)** | 2.175.0 | (temporary) | | [@aws-cdk/aws-ecs:enableImdsBlockingDeprecatedFeature](#aws-cdkaws-ecsenableimdsblockingdeprecatedfeature) | When set to true along with canContainersAccessInstanceRole=false in ECS cluster, new updated commands will be added to UserData to block container accessing IMDS. **Applicable to Linux only. IMPORTANT: See [details.](#aws-cdkaws-ecsenableImdsBlockingDeprecatedFeature)** | 2.175.0 | (temporary) | -| [@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault](#aws-cdkaws-elasticloadbalancingv2albdualstackwithoutpublicipv4securitygrouprulesdefault) | When enabled, the default security group ingress rules will allow IPv6 ingress from anywhere | V2NEXT | (fix) | +| [@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault](#aws-cdkaws-elasticloadbalancingv2albdualstackwithoutpublicipv4securitygrouprulesdefault) | When enabled, the default security group ingress rules will allow IPv6 ingress from anywhere | 2.176.0 | (fix) | | [@aws-cdk/aws-iam:oidcRejectUnauthorizedConnections](#aws-cdkaws-iamoidcrejectunauthorizedconnections) | When enabled, the default behaviour of OIDC provider will reject unauthorized connections | V2NEXT | (fix) | From a26c45bd0db650316b3cae3bec14b692e10d00e9 Mon Sep 17 00:00:00 2001 From: Ike Nefcy Date: Thu, 23 Jan 2025 09:47:50 -0700 Subject: [PATCH 2/2] fix(aws-apigatewayv2): incorrect arn function causing unwanted behavior in websocket iam auth --- .../aws-apigatewayv2/test/websocket/integ.api-grant-invoke.ts | 2 +- packages/aws-cdk-lib/aws-apigatewayv2/lib/websocket/api.ts | 4 ++++ .../aws-cdk-lib/aws-apigatewayv2/test/websocket/api.test.ts | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.ts index 61c6ae7530d86..eb952e8c340e9 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-apigatewayv2/test/websocket/integ.api-grant-invoke.ts @@ -23,7 +23,7 @@ new iam.Role(stack, 'test-iam-role', { actions: ['execute-api:Invoke'], resources: [ websocketApi.arnForExecuteApi(), - websocketApi.arnForExecuteApi('$connect', 'prod'), + websocketApi.arnForExecuteApi('connect', 'prod'), ], }), ], diff --git a/packages/aws-cdk-lib/aws-apigatewayv2/lib/websocket/api.ts b/packages/aws-cdk-lib/aws-apigatewayv2/lib/websocket/api.ts index 1996f1a8b9d4e..95e9acd3dac74 100644 --- a/packages/aws-cdk-lib/aws-apigatewayv2/lib/websocket/api.ts +++ b/packages/aws-cdk-lib/aws-apigatewayv2/lib/websocket/api.ts @@ -197,6 +197,10 @@ export class WebSocketApi extends ApiBase implements IWebSocketApi { * If 'stage' is not specified, it also defaults to '*', representing all stages. */ public arnForExecuteApi(route?: string, stage?: string): string { + if (route&&!route.startsWith('$')) { + route = `$${route}`; + }; + return Stack.of(this).formatArn({ service: 'execute-api', resource: this.apiId, diff --git a/packages/aws-cdk-lib/aws-apigatewayv2/test/websocket/api.test.ts b/packages/aws-cdk-lib/aws-apigatewayv2/test/websocket/api.test.ts index b6def87203400..3e5b951c25f16 100644 --- a/packages/aws-cdk-lib/aws-apigatewayv2/test/websocket/api.test.ts +++ b/packages/aws-cdk-lib/aws-apigatewayv2/test/websocket/api.test.ts @@ -162,7 +162,7 @@ describe('WebSocketApi', () => { { Ref: 'AWS::AccountId' }, ':', stack.resolve(api.apiId), - '/stage/route', + '/stage/$route', ]], }); });