forked from aws/aws-cdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbucket-grants.ts
More file actions
298 lines (267 loc) · 12.4 KB
/
bucket-grants.ts
File metadata and controls
298 lines (267 loc) · 12.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
import type { GrantReplicationPermissionProps } from './bucket';
import * as perms from './perms';
import type { IBucketRef } from './s3.generated';
import type { IEncryptedResource, IGrantable, IResourceWithPolicyV2 } from '../../aws-iam';
import { AnyPrincipal, EncryptedResources, Grant, ResourceWithPolicies } from '../../aws-iam';
import type * as iam from '../../aws-iam/lib/grant';
import { FeatureFlags, Lazy, ValidationError } from '../../core';
import * as cxapi from '../../cx-api/index';
/**
* A bucket in which public access can be allowed or disallowed.
*/
interface IPubliclyAccessibleBucket extends IBucketRef {
/**
* Whether public access is disallowed for this bucket
*/
readonly disallowPublicAccess?: boolean;
}
/**
* Collection of grant methods for a Bucket
*/
export class BucketGrants {
/**
* Creates grants for an IBucketRef
*/
public static fromBucket(bucket: IBucketRef): BucketGrants {
return new BucketGrants(
bucket,
EncryptedResources.of(bucket),
ResourceWithPolicies.of(bucket),
);
}
private constructor(
private readonly bucket: IPubliclyAccessibleBucket,
private readonly encryptedResource?: IEncryptedResource,
private readonly policyResource?: IResourceWithPolicyV2) {
}
/**
* Grant read permissions for this bucket and its contents to an IAM
* principal (Role/Group/User).
*
* If encryption is used, permission to use the key to decrypt the contents
* of the bucket will also be granted to the same principal.
*
* @param identity The principal
* @param objectsKeyPattern Restrict the permission to a certain key pattern (default '*'). Parameter type is `any` but `string` should be passed in.
*/
public read(identity: IGrantable, objectsKeyPattern: any = '*') {
return this.actionsOnBucketAndObjectKeys(identity, objectsKeyPattern, ...perms.BUCKET_READ_ACTIONS, ...perms.KEY_READ_ACTIONS);
}
/**
* Grant write permissions for this bucket and its contents to an IAM
* principal (Role/Group/User).
*
* If encryption is used, permission to use the key to decrypt the contents
* of the bucket will also be granted to the same principal.
*
* @param identity The principal
* @param objectsKeyPattern Restrict the permission to a certain key pattern (default '*'). Parameter type is `any` but `string` should be passed in.
*/
public write(identity: IGrantable, objectsKeyPattern: any = '*', allowedActionPatterns: string[] = []) {
const grantedWriteActions = allowedActionPatterns.length > 0 ? allowedActionPatterns : this.writeActions;
return this.actionsOnBucketAndObjectKeys(identity, objectsKeyPattern, ...grantedWriteActions, ...perms.KEY_WRITE_ACTIONS);
}
/**
* Grants s3:DeleteObject* permission to an IAM principal for objects
* in this bucket.
*
* @param grantee The principal
* @param objectsKeyPattern Restrict the permission to a certain key pattern (default '*'). Parameter type is `any` but `string` should be passed in.
*/
public delete(grantee: IGrantable, objectsKeyPattern: any = '*'): Grant {
return this.actionsOnObjectKeys(grantee, objectsKeyPattern, ...perms.BUCKET_DELETE_ACTIONS);
}
/**
* Allows unrestricted access to objects from this bucket.
*
* IMPORTANT: This permission allows anyone to perform actions on S3 objects
* in this bucket, which is useful for when you configure your bucket as a
* website and want everyone to be able to read objects in the bucket without
* needing to authenticate.
*
* Without arguments, this method will grant read ("s3:GetObject") access to
* all objects ("*") in the bucket.
*
* The method returns the `iam.Grant` object, which can then be modified
* as needed. For example, you can add a condition that will restrict access only
* to an IPv4 range like this:
*
* const grant = bucket.grantPublicAccess();
* grant.resourceStatement!.addCondition(‘IpAddress’, { “aws:SourceIp”: “54.240.143.0/24” });
*
* Note that if this `IBucket` refers to an existing bucket, possibly not
* managed by CloudFormation, this method will have no effect, since it's
* impossible to modify the policy of an existing bucket.
*
* @param keyPrefix the prefix of S3 object keys (e.g. `home/*`). Default is "*".
* @param allowedActions the set of S3 actions to allow. Default is "s3:GetObject".
*/
public publicAccess(keyPrefix = '*', ...allowedActions: string[]) {
if (this.bucket.disallowPublicAccess) {
throw new ValidationError('CannotGrantPublicAccessWhenBlockPublicPolicyEnabled', "Cannot grant public access when 'blockPublicPolicy' is enabled", this.bucket);
}
allowedActions = allowedActions.length > 0 ? allowedActions : ['s3:GetObject'];
return this.actionsOnObjectKeys(new AnyPrincipal(), keyPrefix, ...allowedActions);
}
/**
* Grants s3:PutObject* and s3:Abort* permissions for this bucket to an IAM principal.
*
* If encryption is used, permission to use the key to encrypt the contents
* of written files will also be granted to the same principal.
* @param identity The principal
* @param objectsKeyPattern Restrict the permission to a certain key pattern (default '*'). Parameter type is `any` but `string` should be passed in.
*/
public put(identity: IGrantable, objectsKeyPattern: any = '*') {
return this.actionsOnObjectKeys(identity, objectsKeyPattern, ...this.putActions, ...perms.KEY_WRITE_ACTIONS);
}
/**
* Grants s3:PutObjectAcl and s3:PutObjectVersionAcl permissions for this bucket to an IAM principal.
*
* If encryption is used, permission to use the key to encrypt the contents
* of written files will also be granted to the same principal.
* @param identity The principal
* @param objectsKeyPattern Restrict the permission to a certain key pattern (default '*'). Parameter type is `any` but `string` should be passed in.
*/
public putAcl(identity: IGrantable, objectsKeyPattern: string = '*') {
return this.actionsOnObjectKeys(identity, objectsKeyPattern, ...perms.BUCKET_PUT_ACL_ACTIONS);
}
/**
* Grants the given actions on the bucket's objects to the given principal.
*
* KMS actions (prefixed with `kms:`) are automatically separated and granted on the encryption key.
*
* @param identity The principal to grant permissions to.
* @param objectsKeyPattern Restrict the permission to a certain key pattern (default '*').
* @param actions The S3 and/or KMS actions to grant.
*/
public actionsOnObjectKeys(identity: IGrantable, objectsKeyPattern: string = '*', ...actions: string[]) {
return this.grantActions(identity, actions, this.arnForObjects(objectsKeyPattern));
}
/**
* Grants the given actions on both the bucket and the bucket's objects to the given principal.
*
* KMS actions (prefixed with `kms:`) are automatically separated and granted on the encryption key.
*
* @param identity The principal to grant permissions to.
* @param objectsKeyPattern Restrict the permission to a certain key pattern (default '*').
* @param actions The S3 and/or KMS actions to grant.
*/
public actionsOnBucketAndObjectKeys(identity: IGrantable, objectsKeyPattern: string = '*', ...actions: string[]) {
return this.grantActions(identity, actions, this.bucket.bucketRef.bucketArn, this.arnForObjects(objectsKeyPattern));
}
/**
* Grant read and write permissions for this bucket and its contents to an IAM
* principal (Role/Group/User).
*
* If encryption is used, permission to use the key to decrypt the contents
* of the bucket will also be granted to the same principal.
*
* @param identity The principal
* @param objectsKeyPattern Restrict the permission to a certain key pattern (default '*'). Parameter type is `any` but `string` should be passed in.
*/
public readWrite(identity: IGrantable, objectsKeyPattern: any = '*') {
const bucketActions = perms.BUCKET_READ_ACTIONS.concat(this.writeActions);
// we need unique permissions because some permissions are common between read and write key actions
const keyActions = [...new Set([...perms.KEY_READ_ACTIONS, ...perms.KEY_WRITE_ACTIONS])];
return this.actionsOnBucketAndObjectKeys(identity, objectsKeyPattern, ...bucketActions, ...keyActions);
}
private get putActions(): string[] {
return FeatureFlags.of(this.bucket).isEnabled(cxapi.S3_GRANT_WRITE_WITHOUT_ACL)
? perms.BUCKET_PUT_ACTIONS
: perms.LEGACY_BUCKET_PUT_ACTIONS;
}
private get writeActions(): string[] {
return [
...perms.BUCKET_DELETE_ACTIONS,
...this.putActions,
];
}
/**
* Grant replication permission to a principal.
* This method allows the principal to perform replication operations on this bucket.
*
* Note that when calling this function for source or destination buckets that support KMS encryption,
* you need to specify the KMS key for encryption and the KMS key for decryption, respectively.
*
* @param identity The principal to grant replication permission to.
* @param props The properties of the replication source and destination buckets.
*/
public replicationPermission(identity: IGrantable, props: GrantReplicationPermissionProps): iam.Grant {
if (props.destinations.length === 0) {
throw new ValidationError('AtLeastOneDestinationBucketRequired', 'At least one destination bucket must be specified in the destinations array', this.bucket);
}
// add permissions to the role
// @see https://docs.aws.amazon.com/AmazonS3/latest/userguide/setting-repl-config-perm-overview.html
let result = this.grant(identity, ['s3:GetReplicationConfiguration', 's3:ListBucket'], [], Lazy.string({ produce: () => this.bucket.bucketRef.bucketArn }));
const g1 = this.grant(
identity,
['s3:GetObjectVersionForReplication', 's3:GetObjectVersionAcl', 's3:GetObjectVersionTagging'],
[],
Lazy.string({ produce: () => this.arnForObjects('*') }),
);
result = result.combine(g1);
const destinationBuckets = props.destinations.map(destination => destination.bucket);
if (destinationBuckets.length > 0) {
const bucketActions = ['s3:ReplicateObject', 's3:ReplicateDelete', 's3:ReplicateTags', 's3:ObjectOwnerOverrideToBucketOwner'];
const g2 = (this.policyResource ? Grant.addToPrincipalOrResource({
actions: bucketActions,
grantee: identity,
resourceArns: destinationBuckets.map(bucket => Lazy.string({ produce: () => bucket.arnForObjects('*') })),
resource: this.policyResource,
}) : Grant.addToPrincipal({
actions: bucketActions,
grantee: identity,
resourceArns: destinationBuckets.map(bucket => Lazy.string({ produce: () => bucket.arnForObjects('*') })),
}));
result = result.combine(g2);
}
props.destinations.forEach(destination => {
const g = destination.encryptionKey?.grantEncrypt(identity);
if (g !== undefined) {
result = result.combine(g);
}
});
// If KMS key encryption is enabled on the source bucket, configure the decrypt permissions.
const grantOnKeyResult = this.encryptedResource?.grantOnKey(identity, 'kms:Decrypt');
if (grantOnKeyResult?.grant) {
result = result.combine(grantOnKeyResult.grant);
}
return result;
}
private grantActions(identity: IGrantable, actions: string[], resourceArn: string, ...otherResourceArns: string[]) {
const keyActions: string[] = [];
const bucketActions: string[] = [];
for (const action of actions) {
if (action.startsWith('kms:')) {
keyActions.push(action);
} else {
bucketActions.push(action);
}
}
return this.grant(identity, bucketActions, keyActions, resourceArn, ...otherResourceArns);
}
private grant(
grantee: IGrantable,
bucketActions: string[],
keyActions: string[],
resourceArn: string, ...otherResourceArns: string[]) {
const resources = [resourceArn, ...otherResourceArns];
const result = (this.policyResource ? Grant.addToPrincipalOrResource({
actions: bucketActions,
grantee: grantee,
resourceArns: resources,
resource: this.policyResource,
}) : Grant.addToPrincipal({
actions: bucketActions,
grantee: grantee,
resourceArns: resources,
}));
if (keyActions.length > 0) {
this.encryptedResource?.grantOnKey(grantee, ...keyActions);
}
return result;
}
private arnForObjects(keyPattern: string): string {
return perms.arnForObjects(this.bucket.bucketRef.bucketArn, keyPattern);
}
}