Scope validation by email

Change-Id: I252eed1861d02861c0bd0830fbc15eeca759c72a
diff --git a/src/main/java/com/googlesource/gerrit/plugins/uploadvalidator/ValidatorConfig.java b/src/main/java/com/googlesource/gerrit/plugins/uploadvalidator/ValidatorConfig.java
index e3d9260..cf80a6c 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/uploadvalidator/ValidatorConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/uploadvalidator/ValidatorConfig.java
@@ -27,6 +27,7 @@
 import org.slf4j.LoggerFactory;
 
 import java.util.Arrays;
+import java.util.regex.Pattern;
 import java.util.stream.Stream;
 
 public class ValidatorConfig {
@@ -49,6 +50,7 @@
     return conf != null
         && isValidConfig(conf, projectName)
         && (activeForRef(conf, refName))
+        && (activeForEmail(conf, user.getAccount().getPreferredEmail()))
         && (!hasCriteria(conf, "skipGroup")
             || !canSkipValidation(conf, validatorOp)
             || !canSkipRef(conf, refName)
@@ -79,22 +81,27 @@
   }
 
   private boolean activeForRef(PluginConfig config, String ref) {
-    return matchCriteria(config, "ref", ref, true);
+    return matchCriteria(config, "ref", ref, true, true);
+  }
+
+  private boolean activeForEmail(PluginConfig config, String email) {
+    return matchCriteria(config, "email", email, true, false);
   }
 
   private boolean canSkipValidation(PluginConfig config, String validatorOp) {
-    return matchCriteria(config, "skipValidation", validatorOp, false);
+    return matchCriteria(config, "skipValidation", validatorOp, false, false);
   }
 
   private boolean canSkipRef(PluginConfig config, String ref) {
-    return matchCriteria(config, "skipRef", ref, true);
+    return matchCriteria(config, "skipRef", ref, true, true);
   }
 
   private boolean matchCriteria(PluginConfig config, String criteria,
-      String value, boolean allowRegex) {
+      String value, boolean allowRegex, boolean refMatcher) {
     boolean match = true;
     for (String s : config.getStringList(criteria)) {
-      if ((allowRegex && match(value, s)) || (!allowRegex && s.equals(value))) {
+      if ((allowRegex && match(value, s, refMatcher)) ||
+          (!allowRegex && s.equals(value))) {
         return true;
       }
       match = false;
@@ -102,8 +109,13 @@
     return match;
   }
 
-  private static boolean match(String value, String pattern) {
-    return RefPatternMatcher.getMatcher(pattern).match(value, null);
+  private static boolean match(String value, String pattern,
+      boolean refMatcher) {
+    if (refMatcher) {
+      return RefPatternMatcher.getMatcher(pattern).match(value, null);
+    } else {
+      return Pattern.matches(pattern, value);
+    }
   }
 
   private boolean canSkipGroup(PluginConfig conf, IdentifiedUser user) {
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index 398a771..a4151a0 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -225,6 +225,22 @@
     ref = ^refs/heads/stable-.*
 ```
 
+Email-specific validations
+---------------------------
+
+By default, the validation will be enabled for all users. However, it can
+be limited to users with particular emails by setting `plugin.@PLUGIN@.email`.
+The emails may be configured using specific emails, patterns, or regular
+expressions. Multiple emails may be specified.
+
+E.g. to limit the validation to all users whose emails match `.*@example.com$`
+the following could be configured:
+
+```
+  [plugin "@PLUGIN@"]
+    email = .*@example.com$
+```
+
 Permission to skip the rules
 ----------------------------
 
diff --git a/src/test/java/com/googlesource/gerrit/plugins/uploadvalidator/EmailAwareValidatorConfigTest.java b/src/test/java/com/googlesource/gerrit/plugins/uploadvalidator/EmailAwareValidatorConfigTest.java
new file mode 100644
index 0000000..46bf3c7
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/uploadvalidator/EmailAwareValidatorConfigTest.java
@@ -0,0 +1,108 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// 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 com.googlesource.gerrit.plugins.uploadvalidator;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.IdentifiedUser;
+
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.junit.Test;
+
+public class EmailAwareValidatorConfigTest {
+  private Project.NameKey projectName = new Project.NameKey("testProject");
+  private IdentifiedUser anyUser = new FakeUserProvider().get();
+
+  @Test
+  public void isEnabledForAllEmailsByDefault() throws Exception {
+    ValidatorConfig config =
+        getConfig("[plugin \"uploadvalidator\"]\n"
+            + "blockedFileExtension = jar");
+
+    assertThat(
+        config.isEnabledForRef(anyUser, projectName, "anyRef",
+            "blockedFileExtension")).isTrue();
+  }
+
+  @Test
+  public void isEnabledForSingleEmail() throws Exception {
+    ValidatorConfig config =
+        getConfig("[plugin \"uploadvalidator\"]\n"
+            + "   email = " + FakeUserProvider.FAKE_EMAIL + "\n"
+            + "   blockedFileExtension = jar");
+
+    assertThat(
+        config.isEnabledForRef(anyUser, projectName, "refs/heads/anyref",
+            "blockedFileExtension")).isTrue();
+  }
+
+  @Test
+  public void isDisabledForInvalidEmail() throws Exception {
+    ValidatorConfig config =
+        getConfig("[plugin \"uploadvalidator\"]\n"
+            + "   email = anInvalidEmail@example.com\n"
+            + "   blockedFileExtension = jar");
+
+    assertThat(
+        config.isEnabledForRef(anyUser, projectName, "refs/heads/anyref",
+            "blockedFileExtension")).isFalse();
+  }
+
+  @Test
+  public void isEnabledForRegexEmail() throws Exception {
+    IdentifiedUser exampleOrgUser = new FakeUserProvider().get("a@example.org");
+    ValidatorConfig config =
+        getConfig("[plugin \"uploadvalidator\"]\n"
+            + "   email = .*@example.org$\n"
+            + "   blockedFileExtension = jar");
+
+    assertThat(
+        config.isEnabledForRef(anyUser, projectName, "refs/heads/anyref",
+            "blockedFileExtension")).isFalse();
+    assertThat(
+        config.isEnabledForRef(exampleOrgUser, projectName,
+            "refs/heads/anyref", "blockedFileExtension")).isTrue();
+  }
+
+  @Test
+  public void isEnabledForMultipleEmails() throws Exception {
+    IdentifiedUser exampleOrgUser = new FakeUserProvider().get("a@example.org");
+    IdentifiedUser xUser = new FakeUserProvider().get("x@example.com");
+    ValidatorConfig config =
+        getConfig("[plugin \"uploadvalidator\"]\n"
+            + "   email = .*@example.org$\n"
+            + "   email = x@example.com\n"
+            + "   blockedFileExtension = jar");
+
+    assertThat(
+        config.isEnabledForRef(exampleOrgUser, projectName, "refs/heads/anyref",
+            "blockedFileExtension")).isTrue();
+    assertThat(
+        config.isEnabledForRef(xUser, projectName, "refs/heads/anyref",
+            "blockedFileExtension")).isTrue();
+    assertThat(
+        config.isEnabledForRef(anyUser, projectName, "refs/heads/anyref",
+            "blockedFileExtension")).isFalse();
+  }
+
+  private ValidatorConfig getConfig(String defaultConfig)
+      throws ConfigInvalidException {
+    ValidatorConfig config =
+        new ValidatorConfig(new FakeConfigFactory(projectName, defaultConfig),
+            new FakeGroupCacheUUIDByName());
+    return config;
+  }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/uploadvalidator/FakeUserProvider.java b/src/test/java/com/googlesource/gerrit/plugins/uploadvalidator/FakeUserProvider.java
index f634090..e49e974 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/uploadvalidator/FakeUserProvider.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/uploadvalidator/FakeUserProvider.java
@@ -18,10 +18,14 @@
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.replay;
 
+import com.google.gerrit.common.TimeUtil;
+import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.inject.Provider;
 
 public class FakeUserProvider implements Provider<IdentifiedUser> {
+  public static String FAKE_EMAIL = "fake@example.com";
+
   private final String[] groupUUID;
 
   public FakeUserProvider(String... groupUUID) {
@@ -30,13 +34,20 @@
 
   @Override
   public IdentifiedUser get() {
-    return createNew();
+    return createNew(FAKE_EMAIL);
   }
 
-  private IdentifiedUser createNew() {
+  public IdentifiedUser get(String email) {
+    return createNew(email);
+  }
+
+  private IdentifiedUser createNew(String email) {
     IdentifiedUser user = createMock(IdentifiedUser.class);
+    Account account = new Account(new Account.Id(1), TimeUtil.nowTs());
+    account.setPreferredEmail(email);
     expect(user.isIdentifiedUser()).andReturn(true);
     expect(user.asIdentifiedUser()).andReturn(user);
+    expect(user.getAccount()).andStubReturn(account);
     expect(user.getEffectiveGroups()).andReturn(
         new FakeGroupMembership(groupUUID));
     replay(user);