Add config option to disallow functions globally

There are cases where the host administrator wants to ensure that all
labels have functions from a set that they define. This commit adds
a configuration parameter to the project.config's plugin section
that makes it so that functions can be disallowed for each label.

Change-Id: I3eb497d7f2d3abb1cdad42c25cf2ad86eac2e01d
diff --git a/src/main/java/com/googlesource/gerrit/plugins/simplesubmitrules/config/ConfigTranslator.java b/src/main/java/com/googlesource/gerrit/plugins/simplesubmitrules/config/ConfigTranslator.java
index de7783c..178fadb 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/simplesubmitrules/config/ConfigTranslator.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/simplesubmitrules/config/ConfigTranslator.java
@@ -16,6 +16,7 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.common.data.LabelFunction;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -31,6 +32,7 @@
 import com.googlesource.gerrit.plugins.simplesubmitrules.api.SubmitConfig;
 import java.util.Collection;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 
 /** Codec class used to convert {@link SubmitConfig} from/to a Gerrit config */
@@ -106,12 +108,15 @@
   }
 
   void applyTo(SubmitConfig inConfig, ProjectState projectState) throws BadRequestException {
-    PluginConfig pluginConfig = pluginConfigFactory.getFromProjectConfig(projectState, pluginName);
-    applyCommentRulesTo(inConfig.comments, pluginConfig);
-    applyLabelsTo(inConfig.labels, projectState);
+    PluginConfig hostPluginConfig = pluginConfigFactory.getFromGerritConfig(pluginName);
+    PluginConfig projectPluginConfig =
+        pluginConfigFactory.getFromProjectConfig(projectState, pluginName);
+    applyCommentRulesTo(inConfig.comments, projectPluginConfig);
+    applyLabelsTo(inConfig.labels, projectState, hostPluginConfig);
   }
 
-  private static void applyLabelsTo(Map<String, LabelDefinition> labels, ProjectState projectState)
+  private static void applyLabelsTo(
+      Map<String, LabelDefinition> labels, ProjectState projectState, PluginConfig hostPluginConfig)
       throws BadRequestException {
     if (labels.isEmpty()) {
       return;
@@ -140,10 +145,20 @@
             "The label " + label + " does not exist. You can't change its config.");
       }
 
-      definition.getFunction().ifPresent(labelType::setFunction);
       if (definition.ignoreSelfApproval != null) {
         labelType.setIgnoreSelfApproval(definition.ignoreSelfApproval);
       }
+
+      if (definition.getFunction().isPresent()) {
+        List<String> disallowedLabelFunctions =
+            ImmutableList.copyOf(
+                hostPluginConfig.getStringList("disallowedLabelFunctions-" + label));
+        LabelFunction function = definition.getFunction().get();
+        if (disallowedLabelFunctions.contains(function.getFunctionName())) {
+          throw new BadRequestException(function.getFunctionName() + " disallowed");
+        }
+        labelType.setFunction(function);
+      }
       applyCopyScoresTo(definition.copyScores, labelType);
     }
   }
diff --git a/src/main/resources/Documentation/about.md b/src/main/resources/Documentation/about.md
index 3305d79..33f465c 100644
--- a/src/main/resources/Documentation/about.md
+++ b/src/main/resources/Documentation/about.md
@@ -106,3 +106,21 @@
 Example: `MaxWithBlock`, `AnyWithBlock`...
 
 See the Labels documentation page for more information.
+
+### Configuration in gerrit.config
+
+The following is a list of configuration options that can be changed
+in gerrit.config. All configs have to be nested under plugin.@PLUGIN@:
+
+#### disallowedLabelFunctions-<label-name>
+
+This config will prevent users from changing the function in the
+label configuration that is referenced in the name to a matching value.
+However, the config does not effect existing labels that already have
+the forbidden value.
+
+Example:
+```
+[plugin "simple-submit"]
+  disallowedLabelFunctions-Code-Review = MaxNoBlock
+```
diff --git a/src/test/java/com/googlesource/gerrit/plugins/simplesubmitrules/config/ConfigServletIT.java b/src/test/java/com/googlesource/gerrit/plugins/simplesubmitrules/config/ConfigServletIT.java
index 815198e..0c078c7 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/simplesubmitrules/config/ConfigServletIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/simplesubmitrules/config/ConfigServletIT.java
@@ -14,9 +14,11 @@
 
 package com.googlesource.gerrit.plugins.simplesubmitrules.config;
 
+import static com.google.common.truth.Truth.assertThat;
 import static com.google.gerrit.server.project.testing.Util.value;
 
 import com.google.common.base.Charsets;
+import com.google.gerrit.acceptance.GerritConfig;
 import com.google.gerrit.acceptance.LightweightPluginDaemonTest;
 import com.google.gerrit.acceptance.RestResponse;
 import com.google.gerrit.acceptance.TestPlugin;
@@ -68,6 +70,16 @@
     r.assertForbidden();
   }
 
+  @Test
+  @GerritConfig(
+      name = "plugin.my-plugin.disallowedLabelFunctions-Code-Review",
+      value = "MaxWithBlock")
+  public void disallowedFunctionThrowsBadRequestException() throws Exception {
+    RawInput rawInput = createConfig();
+    RestResponse r = adminRestSession.putRaw(endpointUrl(project), rawInput);
+    assertThat(r.getEntityContent()).isEqualTo("MaxWithBlock disallowed");
+  }
+
   private static RawInput createConfig() {
     return RawInputUtil.create(
         ("{\n"