UNPKG

@common-creation/aws-cdk-ses-domain-identity

Version:

Constructs for provisioning and referencing domain identities which can be used in SES RuleSets and Actions Construct.

192 lines 7.94 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Verifier = void 0; const Route53 = require("aws-sdk/clients/route53"); const SES = require("aws-sdk/clients/ses"); const record_1 = require("./record"); const util_1 = require("./util"); const DEFAULT_WAIT = { delay: 30, maxAttempts: 30 }; // tslint:disable:no-console class Verifier { constructor(domainName, hostedZoneId, region) { this.domainName = domainName; this.hostedZoneId = hostedZoneId; this.region = region; this.route53 = new Route53({ region: this.region }); this.ses = new SES({ region: this.region }); } static from(props) { return new this(props.DomainName, props.HostedZoneId, props.Region); } async verifyIdentity(upsert = false) { console.log("Verifying Domain for %s", this.domainName); const identityToken = await this.requestIdentityToken(); console.log("Getting current identity record"); const currentIdentityRecord = await this.getCurrentIdentityRecord(); if (currentIdentityRecord) { console.log("Found existing TXT record, adding value to verify domain in zone %s", this.hostedZoneId); } else { console.log("Creating a TXT record for verifying domain into zone %s", this.hostedZoneId); } const shouldUpsert = currentIdentityRecord || upsert; const record = currentIdentityRecord !== null && currentIdentityRecord !== void 0 ? currentIdentityRecord : record_1.Record.forIdentity(this.domainName, []); record.add(identityToken); const changeId = await this.changeRecords([ record.action(shouldUpsert ? "UPSERT" : "CREATE"), ]); console.log("Waiting for DNS records to commit..."); await this.waitForRecordChange(changeId); console.log("Waiting for domain verification..."); await this.waitForIdentityVerified(); } async enableDKIM(upsert = false) { console.log("Enabling DKIM for %s", this.domainName); const tokens = await this.requestDKIMTokens(); console.log("Creating %d DNS records for verifying DKIM into zone %s", tokens.length, this.hostedZoneId); const changeId = await this.changeRecords(tokens.map((token) => record_1.Record.forDKIM(this.domainName, token).action(upsert ? "UPSERT" : "CREATE"))); console.log("Waiting for DNS records to commit..."); await this.waitForRecordChange(changeId); console.log("Waiting for DKIM verification..."); await this.waitForDKIMVerified(); } async revokeIdentity() { console.log("Getting current verification state for domain %s", this.domainName); const identity = await this.describeIdentity(); console.log("Revoking verification for domain %s", this.domainName); await this.ses .deleteIdentity({ Identity: this.domainName, }) .promise(); const identityRecord = await this.getCurrentIdentityRecord(); if (!identityRecord) { console.log("Identity Record does not exist (Maybe drifted?). skipping identity record unprovisioning..."); return; } identityRecord.remove(identity.token); // If the record contained only the validation value, delete the record. // Otherwise just update the record with the value removed. if (identityRecord.size === 0) { // Add delete value to record identityRecord.add(identity.token); console.log("Deleting DNS Records used for domain verification..."); await this.changeRecords([identityRecord.action("DELETE")]); } else { console.log("Updating DNS Records to remove the value used for domain verification..."); await this.changeRecords([identityRecord.action("UPSERT")]); } } async disableDKIM() { console.log("Getting current DKIM state for domain %s", this.domainName); const dkim = await this.describeDKIM(); console.log("Disabling DKIM for domain %s", this.domainName); await this.ses .setIdentityDkimEnabled({ Identity: this.domainName, DkimEnabled: false, }) .promise(); console.log("Deleting DNS Records used for DKIM verification..."); await this.changeRecords(dkim.tokens.map((token) => record_1.Record.forDKIM(this.domainName, token).action("DELETE"))); } async changeRecords(changes, wait = DEFAULT_WAIT) { const change = await this.route53 .changeResourceRecordSets({ HostedZoneId: this.hostedZoneId, ChangeBatch: { Changes: changes, }, }) .promise(); return change.ChangeInfo.Id; } async requestIdentityToken() { const res = await this.ses .verifyDomainIdentity({ Domain: this.domainName, }) .promise(); return res.VerificationToken; } async requestDKIMTokens() { const res = await this.ses .verifyDomainDkim({ Domain: this.domainName, }) .promise(); return res.DkimTokens; } async describeIdentity() { const res = await this.ses .getIdentityVerificationAttributes({ Identities: [this.domainName], }) .promise(); const attr = res.VerificationAttributes[this.domainName]; if (!(attr === null || attr === void 0 ? void 0 : attr.VerificationToken)) { throw new Error("There are no identity for given domain"); } return { status: attr.VerificationStatus, token: attr.VerificationToken, }; } async describeDKIM() { const res = await this.ses .getIdentityDkimAttributes({ Identities: [this.domainName], }) .promise(); const attr = res.DkimAttributes[this.domainName]; if (!(attr === null || attr === void 0 ? void 0 : attr.DkimEnabled)) { throw new Error("DKIM is not configured for given domain"); } return { status: attr.DkimVerificationStatus, tokens: attr.DkimTokens || [], }; } async getCurrentIdentityRecord() { const identity = record_1.Record.forIdentity(this.domainName, []); const res = await this.route53 .listResourceRecordSets({ HostedZoneId: this.hostedZoneId, StartRecordType: identity.type, StartRecordName: identity.name, }) .promise(); const first = res.ResourceRecordSets[0]; return (first === null || first === void 0 ? void 0 : first.Name) === identity.name && (first === null || first === void 0 ? void 0 : first.Type) === identity.type ? record_1.Record.fromResourceRecordSet(first) : null; } async waitForRecordChange(changeId, wait = DEFAULT_WAIT) { await this.route53 .waitFor("resourceRecordSetsChanged", { Id: changeId, // Wait up to 5 minutes $waiter: wait, }) .promise(); } async waitForIdentityVerified(wait = DEFAULT_WAIT) { await this.ses .waitFor("identityExists", { Identities: [this.domainName], // Wait up to 5 minutes $waiter: wait, }) .promise(); } async waitForDKIMVerified(wait = DEFAULT_WAIT) { await (0, util_1.waitFor)(() => this.describeDKIM(), (state) => state.status === "Success", { ...wait, failureMessage: "Failed to verify DKIM status", }); } } exports.Verifier = Verifier; // tslint:enable:no-console //# sourceMappingURL=verifier.js.map