Skip to content

Commit 1bb8ca9

Browse files
authored
feat(aws-iam): refactor grants, add OrganizationPrincipal (#1623)
Add support for non-identity Principals in grants (for example, principals that represent accounts or organization IDs). For resources that support them, the required IAM statements will be added to the resource policy. For resources that don't support them (because they don't have resource policies) an error will be thrown. Add a new `OrganizationPrincipal` principal which represents all identities in the given AWS Organization. Grant methods no longer accept an optional principal. Instead, they accept an `IGrantable`, which encodes constructs that have a principal to grant to. This principal must be always present, but may be a principal that can't do any work other than emitting warnings for imported resources. For construct authors, all grant methods must now return an `iam.Grant` object, and all should be implemented by calling one of the static factory methods on `iam.Grant` and returning its result. Fixes #236. BREAKING CHANGE: `grant(function.role)` and `grant(project.role)` are now `grant(function)` and `grant(role)`.
1 parent ccbb6dd commit 1bb8ca9

File tree

65 files changed

+3349
-544
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+3349
-544
lines changed

design/aws-guidelines.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -326,9 +326,9 @@ export interface IFoo extends cdk.IConstruct, ISomething {
326326
readonly connections: ec2.Connections;
327327

328328
// permission grants (adds statements to the principal's policy)
329-
grant(principal?: iam.IPrincipal, ...actions: string[]): void;
330-
grantFoo(principal?: iam.IPrincipal): void;
331-
grantBar(principal?: iam.IPrincipal): void;
329+
grant(grantee?: iam.IGrantable, ...actions: string[]): void;
330+
grantFoo(grantee?: iam.IGrantable): void;
331+
grantBar(grantee?: iam.IGrantable): void;
332332

333333
// resource policy (if applicable)
334334
addToResourcePolicy(statement: iam.PolicyStatement): void;
@@ -364,7 +364,7 @@ export abstract class FooBase extends cdk.Construct implements IFoo {
364364
public abstract export(): FooAttributes;
365365

366366
// grants can usually be shared
367-
public grantYyy(principal?: iam.IPrincipal) {
367+
public grantYyy(grantee?: iam.IGrantable) {
368368
// ...
369369
}
370370

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"include": "dependencies/node-version"
55
},
66
"scripts": {
7-
"pkglint": "tools/pkglint/bin/pkglint -f ."
7+
"pkglint": "tools/pkglint/bin/pkglint -f .",
8+
"build-all": "tsc -b"
89
},
910
"devDependencies": {
1011
"@types/node": "8.10.40",

packages/@aws-cdk/assets-docker/test/test.image-asset.ts

+2-11
Original file line numberDiff line numberDiff line change
@@ -61,22 +61,13 @@ export = {
6161
":",
6262
{ "Ref": "AWS::AccountId" },
6363
":repository/",
64-
{
65-
"Fn::GetAtt": [
66-
"ImageAdoptRepositoryE1E84E35",
67-
"RepositoryName"
68-
]
69-
}
64+
{ "Fn::GetAtt": [ "ImageAdoptRepositoryE1E84E35", "RepositoryName" ] }
7065
]
7166
]
7267
}
7368
},
7469
{
75-
"Action": [
76-
"ecr:GetAuthorizationToken",
77-
"logs:CreateLogStream",
78-
"logs:PutLogEvents"
79-
],
70+
"Action": "ecr:GetAuthorizationToken",
8071
"Effect": "Allow",
8172
"Resource": "*"
8273
}

packages/@aws-cdk/assets/lib/asset.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export interface GenericAssetProps {
3636
* A list of principals that should be able to read this asset from S3.
3737
* You can use `asset.grantRead(principal)` to grant read permissions later.
3838
*/
39-
readonly readers?: iam.IPrincipal[];
39+
readonly readers?: iam.IGrantable[];
4040
}
4141

4242
/**
@@ -171,12 +171,12 @@ export class Asset extends cdk.Construct {
171171
/**
172172
* Grants read permissions to the principal on the asset's S3 object.
173173
*/
174-
public grantRead(principal?: iam.IPrincipal) {
174+
public grantRead(grantee: iam.IGrantable) {
175175
// We give permissions on all files with the same prefix. Presumably
176176
// different versions of the same file will have the same prefix
177177
// and we don't want to accidentally revoke permission on old versions
178178
// when deploying a new version.
179-
this.bucket.grantRead(principal, `${this.s3Prefix}*`);
179+
this.bucket.grantRead(grantee, `${this.s3Prefix}*`);
180180
}
181181
}
182182

@@ -190,7 +190,7 @@ export interface FileAssetProps {
190190
* A list of principals that should be able to read this file asset from S3.
191191
* You can use `asset.grantRead(principal)` to grant read permissions later.
192192
*/
193-
readonly readers?: iam.IPrincipal[];
193+
readonly readers?: iam.IGrantable[];
194194
}
195195

196196
/**
@@ -212,7 +212,7 @@ export interface ZipDirectoryAssetProps {
212212
* A list of principals that should be able to read this ZIP file from S3.
213213
* You can use `asset.grantRead(principal)` to grant read permissions later.
214214
*/
215-
readonly readers?: iam.IPrincipal[];
215+
readonly readers?: iam.IGrantable[];
216216
}
217217

218218
/**

packages/@aws-cdk/aws-cloudwatch/lib/metric.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -85,14 +85,14 @@ export class Metric {
8585
/**
8686
* Grant permissions to the given identity to write metrics.
8787
*
88-
* @param identity The IAM identity to give permissions to.
88+
* @param grantee The IAM identity to give permissions to.
8989
*/
90-
public static grantPutMetricData(identity?: iam.IPrincipal) {
91-
if (!identity) { return; }
92-
93-
identity.addToPolicy(new iam.PolicyStatement()
94-
.addAllResources()
95-
.addAction("cloudwatch:PutMetricData"));
90+
public static grantPutMetricData(grantee: iam.IGrantable): iam.Grant {
91+
return iam.Grant.addToPrincipal({
92+
grantee,
93+
actions: ['cloudwatch:PutMetricData'],
94+
resourceArns: ['*']
95+
});
9696
}
9797

9898
public readonly dimensions?: DimensionHash;

packages/@aws-cdk/aws-codebuild/lib/artifacts.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ export class S3BucketBuildArtifacts extends BuildArtifacts {
130130
* @internal
131131
*/
132132
public _bind(project: Project) {
133-
this.props.bucket.grantReadWrite(project.role);
133+
this.props.bucket.grantReadWrite(project);
134134
}
135135

136136
protected toArtifactsProperty(): any {

packages/@aws-cdk/aws-codebuild/lib/project.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const CODEPIPELINE_TYPE = 'CODEPIPELINE';
1616
const S3_BUCKET_ENV = 'SCRIPT_S3_BUCKET';
1717
const S3_KEY_ENV = 'SCRIPT_S3_KEY';
1818

19-
export interface IProject extends cdk.IConstruct, events.IEventRuleTarget {
19+
export interface IProject extends cdk.IConstruct, events.IEventRuleTarget, iam.IGrantable {
2020
/** The ARN of this Project. */
2121
readonly projectArn: string;
2222

@@ -156,13 +156,15 @@ export interface ProjectImportProps {
156156
* use the {@link import} method.
157157
*/
158158
export abstract class ProjectBase extends cdk.Construct implements IProject {
159+
public abstract readonly grantPrincipal: iam.IPrincipal;
160+
159161
/** The ARN of this Project. */
160162
public abstract readonly projectArn: string;
161163

162164
/** The human-visible name of this Project. */
163165
public abstract readonly projectName: string;
164166

165-
/** The IAM service Role of this Project. Undefined for imported Projects. */
167+
/** The IAM service Role of this Project. */
166168
public abstract readonly role?: iam.IRole;
167169

168170
/** A role used by CloudWatch events to trigger a build */
@@ -369,6 +371,7 @@ export abstract class ProjectBase extends cdk.Construct implements IProject {
369371
}
370372

371373
class ImportedProject extends ProjectBase {
374+
public readonly grantPrincipal: iam.IPrincipal;
372375
public readonly projectArn: string;
373376
public readonly projectName: string;
374377
public readonly role?: iam.Role = undefined;
@@ -381,6 +384,7 @@ class ImportedProject extends ProjectBase {
381384
resource: 'project',
382385
resourceName: props.projectName,
383386
});
387+
this.grantPrincipal = new iam.ImportedResourcePrincipal({ resource: this });
384388

385389
this.projectName = props.projectName;
386390
}
@@ -572,6 +576,8 @@ export class Project extends ProjectBase {
572576
return new ImportedProject(scope, id, props);
573577
}
574578

579+
public readonly grantPrincipal: iam.IPrincipal;
580+
575581
/**
576582
* The IAM role for this project.
577583
*/
@@ -603,6 +609,7 @@ export class Project extends ProjectBase {
603609
this.role = props.role || new iam.Role(this, 'Role', {
604610
assumedBy: new iam.ServicePrincipal('codebuild.amazonaws.com')
605611
});
612+
this.grantPrincipal = this.role;
606613

607614
let cache: CfnProject.ProjectCacheProperty | undefined;
608615
if (props.cacheBucket) {

packages/@aws-cdk/aws-codebuild/lib/source.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ export class S3BucketSource extends BuildSource {
167167
* @internal
168168
*/
169169
public _bind(project: Project) {
170-
this.bucket.grantRead(project.role);
170+
this.bucket.grantRead(project);
171171
}
172172

173173
protected toSourceProperty(): any {

packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-group.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ export class LambdaDeploymentGroup extends cdk.Construct implements ILambdaDeplo
194194
throw new Error('A pre-hook function is already defined for this deployment group');
195195
}
196196
this.preHook = preHook;
197-
this.grantPutLifecycleEventHookExecutionStatus(this.preHook.role);
197+
this.grantPutLifecycleEventHookExecutionStatus(this.preHook);
198198
this.preHook.grantInvoke(this.role);
199199
}
200200

@@ -208,7 +208,7 @@ export class LambdaDeploymentGroup extends cdk.Construct implements ILambdaDeplo
208208
throw new Error('A post-hook function is already defined for this deployment group');
209209
}
210210
this.postHook = postHook;
211-
this.grantPutLifecycleEventHookExecutionStatus(this.postHook.role);
211+
this.grantPutLifecycleEventHookExecutionStatus(this.postHook);
212212
this.postHook.grantInvoke(this.role);
213213
}
214214

@@ -217,12 +217,12 @@ export class LambdaDeploymentGroup extends cdk.Construct implements ILambdaDeplo
217217
* on this deployment group resource.
218218
* @param principal to grant permission to
219219
*/
220-
public grantPutLifecycleEventHookExecutionStatus(principal?: iam.IPrincipal): void {
221-
if (principal) {
222-
principal.addToPolicy(new iam.PolicyStatement()
223-
.addResource(this.deploymentGroupArn)
224-
.addAction('codedeploy:PutLifecycleEventHookExecutionStatus'));
225-
}
220+
public grantPutLifecycleEventHookExecutionStatus(grantee: iam.IGrantable): iam.Grant {
221+
return iam.Grant.addToPrincipal({
222+
grantee,
223+
resourceArns: [this.deploymentGroupArn],
224+
actions: ['codedeploy:PutLifecycleEventHookExecutionStatus'],
225+
});
226226
}
227227

228228
public export(): LambdaDeploymentGroupImportProps {

packages/@aws-cdk/aws-codepipeline-actions/lib/codebuild/pipeline-actions.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,9 @@ function setCodeBuildNeededPermissions(info: codepipeline.ActionBind, project: c
186186

187187
// allow the Project access to the Pipline's artifact Bucket
188188
if (needsPipelineBucketWrite) {
189-
info.pipeline.grantBucketReadWrite(project.role);
189+
info.pipeline.grantBucketReadWrite(project);
190190
} else {
191-
info.pipeline.grantBucketRead(project.role);
191+
info.pipeline.grantBucketRead(project);
192192
}
193193
}
194194

packages/@aws-cdk/aws-codepipeline-actions/test/cloudformation/test.pipeline-actions.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -317,11 +317,11 @@ class PipelineDouble extends cdk.Construct implements codepipeline.IPipeline {
317317
throw new Error('asEventRuleTarget() is unsupported in PipelineDouble');
318318
}
319319

320-
public grantBucketRead(_identity?: iam.IPrincipal): void {
320+
public grantBucketRead(_identity?: iam.IGrantable): iam.Grant {
321321
throw new Error('grantBucketRead() is unsupported in PipelineDouble');
322322
}
323323

324-
public grantBucketReadWrite(_identity?: iam.IPrincipal): void {
324+
public grantBucketReadWrite(_identity?: iam.IGrantable): iam.Grant {
325325
throw new Error('grantBucketReadWrite() is unsupported in PipelineDouble');
326326
}
327327
}
@@ -369,9 +369,10 @@ class RoleDouble extends iam.Role {
369369
super(scope, id, props);
370370
}
371371

372-
public addToPolicy(statement: iam.PolicyStatement) {
372+
public addToPolicy(statement: iam.PolicyStatement): boolean {
373373
super.addToPolicy(statement);
374374
this.statements.push(statement);
375+
return true;
375376
}
376377
}
377378

packages/@aws-cdk/aws-codepipeline/lib/action.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -83,14 +83,14 @@ export interface IPipeline extends cdk.IConstruct, events.IEventRuleTarget {
8383
*
8484
* @param identity the IAM Identity to grant the permissions to
8585
*/
86-
grantBucketRead(identity?: iam.IPrincipal): void;
86+
grantBucketRead(identity: iam.IGrantable): iam.Grant;
8787

8888
/**
8989
* Grants read & write permissions to the Pipeline's S3 Bucket to the given Identity.
9090
*
9191
* @param identity the IAM Identity to grant the permissions to
9292
*/
93-
grantBucketReadWrite(identity?: iam.IPrincipal): void;
93+
grantBucketReadWrite(identity: iam.IGrantable): iam.Grant;
9494
}
9595

9696
/**

packages/@aws-cdk/aws-codepipeline/lib/pipeline.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -293,12 +293,12 @@ export class Pipeline extends cdk.Construct implements IPipeline {
293293
return this.stages.length;
294294
}
295295

296-
public grantBucketRead(identity?: iam.IPrincipal): void {
297-
this.artifactBucket.grantRead(identity);
296+
public grantBucketRead(identity: iam.IGrantable): iam.Grant {
297+
return this.artifactBucket.grantRead(identity);
298298
}
299299

300-
public grantBucketReadWrite(identity?: iam.IPrincipal): void {
301-
this.artifactBucket.grantReadWrite(identity);
300+
public grantBucketReadWrite(identity: iam.IGrantable): iam.Grant {
301+
return this.artifactBucket.grantReadWrite(identity);
302302
}
303303

304304
/**

0 commit comments

Comments
 (0)