Skip to content

Commit

Permalink
move grant methods to IDomain
Browse files Browse the repository at this point in the history
  • Loading branch information
bweigel authored and Benjamin committed Sep 25, 2024
1 parent b2e42b6 commit 6f88edc
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 89 deletions.
188 changes: 118 additions & 70 deletions src/aws-codeartifact/domain.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
import { ArnFormat, IResource, ITaggableV2, Lazy, Resource, Stack, TagManager, TagType } from 'aws-cdk-lib';
import {
Annotations,
ArnFormat,
IResource,
ITaggableV2,
Lazy,
Resource,
Stack,
TagManager,
TagType,
} from 'aws-cdk-lib';
import { CfnDomain, CfnDomainProps } from 'aws-cdk-lib/aws-codeartifact';
import {
AddToResourcePolicyResult,
Expand Down Expand Up @@ -43,6 +53,31 @@ export interface IDomain extends IResource {
* @attribute
*/
readonly domainOwner: string;

/**
* Adds a statement to the Codeartifact domain resource policy.
* @param statement The policy statement to add
*/
addToResourcePolicy(statement: PolicyStatement): AddToResourcePolicyResult;

/**
* Grants permissions to the specified grantee on this CodeArtifact domain.
*
* It handles both same-environment and cross-environment scenarios:
* - For same-environment grants, it adds the permissions to the principal or resource.
* - For cross-environment grants, it adds the permissions to both the principal and the resource.
*
* @param grantee - The principal to grant permissions to.
* @param actions - The actions to grant. These should be valid CodeArtifact actions.
*/
grant(grantee: IGrantable, ...actions: string[]): Grant;

/**
* Grants contribute permissions to the specified grantee on this CodeArtifact domain.
*
* @param grantee - The principal to grant contribute permissions to.
*/
grantContribute(grantee: IGrantable): Grant;
}

/**
Expand All @@ -68,6 +103,86 @@ abstract class DomainBase extends Resource implements IDomain {
* The AWS account ID that owns the domain.
*/
public abstract readonly domainOwner: string;

/**
* Optional policy document that represents the resource policy of this key.
*/
protected abstract readonly policy?: PolicyDocument;

/**
* Adds a statement to the Codeartifact domain resource policy.
* @param statement The policy statement to add
*/
public addToResourcePolicy(statement: PolicyStatement): AddToResourcePolicyResult {
const stack = Stack.of(this);

if (!this.policy) {
Annotations.of(stack).addWarningV2(
'NoResourcePolicyStatementAdded',
`No statements added to imported resource ${this.domainArn}.`,
);
return { statementAdded: false };
}

this.policy.addStatements(statement);
return { statementAdded: true, policyDependable: this.policy };
}

private isCrossEnvironmentGrantee(grantee: IGrantable): boolean {
if (!principalIsOwnedResource(grantee.grantPrincipal)) {
return false;
}
const thisStack = Stack.of(this);
const identityStack = Stack.of(grantee.grantPrincipal);
return thisStack.region !== identityStack.region || thisStack.account !== identityStack.account;
}

/**
* Grants permissions to the specified grantee on this CodeArtifact domain.
*
* It handles both same-environment and cross-environment scenarios:
* - For same-environment grants, it adds the permissions to the principal or resource.
* - For cross-environment grants, it adds the permissions to both the principal and the resource.
*
* @param grantee - The principal to grant permissions to.
* @param actions - The actions to grant. These should be valid CodeArtifact actions.
*/
public grant(grantee: IGrantable, ...actions: string[]): Grant {
const crossEnvironment = this.isCrossEnvironmentGrantee(grantee);
const grantOptions: GrantWithResourceOptions = {
grantee,
actions,
resource: this,
resourceArns: [this.domainArn],
resourceSelfArns: crossEnvironment ? undefined : ['*'],
};
if (!crossEnvironment) {
return Grant.addToPrincipalOrResource(grantOptions);
} else {
return Grant.addToPrincipalAndResource({
...grantOptions,
resourceArns: [this.domainArn],
resourcePolicyPrincipal: grantee.grantPrincipal,
});
}
}

/**
* Grants contribute permissions to the specified grantee on this CodeArtifact domain.
*
* @param grantee - The principal to grant contribute permissions to.
*/
public grantContribute(grantee: IGrantable) {
return this.grant(
grantee,
'codeartifact:CreateRepository',
'codeartifact:DescribeDomain',
'codeartifact:GetAuthorizationToken',
'codeartifact:GetDomainPermissionsPolicy',
'codeartifact:ListRepositoriesInDomain',
'sts:GetServiceBearerToken',
);
}
}

/**
Expand Down Expand Up @@ -126,6 +241,7 @@ export class Domain extends DomainBase implements IDomain, ITaggableV2 {
public readonly domainName = attrs.domainName;
public readonly encryptionKey = attrs.encryptionKey;
public readonly domainOwner = attrs.domainOwner;
protected readonly policy?: PolicyDocument | undefined = undefined;
}

return new Import(scope, id);
Expand Down Expand Up @@ -194,6 +310,7 @@ export class Domain extends DomainBase implements IDomain, ITaggableV2 {
constructor(scope: Construct, id: string, props: DomainProps) {
super(scope, id);
this.cdkTagManager = new TagManager(TagType.KEY_VALUE, 'AWS::CodeArtifact::Domain');
this.policy = new PolicyDocument();

const encryptionKey =
props.encryptionKey ??
Expand All @@ -218,73 +335,4 @@ export class Domain extends DomainBase implements IDomain, ITaggableV2 {
protected createCfnResource(): CfnDomain {
return new CfnDomain(this, 'Resource', this.cfnResourceProps);
}

/**
* Adds a statement to the KMS key resource policy.
* @param statement The policy statement to add
*/
public addToResourcePolicy(statement: PolicyStatement): AddToResourcePolicyResult {
if (!this.policy) {
this.policy = new PolicyDocument();
}

this.policy.addStatements(statement);
return { statementAdded: true, policyDependable: this.policy };
}

private isCrossEnvironmentGrantee(grantee: IGrantable): boolean {
if (!principalIsOwnedResource(grantee.grantPrincipal)) {
return false;
}
const thisStack = Stack.of(this);
const identityStack = Stack.of(grantee.grantPrincipal);
return thisStack.region !== identityStack.region || thisStack.account !== identityStack.account;
}

/**
* Grants permissions to the specified grantee on this CodeArtifact domain.
*
* It handles both same-environment and cross-environment scenarios:
* - For same-environment grants, it adds the permissions to the principal or resource.
* - For cross-environment grants, it adds the permissions to both the principal and the resource.
*
* @param grantee - The principal to grant permissions to.
* @param actions - The actions to grant. These should be valid CodeArtifact actions.
*/
public grant(grantee: IGrantable, ...actions: string[]): Grant {
const crossEnvironment = this.isCrossEnvironmentGrantee(grantee);
const grantOptions: GrantWithResourceOptions = {
grantee,
actions,
resource: this,
resourceArns: [this.domainArn],
resourceSelfArns: crossEnvironment ? undefined : ['*'],
};
if (!crossEnvironment) {
return Grant.addToPrincipalOrResource(grantOptions);
} else {
return Grant.addToPrincipalAndResource({
...grantOptions,
resourceArns: [this.domainArn],
resourcePolicyPrincipal: grantee.grantPrincipal,
});
}
}

/**
* Grants contribute permissions to the specified grantee on this CodeArtifact domain.
*
* @param grantee - The principal to grant contribute permissions to.
*/
public grantContribute(grantee: IGrantable) {
return this.grant(
grantee,
'codeartifact:CreateRepository',
'codeartifact:DescribeDomain',
'codeartifact:GetAuthorizationToken',
'codeartifact:GetDomainPermissionsPolicy',
'codeartifact:ListRepositoriesInDomain',
'sts:GetServiceBearerToken',
);
}
}
25 changes: 7 additions & 18 deletions src/aws-codeartifact/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,8 @@ export interface IRepository extends IResource {
* Adds a statement to the CodeArtifact repository resource policy.
*
* @param statement The policy statement to add
* @param allowNoOp If this is set to `false` and there is no policy
* defined (i.e. external repository), the operation will fail. Otherwise, it will
* no-op.
*/
addToResourcePolicy(statement: PolicyStatement, allowNoOp?: boolean): AddToResourcePolicyResult;
addToResourcePolicy(statement: PolicyStatement): AddToResourcePolicyResult;

/**
* Grants the given principal identity permissions to perform the actions on the repository.
Expand Down Expand Up @@ -182,7 +179,7 @@ abstract class RepositoryBase extends Resource implements IRepository {
public abstract domain: IDomain;

/**
* Optional policy document that represents the resource policy of this key.
* Optional policy document that represents the resource policy of this repository.
*
* If specified, addToResourcePolicy can be used to edit this policy.
* Otherwise this method will no-op.
Expand All @@ -201,24 +198,16 @@ abstract class RepositoryBase extends Resource implements IRepository {
/**
* Adds a statement to the CodeArtifact repository resource policy.
* @param statement The policy statement to add
* @param allowNoOp If this is set to `false` and there is no policy
* defined (i.e. external key), the operation will fail. Otherwise, it will
* no-op and add a warning annotation.
*/
public addToResourcePolicy(statement: PolicyStatement, allowNoOp = true): AddToResourcePolicyResult {
public addToResourcePolicy(statement: PolicyStatement): AddToResourcePolicyResult {
const stack = Stack.of(this);

if (!this.policy) {
if (allowNoOp) {
Annotations.of(stack).addWarningV2(
'NoResourcePolicyStatementAdded',
`No statements added to imported resource ${this.repositoryArn}.`,
);
return { statementAdded: false };
}
throw new Error(
`Unable to add statement to IAM resource policy for Codeartifact Repository: ${this.repositoryArn}`,
Annotations.of(stack).addWarningV2(
'NoResourcePolicyStatementAdded',
`No statements added to imported resource ${this.repositoryArn}.`,
);
return { statementAdded: false };
}

this.policy.addStatements(statement);
Expand Down
2 changes: 1 addition & 1 deletion test/aws-codeartifact/repository.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { App, Stack, Tags } from 'aws-cdk-lib';
import { Stack, Tags } from 'aws-cdk-lib';
import { Match, Template } from 'aws-cdk-lib/assertions';
import { AccountPrincipal, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam';
import { Construct } from 'constructs';
Expand Down

0 comments on commit 6f88edc

Please sign in to comment.