Reject %wip/%ready from non-owners of change

When a user who is not the owner of a change pushes a new patch set to
that change, prevent them from modifying the WIP state of the change.

Bug: Issue 6596
Change-Id: I4931d9cea634eedec09ba75f458e8383756d3560
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
index 773ad44..bcf62bd 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -62,6 +62,7 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.git.ReceiveCommits;
 import com.google.gerrit.server.mail.Address;
 import com.google.gerrit.server.project.Util;
 import com.google.gerrit.server.query.change.ChangeData;
@@ -454,16 +455,52 @@
     assertThat(r.getChange().change().isWorkInProgress()).isFalse();
 
     // Make the change work-in-progress again.
-    r = pushTo("refs/for/master%wip");
+    r = amendChange(r.getChangeId(), "refs/for/master%wip");
     r.assertOkStatus();
     assertThat(r.getChange().change().isWorkInProgress()).isTrue();
 
     // Can't use --wip and --ready together.
-    r = pushTo("refs/for/master%wip,ready");
+    r = amendChange(r.getChangeId(), "refs/for/master%wip,ready");
     r.assertErrorStatus();
   }
 
   @Test
+  public void pushWorkInProgressChangeWhenNotOwner() throws Exception {
+    TestRepository<?> userRepo = cloneProject(project, user);
+    PushOneCommit.Result r =
+        pushFactory.create(db, user.getIdent(), userRepo).to("refs/for/master%wip");
+    r.assertOkStatus();
+    assertThat(r.getChange().change().getOwner()).isEqualTo(user.id);
+    assertThat(r.getChange().change().isWorkInProgress()).isTrue();
+
+    // Other user trying to move from WIP to ready should fail.
+    GitUtil.fetch(testRepo, r.getPatchSet().getRefName() + ":ps");
+    testRepo.reset("ps");
+    r = amendChange(r.getChangeId(), "refs/for/master%ready", admin, testRepo);
+    r.assertErrorStatus(ReceiveCommits.ONLY_OWNER_CAN_MODIFY_WIP);
+
+    // Other user trying to move from WIP to WIP should succeed.
+    r = amendChange(r.getChangeId(), "refs/for/master%wip", admin, testRepo);
+    r.assertOkStatus();
+    assertThat(r.getChange().change().isWorkInProgress()).isTrue();
+
+    // Push as change owner to move change from WIP to ready.
+    r = pushFactory.create(db, user.getIdent(), userRepo).to("refs/for/master%ready");
+    r.assertOkStatus();
+    assertThat(r.getChange().change().isWorkInProgress()).isFalse();
+
+    // Other user trying to move from ready to WIP should fail.
+    GitUtil.fetch(testRepo, r.getPatchSet().getRefName() + ":ps");
+    testRepo.reset("ps");
+    r = amendChange(r.getChangeId(), "refs/for/master%wip", admin, testRepo);
+    r.assertErrorStatus(ReceiveCommits.ONLY_OWNER_CAN_MODIFY_WIP);
+
+    // Other user trying to move from ready to ready should succeed.
+    r = amendChange(r.getChangeId(), "refs/for/master%ready", admin, testRepo);
+    r.assertOkStatus();
+  }
+
+  @Test
   public void pushForMasterAsDraft() throws Exception {
     // create draft by pushing to 'refs/drafts/'
     PushOneCommit.Result r = pushTo("refs/drafts/master");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
index 6b3f173..5cd2184 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -202,6 +202,9 @@
           + "Squash the commits with the same Change-Id or "
           + "ensure Change-Ids are unique for each commit";
 
+  public static final String ONLY_OWNER_CAN_MODIFY_WIP =
+      "only change owner can modify Work-in-Progress";
+
   private enum Error {
     CONFIG_UPDATE(
         "You are not allowed to perform this operation.\n"
@@ -2484,6 +2487,14 @@
         }
       }
 
+      if (magicBranch != null
+          && (magicBranch.workInProgress || magicBranch.ready)
+          && magicBranch.workInProgress != change.isWorkInProgress()
+          && !user.getAccountId().equals(change.getOwner())) {
+        reject(inputCommand, ONLY_OWNER_CAN_MODIFY_WIP);
+        return false;
+      }
+
       if (magicBranch != null && magicBranch.edit) {
         return newEdit();
       }