mirror of
https://github.com/google/nomulus.git
synced 2025-07-23 11:16:04 +02:00
Add placeholder configs for Gmail (#2089)
Add placeholder configs for sending emails using Gmail in GSuite. The names of the new configs are temporary. After migration they will revert to the names currently in use by the AppEngine email API.
This commit is contained in:
parent
1e0a0cf29e
commit
10d28efa1c
8 changed files with 117 additions and 8 deletions
|
@ -71,7 +71,7 @@ public class CannedScriptExecutionAction implements Runnable {
|
|||
GmailClient gmailClient,
|
||||
@Config("projectId") String projectId,
|
||||
@Config("gSuiteDomainName") String gSuiteDomainName,
|
||||
@Config("alertRecipientEmailAddress") InternetAddress recipientAddress) {
|
||||
@Config("newAlertRecipientEmailAddress") InternetAddress recipientAddress) {
|
||||
this.groupsConnection = groupsConnection;
|
||||
this.gmailClient = gmailClient;
|
||||
this.gSuiteDomainName = gSuiteDomainName;
|
||||
|
@ -116,6 +116,8 @@ public class CannedScriptExecutionAction implements Runnable {
|
|||
try {
|
||||
Set<String> currentMembers = groupsConnection.getMembersOfGroup(groupKey);
|
||||
logger.atInfo().log("%s has %s members.", groupKey, currentMembers.size());
|
||||
// One success is enough for validation.
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
logger.atWarning().withCause(e).log("Failed to check %s", groupKey);
|
||||
}
|
||||
|
|
|
@ -93,13 +93,56 @@ public abstract class CredentialModule {
|
|||
@AdcDelegatedCredential
|
||||
@Provides
|
||||
@Singleton
|
||||
public static GoogleCredentialsBundle provideSelfSignedDelegatedCredential(
|
||||
public static GoogleCredentialsBundle provideSelfSignedAdminDelegatedCredential(
|
||||
@Config("defaultCredentialOauthScopes") ImmutableList<String> defaultScopes,
|
||||
@Config("delegatedCredentialOauthScopes") ImmutableList<String> delegationScopes,
|
||||
@ApplicationDefaultCredential GoogleCredentialsBundle credentialsBundle,
|
||||
@Config("gSuiteAdminAccountEmailAddress") String gSuiteAdminAccountEmailAddress,
|
||||
@Config("tokenRefreshDelay") Duration tokenRefreshDelay,
|
||||
Clock clock) {
|
||||
return createSelfSignedDelegatedCredential(
|
||||
defaultScopes,
|
||||
delegationScopes,
|
||||
credentialsBundle,
|
||||
gSuiteAdminAccountEmailAddress,
|
||||
tokenRefreshDelay,
|
||||
clock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a {@link GoogleCredentialsBundle} for sending emails through Google Workspace.
|
||||
*
|
||||
* <p>The Workspace domain must grant delegated admin access to the default service account user
|
||||
* (project-id@appspot.gserviceaccount.com on AppEngine) with all scopes in {@code defaultScopes}
|
||||
* and {@code delegationScopes}. In addition, the user {@code gSuiteOutgoingEmailAddress} must
|
||||
* have the permission to send emails.
|
||||
*/
|
||||
@GmailDelegatedCredential
|
||||
@Provides
|
||||
@Singleton
|
||||
public static GoogleCredentialsBundle provideSelfSignedGmailDelegatedCredential(
|
||||
@Config("defaultCredentialOauthScopes") ImmutableList<String> defaultScopes,
|
||||
@Config("delegatedCredentialOauthScopes") ImmutableList<String> delegationScopes,
|
||||
@ApplicationDefaultCredential GoogleCredentialsBundle credentialsBundle,
|
||||
@Config("gSuiteNewOutgoingEmailAddress") String gSuiteOutgoingEmailAddress,
|
||||
@Config("tokenRefreshDelay") Duration tokenRefreshDelay,
|
||||
Clock clock) {
|
||||
return createSelfSignedDelegatedCredential(
|
||||
defaultScopes,
|
||||
delegationScopes,
|
||||
credentialsBundle,
|
||||
gSuiteOutgoingEmailAddress,
|
||||
tokenRefreshDelay,
|
||||
clock);
|
||||
}
|
||||
|
||||
public static GoogleCredentialsBundle createSelfSignedDelegatedCredential(
|
||||
ImmutableList<String> defaultScopes,
|
||||
ImmutableList<String> delegationScopes,
|
||||
GoogleCredentialsBundle credentialsBundle,
|
||||
String gSuiteUserEmailAddress,
|
||||
Duration tokenRefreshDelay,
|
||||
Clock clock) {
|
||||
GoogleCredentials signer = credentialsBundle.getGoogleCredentials();
|
||||
|
||||
checkArgument(
|
||||
|
@ -118,7 +161,7 @@ public abstract class CredentialModule {
|
|||
DelegatedCredentials.createSelfSignedDelegatedCredential(
|
||||
(ServiceAccountSigner) signer,
|
||||
ImmutableList.<String>builder().addAll(defaultScopes).addAll(delegationScopes).build(),
|
||||
gSuiteAdminAccountEmailAddress,
|
||||
gSuiteUserEmailAddress,
|
||||
clock,
|
||||
tokenRefreshDelay);
|
||||
return GoogleCredentialsBundle.create(credential);
|
||||
|
@ -136,6 +179,15 @@ public abstract class CredentialModule {
|
|||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface GoogleWorkspaceCredential {}
|
||||
|
||||
/**
|
||||
* Dagger qualifier for a credential with delegated Email-sending permission for a dasher domain
|
||||
* (for Google Workspace) backed by the application default credential (ADC).
|
||||
*/
|
||||
@Qualifier
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface GmailDelegatedCredential {}
|
||||
|
||||
/**
|
||||
* Dagger qualifier for a credential with delegated admin access for a dasher domain (for Google
|
||||
* Workspace) backed by the application default credential (ADC).
|
||||
|
|
|
@ -538,6 +538,13 @@ public final class RegistryConfig {
|
|||
return parseEmailAddress(config.gSuite.outgoingEmailAddress);
|
||||
}
|
||||
|
||||
// TODO(b/279671974): reuse the 'gSuiteOutgoingEmailAddress' annotation after migration
|
||||
@Provides
|
||||
@Config("gSuiteNewOutgoingEmailAddress")
|
||||
public static String provideGSuiteNewOutgoingEmailAddress(RegistryConfigSettings config) {
|
||||
return config.gSuite.newOutgoingEmailAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* The display name that is used on outgoing emails sent by Nomulus.
|
||||
*
|
||||
|
@ -549,6 +556,16 @@ public final class RegistryConfig {
|
|||
return config.gSuite.outgoingEmailDisplayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the `reply-to` address for outgoing email messages. This address may be outside the
|
||||
* GSuite domain.
|
||||
*/
|
||||
@Provides
|
||||
@Config("replyToEmailAddress")
|
||||
public static InternetAddress provideReplyToEmailAddress(RegistryConfigSettings config) {
|
||||
return parseEmailAddress(config.gSuite.replyToEmailAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether an SSL certificate hash is required to log in via EPP and run flows.
|
||||
*
|
||||
|
@ -859,6 +876,14 @@ public final class RegistryConfig {
|
|||
return parseEmailAddress(config.misc.alertRecipientEmailAddress);
|
||||
}
|
||||
|
||||
// TODO(b/279671974): remove below method after migration
|
||||
@Provides
|
||||
@Config("newAlertRecipientEmailAddress")
|
||||
public static InternetAddress provideNewAlertRecipientEmailAddress(
|
||||
RegistryConfigSettings config) {
|
||||
return parseEmailAddress(config.misc.newAlertRecipientEmailAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the email address to which spec 11 email should be replied.
|
||||
*
|
||||
|
|
|
@ -77,6 +77,9 @@ public class RegistryConfigSettings {
|
|||
public static class GSuite {
|
||||
public String domainName;
|
||||
public String outgoingEmailAddress;
|
||||
// TODO(b/279671974): remove below field after migration
|
||||
public String newOutgoingEmailAddress;
|
||||
public String replyToEmailAddress;
|
||||
public String outgoingEmailDisplayName;
|
||||
public String adminAccountEmailAddress;
|
||||
public String supportGroupEmailAddress;
|
||||
|
@ -203,6 +206,8 @@ public class RegistryConfigSettings {
|
|||
public static class Misc {
|
||||
public String sheetExportId;
|
||||
public String alertRecipientEmailAddress;
|
||||
// TODO(b/279671974): remove below field after migration
|
||||
public String newAlertRecipientEmailAddress;
|
||||
public String spec11OutgoingEmailAddress;
|
||||
public List<String> spec11BccEmailAddresses;
|
||||
public int transientFailureRetries;
|
||||
|
|
|
@ -33,6 +33,9 @@ gSuite:
|
|||
# https://cloud.google.com/appengine/docs/standard/java/mail/#who_can_send_mail
|
||||
outgoingEmailDisplayName: Example Registry
|
||||
outgoingEmailAddress: noreply@project-id.appspotmail.com
|
||||
# TODO(b/279671974): reuse `outgoingEmailAddress` after migration
|
||||
newOutgoingEmailAddress: noreply@example.com
|
||||
replyToEmailAddress: reply-to@example.com
|
||||
|
||||
# Email address of the admin account on the G Suite app. This is used for
|
||||
# logging in to perform administrative actions, not sending emails.
|
||||
|
@ -432,6 +435,9 @@ misc:
|
|||
# Address we send alert summary emails to.
|
||||
alertRecipientEmailAddress: email@example.com
|
||||
|
||||
# TODO(b/279671974): reuse `alertRecipientEmailAddress` after migration
|
||||
newAlertRecipientEmailAddress: email@example.com
|
||||
|
||||
# Address from which Spec 11 emails to registrars are sent. This needs
|
||||
# to be a deliverable email address to handle replies from registrars as well.
|
||||
spec11OutgoingEmailAddress: abuse@example.com
|
||||
|
|
|
@ -20,10 +20,12 @@ import com.google.api.services.gmail.Gmail;
|
|||
import com.google.api.services.gmail.model.Message;
|
||||
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;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Properties;
|
||||
import javax.inject.Inject;
|
||||
import javax.mail.Address;
|
||||
|
@ -42,10 +44,24 @@ import org.apache.commons.codec.binary.Base64;
|
|||
public final class GmailClient {
|
||||
|
||||
private final Gmail gmail;
|
||||
private final InternetAddress outgoingEmailAddressWithUsername;
|
||||
private final InternetAddress replyToEmailAddress;
|
||||
|
||||
@Inject
|
||||
GmailClient(Gmail gmail) {
|
||||
GmailClient(
|
||||
Gmail gmail,
|
||||
@Config("gSuiteNewOutgoingEmailAddress") String gSuiteOutgoingEmailAddress,
|
||||
@Config("gSuiteOutgoingEmailDisplayName") String gSuiteOutgoingEmailDisplayName,
|
||||
@Config("replyToEmailAddress") InternetAddress replyToEmailAddress) {
|
||||
|
||||
this.gmail = gmail;
|
||||
this.replyToEmailAddress = replyToEmailAddress;
|
||||
try {
|
||||
this.outgoingEmailAddressWithUsername =
|
||||
new InternetAddress(gSuiteOutgoingEmailAddress, gSuiteOutgoingEmailDisplayName);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -58,6 +74,7 @@ public final class GmailClient {
|
|||
public Message sendEmail(EmailMessage emailMessage) {
|
||||
Message message = toGmailMessage(toMimeMessage(emailMessage));
|
||||
try {
|
||||
// "me" is reserved word for the authorized user of the Gmail API.
|
||||
return gmail.users().messages().send("me", message).execute();
|
||||
} catch (IOException e) {
|
||||
throw new EmailException(e);
|
||||
|
@ -78,11 +95,12 @@ public final class GmailClient {
|
|||
}
|
||||
}
|
||||
|
||||
static MimeMessage toMimeMessage(EmailMessage emailMessage) {
|
||||
MimeMessage toMimeMessage(EmailMessage emailMessage) {
|
||||
try {
|
||||
MimeMessage msg =
|
||||
new MimeMessage(Session.getDefaultInstance(new Properties(), /* authenticator= */ null));
|
||||
msg.setFrom(emailMessage.from());
|
||||
msg.setFrom(this.outgoingEmailAddressWithUsername);
|
||||
msg.setReplyTo(new InternetAddress[] {replyToEmailAddress});
|
||||
msg.addRecipients(
|
||||
RecipientType.TO, toArray(emailMessage.recipients(), InternetAddress.class));
|
||||
msg.setSubject(emailMessage.subject());
|
||||
|
|
|
@ -17,7 +17,7 @@ package google.registry.groups;
|
|||
import com.google.api.services.gmail.Gmail;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.config.CredentialModule.AdcDelegatedCredential;
|
||||
import google.registry.config.CredentialModule.GmailDelegatedCredential;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.util.GoogleCredentialsBundle;
|
||||
import javax.inject.Singleton;
|
||||
|
@ -29,7 +29,7 @@ public class GmailModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
Gmail provideGmail(
|
||||
@AdcDelegatedCredential GoogleCredentialsBundle credentialsBundle,
|
||||
@GmailDelegatedCredential GoogleCredentialsBundle credentialsBundle,
|
||||
@Config("projectId") String projectId) {
|
||||
return new Gmail.Builder(
|
||||
credentialsBundle.getHttpTransport(),
|
||||
|
|
|
@ -46,6 +46,7 @@ public abstract class EmailMessage {
|
|||
|
||||
public abstract ImmutableSet<InternetAddress> recipients();
|
||||
|
||||
// TODO(b/279671974): remove `from` after migration.
|
||||
public abstract InternetAddress from();
|
||||
|
||||
public abstract ImmutableSet<InternetAddress> ccs();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue