Skip to content

Commit

Permalink
Add docs, plus addCondition/addConditions methods
Browse files Browse the repository at this point in the history
`addCondition` and `addConditions` are added to the
`PrincipalWithConditions` class to make its interface more
consistent with what users are used to from the
`PolicyStatement` class.
  • Loading branch information
zakwalters committed Mar 25, 2020
1 parent e3b4e49 commit b038bb1
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 32 deletions.
13 changes: 4 additions & 9 deletions packages/@aws-cdk/aws-iam/lib/policy-statement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ export class PolicyStatement {
this.addPrincipals(new ArnPrincipal(arn));
}

// TODO: these helpers should add PrincipalWithConditions if `conditions` is passed
/**
* Adds a service principal to this policy statement.
*
Expand Down Expand Up @@ -201,7 +200,7 @@ export class PolicyStatement {
/**
* Add a condition to the Policy
*/
public addCondition(key: string, value: any) {
public addCondition(key: string, value: { [key: string]: string | string[] }) {
const existingValue = this.condition[key];
this.condition[key] = existingValue ? { ...existingValue, ...value } : value;
}
Expand Down Expand Up @@ -305,16 +304,12 @@ export enum Effect {
}

/**
* TODO: docs
* https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition.html
* https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-keys.html
* Conditions for when an IAM Policy is in effect.
*
* TODO: note for commit - JSII blocks this from being more strongly modelled
* TODO: check this type is used in all places (e.g. statement.addCondition)...
* technically a breaking change to narrow the type but seeing as Cfn enforces it anyway it should be acceptable
* See https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition.html
*/
export interface Conditions {
[operator: string]: { [conditionKey: string]: string | string[] };
[operator: string]: { [key: string]: string | string[] };
}

/**
Expand Down
61 changes: 40 additions & 21 deletions packages/@aws-cdk/aws-iam/lib/principals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export interface IGrantable {
* Represents a logical IAM principal.
*
* An IPrincipal describes a logical entity that can perform AWS API calls
* against sets of resources, optionally under certain conditions. // TODO: may need to update these docs
* against sets of resources, optionally under certain conditions.
*
* Examples of simple principals are IAM objects that you create, such
* as Users or Roles.
Expand Down Expand Up @@ -83,44 +83,58 @@ export abstract class PrincipalBase implements IPrincipal {
return this.policyFragment.principalJson;
}

// TODO: should this be part of IPrincipal too?
/**
* TODO: docs
* Returns a new PrincipalWithConditions using this principal as the base, with the
* passed conditions added.
*
* When there is a value for the same operator and key in both the principal and the
* conditions parameter, the value from the conditions parameter will be used.
*
* @returns a new PrincipalWithConditions object.
*/
public withConditions(conditions: Conditions): IPrincipal {
return new PrincipalWithConditions(this, conditions);
}
}

/**
* A principal with conditions TODO: improve docs; should this extend BasePrincipal instead?
* An IAM principal with additional conditions specifying when the policy is in effect.
*
* For more information about conditions, see:
* https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition.html
*/
export class PrincipalWithConditions<PrincipalType extends PrincipalBase> implements IPrincipal {
/**
* TODO: docs (or exclude in package.json)
*/
public readonly conditions: Conditions;
public readonly grantPrincipal: IPrincipal = this;

/**
* When this Principal is used in an AssumeRole policy, the action to use.
*/
public readonly assumeRoleAction: string = 'sts:AssumeRole';

constructor(
/**
* TODO: docs (or exclude in package.json)
*/
public readonly principal: PrincipalType,
conditions: Conditions,
) {
this.conditions = conditions;
Object.entries(principal.policyFragment.conditions).forEach(([key, valueFromPrincipal]) => {
const valueFromConditions = this.conditions[key];
this.conditions[key] = valueFromConditions
? { ...valueFromPrincipal, ...valueFromConditions }
: valueFromPrincipal;
});
// Copy the intiial conditions from the principal
this.conditions = JSON.parse(JSON.stringify(this.principal.policyFragment.conditions));
this.addConditions(conditions);
}

/**
* Add a condition to the principal
*/
public addCondition(key: string, value: { [key: string]: string | string[] }) {
const existingValue = this.conditions[key];
this.conditions[key] = existingValue ? { ...existingValue, ...value } : value;
}

/**
* Adds multiple conditions to the principal
*
* Values from the conditions parameter will overwrite existing values with the same operator
* and key.
*/
public addConditions(conditions: Conditions) {
Object.entries(conditions).forEach(([key, value]) => {
this.addCondition(key, value);
});
}

public get policyFragment(): PrincipalPolicyFragment {
Expand All @@ -134,6 +148,11 @@ export class PrincipalWithConditions<PrincipalType extends PrincipalBase> implem
public toString() {
return this.principal.toString();
}

public toJSON() {
// Have to implement toJSON() because the default will lead to infinite recursion.
return this.policyFragment.principalJson;
}
}

/**
Expand Down
3 changes: 3 additions & 0 deletions packages/@aws-cdk/aws-iam/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@
"docs-public-apis:@aws-cdk/aws-iam.PrincipalBase.toJSON",
"docs-public-apis:@aws-cdk/aws-iam.PrincipalPolicyFragment.conditions",
"docs-public-apis:@aws-cdk/aws-iam.PrincipalPolicyFragment.principalJson",
"docs-public-apis:@aws-cdk/aws-iam.PrincipalWithConditions.conditions",
"docs-public-apis:@aws-cdk/aws-iam.PrincipalWithConditions.principal",
"docs-public-apis:@aws-cdk/aws-iam.PrincipalWithConditions.toJSON",
"docs-public-apis:@aws-cdk/aws-iam.ServicePrincipal.service",
"props-default-doc:@aws-cdk/aws-iam.GrantOnPrincipalOptions.scope",
"docs-public-apis:@aws-cdk/aws-iam.GroupProps",
Expand Down
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-iam/test/policy-document.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -449,11 +449,11 @@ describe('IAM polocy document', () => {

// add via policy statement
statement.addArnPrincipal('aws-principal-3');
statement.addCondition('cond2', { boom: 123 });
statement.addCondition('cond2', { boom: "123" });

expect(stack.resolve(statement.toStatementJson())).toEqual({
Condition: {
cond2: { boom: 123 }
cond2: { boom: "123" }
},
Effect: 'Allow',
Principal: {
Expand Down

0 comments on commit b038bb1

Please sign in to comment.