Merge "Disallow ref creation when passed object is neither Commit or Tag"
diff --git a/java/com/google/gerrit/server/project/CreateRefControl.java b/java/com/google/gerrit/server/project/CreateRefControl.java
index ab134b5..c3d3978 100644
--- a/java/com/google/gerrit/server/project/CreateRefControl.java
+++ b/java/com/google/gerrit/server/project/CreateRefControl.java
@@ -112,7 +112,9 @@
                 RefPermission.READ.describeForException()));
         throw e;
       }
-    } else if (object instanceof RevTag) {
+      return;
+    }
+    if (object instanceof RevTag) {
       RevTag tag = (RevTag) object;
       try (RevWalk rw = new RevWalk(repo)) {
         rw.parseBody(tag);
@@ -145,7 +147,11 @@
       } else {
         forRef.check(RefPermission.CREATE_TAG);
       }
+      return;
     }
+    throw new AuthException(
+        String.format(
+            "Ref creation not allowed. Object %s is neither Commit or Tag.", object.getId()));
   }
 
   /**
diff --git a/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java b/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java
index f94aa12..fa6d2e4 100644
--- a/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java
@@ -45,6 +45,7 @@
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.transport.PushResult;
 import org.eclipse.jgit.transport.RefSpec;
 import org.eclipse.jgit.transport.RemoteRefUpdate;
@@ -405,6 +406,33 @@
     assertThat(r).hasProcessed(ImmutableMap.of("refs", 1));
   }
 
+  @Test
+  public void pushTreeIsNotAllowed() throws Exception {
+    RevCommit commit = testRepo.branch("HEAD").commit().create();
+    projectOperations
+        .project(project)
+        .forUpdate()
+        .add(allow(Permission.CREATE).ref("refs/*").group(REGISTERED_USERS))
+        .add(allow(Permission.PUSH).ref("refs/*").group(REGISTERED_USERS))
+        .update();
+
+    // We use "refs/main" instead of "refs/heads/main", because the latter only allows commits.
+    {
+      // An extra colon (:) makes it a tree reference
+      PushResult r = push(commit.getId().getName() + "::refs/main");
+      RemoteRefUpdate refUpdate = r.getRemoteUpdates().stream().findFirst().get();
+      assertThat(refUpdate.getStatus()).isEqualTo(Status.REJECTED_OTHER_REASON);
+      assertThat(refUpdate.getMessage()).contains("is neither Commit or Tag");
+    }
+
+    {
+      PushResult r = push(commit.getTree().getId().getName() + ":refs/main");
+      RemoteRefUpdate refUpdate = r.getRemoteUpdates().stream().findFirst().get();
+      assertThat(refUpdate.getStatus()).isEqualTo(Status.REJECTED_OTHER_REASON);
+      assertThat(refUpdate.getMessage()).contains("is neither Commit or Tag");
+    }
+  }
+
   private static void removeAllBranchPermissions(ProjectConfig cfg, String... permissions) {
     for (AccessSection s : cfg.getAccessSections()) {
       if (s.getName().startsWith("refs/heads/")