Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ses): grant methods to IEmailIdentity #29084

Merged
merged 9 commits into from
Feb 13, 2024
74 changes: 72 additions & 2 deletions packages/aws-cdk-lib/aws-ses/lib/email-identity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Construct } from 'constructs';
import { IConfigurationSet } from './configuration-set';
import { undefinedIfNoKeys } from './private/utils';
import { CfnEmailIdentity } from './ses.generated';
import { Grant, IGrantable } from '../../aws-iam';
import { IPublicHostedZone } from '../../aws-route53';
import * as route53 from '../../aws-route53';
import { IResource, Lazy, Resource, SecretValue, Stack } from '../../core';
Expand All @@ -16,6 +17,23 @@ export interface IEmailIdentity extends IResource {
* @attribute
*/
readonly emailIdentityName: string;

/**
* Adds an IAM policy statement associated with this email identity to an IAM principal's policy.
*
* @param grantee the principal (no-op if undefined)
* @param actions the set of actions to allow
*/
grant(grantee: IGrantable, ...actions: string[]): Grant;

/**
* Permits an IAM principal the send email action.
*
* Actions: SendEmail.
*
* @param grantee the principal to grant access to
*/
grantSendEmail(grantee: IGrantable): Grant;
}

/**
Expand Down Expand Up @@ -310,22 +328,68 @@ export enum EasyDkimSigningKeyLength {
RSA_2048_BIT = 'RSA_2048_BIT',
}

abstract class EmailIdentityBase extends Resource implements IEmailIdentity {
public abstract readonly emailIdentityName: string;

/**
* The ARN of the identity.
*
* @attribute
*/
public abstract readonly emailIdentityArn: string;

/**
* Adds an IAM policy statement associated with this email identity to an IAM principal's policy.
*
* @param grantee the principal (no-op if undefined)
* @param actions the set of actions to allow
*/
public grant(grantee: IGrantable, ...actions: string[]): Grant {
const resourceArns = [this.emailIdentityArn];
return Grant.addToPrincipal({
grantee,
actions,
resourceArns,
scope: this,
});
}

/**
* Permits an IAM principal the send email action.
*
* Actions: SendEmail.
*
* @param grantee the principal to grant access to
*/
public grantSendEmail(grantee: IGrantable): Grant {
return this.grant(grantee, 'ses:SendEmail');
}
}

/**
* An email identity
*/
export class EmailIdentity extends Resource implements IEmailIdentity {
export class EmailIdentity extends EmailIdentityBase {
/**
* Use an existing email identity
*/
public static fromEmailIdentityName(scope: Construct, id: string, emailIdentityName: string): IEmailIdentity {
class Import extends Resource implements IEmailIdentity {
class Import extends EmailIdentityBase {
public readonly emailIdentityName = emailIdentityName;

public readonly emailIdentityArn = this.stack.formatArn({
service: 'ses',
resource: 'identity',
resourceName: this.emailIdentityName,
});
}
return new Import(scope, id);
}

public readonly emailIdentityName: string;

public readonly emailIdentityArn: string;

/**
* The host name for the first token that you have to add to the
* DNS configurationfor your domain
Expand Down Expand Up @@ -421,6 +485,12 @@ export class EmailIdentity extends Resource implements IEmailIdentity {

this.emailIdentityName = identity.ref;

this.emailIdentityArn = this.stack.formatArn({
service: 'ses',
resource: 'identity',
resourceName: this.emailIdentityName,
});

this.dkimDnsTokenName1 = identity.attrDkimDnsTokenName1;
this.dkimDnsTokenName2 = identity.attrDkimDnsTokenName2;
this.dkimDnsTokenName3 = identity.attrDkimDnsTokenName3;
Expand Down
133 changes: 133 additions & 0 deletions packages/aws-cdk-lib/aws-ses/test/email-identity.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Template } from '../../assertions';
import { User } from '../../aws-iam';
import * as route53 from '../../aws-route53';
import { SecretValue, Stack } from '../../core';
import { ConfigurationSet, DkimIdentity, EmailIdentity, Identity, MailFromBehaviorOnMxFailure } from '../lib';
Expand Down Expand Up @@ -191,3 +192,135 @@ test('with mail from and hosted zone', () => {
});
});

describe('grants', () => {
test('grant on a domain identity', () => {
// GIVEN
stack = new Stack(undefined, 'Stack', { env: { region: 'us-west-2', account: '123456789012' } });
const user = new User(stack, 'User');
const emailIdentity = new EmailIdentity(stack, 'Identity', {
identity: Identity.domain('cdk.dev'),
});

// WHEN
emailIdentity.grant(user, 'ses:action1', 'ses:action2');

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', {
PolicyDocument: {
Statement: [
{
Action: ['ses:action1', 'ses:action2'],
Effect: 'Allow',
Resource: {
'Fn::Join': [
'',
[
'arn:',
{ Ref: 'AWS::Partition' },
':ses:us-west-2:123456789012:identity/',
{ Ref: 'Identity2D60E2CC' },
],
],
},
},
],
Version: '2012-10-17',
},
PolicyName: 'UserDefaultPolicy1F97781E',
Users: [
{
Ref: 'User00B015A1',
},
],
});
});

test('grant on an email identity from name', () => {
// GIVEN
stack = new Stack(undefined, 'Stack', { env: { region: 'us-west-2', account: '123456789012' } });
const user = new User(stack, 'User');
const emailIdentity = EmailIdentity.fromEmailIdentityName(
stack,
'Identity',
'cdk.dev',
);

// WHEN
emailIdentity.grant(user, 'ses:action1', 'ses:action2');

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', {
PolicyDocument: {
Statement: [
{
Action: [
'ses:action1',
'ses:action2',
],
Effect: 'Allow',
Resource: {
'Fn::Join': [
'',
[
'arn:',
{ Ref: 'AWS::Partition' },
':ses:us-west-2:123456789012:identity/cdk.dev',
],
],
},
},
],
Version: '2012-10-17',
},
PolicyName: 'UserDefaultPolicy1F97781E',
Users: [
{
Ref: 'User00B015A1',
},
],
});
});

test('grantSendEmail', () => {
// GIVEN
stack = new Stack(undefined, 'Stack', { env: { region: 'us-west-2', account: '123456789012' } });
const user = new User(stack, 'User');
const emailIdentity = EmailIdentity.fromEmailIdentityName(
stack,
'Identity',
'cdk.dev',
);

// WHEN
emailIdentity.grantSendEmail(user);

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', {
PolicyDocument: {
Statement: [
{
Action: 'ses:SendEmail',
Effect: 'Allow',
Resource: {
'Fn::Join': [
'',
[
'arn:',
{ Ref: 'AWS::Partition' },
':ses:us-west-2:123456789012:identity/cdk.dev',
],
],
},
},
],
Version: '2012-10-17',
},
PolicyName: 'UserDefaultPolicy1F97781E',
Users: [
{
Ref: 'User00B015A1',
},
],
});
});
});
Loading