Disallow current_user/1 predicate in submit type rules

Submit type is used as part of the mergeability bit computation, which
is persistent and so needs a consistent view of submit type. An
informal poll of repo-discuss[1] indicated that current_user/1 is
pretty much only useful for submit rules, not submit type rules.

[1] https://groups.google.com/d/topic/repo-discuss/vW6XhUOkqik/discussion

Change-Id: Ia938a685409025b36e16979fcafd92afbed2d91f
diff --git a/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java b/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java
index 356b7d5..c7c94f8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java
@@ -25,6 +25,7 @@
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.AnonymousUser;
+import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.patch.PatchList;
@@ -50,7 +51,13 @@
   public static final StoredValue<ReviewDb> REVIEW_DB = create(ReviewDb.class);
   public static final StoredValue<ChangeData> CHANGE_DATA = create(ChangeData.class);
   public static final StoredValue<PatchSet> PATCH_SET = create(PatchSet.class);
+
+  // Note: no guarantees are made about the user passed in the ChangeControl; do
+  // not depend on this directly. Either use .forUser(otherUser) to get a
+  // control for a specific known user, or use CURRENT_USER, which may be null
+  // for rule types that may not depend on the current user.
   public static final StoredValue<ChangeControl> CHANGE_CONTROL = create(ChangeControl.class);
+  public static final StoredValue<CurrentUser> CURRENT_USER = create(CurrentUser.class);
 
   public static Change getChange(Prolog engine) throws SystemException {
     ChangeData cd = CHANGE_DATA.get(engine);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
index 48c67ed..54a95f1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
@@ -28,6 +28,7 @@
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.rules.PrologEnvironment;
 import com.google.gerrit.rules.StoredValues;
+import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gwtorm.server.OrmException;
 
@@ -209,7 +210,8 @@
     List<Term> results;
     try {
       results = evaluateImpl("locate_submit_rule", "can_submit",
-          "locate_submit_filter", "filter_submit_results");
+          "locate_submit_filter", "filter_submit_results",
+          control.getCurrentUser());
     } catch (RuleEvalException e) {
       return ruleError(e.getMessage(), e);
     }
@@ -386,7 +388,11 @@
     List<Term> results;
     try {
       results = evaluateImpl("locate_submit_type", "get_submit_type",
-          "locate_submit_type_filter", "filter_submit_type_results");
+          "locate_submit_type_filter", "filter_submit_type_results",
+          // Do not include current user in submit type evaluation. This is used
+          // for mergeability checks, which are stored persistently and so must
+          // have a consistent view of the submit type.
+          null);
     } catch (RuleEvalException e) {
       return typeError(e.getMessage(), e);
     }
@@ -436,8 +442,9 @@
       String userRuleLocatorName,
       String userRuleWrapperName,
       String filterRuleLocatorName,
-      String filterRuleWrapperName) throws RuleEvalException {
-    PrologEnvironment env = getPrologEnvironment();
+      String filterRuleWrapperName,
+      CurrentUser user) throws RuleEvalException {
+    PrologEnvironment env = getPrologEnvironment(user);
     try {
       Term sr = env.once("gerrit", userRuleLocatorName, new VariableTerm());
       if (fastEvalLabels) {
@@ -479,7 +486,8 @@
     }
   }
 
-  private PrologEnvironment getPrologEnvironment() throws RuleEvalException {
+  private PrologEnvironment getPrologEnvironment(CurrentUser user)
+      throws RuleEvalException {
     checkState(patchSet != null,
         "getPrologEnvironment() called before initPatchSet()");
     ProjectState projectState = control.getProjectControl().getProjectState();
@@ -499,6 +507,9 @@
     env.set(StoredValues.CHANGE_DATA, cd);
     env.set(StoredValues.PATCH_SET, patchSet);
     env.set(StoredValues.CHANGE_CONTROL, control);
+    if (user != null) {
+      env.set(StoredValues.CURRENT_USER, user);
+    }
     return env;
   }
 
diff --git a/gerrit-server/src/main/java/gerrit/PRED_current_user_1.java b/gerrit-server/src/main/java/gerrit/PRED_current_user_1.java
index dd2a008..6d0dd0f 100644
--- a/gerrit-server/src/main/java/gerrit/PRED_current_user_1.java
+++ b/gerrit-server/src/main/java/gerrit/PRED_current_user_1.java
@@ -20,7 +20,6 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.PeerDaemonUser;
-import com.google.gerrit.server.project.ChangeControl;
 
 import com.googlecode.prolog_cafe.lang.EvaluationException;
 import com.googlecode.prolog_cafe.lang.IntegerTerm;
@@ -47,8 +46,11 @@
     engine.setB0();
     Term a1 = arg1.dereference();
 
-    ChangeControl cControl = StoredValues.CHANGE_CONTROL.get(engine);
-    CurrentUser curUser = cControl.getCurrentUser();
+    CurrentUser curUser = StoredValues.CURRENT_USER.getOrNull(engine);
+    if (curUser == null) {
+      throw new EvaluationException(
+          "Current user not available in this rule type");
+    }
     Term resultTerm;
 
     if (curUser.isIdentifiedUser()) {
@@ -67,4 +69,4 @@
     }
     return cont;
   }
-}
\ No newline at end of file
+}