Skip to content

Commit

Permalink
chore(iam): move principals to their own file (#2656)
Browse files Browse the repository at this point in the history
The list of principals is quite big, and they
deserve to be grouped together in their own file.
  • Loading branch information
rix0rrr authored May 28, 2019
1 parent b282132 commit e6a9f9e
Show file tree
Hide file tree
Showing 7 changed files with 291 additions and 290 deletions.
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-iam/lib/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { Construct, Resource } from '@aws-cdk/cdk';
import { CfnGroup } from './iam.generated';
import { IIdentity } from './identity-base';
import { Policy } from './policy';
import { ArnPrincipal, PolicyStatement, PrincipalPolicyFragment } from './policy-document';
import { IPrincipal } from './principals';
import { PolicyStatement } from './policy-document';
import { ArnPrincipal, IPrincipal, PrincipalPolicyFragment } from './principals';
import { IUser } from './user';
import { AttachedPolicies, undefinedIfEmpty } from './util';

Expand Down
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-iam/lib/imported-resource-principal.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import cdk = require('@aws-cdk/cdk');
import { PolicyStatement, PrincipalPolicyFragment } from './policy-document';
import { IPrincipal } from './principals';
import { PolicyStatement } from './policy-document';
import { IPrincipal, PrincipalPolicyFragment } from './principals';

/**
* Properties for an ImportedResourcePrincipal
Expand Down
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-iam/lib/lazy-role.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import cdk = require('@aws-cdk/cdk');
import { Grant } from './grant';
import { Policy } from './policy';
import { PolicyStatement, PrincipalPolicyFragment } from './policy-document';
import { IPrincipal } from './principals';
import { PolicyStatement } from './policy-document';
import { IPrincipal, PrincipalPolicyFragment } from './principals';
import { IRole, Role, RoleProps } from './role';

// tslint:disable-next-line:no-empty-interface
Expand Down
282 changes: 3 additions & 279 deletions packages/@aws-cdk/aws-iam/lib/policy-document.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import cdk = require('@aws-cdk/cdk');
import { Default, RegionInfo } from '@aws-cdk/region-info';
import { IPrincipal } from './principals';
import { AccountPrincipal, AccountRootPrincipal, Anyone, ArnPrincipal, CanonicalUserPrincipal,
FederatedPrincipal, IPrincipal, ServicePrincipal, ServicePrincipalOpts } from './principals';
import { mergePrincipal } from './util';

export class PolicyDocument extends cdk.Token implements cdk.IResolvedValuePostProcessor {
Expand Down Expand Up @@ -94,244 +94,6 @@ export class PolicyDocument extends cdk.Token implements cdk.IResolvedValuePostP
}
}

/**
* Base class for policy principals
*/
export abstract class PrincipalBase implements IPrincipal {
public readonly grantPrincipal: IPrincipal = this;

/**
* Return the policy fragment that identifies this principal in a Policy.
*/
public abstract readonly policyFragment: PrincipalPolicyFragment;

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

public addToPolicy(_statement: PolicyStatement): boolean {
// This base class is used for non-identity principals. None of them
// have a PolicyDocument to add to.
return false;
}

public toString() {
// This is a first pass to make the object readable. Descendant principals
// should return something nicer.
return JSON.stringify(this.policyFragment.principalJson);
}

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

/**
* A collection of the fields in a PolicyStatement that can be used to identify a principal.
*
* This consists of the JSON used in the "Principal" field, and optionally a
* set of "Condition"s that need to be applied to the policy.
*/
export class PrincipalPolicyFragment {
constructor(
public readonly principalJson: { [key: string]: string[] },
public readonly conditions: { [key: string]: any } = { }) {
}
}

export class ArnPrincipal extends PrincipalBase {
constructor(public readonly arn: string) {
super();
}

public get policyFragment(): PrincipalPolicyFragment {
return new PrincipalPolicyFragment({ AWS: [ this.arn ] });
}

public toString() {
return `ArnPrincipal(${this.arn})`;
}
}

export class AccountPrincipal extends ArnPrincipal {
constructor(public readonly accountId: any) {
super(new StackDependentToken(stack => `arn:${stack.partition}:iam::${accountId}:root`).toString());
}

public toString() {
return `AccountPrincipal(${this.accountId})`;
}
}

/**
* An IAM principal that represents an AWS service (i.e. sqs.amazonaws.com).
*/
export class ServicePrincipal extends PrincipalBase {
constructor(public readonly service: string, private readonly opts: ServicePrincipalOpts = {}) {
super();
}

public get policyFragment(): PrincipalPolicyFragment {
return new PrincipalPolicyFragment({
Service: [
new ServicePrincipalToken(this.service, this.opts).toString()
]
});
}

public toString() {
return `ServicePrincipal(${this.service})`;
}
}

/**
* A principal that represents an AWS Organization
*/
export class OrganizationPrincipal extends PrincipalBase {
constructor(public readonly organizationId: string) {
super();
}

public get policyFragment(): PrincipalPolicyFragment {
return new PrincipalPolicyFragment(
{ AWS: ['*'] },
{ StringEquals: { 'aws:PrincipalOrgID': this.organizationId } }
);
}

public toString() {
return `OrganizationPrincipal(${this.organizationId})`;
}
}

/**
* A policy prinicipal for canonicalUserIds - useful for S3 bucket policies that use
* Origin Access identities.
*
* See https://docs.aws.amazon.com/general/latest/gr/acct-identifiers.html
*
* and
*
* https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html
*
* for more details.
*
*/
export class CanonicalUserPrincipal extends PrincipalBase {
constructor(public readonly canonicalUserId: string) {
super();
}

public get policyFragment(): PrincipalPolicyFragment {
return new PrincipalPolicyFragment({ CanonicalUser: [ this.canonicalUserId ] });
}

public toString() {
return `CanonicalUserPrincipal(${this.canonicalUserId})`;
}
}

export class FederatedPrincipal extends PrincipalBase {
public readonly assumeRoleAction: string;

constructor(
public readonly federated: string,
public readonly conditions: {[key: string]: any},
assumeRoleAction: string = 'sts:AssumeRole') {
super();

this.assumeRoleAction = assumeRoleAction;
}

public get policyFragment(): PrincipalPolicyFragment {
return new PrincipalPolicyFragment({ Federated: [ this.federated ] }, this.conditions);
}

public toString() {
return `FederatedPrincipal(${this.federated})`;
}
}

export class AccountRootPrincipal extends AccountPrincipal {
constructor() {
super(new StackDependentToken(stack => stack.accountId).toString());
}

public toString() {
return `AccountRootPrincipal()`;
}
}

/**
* A principal representing all identities in all accounts
*/
export class AnyPrincipal extends ArnPrincipal {
constructor() {
super('*');
}

public toString() {
return `AnyPrincipal()`;
}
}

/**
* A principal representing all identities in all accounts
* @deprecated use `AnyPrincipal`
*/
export class Anyone extends AnyPrincipal { }

export class CompositePrincipal extends PrincipalBase {
public readonly assumeRoleAction: string;
private readonly principals = new Array<PrincipalBase>();

constructor(...principals: PrincipalBase[]) {
super();
if (principals.length === 0) {
throw new Error('CompositePrincipals must be constructed with at least 1 Principal but none were passed.');
}
this.assumeRoleAction = principals[0].assumeRoleAction;
this.addPrincipals(...principals);
}

public addPrincipals(...principals: PrincipalBase[]): this {
for (const p of principals) {
if (p.assumeRoleAction !== this.assumeRoleAction) {
throw new Error(
`Cannot add multiple principals with different "assumeRoleAction". ` +
`Expecting "${this.assumeRoleAction}", got "${p.assumeRoleAction}"`);
}

const fragment = p.policyFragment;
if (fragment.conditions && Object.keys(fragment.conditions).length > 0) {
throw new Error(
`Components of a CompositePrincipal must not have conditions. ` +
`Tried to add the following fragment: ${JSON.stringify(fragment)}`);
}

this.principals.push(p);
}

return this;
}

public get policyFragment(): PrincipalPolicyFragment {
const principalJson: { [key: string]: string[] } = { };

for (const p of this.principals) {
mergePrincipal(principalJson, p.policyFragment.principalJson);
}

return new PrincipalPolicyFragment(principalJson);
}

public toString() {
return `CompositePrincipal(${this.principals})`;
}
}

/**
* Represents a statement in an IAM policy document.
*/
Expand Down Expand Up @@ -581,42 +343,4 @@ export class PolicyStatement extends cdk.Token {
export enum PolicyStatementEffect {
Allow = 'Allow',
Deny = 'Deny',
}

/**
* A lazy token that requires an instance of Stack to evaluate
*/
class StackDependentToken extends cdk.Token {
constructor(private readonly fn: (stack: cdk.Stack) => any) {
super();
}

public resolve(context: cdk.IResolveContext) {
return this.fn(context.scope.node.stack);
}
}

class ServicePrincipalToken extends cdk.Token {
constructor(private readonly service: string,
private readonly opts: ServicePrincipalOpts) {
super();
}

public resolve(ctx: cdk.IResolveContext) {
const region = this.opts.region || ctx.scope.node.stack.region;
const fact = RegionInfo.get(region).servicePrincipal(this.service);
return fact || Default.servicePrincipal(this.service, region, ctx.scope.node.stack.urlSuffix);
}
}

/**
* Options for a service principal.
*/
export interface ServicePrincipalOpts {
/**
* The region in which the service is operating.
*
* @default the current Stack's region.
*/
readonly region?: string;
}
}
Loading

0 comments on commit e6a9f9e

Please sign in to comment.