diff --git a/core/src/main/java/google/registry/config/RegistryConfig.java b/core/src/main/java/google/registry/config/RegistryConfig.java index b991aab5e..494463cb6 100644 --- a/core/src/main/java/google/registry/config/RegistryConfig.java +++ b/core/src/main/java/google/registry/config/RegistryConfig.java @@ -158,6 +158,12 @@ public final class RegistryConfig { return config.registryPolicy.contactAndHostRoidSuffix; } + @Provides + @Config("isEmailSendingEnabled") + public static boolean provideIsEmailSendingEnabled(RegistryConfigSettings config) { + return config.misc.isEmailSendingEnabled; + } + /** * The e-mail address for questions about integrating with the registry. Used in the * "contact-us" section of the registrar console. diff --git a/core/src/main/java/google/registry/config/RegistryConfigSettings.java b/core/src/main/java/google/registry/config/RegistryConfigSettings.java index 5430e260d..54df4a7cf 100644 --- a/core/src/main/java/google/registry/config/RegistryConfigSettings.java +++ b/core/src/main/java/google/registry/config/RegistryConfigSettings.java @@ -205,6 +205,7 @@ public class RegistryConfigSettings { /** Miscellaneous configuration that doesn't quite fit in anywhere else. */ public static class Misc { public String sheetExportId; + public boolean isEmailSendingEnabled; public String alertRecipientEmailAddress; // TODO(b/279671974): remove below field after migration public String newAlertRecipientEmailAddress; diff --git a/core/src/main/java/google/registry/config/files/default-config.yaml b/core/src/main/java/google/registry/config/files/default-config.yaml index f872567fc..63ebbe306 100644 --- a/core/src/main/java/google/registry/config/files/default-config.yaml +++ b/core/src/main/java/google/registry/config/files/default-config.yaml @@ -432,6 +432,9 @@ misc: # to. Leave this null to disable syncing. sheetExportId: null + # Whether emails may be sent. For Prod and Sandbox this should be true. + isEmailSendingEnabled: false + # Address we send alert summary emails to. alertRecipientEmailAddress: email@example.com diff --git a/core/src/main/java/google/registry/groups/GmailClient.java b/core/src/main/java/google/registry/groups/GmailClient.java index 5a30bc5e2..cc54aa937 100644 --- a/core/src/main/java/google/registry/groups/GmailClient.java +++ b/core/src/main/java/google/registry/groups/GmailClient.java @@ -14,14 +14,15 @@ package google.registry.groups; +import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.common.collect.Iterables.toArray; import com.google.api.client.http.HttpResponseException; import com.google.api.services.gmail.Gmail; import com.google.api.services.gmail.model.Message; import com.google.common.collect.ImmutableSet; +import com.google.common.flogger.FluentLogger; import com.google.common.net.MediaType; -import com.google.errorprone.annotations.CanIgnoreReturnValue; import google.registry.config.RegistryConfig.Config; import google.registry.util.EmailMessage; import google.registry.util.EmailMessage.Attachment; @@ -46,8 +47,11 @@ import javax.mail.internet.MimeMultipart; /** Sends {@link EmailMessage EmailMessages} through Google Workspace using {@link Gmail}. */ public final class GmailClient { + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + private final Gmail gmail; private final Retrier retrier; + private final boolean isEmailSendingEnabled; private final InternetAddress outgoingEmailAddressWithUsername; private final InternetAddress replyToEmailAddress; @@ -55,12 +59,14 @@ public final class GmailClient { GmailClient( Gmail gmail, Retrier retrier, + @Config("isEmailSendingEnabled") boolean isEmailSendingEnabled, @Config("gSuiteNewOutgoingEmailAddress") String gSuiteOutgoingEmailAddress, @Config("gSuiteOutgoingEmailDisplayName") String gSuiteOutgoingEmailDisplayName, @Config("replyToEmailAddress") InternetAddress replyToEmailAddress) { this.gmail = gmail; this.retrier = retrier; + this.isEmailSendingEnabled = isEmailSendingEnabled; this.replyToEmailAddress = replyToEmailAddress; try { this.outgoingEmailAddressWithUsername = @@ -76,11 +82,22 @@ public final class GmailClient { *
If the sender as specified by {@link EmailMessage#from} differs from the caller's identity, * the caller must have delegated `send` authority to the sender. */ - @CanIgnoreReturnValue - public Message sendEmail(EmailMessage emailMessage) { + public void sendEmail(EmailMessage emailMessage) { + if (!isEmailSendingEnabled) { + logger.atInfo().log( + String.format( + "Email with subject %s would have been sent to recipients %s", + emailMessage.subject().substring(0, Math.min(emailMessage.subject().length(), 15)), + String.join( + " , ", + emailMessage.recipients().stream() + .map(ia -> ia.toString()) + .collect(toImmutableSet())))); + return; + } Message message = toGmailMessage(toMimeMessage(emailMessage)); // Unlike other Cloud APIs such as GCS and SecretManager, Gmail does not retry on errors. - return retrier.callWithRetry( + retrier.callWithRetry( // "me" is reserved word for the authorized user of the Gmail API. () -> this.gmail.users().messages().send("me", message).execute(), RetriableGmailExceptionPredicate.INSTANCE); diff --git a/core/src/test/java/google/registry/groups/GmailClientTest.java b/core/src/test/java/google/registry/groups/GmailClientTest.java index 759ba1912..e6e7a9b92 100644 --- a/core/src/test/java/google/registry/groups/GmailClientTest.java +++ b/core/src/test/java/google/registry/groups/GmailClientTest.java @@ -19,12 +19,19 @@ import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8; import static com.google.common.truth.Truth.assertThat; import static java.nio.charset.StandardCharsets.UTF_8; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import com.google.api.client.http.HttpResponseException; import com.google.api.services.gmail.Gmail; +import com.google.api.services.gmail.Gmail.Users; +import com.google.api.services.gmail.Gmail.Users.Messages; +import com.google.api.services.gmail.Gmail.Users.Messages.Send; import com.google.api.services.gmail.model.Message; import google.registry.groups.GmailClient.RetriableGmailExceptionPredicate; import google.registry.util.EmailMessage; @@ -37,7 +44,6 @@ import javax.mail.Part; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; @@ -52,17 +58,45 @@ public class GmailClientTest { @Mock private Gmail gmail; @Mock private HttpResponseException httpResponseException; - private GmailClient gmailClient; - @BeforeEach - public void setup() throws Exception { - gmailClient = - new GmailClient( - gmail, - new Retrier(new SystemSleeper(), 3), - "from@example.com", - "My sender", - new InternetAddress("replyTo@example.com")); + private GmailClient getGmailClient(boolean isExternalEmailAllowed) throws Exception { + return new GmailClient( + gmail, + new Retrier(new SystemSleeper(), 3), + isExternalEmailAllowed, + "from@example.com", + "My sender", + new InternetAddress("replyTo@example.com")); + } + + @Test + public void sendEmail_sentWhenAllowed() throws Exception { + EmailMessage message = + EmailMessage.create( + "subject", + "body", + new InternetAddress("from@example.com"), + new InternetAddress("to@example.com")); + Send gSend = mock(Send.class); + Messages gMessages = mock(Messages.class); + Users gUsers = mock(Users.class); + when(gmail.users()).thenReturn(gUsers); + when(gUsers.messages()).thenReturn(gMessages); + when(gMessages.send(anyString(), any())).thenReturn(gSend); + getGmailClient(true).sendEmail(message); + verify(gmail, times(1)).users(); + } + + @Test + public void sendEmail_notSentWhenNotAllowed() throws Exception { + EmailMessage message = + EmailMessage.create( + "subject", + "body", + new InternetAddress("from@example.com"), + new InternetAddress("to@example.com")); + getGmailClient(false).sendEmail(message); + verifyNoInteractions(gmail); } @Test @@ -86,7 +120,7 @@ public class GmailClientTest { .setContentType(CSV_UTF_8) .build()) .build(); - MimeMessage mimeMessage = gmailClient.toMimeMessage(emailMessage); + MimeMessage mimeMessage = getGmailClient(true).toMimeMessage(emailMessage); assertThat(mimeMessage.getFrom()).asList().containsExactly(fromAddr); assertThat(mimeMessage.getRecipients(RecipientType.TO)).asList().containsExactly(toAddr); assertThat(mimeMessage.getRecipients(RecipientType.CC)).asList().containsExactly(ccAddr);