mirror of
https://github.com/google/nomulus.git
synced 2025-07-20 09:46:03 +02:00
Add sending notification email mechanism for expiring certificates (#1179)
* Resolve rebase conflict * Fix and imporove based on feedback.
This commit is contained in:
parent
03bb360a94
commit
2cea084e4d
6 changed files with 1001 additions and 49 deletions
|
@ -0,0 +1,326 @@
|
|||
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.batch;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR;
|
||||
import static org.apache.http.HttpStatus.SC_OK;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.flows.certs.CertificateChecker;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarContact;
|
||||
import google.registry.model.registrar.RegistrarContact.Type;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.EmailMessage;
|
||||
import google.registry.util.SendEmailService;
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import javax.mail.internet.AddressException;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
|
||||
/** An action that sends notification emails to registrars whose certificates are expiring soon. */
|
||||
@Action(
|
||||
service = Action.Service.BACKEND,
|
||||
path = SendExpiringCertificateNotificationEmailAction.PATH,
|
||||
auth = Auth.AUTH_INTERNAL_OR_ADMIN)
|
||||
public class SendExpiringCertificateNotificationEmailAction implements Runnable {
|
||||
public static final String PATH = "/_dr/task/sendExpiringCertificateNotificationEmail";
|
||||
/**
|
||||
* Used as an offset when storing the last notification email sent date.
|
||||
*
|
||||
* <p>This is used to handle edges cases when the update happens in between the day switch. For
|
||||
* instance,if the job starts at 2:00 am every day and it finishes at 2:03 of the same day, then
|
||||
* next day at 2am, the date difference will be less than a day, which will lead to the date
|
||||
* difference between two successive email sent date being the expected email interval days + 1;
|
||||
*/
|
||||
protected static final Duration UPDATE_TIME_OFFSET = Duration.standardMinutes(10);
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormat.forPattern("yyyy-MM-dd");
|
||||
|
||||
private final CertificateChecker certificateChecker;
|
||||
private final String expirationWarningEmailBodyText;
|
||||
private final SendEmailService sendEmailService;
|
||||
private final String expirationWarningEmailSubjectText;
|
||||
private final InternetAddress gSuiteOutgoingEmailAddress;
|
||||
private final Response response;
|
||||
|
||||
@Inject
|
||||
public SendExpiringCertificateNotificationEmailAction(
|
||||
@Config("expirationWarningEmailBodyText") String expirationWarningEmailBodyText,
|
||||
@Config("expirationWarningEmailSubjectText") String expirationWarningEmailSubjectText,
|
||||
@Config("gSuiteOutgoingEmailAddress") InternetAddress gSuiteOutgoingEmailAddress,
|
||||
SendEmailService sendEmailService,
|
||||
CertificateChecker certificateChecker,
|
||||
Response response) {
|
||||
this.certificateChecker = certificateChecker;
|
||||
this.expirationWarningEmailSubjectText = expirationWarningEmailSubjectText;
|
||||
this.sendEmailService = sendEmailService;
|
||||
this.gSuiteOutgoingEmailAddress = gSuiteOutgoingEmailAddress;
|
||||
this.expirationWarningEmailBodyText = expirationWarningEmailBodyText;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
|
||||
try {
|
||||
sendNotificationEmails();
|
||||
response.setStatus(SC_OK);
|
||||
} catch (Exception e) {
|
||||
logger.atWarning().withCause(e).log(
|
||||
"Exception thrown when sending expiring certificate notification emails.");
|
||||
response.setStatus(SC_INTERNAL_SERVER_ERROR);
|
||||
response.setPayload(String.format("Exception thrown with cause: %s", e));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of registrars that should receive expiring notification emails. 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
|
||||
ImmutableList<RegistrarInfo> getRegistrarsWithExpiringCertificates() {
|
||||
return Streams.stream(Registrar.loadAllCached())
|
||||
.map(
|
||||
registrar ->
|
||||
RegistrarInfo.create(
|
||||
registrar,
|
||||
registrar.getClientCertificate().isPresent()
|
||||
&& certificateChecker.shouldReceiveExpiringNotification(
|
||||
registrar.getLastExpiringCertNotificationSentDate(),
|
||||
registrar.getClientCertificate().get()),
|
||||
registrar.getFailoverClientCertificate().isPresent()
|
||||
&& certificateChecker.shouldReceiveExpiringNotification(
|
||||
registrar.getLastExpiringFailoverCertNotificationSentDate(),
|
||||
registrar.getFailoverClientCertificate().get())))
|
||||
.filter(
|
||||
registrarInfo ->
|
||||
registrarInfo.isCertExpiring() || registrarInfo.isFailOverCertExpiring())
|
||||
.collect(toImmutableList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a notification email to the registrar regarding the expiring certificate and returns true
|
||||
* if it's sent successfully.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean sendNotificationEmail(
|
||||
Registrar registrar,
|
||||
DateTime lastExpiringCertNotificationSentDate,
|
||||
CertificateType certificateType,
|
||||
Optional<String> certificate) {
|
||||
if (!certificate.isPresent()
|
||||
|| !certificateChecker.shouldReceiveExpiringNotification(
|
||||
lastExpiringCertNotificationSentDate, certificate.get())) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
ImmutableSet<InternetAddress> recipients = getEmailAddresses(registrar, Type.TECH);
|
||||
if (recipients.isEmpty()) {
|
||||
logger.atWarning().log(
|
||||
"Registrar %s contains no email addresses to receive notification email.",
|
||||
registrar.getRegistrarName());
|
||||
return false;
|
||||
}
|
||||
sendEmailService.sendEmail(
|
||||
EmailMessage.newBuilder()
|
||||
.setFrom(gSuiteOutgoingEmailAddress)
|
||||
.setSubject(expirationWarningEmailSubjectText)
|
||||
.setBody(
|
||||
getEmailBody(
|
||||
registrar.getRegistrarName(),
|
||||
certificateType,
|
||||
certificateChecker.getCertificate(certificate.get()).getNotAfter()))
|
||||
.setRecipients(recipients)
|
||||
.setCcs(getEmailAddresses(registrar, Type.ADMIN))
|
||||
.build());
|
||||
/*
|
||||
* A duration time offset is used here to ensure that date comparison between two
|
||||
* successive dates is always greater than 1 day. This date is set as last updated date,
|
||||
* for applicable certificate.
|
||||
*/
|
||||
updateLastNotificationSentDate(
|
||||
registrar,
|
||||
DateTime.now(UTC).minusMinutes((int) UPDATE_TIME_OFFSET.getStandardMinutes()),
|
||||
certificateType);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(
|
||||
String.format(
|
||||
"Failed to send expiring certificate notification email to registrar %s.",
|
||||
registrar.getRegistrarName()));
|
||||
}
|
||||
}
|
||||
|
||||
/** Updates the last notification sent date in database. */
|
||||
@VisibleForTesting
|
||||
void updateLastNotificationSentDate(
|
||||
Registrar registrar, DateTime now, CertificateType certificateType) {
|
||||
try {
|
||||
tm().transact(
|
||||
() -> {
|
||||
Registrar.Builder newRegistrar = tm().loadByEntity(registrar).asBuilder();
|
||||
switch (certificateType) {
|
||||
case PRIMARY:
|
||||
newRegistrar.setLastExpiringCertNotificationSentDate(now);
|
||||
tm().put(newRegistrar.build());
|
||||
logger.atInfo().log(
|
||||
"Updated last notification email sent date for %s certificate of "
|
||||
+ "registrar %s.",
|
||||
certificateType.getDisplayName(), registrar.getRegistrarName());
|
||||
break;
|
||||
case FAILOVER:
|
||||
newRegistrar.setLastExpiringFailoverCertNotificationSentDate(now);
|
||||
tm().put(newRegistrar.build());
|
||||
logger.atInfo().log(
|
||||
"Updated last notification email sent date for %s certificate of "
|
||||
+ "registrar %s.",
|
||||
certificateType.getDisplayName(), registrar.getRegistrarName());
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"Unsupported certificate type: %s being passed in when updating "
|
||||
+ "the last notification sent date to registrar %s.",
|
||||
certificateType.toString(), registrar.getRegistrarName()));
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(
|
||||
String.format(
|
||||
"Failed to update the last notification sent date to Registrar %s for the %s "
|
||||
+ "certificate.",
|
||||
registrar.getRegistrarName(), certificateType.getDisplayName()));
|
||||
}
|
||||
}
|
||||
|
||||
/** Sends notification emails to registrars with expiring certificates. */
|
||||
@VisibleForTesting
|
||||
int sendNotificationEmails() {
|
||||
int emailsSent = 0;
|
||||
for (RegistrarInfo registrarInfo : getRegistrarsWithExpiringCertificates()) {
|
||||
Registrar registrar = registrarInfo.registrar();
|
||||
if (registrarInfo.isCertExpiring()) {
|
||||
sendNotificationEmail(
|
||||
registrar,
|
||||
registrar.getLastExpiringCertNotificationSentDate(),
|
||||
CertificateType.PRIMARY,
|
||||
registrar.getClientCertificate());
|
||||
emailsSent++;
|
||||
}
|
||||
if (registrarInfo.isFailOverCertExpiring()) {
|
||||
sendNotificationEmail(
|
||||
registrar,
|
||||
registrar.getLastExpiringFailoverCertNotificationSentDate(),
|
||||
CertificateType.FAILOVER,
|
||||
registrar.getFailoverClientCertificate());
|
||||
emailsSent++;
|
||||
}
|
||||
}
|
||||
logger.atInfo().log(
|
||||
"Sent %d expiring certificate notification emails to registrars.", emailsSent);
|
||||
return emailsSent;
|
||||
}
|
||||
|
||||
/** Returns a list of email addresses of the registrar that should receive a notification email */
|
||||
@VisibleForTesting
|
||||
ImmutableSet<InternetAddress> getEmailAddresses(Registrar registrar, Type contactType) {
|
||||
ImmutableSortedSet<RegistrarContact> contacts = registrar.getContactsOfType(contactType);
|
||||
ImmutableSet.Builder<InternetAddress> recipientEmails = new ImmutableSet.Builder<>();
|
||||
for (RegistrarContact contact : contacts) {
|
||||
try {
|
||||
recipientEmails.add(new InternetAddress(contact.getEmailAddress()));
|
||||
} catch (AddressException e) {
|
||||
logger.atWarning().withCause(e).log(
|
||||
"Registrar Contact email address %s of Registrar %s is invalid; skipping.",
|
||||
contact.getEmailAddress(), registrar.getRegistrarName());
|
||||
}
|
||||
}
|
||||
return recipientEmails.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates email content by taking registrar name, certificate type and expiration date as
|
||||
* parameters.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
@SuppressWarnings("lgtm[java/dereferenced-value-may-be-null]")
|
||||
String getEmailBody(String registrarName, CertificateType type, Date expirationDate) {
|
||||
checkArgumentNotNull(expirationDate, "Expiration date cannot be null");
|
||||
checkArgumentNotNull(type, "Certificate type cannot be null");
|
||||
return String.format(
|
||||
expirationWarningEmailBodyText,
|
||||
registrarName,
|
||||
type.getDisplayName(),
|
||||
DATE_FORMATTER.print(new DateTime(expirationDate)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Certificate types for X509Certificate.
|
||||
*
|
||||
* <p><b>Note:</b> These types are only used to indicate the type of expiring certificate in
|
||||
* notification emails.
|
||||
*/
|
||||
protected enum CertificateType {
|
||||
PRIMARY("primary"),
|
||||
FAILOVER("fail-over");
|
||||
|
||||
private final String displayName;
|
||||
|
||||
CertificateType(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
public abstract static class RegistrarInfo {
|
||||
static RegistrarInfo create(
|
||||
Registrar registrar, boolean isCertExpiring, boolean isFailOverCertExpiring) {
|
||||
return new AutoValue_SendExpiringCertificateNotificationEmailAction_RegistrarInfo(
|
||||
registrar, isCertExpiring, isFailOverCertExpiring);
|
||||
}
|
||||
|
||||
public abstract Registrar registrar();
|
||||
|
||||
public abstract boolean isCertExpiring();
|
||||
|
||||
public abstract boolean isFailOverCertExpiring();
|
||||
}
|
||||
}
|
|
@ -355,6 +355,12 @@
|
|||
<url-pattern>/_dr/task/deleteExpiredDomains</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Background action to send notification emails to registrars with expiring certificate. -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>backend-servlet</servlet-name>
|
||||
<url-pattern>/_dr/task/sendExpiringCertificateNotificationEmail</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Mapreduce to import contacts from escrow file -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>backend-servlet</servlet-name>
|
||||
|
|
|
@ -168,6 +168,15 @@
|
|||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/sendExpiringCertificateNotificationEmail]]></url>
|
||||
<description>
|
||||
This job runs an action that sends emails to partners if their certificates are expiring soon.
|
||||
</description>
|
||||
<schedule>every day 04:30</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=export-snapshot&endpoint=/_dr/task/backupDatastore&runInEmpty]]></url>
|
||||
<description>
|
||||
|
|
|
@ -31,6 +31,7 @@ import google.registry.batch.RefreshDnsOnHostRenameAction;
|
|||
import google.registry.batch.RelockDomainAction;
|
||||
import google.registry.batch.ResaveAllEppResourcesAction;
|
||||
import google.registry.batch.ResaveEntityAction;
|
||||
import google.registry.batch.SendExpiringCertificateNotificationEmailAction;
|
||||
import google.registry.batch.WipeOutCloudSqlAction;
|
||||
import google.registry.batch.WipeoutDatastoreAction;
|
||||
import google.registry.cron.CommitLogFanoutAction;
|
||||
|
@ -193,6 +194,8 @@ interface BackendRequestComponent {
|
|||
|
||||
ResaveEntityAction resaveEntityAction();
|
||||
|
||||
SendExpiringCertificateNotificationEmailAction sendExpiringCertificateNotificationEmailAction();
|
||||
|
||||
SyncGroupMembersAction syncGroupMembersAction();
|
||||
|
||||
SyncRegistrarsSheetAction syncRegistrarsSheetAction();
|
||||
|
|
|
@ -0,0 +1,607 @@
|
|||
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.batch;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.AppEngineExtension.makeRegistrar1;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.DatabaseHelper.persistSimpleResources;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.batch.SendExpiringCertificateNotificationEmailAction.CertificateType;
|
||||
import google.registry.batch.SendExpiringCertificateNotificationEmailAction.RegistrarInfo;
|
||||
import google.registry.flows.certs.CertificateChecker;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarAddress;
|
||||
import google.registry.model.registrar.RegistrarContact;
|
||||
import google.registry.model.registrar.RegistrarContact.Type;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.testing.AppEngineExtension;
|
||||
import google.registry.testing.DualDatabaseTest;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.InjectExtension;
|
||||
import google.registry.testing.TestOfyAndSql;
|
||||
import google.registry.util.SelfSignedCaCertificate;
|
||||
import google.registry.util.SendEmailService;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link SendExpiringCertificateNotificationEmailAction}. */
|
||||
@DualDatabaseTest
|
||||
class SendExpiringCertificateNotificationEmailActionTest {
|
||||
|
||||
@RegisterExtension
|
||||
public final AppEngineExtension appEngine =
|
||||
AppEngineExtension.builder().withDatastoreAndCloudSql().withTaskQueue().build();
|
||||
|
||||
@RegisterExtension public final InjectExtension inject = new InjectExtension();
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2021-05-24T20:21:22Z"));
|
||||
private final SendEmailService sendEmailService = mock(SendEmailService.class);
|
||||
private CertificateChecker certificateChecker;
|
||||
private SendExpiringCertificateNotificationEmailAction action;
|
||||
private Registrar sampleRegistrar;
|
||||
private Response response;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
certificateChecker =
|
||||
new CertificateChecker(
|
||||
ImmutableSortedMap.of(START_OF_TIME, 825, DateTime.parse("2020-09-01T00:00:00Z"), 398),
|
||||
30,
|
||||
15,
|
||||
2048,
|
||||
ImmutableSet.of("secp256r1", "secp384r1"),
|
||||
clock);
|
||||
String expirationWarningEmailBodyText =
|
||||
" Hello Registrar %s,\n" + " The %s certificate is expiring on %s.";
|
||||
String expirationWarningEmailSubjectText = "expiring certificate notification email";
|
||||
|
||||
action =
|
||||
new SendExpiringCertificateNotificationEmailAction(
|
||||
expirationWarningEmailBodyText,
|
||||
expirationWarningEmailSubjectText,
|
||||
new InternetAddress("test@example.com"),
|
||||
sendEmailService,
|
||||
certificateChecker,
|
||||
response);
|
||||
|
||||
sampleRegistrar =
|
||||
persistResource(createRegistrar("clientId", "sampleRegistrar", null, null).build());
|
||||
}
|
||||
|
||||
/** Returns a sample registrar with a customized registrar name, client id and certificate* */
|
||||
private Registrar.Builder createRegistrar(
|
||||
String clientId,
|
||||
String registrarName,
|
||||
@Nullable X509Certificate certificate,
|
||||
@Nullable X509Certificate failOverCertificate)
|
||||
throws Exception {
|
||||
// set up only required fields sample test data
|
||||
Registrar.Builder builder =
|
||||
new Registrar.Builder()
|
||||
.setClientId(clientId)
|
||||
.setRegistrarName(registrarName)
|
||||
.setType(Registrar.Type.REAL)
|
||||
.setIanaIdentifier(8L)
|
||||
.setState(Registrar.State.ACTIVE)
|
||||
.setInternationalizedAddress(
|
||||
new RegistrarAddress.Builder()
|
||||
.setStreet(ImmutableList.of("very fake street"))
|
||||
.setCity("city")
|
||||
.setState("state")
|
||||
.setZip("99999")
|
||||
.setCountryCode("US")
|
||||
.build())
|
||||
.setPhoneNumber("+0.000000000")
|
||||
.setFaxNumber("+9.999999999")
|
||||
.setEmailAddress("contact-us@test.example")
|
||||
.setWhoisServer("whois.registrar.example")
|
||||
.setUrl("http://www.test.example");
|
||||
|
||||
if (failOverCertificate != null) {
|
||||
builder.setFailoverClientCertificate(
|
||||
certificateChecker.serializeCertificate(failOverCertificate), clock.nowUtc());
|
||||
}
|
||||
if (certificate != null) {
|
||||
builder.setClientCertificate(
|
||||
certificateChecker.serializeCertificate(certificate), clock.nowUtc());
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void sendNotificationEmail_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());
|
||||
ImmutableList<RegistrarContact> contacts =
|
||||
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(
|
||||
action.sendNotificationEmail(registrar, START_OF_TIME, CertificateType.FAILOVER, cert))
|
||||
.isEqualTo(true);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void sendNotificationEmail_returnsFalse_noEmailRecipients() throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-02T00:00:00Z"))
|
||||
.cert();
|
||||
Optional<String> cert =
|
||||
Optional.of(certificateChecker.serializeCertificate(expiringCertificate));
|
||||
assertThat(
|
||||
action.sendNotificationEmail(
|
||||
sampleRegistrar, START_OF_TIME, CertificateType.FAILOVER, cert))
|
||||
.isEqualTo(false);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void sendNotificationEmail_throwsRunTimeException() throws Exception {
|
||||
doThrow(new RuntimeException("this is a runtime exception"))
|
||||
.when(sendEmailService)
|
||||
.sendEmail(any());
|
||||
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());
|
||||
ImmutableList<RegistrarContact> contacts =
|
||||
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);
|
||||
RuntimeException thrown =
|
||||
assertThrows(
|
||||
RuntimeException.class,
|
||||
() ->
|
||||
action.sendNotificationEmail(
|
||||
registrar, START_OF_TIME, CertificateType.FAILOVER, cert));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.contains(
|
||||
String.format(
|
||||
"Failed to send expiring certificate notification email to registrar %s",
|
||||
registrar.getRegistrarName()));
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void sendNotificationEmail_returnsFalse_noCertificate() {
|
||||
assertThat(
|
||||
action.sendNotificationEmail(
|
||||
sampleRegistrar, START_OF_TIME, CertificateType.FAILOVER, Optional.empty()))
|
||||
.isEqualTo(false);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void sendNotificationEmails_allEmailsBeingAttemptedToSend() throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-01T00:00:00Z"))
|
||||
.cert();
|
||||
X509Certificate certificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-10-01T00:00:00Z"))
|
||||
.cert();
|
||||
int numOfRegistrars = 10;
|
||||
int numOfRegistrarsWithExpiringCertificates = 2;
|
||||
for (int i = 1; i <= numOfRegistrarsWithExpiringCertificates; i++) {
|
||||
persistResource(
|
||||
createRegistrar("oldcert" + i, "name" + i, expiringCertificate, null).build());
|
||||
}
|
||||
for (int i = numOfRegistrarsWithExpiringCertificates; i <= numOfRegistrars; i++) {
|
||||
persistResource(createRegistrar("goodcert" + i, "name" + i, certificate, null).build());
|
||||
}
|
||||
assertThat(action.sendNotificationEmails()).isEqualTo(numOfRegistrarsWithExpiringCertificates);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void sendNotificationEmails_allEmailsBeingAttemptedToSend_onlyMainCertificates()
|
||||
throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-01T00:00:00Z"))
|
||||
.cert();
|
||||
int numOfRegistrars = 10;
|
||||
for (int i = 1; i <= numOfRegistrars; i++) {
|
||||
persistResource(
|
||||
createRegistrar("oldcert" + i, "name" + i, expiringCertificate, null).build());
|
||||
}
|
||||
assertThat(action.sendNotificationEmails()).isEqualTo(numOfRegistrars);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void sendNotificationEmails_allEmailsBeingAttemptedToSend_onlyFailOverCertificates()
|
||||
throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-01T00:00:00Z"))
|
||||
.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
|
||||
void sendNotificationEmails_allEmailsBeingAttemptedToSend_mixedOfCertificates() throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-01T00:00:00Z"))
|
||||
.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++) {
|
||||
persistResource(
|
||||
createRegistrar("cli" + i, "expiringPrimaryOnly" + i, expiringCertificate, null).build());
|
||||
}
|
||||
for (int i = numOfExpiringFailOverOnly + numOfExpiringPrimaryOnly + 1;
|
||||
i <= numOfRegistrars;
|
||||
i++) {
|
||||
persistResource(
|
||||
createRegistrar("client" + i, "regularReg" + i, expiringCertificate, expiringCertificate)
|
||||
.build());
|
||||
}
|
||||
assertThat(action.sendNotificationEmails())
|
||||
.isEqualTo(numOfRegistrars + numOfExpiringFailOverOnly + numOfExpiringPrimaryOnly);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void updateLastNotificationSentDate_updatedSuccessfully_primaryCertificate() throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-02T00:00:00Z"))
|
||||
.cert();
|
||||
Registrar registrar =
|
||||
createRegistrar("testClientId", "registrar", expiringCertificate, null).build();
|
||||
persistResource(registrar);
|
||||
action.updateLastNotificationSentDate(registrar, clock.nowUtc(), CertificateType.PRIMARY);
|
||||
assertThat(loadByEntity(registrar).getLastExpiringCertNotificationSentDate())
|
||||
.isEqualTo(clock.nowUtc());
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void updateLastNotificationSentDate_updatedSuccessfully_failOverCertificate() throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-01T00:00:00Z"))
|
||||
.cert();
|
||||
Registrar registrar =
|
||||
createRegistrar("testClientId", "registrar", null, expiringCertificate).build();
|
||||
persistResource(registrar);
|
||||
action.updateLastNotificationSentDate(registrar, clock.nowUtc(), CertificateType.FAILOVER);
|
||||
assertThat(loadByEntity(registrar).getLastExpiringFailoverCertNotificationSentDate())
|
||||
.isEqualTo(clock.nowUtc());
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void updateLastNotificationSentDate_noUpdates_noLastNotificationSentDate() throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-01T00:00:00Z"))
|
||||
.cert();
|
||||
Registrar registrar =
|
||||
createRegistrar("testClientId", "registrar", null, expiringCertificate).build();
|
||||
persistResource(registrar);
|
||||
RuntimeException thrown =
|
||||
assertThrows(
|
||||
RuntimeException.class,
|
||||
() -> action.updateLastNotificationSentDate(registrar, null, CertificateType.FAILOVER));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.contains("Failed to update the last notification sent date to Registrar");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void updateLastNotificationSentDate_noUpdates_invalidCertificateType() throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-01T00:00:00Z"))
|
||||
.cert();
|
||||
Registrar registrar =
|
||||
createRegistrar("testClientId", "registrar", null, expiringCertificate).build();
|
||||
persistResource(registrar);
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
action.updateLastNotificationSentDate(
|
||||
registrar, clock.nowUtc(), CertificateType.valueOf("randomType")));
|
||||
assertThat(thrown).hasMessageThat().contains("No enum constant");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void getRegistrarsWithExpiringCertificates_returnsPartOfRegistrars() throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-01T00:00:00Z"))
|
||||
.cert();
|
||||
X509Certificate certificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-10-01T00:00:00Z"))
|
||||
.cert();
|
||||
int numOfRegistrars = 10;
|
||||
int numOfRegistrarsWithExpiringCertificates = 2;
|
||||
for (int i = 1; i <= numOfRegistrarsWithExpiringCertificates; i++) {
|
||||
persistResource(
|
||||
createRegistrar("oldcert" + i, "name" + i, expiringCertificate, null).build());
|
||||
}
|
||||
for (int i = numOfRegistrarsWithExpiringCertificates; i <= numOfRegistrars; i++) {
|
||||
persistResource(createRegistrar("goodcert" + i, "name" + i, certificate, null).build());
|
||||
}
|
||||
|
||||
ImmutableList<RegistrarInfo> results = action.getRegistrarsWithExpiringCertificates();
|
||||
assertThat(results.size()).isEqualTo(numOfRegistrarsWithExpiringCertificates);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void getRegistrarsWithExpiringCertificates_returnsPartOfRegistrars_failOverCertificateBranch()
|
||||
throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-01T00:00:00Z"))
|
||||
.cert();
|
||||
X509Certificate certificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-10-01T00:00:00Z"))
|
||||
.cert();
|
||||
int numOfRegistrars = 10;
|
||||
int numOfRegistrarsWithExpiringCertificates = 2;
|
||||
for (int i = 1; i <= numOfRegistrarsWithExpiringCertificates; i++) {
|
||||
persistResource(
|
||||
createRegistrar("oldcert" + i, "name" + i, null, expiringCertificate).build());
|
||||
}
|
||||
for (int i = numOfRegistrarsWithExpiringCertificates; i <= numOfRegistrars; i++) {
|
||||
persistResource(createRegistrar("goodcert" + i, "name" + i, null, certificate).build());
|
||||
}
|
||||
|
||||
assertThat(action.getRegistrarsWithExpiringCertificates().size())
|
||||
.isEqualTo(numOfRegistrarsWithExpiringCertificates);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void getRegistrarsWithExpiringCertificates_returnsAllRegistrars() throws Exception {
|
||||
X509Certificate expiringCertificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-06-01T00:00:00Z"))
|
||||
.cert();
|
||||
|
||||
int numOfRegistrarsWithExpiringCertificates = 5;
|
||||
for (int i = 1; i <= numOfRegistrarsWithExpiringCertificates; i++) {
|
||||
persistResource(
|
||||
createRegistrar("oldcert" + i, "name" + i, expiringCertificate, null).build());
|
||||
}
|
||||
assertThat(action.getRegistrarsWithExpiringCertificates().size())
|
||||
.isEqualTo(numOfRegistrarsWithExpiringCertificates);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void getRegistrarsWithExpiringCertificates_returnsNoRegistrars() throws Exception {
|
||||
X509Certificate certificate =
|
||||
SelfSignedCaCertificate.create(
|
||||
"www.example.tld",
|
||||
DateTime.parse("2020-09-02T00:00:00Z"),
|
||||
DateTime.parse("2021-10-01T00:00:00Z"))
|
||||
.cert();
|
||||
int numOfRegistrars = 10;
|
||||
for (int i = 1; i <= numOfRegistrars; i++) {
|
||||
persistResource(createRegistrar("goodcert" + i, "name" + i, certificate, null).build());
|
||||
}
|
||||
assertThat(action.getRegistrarsWithExpiringCertificates()).isEmpty();
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void getRegistrarsWithExpiringCertificates_noRegistrarsInDatabase() {
|
||||
assertThat(action.getRegistrarsWithExpiringCertificates()).isEmpty();
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void getEmailAddresses_success_returnsAnEmptyList() {
|
||||
assertThat(action.getEmailAddresses(sampleRegistrar, Type.TECH)).isEmpty();
|
||||
assertThat(action.getEmailAddresses(sampleRegistrar, Type.ADMIN)).isEmpty();
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void getEmailAddresses_success_returnsAListOfEmails() throws Exception {
|
||||
Registrar registrar = persistResource(makeRegistrar1());
|
||||
ImmutableList<RegistrarContact> contacts =
|
||||
ImmutableList.of(
|
||||
new RegistrarContact.Builder()
|
||||
.setParent(registrar)
|
||||
.setName("John Doe")
|
||||
.setEmailAddress("jd@example-registrar.tld")
|
||||
.setPhoneNumber("+1.3105551213")
|
||||
.setFaxNumber("+1.3105551213")
|
||||
.setTypes(ImmutableSet.of(RegistrarContact.Type.TECH))
|
||||
.setVisibleInWhoisAsAdmin(true)
|
||||
.setVisibleInWhoisAsTech(false)
|
||||
.build(),
|
||||
new RegistrarContact.Builder()
|
||||
.setParent(registrar)
|
||||
.setName("John Smith")
|
||||
.setEmailAddress("js@example-registrar.tld")
|
||||
.setPhoneNumber("+1.1111111111")
|
||||
.setFaxNumber("+1.1111111111")
|
||||
.setTypes(ImmutableSet.of(RegistrarContact.Type.TECH))
|
||||
.build(),
|
||||
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(),
|
||||
new RegistrarContact.Builder()
|
||||
.setParent(registrar)
|
||||
.setName("Mike Doe")
|
||||
.setEmailAddress("mike@example-registrar.tld")
|
||||
.setPhoneNumber("+1.1111111111")
|
||||
.setFaxNumber("+1.1111111111")
|
||||
.setTypes(ImmutableSet.of(RegistrarContact.Type.ADMIN))
|
||||
.build(),
|
||||
new RegistrarContact.Builder()
|
||||
.setParent(registrar)
|
||||
.setName("John T")
|
||||
.setEmailAddress("john@example-registrar.tld")
|
||||
.setPhoneNumber("+1.3105551215")
|
||||
.setFaxNumber("+1.3105551216")
|
||||
.setTypes(ImmutableSet.of(RegistrarContact.Type.ADMIN))
|
||||
.setVisibleInWhoisAsTech(true)
|
||||
.build());
|
||||
persistSimpleResources(contacts);
|
||||
assertThat(action.getEmailAddresses(registrar, Type.TECH))
|
||||
.containsExactly(
|
||||
new InternetAddress("will@example-registrar.tld"),
|
||||
new InternetAddress("jd@example-registrar.tld"),
|
||||
new InternetAddress("js@example-registrar.tld"));
|
||||
assertThat(action.getEmailAddresses(registrar, Type.ADMIN))
|
||||
.containsExactly(
|
||||
new InternetAddress("janedoe@theregistrar.com"), // comes with makeRegistrar1()
|
||||
new InternetAddress("mike@example-registrar.tld"),
|
||||
new InternetAddress("john@example-registrar.tld"));
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void getEmailAddresses_failure_returnsPartialListOfEmails_skipInvalidEmails() {
|
||||
// when building a new RegistrarContact object, there's already an email validation process.
|
||||
// if the registrarContact is created successful, the email address of the contact object
|
||||
// should already be validated. Ideally, there should not be an AddressException when creating
|
||||
// a new InternetAddress using the email address string of the contact object.
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void getEmailBody_returnsEmailBodyText() {
|
||||
String registrarName = "good registrar";
|
||||
String certExpirationDateStr = "2021-06-15";
|
||||
CertificateType certificateType = CertificateType.PRIMARY;
|
||||
String emailBody =
|
||||
action.getEmailBody(
|
||||
registrarName, certificateType, DateTime.parse(certExpirationDateStr).toDate());
|
||||
assertThat(emailBody).contains(registrarName);
|
||||
assertThat(emailBody).contains(certificateType.getDisplayName());
|
||||
assertThat(emailBody).contains(certExpirationDateStr);
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void getEmailBody_throwsIllegalArgumentException_noExpirationDate() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> action.getEmailBody("good registrar", CertificateType.FAILOVER, null));
|
||||
assertThat(thrown).hasMessageThat().contains("Expiration date cannot be null");
|
||||
}
|
||||
|
||||
@TestOfyAndSql
|
||||
void getEmailBody_throwsIllegalArgumentException_noCertificateType() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
action.getEmailBody("good registrar", null, DateTime.parse("2021-06-15").toDate()));
|
||||
assertThat(thrown).hasMessageThat().contains("Certificate type cannot be null");
|
||||
}
|
||||
}
|
|
@ -1,49 +1,50 @@
|
|||
PATH CLASS METHODS OK AUTH_METHODS MIN USER_POLICY
|
||||
/_dr/cron/commitLogCheckpoint CommitLogCheckpointAction GET y INTERNAL,API APP ADMIN
|
||||
/_dr/cron/commitLogFanout CommitLogFanoutAction GET y INTERNAL,API APP ADMIN
|
||||
/_dr/cron/fanout TldFanoutAction GET y INTERNAL,API APP ADMIN
|
||||
/_dr/cron/readDnsQueue ReadDnsQueueAction GET y INTERNAL,API APP ADMIN
|
||||
/_dr/dnsRefresh RefreshDnsAction GET y INTERNAL,API APP ADMIN
|
||||
/_dr/task/backupDatastore BackupDatastoreAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/brdaCopy BrdaCopyAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/checkDatastoreBackup CheckBackupAction POST,GET y INTERNAL,API APP ADMIN
|
||||
/_dr/task/copyDetailReports CopyDetailReportsAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/createSyntheticHistoryEntries CreateSyntheticHistoryEntriesAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/deleteContactsAndHosts DeleteContactsAndHostsAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/deleteExpiredDomains DeleteExpiredDomainsAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/deleteLoadTestData DeleteLoadTestDataAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/deleteOldCommitLogs DeleteOldCommitLogsAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/deleteProberData DeleteProberDataAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/expandRecurringBillingEvents ExpandRecurringBillingEventsAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/exportCommitLogDiff ExportCommitLogDiffAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/exportDomainLists ExportDomainListsAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/exportPremiumTerms ExportPremiumTermsAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/exportReservedTerms ExportReservedTermsAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/generateInvoices GenerateInvoicesAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/generateSpec11 GenerateSpec11ReportAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/icannReportingStaging IcannReportingStagingAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/icannReportingUpload IcannReportingUploadAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/nordnUpload NordnUploadAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/nordnVerify NordnVerifyAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/pollBigqueryJob BigqueryPollJobAction GET,POST y INTERNAL APP IGNORED
|
||||
/_dr/task/publishDnsUpdates PublishDnsUpdatesAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/publishInvoices PublishInvoicesAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/publishSpec11 PublishSpec11ReportAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/rdeReport RdeReportAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/rdeStaging RdeStagingAction GET,POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/rdeUpload RdeUploadAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/refreshDnsOnHostRename RefreshDnsOnHostRenameAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/relockDomain RelockDomainAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/replayCommitLogsToSql ReplayCommitLogsToSqlAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/resaveAllEppResources ResaveAllEppResourcesAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/resaveEntity ResaveEntityAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/syncGroupMembers SyncGroupMembersAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/syncRegistrarsSheet SyncRegistrarsSheetAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/tmchCrl TmchCrlAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/tmchDnl TmchDnlAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/tmchSmdrl TmchSmdrlAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/updateRegistrarRdapBaseUrls UpdateRegistrarRdapBaseUrlsAction GET y INTERNAL,API APP ADMIN
|
||||
/_dr/task/updateSnapshotView UpdateSnapshotViewAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/uploadDatastoreBackup UploadDatastoreBackupAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/wipeOutCloudSql WipeOutCloudSqlAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/wipeOutDatastore WipeoutDatastoreAction GET n INTERNAL,API APP ADMIN
|
||||
PATH CLASS METHODS OK AUTH_METHODS MIN USER_POLICY
|
||||
/_dr/cron/commitLogCheckpoint CommitLogCheckpointAction GET y INTERNAL,API APP ADMIN
|
||||
/_dr/cron/commitLogFanout CommitLogFanoutAction GET y INTERNAL,API APP ADMIN
|
||||
/_dr/cron/fanout TldFanoutAction GET y INTERNAL,API APP ADMIN
|
||||
/_dr/cron/readDnsQueue ReadDnsQueueAction GET y INTERNAL,API APP ADMIN
|
||||
/_dr/dnsRefresh RefreshDnsAction GET y INTERNAL,API APP ADMIN
|
||||
/_dr/task/backupDatastore BackupDatastoreAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/brdaCopy BrdaCopyAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/checkDatastoreBackup CheckBackupAction POST,GET y INTERNAL,API APP ADMIN
|
||||
/_dr/task/copyDetailReports CopyDetailReportsAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/createSyntheticHistoryEntries CreateSyntheticHistoryEntriesAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/deleteContactsAndHosts DeleteContactsAndHostsAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/deleteExpiredDomains DeleteExpiredDomainsAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/deleteLoadTestData DeleteLoadTestDataAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/deleteOldCommitLogs DeleteOldCommitLogsAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/deleteProberData DeleteProberDataAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/expandRecurringBillingEvents ExpandRecurringBillingEventsAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/exportCommitLogDiff ExportCommitLogDiffAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/exportDomainLists ExportDomainListsAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/exportPremiumTerms ExportPremiumTermsAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/exportReservedTerms ExportReservedTermsAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/generateInvoices GenerateInvoicesAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/generateSpec11 GenerateSpec11ReportAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/icannReportingStaging IcannReportingStagingAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/icannReportingUpload IcannReportingUploadAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/nordnUpload NordnUploadAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/nordnVerify NordnVerifyAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/pollBigqueryJob BigqueryPollJobAction GET,POST y INTERNAL APP IGNORED
|
||||
/_dr/task/publishDnsUpdates PublishDnsUpdatesAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/publishInvoices PublishInvoicesAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/publishSpec11 PublishSpec11ReportAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/rdeReport RdeReportAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/rdeStaging RdeStagingAction GET,POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/rdeUpload RdeUploadAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/refreshDnsOnHostRename RefreshDnsOnHostRenameAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/relockDomain RelockDomainAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/replayCommitLogsToSql ReplayCommitLogsToSqlAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/resaveAllEppResources ResaveAllEppResourcesAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/resaveEntity ResaveEntityAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/sendExpiringCertificateNotificationEmail SendExpiringCertificateNotificationEmailAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/syncGroupMembers SyncGroupMembersAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/syncRegistrarsSheet SyncRegistrarsSheetAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/tmchCrl TmchCrlAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/tmchDnl TmchDnlAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/tmchSmdrl TmchSmdrlAction POST y INTERNAL,API APP ADMIN
|
||||
/_dr/task/updateRegistrarRdapBaseUrls UpdateRegistrarRdapBaseUrlsAction GET y INTERNAL,API APP ADMIN
|
||||
/_dr/task/updateSnapshotView UpdateSnapshotViewAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/uploadDatastoreBackup UploadDatastoreBackupAction POST n INTERNAL,API APP ADMIN
|
||||
/_dr/task/wipeOutCloudSql WipeOutCloudSqlAction GET n INTERNAL,API APP ADMIN
|
||||
/_dr/task/wipeOutDatastore WipeoutDatastoreAction GET n INTERNAL,API APP ADMIN
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue