@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
JavaScript
"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