Skip to content

Commit

Permalink
fix(dynamodb): add kms permissions to grantStreamRead (aws#23400)
Browse files Browse the repository at this point in the history
Currently [combinedGrant](https://github.com/aws/aws-cdk/blob/dfcfb8da34a495987214e70713b0c61f368ce962/packages/%40aws-cdk/aws-dynamodb/lib/table.ts#L1025-L1061) method does nothing if an encryption key is provided with stream action(s) present for the table. 
This impacts `grantStreamRead` method and is not attaching relevant KMS permissions to the policy.

Resolves aws#22796


### All Submissions:

* [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md)

### Adding new Construct Runtime Dependencies:

* [ ] This PR adds new construct runtime dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-construct-runtime-dependencies)

### New Features

* [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)?
	* [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)?

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
vinayak-kukreja authored and Brennan Ho committed Feb 22, 2023
1 parent 78e4235 commit 3830a96
Show file tree
Hide file tree
Showing 15 changed files with 587 additions and 93 deletions.
6 changes: 3 additions & 3 deletions packages/@aws-cdk/aws-dynamodb/lib/table.ts
Expand Up @@ -1026,6 +1026,9 @@ abstract class TableBase extends Resource implements ITable {
grantee: iam.IGrantable,
opts: { keyActions?: string[], tableActions?: string[], streamActions?: string[] },
): iam.Grant {
if (this.encryptionKey && opts.keyActions) {
this.encryptionKey.grant(grantee, ...opts.keyActions);
}
if (opts.tableActions) {
const resources = [this.tableArn,
Lazy.string({ produce: () => this.hasIndex ? `${this.tableArn}/index/*` : Aws.NO_VALUE }),
Expand All @@ -1039,9 +1042,6 @@ abstract class TableBase extends Resource implements ITable {
resourceArns: resources,
scope: this,
});
if (this.encryptionKey && opts.keyActions) {
this.encryptionKey.grant(grantee, ...opts.keyActions);
}
return ret;
}
if (opts.streamActions) {
Expand Down
54 changes: 54 additions & 0 deletions packages/@aws-cdk/aws-dynamodb/test/dynamodb.test.ts
Expand Up @@ -2264,6 +2264,60 @@ describe('import', () => {
});
});

test('if an encryption key is included, encrypt/decrypt permissions are added to the principal for grantStreamRead', () => {
const stack = new Stack();

const tableName = 'MyTable';
const tableStreamArn = 'arn:foo:bar:baz:TrustMeThisIsATableStream';
const encryptionKey = new kms.Key(stack, 'Key', {
enableKeyRotation: true,
});

const table = Table.fromTableAttributes(stack, 'ImportedTable', { tableName, tableStreamArn, encryptionKey });

const role = new iam.Role(stack, 'NewRole', {
assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'),
});

expect(table.grantStreamRead(role)).toBeDefined();

Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', {
PolicyDocument: {
Statement: [
{
'Action': 'dynamodb:ListStreams',
'Effect': 'Allow',
'Resource': '*',
},
{
'Action': [
'kms:Decrypt',
'kms:DescribeKey',
],
'Effect': 'Allow',
'Resource': {
'Fn::GetAtt': [
'Key961B73FD',
'Arn',
],
},
},
{
'Action': [
'dynamodb:DescribeStream',
'dynamodb:GetRecords',
'dynamodb:GetShardIterator',
],
'Effect': 'Allow',
'Resource': 'arn:foo:bar:baz:TrustMeThisIsATableStream',
},
],
Version: '2012-10-17',
},
Roles: [stack.resolve(role.roleName)],
});
});

test('creates the correct index grant if indexes have been provided when importing', () => {
const stack = new Stack();

Expand Down
@@ -1,15 +1,15 @@
{
"version": "20.0.0",
"version": "22.0.0",
"files": {
"28f29dc1ca8be205ffc54093c72bcd51a45a56b3375537b6cfa7e2b132bbdc9e": {
"aaaac6e8f3f1ffa9992cbb900021f1d1b5ec67af132595b4b296680991a0d152": {
"source": {
"path": "aws-cdk-dynamodb.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "28f29dc1ca8be205ffc54093c72bcd51a45a56b3375537b6cfa7e2b132bbdc9e.json",
"objectKey": "aaaac6e8f3f1ffa9992cbb900021f1d1b5ec67af132595b4b296680991a0d152.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
Expand Down
Expand Up @@ -371,6 +371,143 @@
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
},
"Key961B73FD": {
"Type": "AWS::KMS::Key",
"Properties": {
"KeyPolicy": {
"Statement": [
{
"Action": "kms:*",
"Effect": "Allow",
"Principal": {
"AWS": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":iam::",
{
"Ref": "AWS::AccountId"
},
":root"
]
]
}
},
"Resource": "*"
}
],
"Version": "2012-10-17"
},
"EnableKeyRotation": true
},
"UpdateReplacePolicy": "Retain",
"DeletionPolicy": "Retain"
},
"TableWithCustomerManagedKeyD5C58807": {
"Type": "AWS::DynamoDB::Table",
"Properties": {
"KeySchema": [
{
"AttributeName": "hashKey",
"KeyType": "HASH"
}
],
"AttributeDefinitions": [
{
"AttributeName": "hashKey",
"AttributeType": "S"
}
],
"ProvisionedThroughput": {
"ReadCapacityUnits": 5,
"WriteCapacityUnits": 5
},
"SSESpecification": {
"KMSMasterKeyId": {
"Fn::GetAtt": [
"Key961B73FD",
"Arn"
]
},
"SSEEnabled": true,
"SSEType": "KMS"
},
"StreamSpecification": {
"StreamViewType": "NEW_AND_OLD_IMAGES"
}
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
},
"Role1ABCC5F0": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "sqs.amazonaws.com"
}
}
],
"Version": "2012-10-17"
}
}
},
"RoleDefaultPolicy5FFB7DAB": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyDocument": {
"Statement": [
{
"Action": "dynamodb:ListStreams",
"Effect": "Allow",
"Resource": "*"
},
{
"Action": [
"kms:Decrypt",
"kms:DescribeKey"
],
"Effect": "Allow",
"Resource": {
"Fn::GetAtt": [
"Key961B73FD",
"Arn"
]
}
},
{
"Action": [
"dynamodb:DescribeStream",
"dynamodb:GetRecords",
"dynamodb:GetShardIterator"
],
"Effect": "Allow",
"Resource": {
"Fn::GetAtt": [
"TableWithCustomerManagedKeyD5C58807",
"StreamArn"
]
}
}
],
"Version": "2012-10-17"
},
"PolicyName": "RoleDefaultPolicy5FFB7DAB",
"Roles": [
{
"Ref": "Role1ABCC5F0"
}
]
}
},
"User00B015A1": {
"Type": "AWS::IAM::User"
},
Expand Down
@@ -1 +1 @@
{"version":"20.0.0"}
{"version":"22.0.0"}
@@ -1,5 +1,5 @@
{
"version": "20.0.0",
"version": "22.0.0",
"testCases": {
"integ.dynamodb": {
"stacks": [
Expand Down
@@ -1,12 +1,6 @@
{
"version": "20.0.0",
"version": "22.0.0",
"artifacts": {
"Tree": {
"type": "cdk:tree",
"properties": {
"file": "tree.json"
}
},
"aws-cdk-dynamodb.assets": {
"type": "cdk:asset-manifest",
"properties": {
Expand All @@ -23,7 +17,7 @@
"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}/28f29dc1ca8be205ffc54093c72bcd51a45a56b3375537b6cfa7e2b132bbdc9e.json",
"stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/aaaac6e8f3f1ffa9992cbb900021f1d1b5ec67af132595b4b296680991a0d152.json",
"requiresBootstrapStackVersion": 6,
"bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version",
"additionalDependencies": [
Expand Down Expand Up @@ -63,6 +57,30 @@
"data": "TableWithLocalSecondaryIndex4DA3D08F"
}
],
"/aws-cdk-dynamodb/Key/Resource": [
{
"type": "aws:cdk:logicalId",
"data": "Key961B73FD"
}
],
"/aws-cdk-dynamodb/TableWithCustomerManagedKey/Resource": [
{
"type": "aws:cdk:logicalId",
"data": "TableWithCustomerManagedKeyD5C58807"
}
],
"/aws-cdk-dynamodb/Role/Resource": [
{
"type": "aws:cdk:logicalId",
"data": "Role1ABCC5F0"
}
],
"/aws-cdk-dynamodb/Role/DefaultPolicy/Resource": [
{
"type": "aws:cdk:logicalId",
"data": "RoleDefaultPolicy5FFB7DAB"
}
],
"/aws-cdk-dynamodb/User/Resource": [
{
"type": "aws:cdk:logicalId",
Expand All @@ -89,6 +107,12 @@
]
},
"displayName": "aws-cdk-dynamodb"
},
"Tree": {
"type": "cdk:tree",
"properties": {
"file": "tree.json"
}
}
}
}

0 comments on commit 3830a96

Please sign in to comment.