Send expiring notification emails to admins if no tech emails are on file (#1387)

* Send emails to admin if tech emails are not present

* Improve test cases and comments
This commit is contained in:
Rachel Guan 2021-10-21 12:59:31 -04:00 committed by GitHub
parent 1a4a31569e
commit 43074ea32f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 182 additions and 121 deletions

View file

@ -113,9 +113,11 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
} }
/** /**
* Returns a list of registrars that should receive expiring notification emails. There are two * Returns a list of registrars that should receive expiring notification emails.
* certificates that should be considered (the main certificate and failOver certificate). The *
* registrars should receive notifications if one of the certificate checks returns true. * <p>There are two certificates that should be considered (the main certificate and failOver
* certificate). The registrars should receive notifications if one of the certificate checks
* returns true.
*/ */
@VisibleForTesting @VisibleForTesting
ImmutableList<RegistrarInfo> getRegistrarsWithExpiringCertificates() { ImmutableList<RegistrarInfo> getRegistrarsWithExpiringCertificates() {
@ -157,15 +159,17 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
} }
try { try {
ImmutableSet<InternetAddress> recipients = getEmailAddresses(registrar, Type.TECH); ImmutableSet<InternetAddress> recipients = getEmailAddresses(registrar, Type.TECH);
ImmutableSet<InternetAddress> ccs = getEmailAddresses(registrar, Type.ADMIN);
Date expirationDate = certificateChecker.getCertificate(certificate.get()).getNotAfter(); Date expirationDate = certificateChecker.getCertificate(certificate.get()).getNotAfter();
logger.atInfo().log( logger.atInfo().log(
"Registrar %s should receive an email that its %s SSL certificate will expire on %s.", " %s SSL certificate of registrar '%s' will expire on %s.",
registrar.getRegistrarName(),
certificateType.getDisplayName(), certificateType.getDisplayName(),
registrar.getRegistrarName(),
expirationDate.toString()); expirationDate.toString());
if (recipients.isEmpty()) { if (recipients.isEmpty() && ccs.isEmpty()) {
logger.atWarning().log( logger.atWarning().log(
"Registrar %s contains no email addresses to receive notification email.", "Registrar %s contains no TECH nor ADMIN email addresses to receive notification"
+ " email.",
registrar.getRegistrarName()); registrar.getRegistrarName());
return false; return false;
} }
@ -180,7 +184,7 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
expirationDate, expirationDate,
registrar.getRegistrarId())) registrar.getRegistrarId()))
.setRecipients(recipients) .setRecipients(recipients)
.setCcs(getEmailAddresses(registrar, Type.ADMIN)) .setCcs(ccs)
.build()); .build());
/* /*
* A duration time offset is used here to ensure that date comparison between two * A duration time offset is used here to ensure that date comparison between two
@ -249,30 +253,32 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
/** Sends notification emails to registrars with expiring certificates. */ /** Sends notification emails to registrars with expiring certificates. */
@VisibleForTesting @VisibleForTesting
int sendNotificationEmails() { int sendNotificationEmails() {
int emailsSent = 0; int numEmailsSent = 0;
for (RegistrarInfo registrarInfo : getRegistrarsWithExpiringCertificates()) { for (RegistrarInfo registrarInfo : getRegistrarsWithExpiringCertificates()) {
Registrar registrar = registrarInfo.registrar(); Registrar registrar = registrarInfo.registrar();
if (registrarInfo.isCertExpiring()) { if (registrarInfo.isCertExpiring()
sendNotificationEmail( && sendNotificationEmail(
registrar, registrar,
registrar.getLastExpiringCertNotificationSentDate(), registrar.getLastExpiringCertNotificationSentDate(),
CertificateType.PRIMARY, CertificateType.PRIMARY,
registrar.getClientCertificate()); registrar.getClientCertificate())) {
emailsSent++; numEmailsSent++;
} }
if (registrarInfo.isFailOverCertExpiring()) { if (registrarInfo.isFailOverCertExpiring()
sendNotificationEmail( && sendNotificationEmail(
registrar, registrar,
registrar.getLastExpiringFailoverCertNotificationSentDate(), registrar.getLastExpiringFailoverCertNotificationSentDate(),
CertificateType.FAILOVER, CertificateType.FAILOVER,
registrar.getFailoverClientCertificate()); registrar.getFailoverClientCertificate())) {
emailsSent++; numEmailsSent++;
} }
} }
return emailsSent; return numEmailsSent;
} }
/** Returns a list of email addresses of the registrar that should receive a notification email */ /**
* Returns a list of email addresses of the registrar that should receive a notification email.
*/
@VisibleForTesting @VisibleForTesting
ImmutableSet<InternetAddress> getEmailAddresses(Registrar registrar, Type contactType) { ImmutableSet<InternetAddress> getEmailAddresses(Registrar registrar, Type contactType) {
ImmutableSortedSet<RegistrarContact> contacts = registrar.getContactsOfType(contactType); ImmutableSortedSet<RegistrarContact> contacts = registrar.getContactsOfType(contactType);

View file

@ -142,7 +142,7 @@ class SendExpiringCertificateNotificationEmailActionTest {
} }
@TestOfyAndSql @TestOfyAndSql
void sendNotificationEmail_returnsTrue() throws Exception { void sendNotificationEmail_techEMailAsRecipient_returnsTrue() throws Exception {
X509Certificate expiringCertificate = X509Certificate expiringCertificate =
SelfSignedCaCertificate.create( SelfSignedCaCertificate.create(
"www.example.tld", "www.example.tld",
@ -157,25 +157,64 @@ class SendExpiringCertificateNotificationEmailActionTest {
.asBuilder() .asBuilder()
.setFailoverClientCertificate(cert.get(), clock.nowUtc()) .setFailoverClientCertificate(cert.get(), clock.nowUtc())
.build()); .build());
ImmutableList<RegistrarContact> contacts = persistSampleContacts(registrar, Type.TECH);
ImmutableList.of(
new RegistrarContact.Builder()
.setParent(registrar)
.setName("Will Doe")
.setEmailAddress("will@example-registrar.tld")
.setPhoneNumber("+1.3105551213")
.setFaxNumber("+1.3105551213")
.setTypes(ImmutableSet.of(RegistrarContact.Type.TECH))
.setVisibleInWhoisAsAdmin(true)
.setVisibleInWhoisAsTech(false)
.build());
persistSimpleResources(contacts);
persistResource(registrar);
assertThat( assertThat(
action.sendNotificationEmail(registrar, START_OF_TIME, CertificateType.FAILOVER, cert)) action.sendNotificationEmail(registrar, START_OF_TIME, CertificateType.FAILOVER, cert))
.isEqualTo(true); .isEqualTo(true);
} }
@TestOfyAndSql
void sendNotificationEmail_adminEMailAsRecipient_returnsTrue() throws Exception {
X509Certificate expiringCertificate =
SelfSignedCaCertificate.create(
"www.example.tld",
DateTime.parse("2020-09-02T00:00:00Z"),
DateTime.parse("2021-06-01T00:00:00Z"))
.cert();
Optional<String> cert =
Optional.of(certificateChecker.serializeCertificate(expiringCertificate));
Registrar registrar =
persistResource(
makeRegistrar1()
.asBuilder()
.setFailoverClientCertificate(cert.get(), clock.nowUtc())
.build());
persistSampleContacts(registrar, Type.ADMIN);
assertThat(
action.sendNotificationEmail(registrar, START_OF_TIME, CertificateType.FAILOVER, cert))
.isEqualTo(true);
}
@TestOfyAndSql
void sendNotificationEmail_returnsFalse_unsupportedEmailType() throws Exception {
Registrar registrar =
persistResource(
createRegistrar(
"testId",
"testName",
SelfSignedCaCertificate.create(
"www.example.tld",
DateTime.parse("2020-09-02T00:00:00Z"),
DateTime.parse("2021-06-01T00:00:00Z"))
.cert(),
null)
.build());
persistSampleContacts(registrar, Type.LEGAL);
assertThat(
action.sendNotificationEmail(
registrar,
START_OF_TIME,
CertificateType.FAILOVER,
Optional.of(
certificateChecker.serializeCertificate(
SelfSignedCaCertificate.create(
"www.example.tld",
DateTime.parse("2020-09-02T00:00:00Z"),
DateTime.parse("2021-06-01T00:00:00Z"))
.cert()))))
.isEqualTo(false);
}
@TestOfyAndSql @TestOfyAndSql
void sendNotificationEmail_returnsFalse_noEmailRecipients() throws Exception { void sendNotificationEmail_returnsFalse_noEmailRecipients() throws Exception {
X509Certificate expiringCertificate = X509Certificate expiringCertificate =
@ -247,93 +286,82 @@ class SendExpiringCertificateNotificationEmailActionTest {
} }
@TestOfyAndSql @TestOfyAndSql
void sendNotificationEmails_allEmailsBeingAttemptedToSend() throws Exception { void sendNotificationEmails_allEmailsBeingSent_onlyMainCertificates() throws Exception {
X509Certificate expiringCertificate = for (int i = 1; i <= 10; i++) {
SelfSignedCaCertificate.create( Registrar registrar =
"www.example.tld", persistResource(
DateTime.parse("2020-09-02T00:00:00Z"), createRegistrar(
DateTime.parse("2021-06-01T00:00:00Z")) "oldcert" + i,
.cert(); "name" + i,
X509Certificate certificate = SelfSignedCaCertificate.create(
SelfSignedCaCertificate.create( "www.example.tld",
"www.example.tld", DateTime.parse("2020-09-02T00:00:00Z"),
DateTime.parse("2020-09-02T00:00:00Z"), DateTime.parse("2021-06-01T00:00:00Z"))
DateTime.parse("2021-10-01T00:00:00Z")) .cert(),
.cert(); null)
int numOfRegistrars = 10; .build());
int numOfRegistrarsWithExpiringCertificates = 2; persistSampleContacts(registrar, Type.TECH);
for (int i = 1; i <= numOfRegistrarsWithExpiringCertificates; i++) {
persistResource(
createRegistrar("oldcert" + i, "name" + i, expiringCertificate, null).build());
} }
for (int i = numOfRegistrarsWithExpiringCertificates; i <= numOfRegistrars; i++) { assertThat(action.sendNotificationEmails()).isEqualTo(10);
persistResource(createRegistrar("goodcert" + i, "name" + i, certificate, null).build());
}
assertThat(action.sendNotificationEmails()).isEqualTo(numOfRegistrarsWithExpiringCertificates);
} }
@TestOfyAndSql @TestOfyAndSql
void sendNotificationEmails_allEmailsBeingAttemptedToSend_onlyMainCertificates() void sendNotificationEmails_allEmailsBeingSent_onlyFailOverCertificates() throws Exception {
throws Exception { for (int i = 1; i <= 10; i++) {
X509Certificate expiringCertificate = Registrar registrar =
SelfSignedCaCertificate.create( persistResource(
"www.example.tld", createRegistrar(
DateTime.parse("2020-09-02T00:00:00Z"), "oldcert" + i,
DateTime.parse("2021-06-01T00:00:00Z")) "name" + i,
.cert(); null,
int numOfRegistrars = 10; SelfSignedCaCertificate.create(
for (int i = 1; i <= numOfRegistrars; i++) { "www.example.tld",
persistResource( DateTime.parse("2020-09-02T00:00:00Z"),
createRegistrar("oldcert" + i, "name" + i, expiringCertificate, null).build()); DateTime.parse("2021-06-01T00:00:00Z"))
.cert())
.build());
persistSampleContacts(registrar, Type.TECH);
} }
assertThat(action.sendNotificationEmails()).isEqualTo(numOfRegistrars); assertThat(action.sendNotificationEmails()).isEqualTo(10);
} }
@TestOfyAndSql @TestOfyAndSql
void sendNotificationEmails_allEmailsBeingAttemptedToSend_onlyFailOverCertificates() void sendNotificationEmails_allEmailsBeingSent_mixedOfCertificates() throws Exception {
throws Exception {
X509Certificate expiringCertificate = X509Certificate expiringCertificate =
SelfSignedCaCertificate.create( SelfSignedCaCertificate.create(
"www.example.tld", "www.example.tld",
DateTime.parse("2020-09-02T00:00:00Z"), DateTime.parse("2020-09-02T00:00:00Z"),
DateTime.parse("2021-06-01T00:00:00Z")) DateTime.parse("2021-06-01T00:00:00Z"))
.cert(); .cert();
int numOfRegistrars = 10;
for (int i = 1; i <= numOfRegistrars; i++) {
persistResource(
createRegistrar("oldcert" + i, "name" + i, null, expiringCertificate).build());
}
assertThat(action.sendNotificationEmails()).isEqualTo(numOfRegistrars);
}
@TestOfyAndSql for (int i = 1; i <= 3; i++) {
void sendNotificationEmails_allEmailsBeingAttemptedToSend_mixedOfCertificates() throws Exception { Registrar registrar =
X509Certificate expiringCertificate = persistResource(
SelfSignedCaCertificate.create( createRegistrar(
"www.example.tld", "cl" + i, "regWIthexpiringFailOverOnly" + i, null, expiringCertificate)
DateTime.parse("2020-09-02T00:00:00Z"), .build());
DateTime.parse("2021-06-01T00:00:00Z")) persistSampleContacts(registrar, Type.TECH);
.cert();
int numOfRegistrars = 10;
int numOfExpiringFailOverOnly = 2;
int numOfExpiringPrimaryOnly = 3;
for (int i = 1; i <= numOfExpiringFailOverOnly; i++) {
persistResource(
createRegistrar("cl" + i, "expiringFailOverOnly" + i, null, expiringCertificate).build());
} }
for (int i = 1; i <= numOfExpiringPrimaryOnly; i++) { for (int i = 1; i <= 5; i++) {
persistResource( Registrar registrar =
createRegistrar("cli" + i, "expiringPrimaryOnly" + i, expiringCertificate, null).build()); persistResource(
createRegistrar(
"cli" + i, "regWithexpiringPrimaryOnly" + i, expiringCertificate, null)
.build());
persistSampleContacts(registrar, Type.TECH);
} }
for (int i = numOfExpiringFailOverOnly + numOfExpiringPrimaryOnly + 1; for (int i = 1; i <= 4; i++) {
i <= numOfRegistrars; Registrar registrar =
i++) { persistResource(
persistResource( createRegistrar(
createRegistrar("client" + i, "regularReg" + i, expiringCertificate, expiringCertificate) "client" + i,
.build()); "regWithTwoExpiring" + i,
expiringCertificate,
expiringCertificate)
.build());
persistSampleContacts(registrar, Type.ADMIN);
} }
assertThat(action.sendNotificationEmails()) assertThat(action.sendNotificationEmails()).isEqualTo(16);
.isEqualTo(numOfRegistrars + numOfExpiringFailOverOnly + numOfExpiringPrimaryOnly);
} }
@TestOfyAndSql @TestOfyAndSql
@ -649,17 +677,19 @@ class SendExpiringCertificateNotificationEmailActionTest {
@TestOfyAndSql @TestOfyAndSql
void run_sentEmails_responseStatusIs200() throws Exception { void run_sentEmails_responseStatusIs200() throws Exception {
for (int i = 1; i <= 5; i++) { for (int i = 1; i <= 5; i++) {
persistResource( Registrar registrar =
createRegistrar( persistResource(
"id_" + i, createRegistrar(
"name" + i, "id_" + i,
SelfSignedCaCertificate.create( "name" + i,
"www.example.tld", SelfSignedCaCertificate.create(
DateTime.parse("2020-09-02T00:00:00Z"), "www.example.tld",
DateTime.parse("2021-06-01T00:00:00Z")) DateTime.parse("2020-09-02T00:00:00Z"),
.cert(), DateTime.parse("2021-06-01T00:00:00Z"))
null) .cert(),
.build()); null)
.build());
persistSampleContacts(registrar, Type.TECH);
} }
action.run(); action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK); assertThat(response.getStatus()).isEqualTo(SC_OK);
@ -667,7 +697,9 @@ class SendExpiringCertificateNotificationEmailActionTest {
.isEqualTo("Done. Sent 5 expiring certificate notification emails in total."); .isEqualTo("Done. Sent 5 expiring certificate notification emails in total.");
} }
/** Returns a sample registrar with a customized registrar name, client id and certificate* */ /**
* Returns a sample registrar builder with a customized registrar name, client id and certificate.
*/
private Registrar.Builder createRegistrar( private Registrar.Builder createRegistrar(
String registrarId, String registrarId,
String registrarName, String registrarName,
@ -706,4 +738,27 @@ class SendExpiringCertificateNotificationEmailActionTest {
} }
return builder; return builder;
} }
/** Returns persisted sample contacts with a customized contact email type. */
private ImmutableList<RegistrarContact> persistSampleContacts(
Registrar registrar, RegistrarContact.Type emailType) {
return persistSimpleResources(
ImmutableList.of(
new RegistrarContact.Builder()
.setParent(registrar)
.setName("Will Doe")
.setEmailAddress("will@example-registrar.tld")
.setPhoneNumber("+1.0105551213")
.setFaxNumber("+1.0105551213")
.setTypes(ImmutableSet.of(emailType))
.build(),
new RegistrarContact.Builder()
.setParent(registrar)
.setName("Will Smith")
.setEmailAddress("will@test-registrar.tld")
.setPhoneNumber("+1.3105551213")
.setFaxNumber("+1.3105551213")
.setTypes(ImmutableSet.of(emailType))
.build()));
}
} }