Make OutgoingEmailValidator a Singleton
Make the class a Singleton so that it can be used by classes that get it injected, rather than calling its methods statically. This is a preparatory step to allowing the validator to get the gerrit configuration injected, which will allow to disable validation using this validator. Change-Id: If5c7cc6d2c034b9ed16b1358b4d1d186d57daf98
This commit is contained in:
		@@ -19,6 +19,7 @@ import static com.google.common.truth.Truth.assert_;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import com.google.gerrit.acceptance.AbstractDaemonTest;
 | 
					import com.google.gerrit.acceptance.AbstractDaemonTest;
 | 
				
			||||||
import com.google.gerrit.server.mail.send.OutgoingEmailValidator;
 | 
					import com.google.gerrit.server.mail.send.OutgoingEmailValidator;
 | 
				
			||||||
 | 
					import com.google.inject.Inject;
 | 
				
			||||||
import java.io.BufferedReader;
 | 
					import java.io.BufferedReader;
 | 
				
			||||||
import java.io.InputStream;
 | 
					import java.io.InputStream;
 | 
				
			||||||
import java.io.InputStreamReader;
 | 
					import java.io.InputStreamReader;
 | 
				
			||||||
@@ -27,9 +28,11 @@ import org.junit.Test;
 | 
				
			|||||||
public class EmailValidatorIT extends AbstractDaemonTest {
 | 
					public class EmailValidatorIT extends AbstractDaemonTest {
 | 
				
			||||||
  private static final String UNSUPPORTED_PREFIX = "#! ";
 | 
					  private static final String UNSUPPORTED_PREFIX = "#! ";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Inject private OutgoingEmailValidator validator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Test
 | 
					  @Test
 | 
				
			||||||
  public void validateLocalDomain() throws Exception {
 | 
					  public void validateLocalDomain() throws Exception {
 | 
				
			||||||
    assertThat(OutgoingEmailValidator.isValid("foo@bar.local")).isTrue();
 | 
					    assertThat(validator.isValid("foo@bar.local")).isTrue();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Test
 | 
					  @Test
 | 
				
			||||||
@@ -49,13 +52,13 @@ public class EmailValidatorIT extends AbstractDaemonTest {
 | 
				
			|||||||
          String test = "test@example." + tld.toLowerCase().substring(UNSUPPORTED_PREFIX.length());
 | 
					          String test = "test@example." + tld.toLowerCase().substring(UNSUPPORTED_PREFIX.length());
 | 
				
			||||||
          assert_()
 | 
					          assert_()
 | 
				
			||||||
              .withFailureMessage("expected invalid TLD \"" + test + "\"")
 | 
					              .withFailureMessage("expected invalid TLD \"" + test + "\"")
 | 
				
			||||||
              .that(OutgoingEmailValidator.isValid(test))
 | 
					              .that(validator.isValid(test))
 | 
				
			||||||
              .isFalse();
 | 
					              .isFalse();
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          String test = "test@example." + tld.toLowerCase();
 | 
					          String test = "test@example." + tld.toLowerCase();
 | 
				
			||||||
          assert_()
 | 
					          assert_()
 | 
				
			||||||
              .withFailureMessage("failed to validate TLD \"" + test + "\"")
 | 
					              .withFailureMessage("failed to validate TLD \"" + test + "\"")
 | 
				
			||||||
              .that(OutgoingEmailValidator.isValid(test))
 | 
					              .that(validator.isValid(test))
 | 
				
			||||||
              .isTrue();
 | 
					              .isTrue();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -77,6 +77,7 @@ public class CreateAccount implements RestModifyView<TopLevelResource, AccountIn
 | 
				
			|||||||
  private final AuditService auditService;
 | 
					  private final AuditService auditService;
 | 
				
			||||||
  private final ExternalIds externalIds;
 | 
					  private final ExternalIds externalIds;
 | 
				
			||||||
  private final ExternalIdsUpdate.User externalIdsUpdateFactory;
 | 
					  private final ExternalIdsUpdate.User externalIdsUpdateFactory;
 | 
				
			||||||
 | 
					  private final OutgoingEmailValidator validator;
 | 
				
			||||||
  private final String username;
 | 
					  private final String username;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Inject
 | 
					  @Inject
 | 
				
			||||||
@@ -95,6 +96,7 @@ public class CreateAccount implements RestModifyView<TopLevelResource, AccountIn
 | 
				
			|||||||
      AuditService auditService,
 | 
					      AuditService auditService,
 | 
				
			||||||
      ExternalIds externalIds,
 | 
					      ExternalIds externalIds,
 | 
				
			||||||
      ExternalIdsUpdate.User externalIdsUpdateFactory,
 | 
					      ExternalIdsUpdate.User externalIdsUpdateFactory,
 | 
				
			||||||
 | 
					      OutgoingEmailValidator validator,
 | 
				
			||||||
      @Assisted String username) {
 | 
					      @Assisted String username) {
 | 
				
			||||||
    this.db = db;
 | 
					    this.db = db;
 | 
				
			||||||
    this.currentUser = currentUser;
 | 
					    this.currentUser = currentUser;
 | 
				
			||||||
@@ -110,6 +112,7 @@ public class CreateAccount implements RestModifyView<TopLevelResource, AccountIn
 | 
				
			|||||||
    this.auditService = auditService;
 | 
					    this.auditService = auditService;
 | 
				
			||||||
    this.externalIds = externalIds;
 | 
					    this.externalIds = externalIds;
 | 
				
			||||||
    this.externalIdsUpdateFactory = externalIdsUpdateFactory;
 | 
					    this.externalIdsUpdateFactory = externalIdsUpdateFactory;
 | 
				
			||||||
 | 
					    this.validator = validator;
 | 
				
			||||||
    this.username = username;
 | 
					    this.username = username;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -141,7 +144,7 @@ public class CreateAccount implements RestModifyView<TopLevelResource, AccountIn
 | 
				
			|||||||
      if (externalIds.get(db, ExternalId.Key.create(SCHEME_MAILTO, input.email)) != null) {
 | 
					      if (externalIds.get(db, ExternalId.Key.create(SCHEME_MAILTO, input.email)) != null) {
 | 
				
			||||||
        throw new UnprocessableEntityException("email '" + input.email + "' already exists");
 | 
					        throw new UnprocessableEntityException("email '" + input.email + "' already exists");
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (!OutgoingEmailValidator.isValid(input.email)) {
 | 
					      if (!validator.isValid(input.email)) {
 | 
				
			||||||
        throw new BadRequestException("invalid email address");
 | 
					        throw new BadRequestException("invalid email address");
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -57,6 +57,7 @@ public class CreateEmail implements RestModifyView<AccountResource, EmailInput>
 | 
				
			|||||||
  private final AccountManager accountManager;
 | 
					  private final AccountManager accountManager;
 | 
				
			||||||
  private final RegisterNewEmailSender.Factory registerNewEmailFactory;
 | 
					  private final RegisterNewEmailSender.Factory registerNewEmailFactory;
 | 
				
			||||||
  private final PutPreferred putPreferred;
 | 
					  private final PutPreferred putPreferred;
 | 
				
			||||||
 | 
					  private final OutgoingEmailValidator validator;
 | 
				
			||||||
  private final String email;
 | 
					  private final String email;
 | 
				
			||||||
  private final boolean isDevMode;
 | 
					  private final boolean isDevMode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -69,6 +70,7 @@ public class CreateEmail implements RestModifyView<AccountResource, EmailInput>
 | 
				
			|||||||
      AccountManager accountManager,
 | 
					      AccountManager accountManager,
 | 
				
			||||||
      RegisterNewEmailSender.Factory registerNewEmailFactory,
 | 
					      RegisterNewEmailSender.Factory registerNewEmailFactory,
 | 
				
			||||||
      PutPreferred putPreferred,
 | 
					      PutPreferred putPreferred,
 | 
				
			||||||
 | 
					      OutgoingEmailValidator validator,
 | 
				
			||||||
      @Assisted String email) {
 | 
					      @Assisted String email) {
 | 
				
			||||||
    this.self = self;
 | 
					    this.self = self;
 | 
				
			||||||
    this.realm = realm;
 | 
					    this.realm = realm;
 | 
				
			||||||
@@ -76,6 +78,7 @@ public class CreateEmail implements RestModifyView<AccountResource, EmailInput>
 | 
				
			|||||||
    this.accountManager = accountManager;
 | 
					    this.accountManager = accountManager;
 | 
				
			||||||
    this.registerNewEmailFactory = registerNewEmailFactory;
 | 
					    this.registerNewEmailFactory = registerNewEmailFactory;
 | 
				
			||||||
    this.putPreferred = putPreferred;
 | 
					    this.putPreferred = putPreferred;
 | 
				
			||||||
 | 
					    this.validator = validator;
 | 
				
			||||||
    this.email = email;
 | 
					    this.email = email;
 | 
				
			||||||
    this.isDevMode = authConfig.getAuthType() == DEVELOPMENT_BECOME_ANY_ACCOUNT;
 | 
					    this.isDevMode = authConfig.getAuthType() == DEVELOPMENT_BECOME_ANY_ACCOUNT;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -93,7 +96,7 @@ public class CreateEmail implements RestModifyView<AccountResource, EmailInput>
 | 
				
			|||||||
      input = new EmailInput();
 | 
					      input = new EmailInput();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!OutgoingEmailValidator.isValid(email)) {
 | 
					    if (!validator.isValid(email)) {
 | 
				
			||||||
      throw new BadRequestException("invalid email address");
 | 
					      throw new BadRequestException("invalid email address");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,13 +44,18 @@ public class ExternalIdsConsistencyChecker {
 | 
				
			|||||||
  private final GitRepositoryManager repoManager;
 | 
					  private final GitRepositoryManager repoManager;
 | 
				
			||||||
  private final AllUsersName allUsers;
 | 
					  private final AllUsersName allUsers;
 | 
				
			||||||
  private final AccountCache accountCache;
 | 
					  private final AccountCache accountCache;
 | 
				
			||||||
 | 
					  private final OutgoingEmailValidator validator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Inject
 | 
					  @Inject
 | 
				
			||||||
  ExternalIdsConsistencyChecker(
 | 
					  ExternalIdsConsistencyChecker(
 | 
				
			||||||
      GitRepositoryManager repoManager, AllUsersName allUsers, AccountCache accountCache) {
 | 
					      GitRepositoryManager repoManager,
 | 
				
			||||||
 | 
					      AllUsersName allUsers,
 | 
				
			||||||
 | 
					      AccountCache accountCache,
 | 
				
			||||||
 | 
					      OutgoingEmailValidator validator) {
 | 
				
			||||||
    this.repoManager = repoManager;
 | 
					    this.repoManager = repoManager;
 | 
				
			||||||
    this.allUsers = allUsers;
 | 
					    this.allUsers = allUsers;
 | 
				
			||||||
    this.accountCache = accountCache;
 | 
					    this.accountCache = accountCache;
 | 
				
			||||||
 | 
					    this.validator = validator;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public List<ConsistencyProblemInfo> check() throws IOException {
 | 
					  public List<ConsistencyProblemInfo> check() throws IOException {
 | 
				
			||||||
@@ -123,7 +128,7 @@ public class ExternalIdsConsistencyChecker {
 | 
				
			|||||||
          problems);
 | 
					          problems);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (extId.email() != null && !OutgoingEmailValidator.isValid(extId.email())) {
 | 
					    if (extId.email() != null && !validator.isValid(extId.email())) {
 | 
				
			||||||
      addError(
 | 
					      addError(
 | 
				
			||||||
          String.format(
 | 
					          String.format(
 | 
				
			||||||
              "External ID '%s' has an invalid email: %s", extId.key().get(), extId.email()),
 | 
					              "External ID '%s' has an invalid email: %s", extId.key().get(), extId.email()),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -101,6 +101,7 @@ public class PostReviewers implements RestModifyView<ChangeResource, AddReviewer
 | 
				
			|||||||
  private final ProjectCache projectCache;
 | 
					  private final ProjectCache projectCache;
 | 
				
			||||||
  private final Provider<AnonymousUser> anonymousProvider;
 | 
					  private final Provider<AnonymousUser> anonymousProvider;
 | 
				
			||||||
  private final PostReviewersOp.Factory postReviewersOpFactory;
 | 
					  private final PostReviewersOp.Factory postReviewersOpFactory;
 | 
				
			||||||
 | 
					  private final OutgoingEmailValidator validator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Inject
 | 
					  @Inject
 | 
				
			||||||
  PostReviewers(
 | 
					  PostReviewers(
 | 
				
			||||||
@@ -120,7 +121,8 @@ public class PostReviewers implements RestModifyView<ChangeResource, AddReviewer
 | 
				
			|||||||
      NotifyUtil notifyUtil,
 | 
					      NotifyUtil notifyUtil,
 | 
				
			||||||
      ProjectCache projectCache,
 | 
					      ProjectCache projectCache,
 | 
				
			||||||
      Provider<AnonymousUser> anonymousProvider,
 | 
					      Provider<AnonymousUser> anonymousProvider,
 | 
				
			||||||
      PostReviewersOp.Factory postReviewersOpFactory) {
 | 
					      PostReviewersOp.Factory postReviewersOpFactory,
 | 
				
			||||||
 | 
					      OutgoingEmailValidator validator) {
 | 
				
			||||||
    this.accounts = accounts;
 | 
					    this.accounts = accounts;
 | 
				
			||||||
    this.reviewerFactory = reviewerFactory;
 | 
					    this.reviewerFactory = reviewerFactory;
 | 
				
			||||||
    this.permissionBackend = permissionBackend;
 | 
					    this.permissionBackend = permissionBackend;
 | 
				
			||||||
@@ -138,6 +140,7 @@ public class PostReviewers implements RestModifyView<ChangeResource, AddReviewer
 | 
				
			|||||||
    this.projectCache = projectCache;
 | 
					    this.projectCache = projectCache;
 | 
				
			||||||
    this.anonymousProvider = anonymousProvider;
 | 
					    this.anonymousProvider = anonymousProvider;
 | 
				
			||||||
    this.postReviewersOpFactory = postReviewersOpFactory;
 | 
					    this.postReviewersOpFactory = postReviewersOpFactory;
 | 
				
			||||||
 | 
					    this.validator = validator;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
@@ -291,7 +294,7 @@ public class PostReviewers implements RestModifyView<ChangeResource, AddReviewer
 | 
				
			|||||||
    } catch (IllegalArgumentException e) {
 | 
					    } catch (IllegalArgumentException e) {
 | 
				
			||||||
      throw new UnprocessableEntityException(String.format("email invalid %s", reviewer));
 | 
					      throw new UnprocessableEntityException(String.format("email invalid %s", reviewer));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (!OutgoingEmailValidator.isValid(adr.getEmail())) {
 | 
					    if (!validator.isValid(adr.getEmail())) {
 | 
				
			||||||
      throw new UnprocessableEntityException(String.format("email invalid %s", reviewer));
 | 
					      throw new UnprocessableEntityException(String.format("email invalid %s", reviewer));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return new Addition(
 | 
					    return new Addition(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -80,6 +80,7 @@ public class EmailArguments {
 | 
				
			|||||||
  final DynamicSet<OutgoingEmailValidationListener> outgoingEmailValidationListeners;
 | 
					  final DynamicSet<OutgoingEmailValidationListener> outgoingEmailValidationListeners;
 | 
				
			||||||
  final StarredChangesUtil starredChangesUtil;
 | 
					  final StarredChangesUtil starredChangesUtil;
 | 
				
			||||||
  final Provider<InternalAccountQuery> accountQueryProvider;
 | 
					  final Provider<InternalAccountQuery> accountQueryProvider;
 | 
				
			||||||
 | 
					  final OutgoingEmailValidator validator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Inject
 | 
					  @Inject
 | 
				
			||||||
  EmailArguments(
 | 
					  EmailArguments(
 | 
				
			||||||
@@ -111,7 +112,8 @@ public class EmailArguments {
 | 
				
			|||||||
      SitePaths site,
 | 
					      SitePaths site,
 | 
				
			||||||
      DynamicSet<OutgoingEmailValidationListener> outgoingEmailValidationListeners,
 | 
					      DynamicSet<OutgoingEmailValidationListener> outgoingEmailValidationListeners,
 | 
				
			||||||
      StarredChangesUtil starredChangesUtil,
 | 
					      StarredChangesUtil starredChangesUtil,
 | 
				
			||||||
      Provider<InternalAccountQuery> accountQueryProvider) {
 | 
					      Provider<InternalAccountQuery> accountQueryProvider,
 | 
				
			||||||
 | 
					      OutgoingEmailValidator validator) {
 | 
				
			||||||
    this.server = server;
 | 
					    this.server = server;
 | 
				
			||||||
    this.projectCache = projectCache;
 | 
					    this.projectCache = projectCache;
 | 
				
			||||||
    this.groupBackend = groupBackend;
 | 
					    this.groupBackend = groupBackend;
 | 
				
			||||||
@@ -141,5 +143,6 @@ public class EmailArguments {
 | 
				
			|||||||
    this.outgoingEmailValidationListeners = outgoingEmailValidationListeners;
 | 
					    this.outgoingEmailValidationListeners = outgoingEmailValidationListeners;
 | 
				
			||||||
    this.starredChangesUtil = starredChangesUtil;
 | 
					    this.starredChangesUtil = starredChangesUtil;
 | 
				
			||||||
    this.accountQueryProvider = accountQueryProvider;
 | 
					    this.accountQueryProvider = accountQueryProvider;
 | 
				
			||||||
 | 
					    this.validator = validator;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -478,7 +478,7 @@ public abstract class OutgoingEmail {
 | 
				
			|||||||
  /** Schedule delivery of this message to the given account. */
 | 
					  /** Schedule delivery of this message to the given account. */
 | 
				
			||||||
  protected void add(final RecipientType rt, final Address addr) {
 | 
					  protected void add(final RecipientType rt, final Address addr) {
 | 
				
			||||||
    if (addr != null && addr.getEmail() != null && addr.getEmail().length() > 0) {
 | 
					    if (addr != null && addr.getEmail() != null && addr.getEmail().length() > 0) {
 | 
				
			||||||
      if (!OutgoingEmailValidator.isValid(addr.getEmail())) {
 | 
					      if (!args.validator.isValid(addr.getEmail())) {
 | 
				
			||||||
        log.warn("Not emailing " + addr.getEmail() + " (invalid email address)");
 | 
					        log.warn("Not emailing " + addr.getEmail() + " (invalid email address)");
 | 
				
			||||||
      } else if (!args.emailSender.canEmail(addr.getEmail())) {
 | 
					      } else if (!args.emailSender.canEmail(addr.getEmail())) {
 | 
				
			||||||
        log.warn("Not emailing " + addr.getEmail() + " (prohibited by allowrcpt)");
 | 
					        log.warn("Not emailing " + addr.getEmail() + " (prohibited by allowrcpt)");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,15 +16,27 @@ package com.google.gerrit.server.mail.send;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import static org.apache.commons.validator.routines.DomainValidator.ArrayType.GENERIC_PLUS;
 | 
					import static org.apache.commons.validator.routines.DomainValidator.ArrayType.GENERIC_PLUS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.inject.Singleton;
 | 
				
			||||||
import org.apache.commons.validator.routines.DomainValidator;
 | 
					import org.apache.commons.validator.routines.DomainValidator;
 | 
				
			||||||
import org.apache.commons.validator.routines.EmailValidator;
 | 
					import org.apache.commons.validator.routines.EmailValidator;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Singleton
 | 
				
			||||||
public class OutgoingEmailValidator {
 | 
					public class OutgoingEmailValidator {
 | 
				
			||||||
  static {
 | 
					  private static final Logger log = LoggerFactory.getLogger(OutgoingEmailValidator.class);
 | 
				
			||||||
    DomainValidator.updateTLDOverride(GENERIC_PLUS, new String[] {"local"});
 | 
					
 | 
				
			||||||
 | 
					  OutgoingEmailValidator() {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      DomainValidator.updateTLDOverride(GENERIC_PLUS, new String[] {"local"});
 | 
				
			||||||
 | 
					    } catch (IllegalStateException e) {
 | 
				
			||||||
 | 
					      // Should only happen in tests, where the OutgoingEmailValidator
 | 
				
			||||||
 | 
					      // is instantiated repeatedly.
 | 
				
			||||||
 | 
					      log.warn("Failed to update TLD override: " + e.getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public static boolean isValid(String addr) {
 | 
					  public boolean isValid(String addr) {
 | 
				
			||||||
    return EmailValidator.getInstance(true, true).isValid(addr);
 | 
					    return EmailValidator.getInstance(true, true).isValid(addr);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user