Allow to configure TLD override for outgoing emails

Email addresses are validated using the commons validator, which uses a
list of TLDs from IANA [1].  The TLD list is updated more frequently than
the commons validator is released and upgraded in Gerrit.

It is possible to configure the validator to allow extra custom TLDs, but
so far it was hard-coded only with "local".

Add a new configuration setting to allow administrators to add extra TLDs
that will be allowed by the validator. Make the default an empty list,
which is a behaviour change from the current; administrators of sites that
have users with @local email addresses will need to explicily add "local"
in the sendemail.allowTLD setting.

[1] http://data.iana.org/TLD/

Bug: Issue 5238
Change-Id: I22a14cdd46a2a2454cafd1761866c29ed1509270
This commit is contained in:
David Pursehouse
2017-04-21 18:18:52 +02:00
parent c7e6021e8b
commit 917b726ca6
4 changed files with 46 additions and 13 deletions

View File

@@ -3792,6 +3792,13 @@ Gerrit can parse back a user's reply.
Defaults to an empty string which adds <<sendemail.from,sendemail.from>> as
Reply-To if inbound email is enabled and the review's author otherwise.
[[sendemail.allowTLD]]sendemail.allowTLD::
+
List of custom TLDs to allow sending emails to in addition to those specified
in the link:http://data.iana.org/TLD/[IANA list].
+
Defaults to an empty list, meaning no additional TLDs are allowed.
[[site]]
=== Section site

View File

@@ -413,10 +413,7 @@ public class AccountIT extends AbstractDaemonTest {
List<String> emails =
ImmutableList.of(
"new.email@example.com",
"new.email@example.systems",
// Not in the list of TLDs but added to override in OutgoingEmailValidator
"new.email@example.local");
"new.email@example.systems");
Set<String> currentEmails = getEmails();
for (String email : emails) {
assertThat(currentEmails).doesNotContain(email);

View File

@@ -18,11 +18,14 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assert_;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.server.mail.send.OutgoingEmailValidator;
import com.google.inject.Inject;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import org.junit.After;
import org.junit.Test;
public class EmailValidatorIT extends AbstractDaemonTest {
@@ -30,9 +33,28 @@ public class EmailValidatorIT extends AbstractDaemonTest {
@Inject private OutgoingEmailValidator validator;
@After
public void resetDomainValidator() throws Exception {
Class<?> c = Class.forName("org.apache.commons.validator.routines.DomainValidator");
Field f = c.getDeclaredField("inUse");
f.setAccessible(true);
f.setBoolean(c, false);
}
@Test
public void validateLocalDomain() throws Exception {
assertThat(validator.isValid("foo@bar.local")).isTrue();
@GerritConfig(name = "sendemail.allowTLD", value = "example")
public void testCustomTopLevelDomain() throws Exception {
assertThat(validator.isValid("foo@bar.local")).isFalse();
assertThat(validator.isValid("foo@bar.example")).isTrue();
assertThat(validator.isValid("foo@example")).isTrue();
}
@Test
@GerritConfig(name = "sendemail.allowTLD", value = "a")
public void testCustomTopLevelDomainOneCharacter() throws Exception {
assertThat(validator.isValid("foo@bar.local")).isFalse();
assertThat(validator.isValid("foo@bar.a")).isTrue();
assertThat(validator.isValid("foo@a")).isTrue();
}
@Test

View File

@@ -16,9 +16,12 @@ package com.google.gerrit.server.mail.send;
import static org.apache.commons.validator.routines.DomainValidator.ArrayType.GENERIC_PLUS;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.apache.commons.validator.routines.DomainValidator;
import org.apache.commons.validator.routines.EmailValidator;
import org.eclipse.jgit.lib.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -26,13 +29,17 @@ import org.slf4j.LoggerFactory;
public class OutgoingEmailValidator {
private static final Logger log = LoggerFactory.getLogger(OutgoingEmailValidator.class);
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());
@Inject
OutgoingEmailValidator(@GerritServerConfig Config config) {
String[] allowTLD = config.getStringList("sendemail", null, "allowTLD");
if (allowTLD.length != 0) {
try {
DomainValidator.updateTLDOverride(GENERIC_PLUS, allowTLD);
} catch (IllegalStateException e) {
// Should only happen in tests, where the OutgoingEmailValidator
// is instantiated repeatedly.
log.error("Failed to update TLD override: " + e.getMessage());
}
}
}