Skip to content

Commit

Permalink
bug fix
Browse files Browse the repository at this point in the history
  • Loading branch information
mats16 committed Oct 27, 2022
1 parent 17297f6 commit 82dd8ea
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 57 deletions.
57 changes: 17 additions & 40 deletions src/aws-workmail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ import * as cr from 'aws-cdk-lib/custom-resources';
import { Construct } from 'constructs';


interface StackProps extends cdk.NestedStackProps {
organization: OrganizationProps;
}

export class Stack extends cdk.NestedStack {
organization: Organization;

constructor(scope: Construct, id: string, props: OrganizationProps) {
super(scope, id, { description: 'Amazon WorkMail for Test Domain' });
constructor(scope: Construct, id: string, props: StackProps) {
super(scope, id, props);

this.organization = new Organization(this, 'Organization', props);
this.organization = new Organization(this, 'Organization', props.organization);
}
}

Expand Down Expand Up @@ -109,6 +113,7 @@ export class Organization extends Construct {
initialPolicy: [
new iam.PolicyStatement({
actions: [
'workmail:DescribeOrganization',
'workmail:CreateUser',
'workmail:DeleteUser',
'workmail:RegisterToWorkMail',
Expand All @@ -125,48 +130,20 @@ export class Organization extends Construct {
});
}

addUser(id: string) {
const user = new User(this, id, { organization: this });
return user;
}
}

interface UserProps {
organization: Organization;
}

export class User extends Construct {
secret: Secret;
addUser(id: string, secret: Secret) {
secret.grantRead(this.createUserProvider.onEventHandler);

constructor(scope: Construct, id: string, props: UserProps) {
super(scope, id);

const userName = id.toLowerCase();
const organization = props.organization;

this.secret = new Secret(this, 'Secret', {
description: `Supabase - WorkMail User Secret - ${userName}`,
generateSecretString: {
secretStringTemplate: JSON.stringify({
username: userName,
email: `${userName}@${organization.domain}`,
}),
generateStringKey: 'password',
passwordLength: 64,
},
});

this.secret.grantRead(organization.createUserProvider.onEventHandler);

new cdk.CfnResource(this, 'Resource', {
const user = new cdk.CfnResource(this, id, {
type: 'Custom::WorkMailUser',
properties: {
ServiceToken: organization.createUserProvider.serviceToken,
Region: organization.region,
OrganizationId: organization.organizationId,
SecretId: this.secret.secretArn,
ServiceToken: this.createUserProvider.serviceToken,
Region: this.region,
OrganizationId: this.organizationId,
SecretId: secret.secretArn,
DisplayName: id,
},
});

return user;
}
}
28 changes: 19 additions & 9 deletions src/functions/create-workmail-user.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';
import { WorkMailClient, CreateUserCommand, DeleteUserCommand, RegisterToWorkMailCommand, DeregisterFromWorkMailCommand } from '@aws-sdk/client-workmail';
import { WorkMailClient, CreateUserCommand, DeleteUserCommand, RegisterToWorkMailCommand, DeregisterFromWorkMailCommand, DescribeOrganizationCommand } from '@aws-sdk/client-workmail';
import { CdkCustomResourceHandler } from 'aws-lambda';

interface WorkMailUserSecret {
username: string;
password: string;
email: string;
}

const getSecretValue = async (secretId: string) => {
Expand All @@ -16,6 +15,14 @@ const getSecretValue = async (secretId: string) => {
return value;
};

const describeMailDomain = async (region: string, organizationId: string) => {
const client = new WorkMailClient({ region });
const cmd = new DescribeOrganizationCommand({ OrganizationId: organizationId });
const { DefaultMailDomain } = await client.send(cmd);
client.destroy();
return DefaultMailDomain!;
};

const registerToWorkMail = async (region: string, organizationId: string, entityId: string, email: string) => {
const client = new WorkMailClient({ region });
const cmd = new RegisterToWorkMailCommand({
Expand All @@ -37,14 +44,16 @@ const deregisterFromWorkMail = async (region: string, organizationId: string, en
client.destroy();
};

const createUser = async (region: string, organizationId: string, secretId: string) => {
const { username, password, email } = await getSecretValue(secretId);
const createUser = async (region: string, organizationId: string, secretId: string, displayName: string) => {
const mailDomain = await describeMailDomain(region, organizationId);
const email = `${displayName.toLowerCase()}@${mailDomain}`;
const { username, password } = await getSecretValue(secretId);
const client = new WorkMailClient({ region });
const cmd = new CreateUserCommand({
OrganizationId: organizationId,
Name: username,
DisplayName: username,
Password: password,
DisplayName: displayName,
});
const output = await client.send(cmd);
const userId = output.UserId!;
Expand Down Expand Up @@ -73,17 +82,18 @@ export const handler: CdkCustomResourceHandler = async (event, _context) => {
const region: string = event.ResourceProperties.Region;
const organizationId: string = event.ResourceProperties.OrganizationId;
const secretId: string = event.ResourceProperties.SecretId;
const displayName: string = event.ResourceProperties.DisplayName;

switch (event.RequestType) {
case 'Create': {
const user = await createUser(region, organizationId, secretId);
return { PhysicalResourceId: user.userId, Data: { UserId: user.userId, Email: user.email } };
const user = await createUser(region, organizationId, secretId, displayName);
return { PhysicalResourceId: user.email, Data: { UserId: user.userId, Email: user.email } };
}
case 'Update': {
const oldUserId = event.PhysicalResourceId;
await deleteUser(region, organizationId, oldUserId);
const user = await createUser(region, organizationId, secretId);
return { PhysicalResourceId: user.userId, Data: { UserId: user.userId, Email: user.email } };
const user = await createUser(region, organizationId, secretId, displayName);
return { PhysicalResourceId: user.email, Data: { UserId: user.userId, Email: user.email } };
}
case 'Delete': {
const userId = event.PhysicalResourceId;
Expand Down
2 changes: 1 addition & 1 deletion src/functions/gen-smtp-password.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const updateSecret = async (secretId: string, region: string) => {
...secret,
username: secret.access_key,
password: smtpPassword,
host: `email-smtp.${region}.amazonaws.com`,
region,
} as sesSecret),
});
await client.send(cmd);
Expand Down
21 changes: 14 additions & 7 deletions src/supabase-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,14 +308,21 @@ export class SupabaseStack extends cdk.Stack {
}],
});

const workMail = new WorkMailStack(this, 'WorkMail', { region: sesRegion.valueAsString, alias: `supabase-${cdk.Aws.ACCOUNT_ID}` });
workMail.organization.addUser('Noreply');
(workMail.node.defaultChild as cdk.CfnStack).addOverride('Condition', workMailEnabled.logicalId);
const mail = new SupabaseMail(this, 'SupabaseMail', { region: sesRegion.valueAsString });

const smtpAdminEmail = cdk.Fn.conditionIf(workMailEnabled.logicalId, `noreply@${workMail.organization.domain}`, senderEmail.valueAsString);
const smtpHost = `email-smtp.${sesRegion.valueAsString}.amazonaws.com`;
const workMail = new WorkMailStack(this, 'WorkMail', {
description: 'Amazon WorkMail for Test Domain',
organization: {
region: sesRegion.valueAsString,
alias: `supabase-${cdk.Aws.ACCOUNT_ID}`,
},
});
const workMailUser = workMail.organization.addUser('Noreply', mail.secret);
(workMail.node.defaultChild as cdk.CfnStack).addOverride('Condition', workMailEnabled.logicalId);

const mail = new SupabaseMail(this, 'SupabaseMail', { region: sesRegion.valueAsString });
const smtpAdminEmail = cdk.Fn.conditionIf(workMailEnabled.logicalId, workMailUser.ref, senderEmail.valueAsString);
const smtpHost = cdk.Fn.conditionIf(workMailEnabled.logicalId, `smtp.mail.${sesRegion.valueAsString}.awsapps.com`, `email-smtp.${sesRegion.valueAsString}.amazonaws.com`);
const smtpUser = cdk.Fn.conditionIf(workMailEnabled.logicalId, workMailUser.ref, mail.secret.secretValueFromJson('username').unsafeUnwrap());

const db = new SupabaseDatabase(this, 'Database', { vpc, multiAzEnabled: dbMultiAzEnabled, minCapacity: minAcu.valueAsNumber, maxCapacity: maxAcu.valueAsNumber });

Expand Down Expand Up @@ -396,6 +403,7 @@ export class SupabaseStack extends cdk.Stack {
GOTRUE_SMTP_ADMIN_EMAIL: smtpAdminEmail.toString(),
GOTRUE_SMTP_HOST: smtpHost.toString(),
GOTRUE_SMTP_PORT: '465',
GOTRUE_SMTP_USER: smtpUser.toString(),
GOTRUE_SMTP_SENDER_NAME: senderName.valueAsString,
GOTRUE_MAILER_AUTOCONFIRM: 'false',
GOTRUE_MAILER_URLPATHS_INVITE: '/auth/v1/verify',
Expand All @@ -408,7 +416,6 @@ export class SupabaseStack extends cdk.Stack {
secrets: {
GOTRUE_DB_DATABASE_URL: ecs.Secret.fromSsmParameter(db.url.writerSearchPathAuth),
GOTRUE_JWT_SECRET: ecs.Secret.fromSecretsManager(jwt.secret),
GOTRUE_SMTP_USER: ecs.Secret.fromSecretsManager(mail.secret, 'username'),
GOTRUE_SMTP_PASS: ecs.Secret.fromSecretsManager(mail.secret, 'password'),
},
healthCheck: {
Expand Down

0 comments on commit 82dd8ea

Please sign in to comment.