Skip to content

Commit

Permalink
feat(s3): onCloudTrailWriteObject matches all update events (#4723)
Browse files Browse the repository at this point in the history
* fix(s3): rule should match all update events

In addition to 'PutObject', onCloudTrailPutObject() should also match on
event names 'CopyObject' and 'CompleteMultipartUpload'; otherwise the event
does not trigger when files are uploaded using those APIs.  E.g., larger
files are uploaded using the multipart API.

fixes #4634

* added unit tests

removed unnecessary integration tests

* update expected cfn output for integ

* new method for matching object writes

* use new method to match write events
  • Loading branch information
njlaw authored and mergify[bot] committed Oct 30, 2019
1 parent b7b4336 commit 46d9885
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export class S3SourceAction extends Action {
// this means a duplicate path for the same bucket - error out
throw new Error(`S3 source action with path '${this.props.bucketKey}' is already present in the pipeline for this source bucket`);
}
this.props.bucket.onCloudTrailPutObject(id, {
this.props.bucket.onCloudTrailWriteObject(id, {
target: new targets.CodePipeline(stage.pipeline),
paths: [this.props.bucketKey]
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -600,8 +600,20 @@
]
},
"eventName": [
"CompleteMultipartUpload",
"CopyObject",
"PutObject"
]
],
"requestParameters": {
"bucketName": [
{
"Ref": "PipelineBucketB967BD35"
}
],
"key": [
"key"
]
}
}
},
"State": "ENABLED",
Expand Down
90 changes: 88 additions & 2 deletions packages/@aws-cdk/aws-s3-notifications/test/notifications.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ test('a notification destination can specify a set of dependencies that must be
});

describe('CloudWatch Events', () => {
test('onPutItem contains the Bucket ARN itself when path is undefined', () => {
test('onCloudTrailPutObject contains the Bucket ARN itself when path is undefined', () => {
const stack = new cdk.Stack();
const bucket = s3.Bucket.fromBucketAttributes(stack, 'Bucket', {
bucketName: 'MyBucket',
Expand Down Expand Up @@ -347,7 +347,7 @@ describe('CloudWatch Events', () => {
});
});

test("onPutItem contains the path when it's provided", () => {
test("onCloudTrailPutObject contains the path when it's provided", () => {
const stack = new cdk.Stack();
const bucket = s3.Bucket.fromBucketAttributes(stack, 'Bucket', {
bucketName: 'MyBucket',
Expand Down Expand Up @@ -389,4 +389,90 @@ describe('CloudWatch Events', () => {
"State": "ENABLED",
});
});

test("onCloudTrailWriteObject matches on events CompleteMultipartUpload, CopyObject, and PutObject", () => {
const stack = new cdk.Stack();
const bucket = s3.Bucket.fromBucketAttributes(stack, 'Bucket', {
bucketName: 'MyBucket',
});
bucket.onCloudTrailWriteObject('OnCloudTrailWriteObjectRule', {
target: {
bind: () => ({ arn: 'ARN', id: '' })
}
});

expect(stack).toHaveResourceLike('AWS::Events::Rule', {
"EventPattern": {
"source": [
"aws.s3",
],
"detail": {
"eventName": [
"CompleteMultipartUpload",
"CopyObject",
"PutObject",
],
},
},
"State": "ENABLED",
});
});

test('onCloudTrailWriteObject matches on the requestParameter bucketName when the path is not provided', () => {
const stack = new cdk.Stack();
const bucket = s3.Bucket.fromBucketAttributes(stack, 'Bucket', {
bucketName: 'MyBucket',
});
bucket.onCloudTrailWriteObject('OnCloudTrailWriteObjectRule', {
target: {
bind: () => ({ arn: 'ARN', id: '' })
},
});

expect(stack).toHaveResourceLike('AWS::Events::Rule', {
"EventPattern": {
"source": [
"aws.s3",
],
"detail": {
"requestParameters": {
"bucketName": [
bucket.bucketName,
],
},
},
},
});
});

test("onCloudTrailWriteObject matches on the requestParameters bucketName and key when the path is provided", () => {
const stack = new cdk.Stack();
const bucket = s3.Bucket.fromBucketAttributes(stack, 'Bucket', {
bucketName: 'MyBucket',
});
bucket.onCloudTrailWriteObject('OnCloudTrailWriteObjectRule', {
target: {
bind: () => ({ arn: 'ARN', id: '' })
},
paths: ['my/path.zip']
});

expect(stack).toHaveResourceLike('AWS::Events::Rule', {
"EventPattern": {
"source": [
"aws.s3",
],
"detail": {
"requestParameters": {
"bucketName": [
bucket.bucketName,
],
"key": [
"my/path.zip",
],
},
},
},
});
});
});
68 changes: 63 additions & 5 deletions packages/@aws-cdk/aws-s3/lib/bucket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ export interface IBucket extends IResource {
grantPublicAccess(keyPrefix?: string, ...allowedActions: string[]): iam.Grant;

/**
* Define a CloudWatch event that triggers when something happens to this repository
* Defines a CloudWatch event that triggers when something happens to this bucket
*
* Requires that there exists at least one CloudTrail Trail in your account
* that captures the event. This method will not create the Trail.
Expand All @@ -183,8 +183,12 @@ export interface IBucket extends IResource {
onCloudTrailEvent(id: string, options?: OnCloudTrailBucketEventOptions): events.Rule;

/**
* Defines an AWS CloudWatch event rule that can trigger a target when an image is pushed to this
* repository.
* Defines an AWS CloudWatch event that triggers when an object is uploaded
* to the specified paths (keys) in this bucket using the PutObject API call.
*
* Note that some tools like `aws s3 cp` will automatically use either
* PutObject or the multipart upload API depending on the file size,
* so using `onCloudTrailWriteObject` may be preferable.
*
* Requires that there exists at least one CloudTrail Trail in your account
* that captures the event. This method will not create the Trail.
Expand All @@ -193,6 +197,23 @@ export interface IBucket extends IResource {
* @param options Options for adding the rule
*/
onCloudTrailPutObject(id: string, options?: OnCloudTrailBucketEventOptions): events.Rule;

/**
* Defines an AWS CloudWatch event that triggers when an object at the
* specified paths (keys) in this bucket are written to. This includes
* the events PutObject, CopyObject, and CompleteMultipartUpload.
*
* Note that some tools like `aws s3 cp` will automatically use either
* PutObject or the multipart upload API depending on the file size,
* so using this method may be preferable to `onCloudTrailPutObject`.
*
* Requires that there exists at least one CloudTrail Trail in your account
* that captures the event. This method will not create the Trail.
*
* @param id The id of the rule
* @param options Options for adding the rule
*/
onCloudTrailWriteObject(id: string, options?: OnCloudTrailBucketEventOptions): events.Rule;
}

/**
Expand Down Expand Up @@ -325,8 +346,12 @@ abstract class BucketBase extends Resource implements IBucket {
}

/**
* Defines an AWS CloudWatch event rule that can trigger a target when an image is pushed to this
* repository.
* Defines an AWS CloudWatch event that triggers when an object is uploaded
* to the specified paths (keys) in this bucket using the PutObject API call.
*
* Note that some tools like `aws s3 cp` will automatically use either
* PutObject or the multipart upload API depending on the file size,
* so using `onCloudTrailWriteObject` may be preferable.
*
* Requires that there exists at least one CloudTrail Trail in your account
* that captures the event. This method will not create the Trail.
Expand All @@ -344,6 +369,39 @@ abstract class BucketBase extends Resource implements IBucket {
return rule;
}

/**
* Defines an AWS CloudWatch event that triggers when an object at the
* specified paths (keys) in this bucket are written to. This includes
* the events PutObject, CopyObject, and CompleteMultipartUpload.
*
* Note that some tools like `aws s3 cp` will automatically use either
* PutObject or the multipart upload API depending on the file size,
* so using this method may be preferable to `onCloudTrailPutObject`.
*
* Requires that there exists at least one CloudTrail Trail in your account
* that captures the event. This method will not create the Trail.
*
* @param id The id of the rule
* @param options Options for adding the rule
*/
public onCloudTrailWriteObject(id: string, options: OnCloudTrailBucketEventOptions = {}): events.Rule {
const rule = this.onCloudTrailEvent(id, options);
rule.addEventPattern({
detail: {
eventName: [
'CompleteMultipartUpload',
'CopyObject',
'PutObject'
],
requestParameters: {
bucketName: [ this.bucketName ],
key: options.paths,
},
},
});
return rule;
}

/**
* Adds a statement to the resource policy for a principal (i.e.
* account/role/service) to perform actions on this bucket and/or it's
Expand Down

0 comments on commit 46d9885

Please sign in to comment.