Add ADD_PATCH_SET logic to lock patch sets
I've added a new permission to distinguish between creating a change and adding new patch sets. This is intended for projects that want to restrict the upload of new patch sets to the change owner (or any other group). Change-Id: If9f9f7ed0f0890fb4b854d0bf71e546ebe43ef96
This commit is contained in:
@@ -564,6 +564,19 @@ that would otherwise be used. Or it may give permission to upload
|
|||||||
new changes for code review, this depends on which namespace the
|
new changes for code review, this depends on which namespace the
|
||||||
permission is granted to.
|
permission is granted to.
|
||||||
|
|
||||||
|
[[category_push_patch_set]]
|
||||||
|
=== Push Patch Set
|
||||||
|
|
||||||
|
This category controls which users are allowed to upload new patch sets to
|
||||||
|
existing changes. Irrespective of this permission, change owners are always
|
||||||
|
allowed to upload new patch sets for their changes. This permission needs to be
|
||||||
|
set on `refs/for/*`.
|
||||||
|
|
||||||
|
The absence of this permission will prevent users from uploading a
|
||||||
|
patch set to a change they do not own. By default, this permission is granted to
|
||||||
|
`Registered Users` on `refs/for/*` allowing all registered users to upload a new
|
||||||
|
patch set to any change on that ref.
|
||||||
|
|
||||||
|
|
||||||
[[category_push_direct]]
|
[[category_push_direct]]
|
||||||
==== Direct Push
|
==== Direct Push
|
||||||
|
|||||||
@@ -741,6 +741,12 @@ public abstract class AbstractDaemonTest {
|
|||||||
|
|
||||||
protected PermissionRule block(String permission, AccountGroup.UUID id, String ref)
|
protected PermissionRule block(String permission, AccountGroup.UUID id, String ref)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
return block(permission, id, ref, project);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PermissionRule block(String permission,
|
||||||
|
AccountGroup.UUID id, String ref, Project.NameKey project)
|
||||||
|
throws Exception {
|
||||||
ProjectConfig cfg = projectCache.checkedGet(project).getConfig();
|
ProjectConfig cfg = projectCache.checkedGet(project).getConfig();
|
||||||
PermissionRule rule = Util.block(cfg, permission, id, ref);
|
PermissionRule rule = Util.block(cfg, permission, id, ref);
|
||||||
saveProjectConfig(project, cfg);
|
saveProjectConfig(project, cfg);
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ import com.google.gerrit.reviewdb.client.AccountGroup;
|
|||||||
import com.google.gerrit.reviewdb.client.Change;
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||||
import com.google.gerrit.reviewdb.client.RefNames;
|
import com.google.gerrit.reviewdb.client.RefNames;
|
||||||
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
import com.google.gerrit.server.change.ChangeResource;
|
import com.google.gerrit.server.change.ChangeResource;
|
||||||
import com.google.gerrit.server.config.AnonymousCowardNameProvider;
|
import com.google.gerrit.server.config.AnonymousCowardNameProvider;
|
||||||
import com.google.gerrit.server.git.ProjectConfig;
|
import com.google.gerrit.server.git.ProjectConfig;
|
||||||
@@ -1506,6 +1507,7 @@ public class ChangeIT extends AbstractDaemonTest {
|
|||||||
db, admin.getIdent(), adminTestRepo);
|
db, admin.getIdent(), adminTestRepo);
|
||||||
PushOneCommit.Result r1 = push.to("refs/for/master");
|
PushOneCommit.Result r1 = push.to("refs/for/master");
|
||||||
r1.assertOkStatus();
|
r1.assertOkStatus();
|
||||||
|
|
||||||
// Amend draft as admin
|
// Amend draft as admin
|
||||||
PushOneCommit.Result r2 = amendChange(
|
PushOneCommit.Result r2 = amendChange(
|
||||||
r1.getChangeId(), "refs/drafts/master", admin, adminTestRepo);
|
r1.getChangeId(), "refs/drafts/master", admin, adminTestRepo);
|
||||||
@@ -1518,10 +1520,154 @@ public class ChangeIT extends AbstractDaemonTest {
|
|||||||
// Amend change as user
|
// Amend change as user
|
||||||
PushOneCommit.Result r3 = amendChange(
|
PushOneCommit.Result r3 = amendChange(
|
||||||
r1.getChangeId(), "refs/for/master", user, userTestRepo);
|
r1.getChangeId(), "refs/for/master", user, userTestRepo);
|
||||||
r3.assertErrorStatus("cannot replace "
|
r3.assertErrorStatus("cannot add patch set to "
|
||||||
+ r3.getChange().change().getChangeId() + ".");
|
+ r3.getChange().change().getChangeId() + ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createNewPatchSetWithoutPermission() throws Exception {
|
||||||
|
// Create new project with clean permissions
|
||||||
|
Project.NameKey p = createProject("addPatchSet1");
|
||||||
|
|
||||||
|
// Clone separate repositories of the same project as admin and as user
|
||||||
|
TestRepository<InMemoryRepository> adminTestRepo =
|
||||||
|
cloneProject(p, admin);
|
||||||
|
TestRepository<InMemoryRepository> userTestRepo =
|
||||||
|
cloneProject(p, user);
|
||||||
|
|
||||||
|
// Block default permission
|
||||||
|
block(Permission.ADD_PATCH_SET,
|
||||||
|
REGISTERED_USERS, "refs/for/*", p);
|
||||||
|
|
||||||
|
// Create change as admin
|
||||||
|
PushOneCommit push = pushFactory.create(
|
||||||
|
db, admin.getIdent(), adminTestRepo);
|
||||||
|
PushOneCommit.Result r1 = push.to("refs/for/master");
|
||||||
|
r1.assertOkStatus();
|
||||||
|
|
||||||
|
// Fetch change
|
||||||
|
GitUtil.fetch(userTestRepo, r1.getPatchSet().getRefName() + ":ps");
|
||||||
|
userTestRepo.reset("ps");
|
||||||
|
|
||||||
|
// Amend change as user
|
||||||
|
PushOneCommit.Result r2 =
|
||||||
|
amendChange(r1.getChangeId(), "refs/for/master", user, userTestRepo);
|
||||||
|
r2.assertErrorStatus("cannot add patch set to "
|
||||||
|
+ r1.getChange().getId().id + ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createNewSetPatchWithPermission() throws Exception {
|
||||||
|
// Clone separate repositories of the same project as admin and as user
|
||||||
|
TestRepository<?> adminTestRepo = cloneProject(project, admin);
|
||||||
|
TestRepository<?> userTestRepo = cloneProject(project, user);
|
||||||
|
|
||||||
|
// Create change as admin
|
||||||
|
PushOneCommit push = pushFactory.create(
|
||||||
|
db, admin.getIdent(), adminTestRepo);
|
||||||
|
PushOneCommit.Result r1 = push.to("refs/for/master");
|
||||||
|
r1.assertOkStatus();
|
||||||
|
|
||||||
|
// Fetch change
|
||||||
|
GitUtil.fetch(userTestRepo, r1.getPatchSet().getRefName() + ":ps");
|
||||||
|
userTestRepo.reset("ps");
|
||||||
|
|
||||||
|
// Amend change as user
|
||||||
|
PushOneCommit.Result r2 = amendChange(
|
||||||
|
r1.getChangeId(), "refs/for/master", user, userTestRepo);
|
||||||
|
r2.assertOkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createNewPatchSetAsOwnerWithoutPermission() throws Exception {
|
||||||
|
// Create new project with clean permissions
|
||||||
|
Project.NameKey p = createProject("addPatchSet2");
|
||||||
|
// Clone separate repositories of the same project as admin and as user
|
||||||
|
TestRepository<?> adminTestRepo = cloneProject(project, admin);
|
||||||
|
|
||||||
|
// Block default permission
|
||||||
|
block(Permission.ADD_PATCH_SET, REGISTERED_USERS, "refs/for/*", p);
|
||||||
|
|
||||||
|
// Create change as admin
|
||||||
|
PushOneCommit push =
|
||||||
|
pushFactory.create(db, admin.getIdent(), adminTestRepo);
|
||||||
|
PushOneCommit.Result r1 = push.to("refs/for/master");
|
||||||
|
r1.assertOkStatus();
|
||||||
|
|
||||||
|
// Fetch change
|
||||||
|
GitUtil.fetch(adminTestRepo, r1.getPatchSet().getRefName() + ":ps");
|
||||||
|
adminTestRepo.reset("ps");
|
||||||
|
|
||||||
|
// Amend change as admin
|
||||||
|
PushOneCommit.Result r2 = amendChange(
|
||||||
|
r1.getChangeId(), "refs/for/master", admin, adminTestRepo);
|
||||||
|
r2.assertOkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createNewPatchSetAsReviewerOnDraftChange() throws Exception {
|
||||||
|
// Clone separate repositories of the same project as admin and as user
|
||||||
|
TestRepository<?> adminTestRepo = cloneProject(project, admin);
|
||||||
|
TestRepository<?> userTestRepo = cloneProject(project, user);
|
||||||
|
|
||||||
|
// Create change as admin
|
||||||
|
PushOneCommit push = pushFactory.create(
|
||||||
|
db, admin.getIdent(), adminTestRepo);
|
||||||
|
PushOneCommit.Result r1 = push.to("refs/drafts/master");
|
||||||
|
r1.assertOkStatus();
|
||||||
|
|
||||||
|
// Add user as reviewer
|
||||||
|
AddReviewerInput in = new AddReviewerInput();
|
||||||
|
in.reviewer = user.email;
|
||||||
|
gApi.changes()
|
||||||
|
.id(r1.getChangeId())
|
||||||
|
.addReviewer(in);
|
||||||
|
|
||||||
|
// Fetch change
|
||||||
|
GitUtil.fetch(userTestRepo, r1.getPatchSet().getRefName() + ":ps");
|
||||||
|
userTestRepo.reset("ps");
|
||||||
|
|
||||||
|
// Amend change as user
|
||||||
|
PushOneCommit.Result r2 = amendChange(
|
||||||
|
r1.getChangeId(), "refs/for/master", user, userTestRepo);
|
||||||
|
r2.assertOkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createNewDraftPatchSetOnDraftChange() throws Exception {
|
||||||
|
// Create new project with clean permissions
|
||||||
|
Project.NameKey p = createProject("addPatchSet4");
|
||||||
|
// Clone separate repositories of the same project as admin and as user
|
||||||
|
TestRepository<?> adminTestRepo = cloneProject(p, admin);
|
||||||
|
TestRepository<?> userTestRepo = cloneProject(p, user);
|
||||||
|
|
||||||
|
// Block default permission
|
||||||
|
block(Permission.ADD_PATCH_SET, REGISTERED_USERS, "refs/for/*", p);
|
||||||
|
|
||||||
|
// Create change as admin
|
||||||
|
PushOneCommit push = pushFactory.create(
|
||||||
|
db, admin.getIdent(), adminTestRepo);
|
||||||
|
PushOneCommit.Result r1 = push.to("refs/drafts/master");
|
||||||
|
r1.assertOkStatus();
|
||||||
|
|
||||||
|
// Add user as reviewer
|
||||||
|
AddReviewerInput in = new AddReviewerInput();
|
||||||
|
in.reviewer = user.email;
|
||||||
|
gApi.changes()
|
||||||
|
.id(r1.getChangeId())
|
||||||
|
.addReviewer(in);
|
||||||
|
|
||||||
|
// Fetch change
|
||||||
|
GitUtil.fetch(userTestRepo, r1.getPatchSet().getRefName() + ":ps");
|
||||||
|
userTestRepo.reset("ps");
|
||||||
|
|
||||||
|
// Amend change as user
|
||||||
|
PushOneCommit.Result r2 = amendChange(
|
||||||
|
r1.getChangeId(), "refs/drafts/master", user, userTestRepo);
|
||||||
|
r2.assertErrorStatus("cannot add patch set to "
|
||||||
|
+ r1.getChange().getId().id + ".");
|
||||||
|
}
|
||||||
|
|
||||||
private static Iterable<Account.Id> getReviewers(
|
private static Iterable<Account.Id> getReviewers(
|
||||||
Collection<AccountInfo> r) {
|
Collection<AccountInfo> r) {
|
||||||
return Iterables.transform(r, new Function<AccountInfo, Account.Id>() {
|
return Iterables.transform(r, new Function<AccountInfo, Account.Id>() {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ package com.google.gerrit.acceptance.edit;
|
|||||||
|
|
||||||
import static com.google.common.collect.Iterables.getOnlyElement;
|
import static com.google.common.collect.Iterables.getOnlyElement;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||||
|
|
||||||
@@ -29,6 +30,7 @@ import com.google.gerrit.acceptance.RestResponse;
|
|||||||
import com.google.gerrit.acceptance.TestProjectInput;
|
import com.google.gerrit.acceptance.TestProjectInput;
|
||||||
import com.google.gerrit.common.RawInputUtil;
|
import com.google.gerrit.common.RawInputUtil;
|
||||||
import com.google.gerrit.common.data.LabelType;
|
import com.google.gerrit.common.data.LabelType;
|
||||||
|
import com.google.gerrit.common.data.Permission;
|
||||||
import com.google.gerrit.extensions.api.changes.ReviewInput;
|
import com.google.gerrit.extensions.api.changes.ReviewInput;
|
||||||
import com.google.gerrit.extensions.client.InheritableBoolean;
|
import com.google.gerrit.extensions.client.InheritableBoolean;
|
||||||
import com.google.gerrit.extensions.client.ListChangesOption;
|
import com.google.gerrit.extensions.client.ListChangesOption;
|
||||||
@@ -42,6 +44,7 @@ import com.google.gerrit.extensions.restapi.BinaryResult;
|
|||||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||||
import com.google.gerrit.reviewdb.client.Change;
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||||
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
import com.google.gerrit.server.change.ChangeEdits.EditMessage;
|
import com.google.gerrit.server.change.ChangeEdits.EditMessage;
|
||||||
import com.google.gerrit.server.change.ChangeEdits.Post;
|
import com.google.gerrit.server.change.ChangeEdits.Post;
|
||||||
@@ -61,6 +64,8 @@ import com.google.gwtorm.server.SchemaFactory;
|
|||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.StringUtils;
|
import org.apache.commons.codec.binary.StringUtils;
|
||||||
|
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
|
||||||
|
import org.eclipse.jgit.junit.TestRepository;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.PersonIdent;
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
import org.eclipse.jgit.lib.RefUpdate;
|
import org.eclipse.jgit.lib.RefUpdate;
|
||||||
@@ -768,6 +773,28 @@ public class ChangeEditIT extends AbstractDaemonTest {
|
|||||||
assertThat(diff.diffHeader.get(0)).contains(FILE_NAME);
|
assertThat(diff.diffHeader.get(0)).contains(FILE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createEditWithoutPushPatchSetPermission() throws Exception {
|
||||||
|
// Create new project with clean permissions
|
||||||
|
Project.NameKey p = createProject("addPatchSetEdit");
|
||||||
|
// Clone repository as user
|
||||||
|
TestRepository<InMemoryRepository> userTestRepo =
|
||||||
|
cloneProject(p, user);
|
||||||
|
|
||||||
|
// Block default permission
|
||||||
|
block(Permission.ADD_PATCH_SET, REGISTERED_USERS, "refs/for/*", p);
|
||||||
|
|
||||||
|
// Create change as user
|
||||||
|
PushOneCommit push = pushFactory.create(
|
||||||
|
db, user.getIdent(), userTestRepo);
|
||||||
|
PushOneCommit.Result r1 = push.to("refs/for/master");
|
||||||
|
r1.assertOkStatus();
|
||||||
|
|
||||||
|
// Try to create edit as admin
|
||||||
|
assertThat(modifier.createEdit(r1.getChange().change(),
|
||||||
|
r1.getPatchSet())).isEqualTo(RefUpdate.Result.REJECTED);
|
||||||
|
}
|
||||||
|
|
||||||
private List<ChangeInfo> queryEdits() throws Exception {
|
private List<ChangeInfo> queryEdits() throws Exception {
|
||||||
return query("project:{" + project.get() + "} has:edit");
|
return query("project:{" + project.get() + "} has:edit");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -429,7 +429,8 @@ public abstract class AbstractPushForReview extends AbstractDaemonTest {
|
|||||||
PushOneCommit.SUBJECT, "b.txt", "anotherContent", r.getChangeId());
|
PushOneCommit.SUBJECT, "b.txt", "anotherContent", r.getChangeId());
|
||||||
revision(r).review(new ReviewInput().label("Patch-Set-Lock", 1));
|
revision(r).review(new ReviewInput().label("Patch-Set-Lock", 1));
|
||||||
r = push.to("refs/for/master");
|
r = push.to("refs/for/master");
|
||||||
r.assertErrorStatus("cannot replace " + r.getChange().change().getChangeId()
|
r.assertErrorStatus("cannot add patch set to "
|
||||||
|
+ r.getChange().change().getChangeId()
|
||||||
+ ". Change is patch set locked.");
|
+ ". Change is patch set locked.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -241,7 +241,6 @@ public class VisibleRefFilterIT extends AbstractDaemonTest {
|
|||||||
setApiUser(admin);
|
setApiUser(admin);
|
||||||
editModifier.createEdit(c, ps1);
|
editModifier.createEdit(c, ps1);
|
||||||
setApiUser(user);
|
setApiUser(user);
|
||||||
editModifier.createEdit(c, ps1);
|
|
||||||
|
|
||||||
assertRefs(
|
assertRefs(
|
||||||
// Change 1 is visible due to accessDatabase capability, even though
|
// Change 1 is visible due to accessDatabase capability, even though
|
||||||
@@ -255,8 +254,7 @@ public class VisibleRefFilterIT extends AbstractDaemonTest {
|
|||||||
// See comment in subsetOfBranchesVisibleNotIncludingHead.
|
// See comment in subsetOfBranchesVisibleNotIncludingHead.
|
||||||
"refs/tags/master-tag",
|
"refs/tags/master-tag",
|
||||||
// All edits are visible due to accessDatabase capability.
|
// All edits are visible due to accessDatabase capability.
|
||||||
"refs/users/00/1000000/edit-" + c1.get() + "/1",
|
"refs/users/00/1000000/edit-" + c1.get() + "/1");
|
||||||
"refs/users/01/1000001/edit-" + c1.get() + "/1");
|
|
||||||
} finally {
|
} finally {
|
||||||
removeGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
|
removeGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ addPermission = Add Permission ...
|
|||||||
# Permission Names
|
# Permission Names
|
||||||
permissionNames = \
|
permissionNames = \
|
||||||
abandon, \
|
abandon, \
|
||||||
|
addPatchSet, \
|
||||||
create, \
|
create, \
|
||||||
deleteDrafts, \
|
deleteDrafts, \
|
||||||
editHashtags, \
|
editHashtags, \
|
||||||
@@ -141,6 +142,7 @@ permissionNames = \
|
|||||||
viewDrafts
|
viewDrafts
|
||||||
|
|
||||||
abandon = Abandon
|
abandon = Abandon
|
||||||
|
addPatchSet = Add Patch Set
|
||||||
create = Create Reference
|
create = Create Reference
|
||||||
deleteDrafts = Delete Drafts
|
deleteDrafts = Delete Drafts
|
||||||
editHashtags = Edit Hashtags
|
editHashtags = Edit Hashtags
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import static com.google.gerrit.server.notedb.ReviewerStateInternal.CC;
|
|||||||
import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
|
import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
|
||||||
|
|
||||||
import com.google.gerrit.extensions.api.changes.NotifyHandling;
|
import com.google.gerrit.extensions.api.changes.NotifyHandling;
|
||||||
|
import com.google.gerrit.extensions.restapi.AuthException;
|
||||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||||
import com.google.gerrit.reviewdb.client.Change;
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
import com.google.gerrit.reviewdb.client.ChangeMessage;
|
import com.google.gerrit.reviewdb.client.ChangeMessage;
|
||||||
@@ -196,7 +197,7 @@ public class PatchSetInserter extends BatchUpdate.Op {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateRepo(RepoContext ctx)
|
public void updateRepo(RepoContext ctx)
|
||||||
throws ResourceConflictException, IOException {
|
throws AuthException, ResourceConflictException, IOException, OrmException {
|
||||||
init();
|
init();
|
||||||
validate(ctx);
|
validate(ctx);
|
||||||
ctx.addRefUpdate(new ReceiveCommand(ObjectId.zeroId(),
|
ctx.addRefUpdate(new ReceiveCommand(ObjectId.zeroId(),
|
||||||
@@ -288,10 +289,15 @@ public class PatchSetInserter extends BatchUpdate.Op {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void validate(RepoContext ctx)
|
private void validate(RepoContext ctx)
|
||||||
throws ResourceConflictException, IOException {
|
throws AuthException, ResourceConflictException, IOException,
|
||||||
|
OrmException {
|
||||||
CommitValidators cv = commitValidatorsFactory.create(
|
CommitValidators cv = commitValidatorsFactory.create(
|
||||||
origCtl.getRefControl(), sshInfo, ctx.getRepository());
|
origCtl.getRefControl(), sshInfo, ctx.getRepository());
|
||||||
|
|
||||||
|
if (!origCtl.canAddPatchSet(ctx.getDb())) {
|
||||||
|
throw new AuthException("cannot add patch set");
|
||||||
|
}
|
||||||
|
|
||||||
String refName = getPatchSetId().toRefName();
|
String refName = getPatchSetId().toRefName();
|
||||||
CommitReceivedEvent event = new CommitReceivedEvent(
|
CommitReceivedEvent event = new CommitReceivedEvent(
|
||||||
new ReceiveCommand(
|
new ReceiveCommand(
|
||||||
|
|||||||
@@ -35,7 +35,9 @@ import com.google.gerrit.server.GerritPersonIdent;
|
|||||||
import com.google.gerrit.server.IdentifiedUser;
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||||
import com.google.gerrit.server.index.change.ChangeIndexer;
|
import com.google.gerrit.server.index.change.ChangeIndexer;
|
||||||
|
import com.google.gerrit.server.project.ChangeControl;
|
||||||
import com.google.gerrit.server.project.InvalidChangeOperationException;
|
import com.google.gerrit.server.project.InvalidChangeOperationException;
|
||||||
|
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
@@ -96,18 +98,21 @@ public class ChangeEditModifier {
|
|||||||
private final ChangeIndexer indexer;
|
private final ChangeIndexer indexer;
|
||||||
private final Provider<ReviewDb> reviewDb;
|
private final Provider<ReviewDb> reviewDb;
|
||||||
private final Provider<CurrentUser> currentUser;
|
private final Provider<CurrentUser> currentUser;
|
||||||
|
private final ChangeControl.GenericFactory changeControlFactory;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ChangeEditModifier(@GerritPersonIdent PersonIdent gerritIdent,
|
ChangeEditModifier(@GerritPersonIdent PersonIdent gerritIdent,
|
||||||
GitRepositoryManager gitManager,
|
GitRepositoryManager gitManager,
|
||||||
ChangeIndexer indexer,
|
ChangeIndexer indexer,
|
||||||
Provider<ReviewDb> reviewDb,
|
Provider<ReviewDb> reviewDb,
|
||||||
Provider<CurrentUser> currentUser) {
|
Provider<CurrentUser> currentUser,
|
||||||
|
ChangeControl.GenericFactory changeControlFactory) {
|
||||||
this.gitManager = gitManager;
|
this.gitManager = gitManager;
|
||||||
this.indexer = indexer;
|
this.indexer = indexer;
|
||||||
this.reviewDb = reviewDb;
|
this.reviewDb = reviewDb;
|
||||||
this.currentUser = currentUser;
|
this.currentUser = currentUser;
|
||||||
this.tz = gerritIdent.getTimeZone();
|
this.tz = gerritIdent.getTimeZone();
|
||||||
|
this.changeControlFactory = changeControlFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -127,10 +132,19 @@ public class ChangeEditModifier {
|
|||||||
if (!currentUser.get().isIdentifiedUser()) {
|
if (!currentUser.get().isIdentifiedUser()) {
|
||||||
throw new AuthException("Authentication required");
|
throw new AuthException("Authentication required");
|
||||||
}
|
}
|
||||||
|
|
||||||
IdentifiedUser me = currentUser.get().asIdentifiedUser();
|
IdentifiedUser me = currentUser.get().asIdentifiedUser();
|
||||||
String refPrefix = RefNames.refsEditPrefix(me.getAccountId(), change.getId());
|
String refPrefix = RefNames.refsEditPrefix(me.getAccountId(), change.getId());
|
||||||
|
|
||||||
|
try {
|
||||||
|
ChangeControl c =
|
||||||
|
changeControlFactory.controlFor(reviewDb.get(), change, me);
|
||||||
|
if (!c.canAddPatchSet(reviewDb.get())) {
|
||||||
|
return RefUpdate.Result.REJECTED;
|
||||||
|
}
|
||||||
|
} catch (NoSuchChangeException e) {
|
||||||
|
return RefUpdate.Result.NO_CHANGE;
|
||||||
|
}
|
||||||
|
|
||||||
try (Repository repo = gitManager.openRepository(change.getProject())) {
|
try (Repository repo = gitManager.openRepository(change.getProject())) {
|
||||||
Map<String, Ref> refs = repo.getRefDatabase().getRefs(refPrefix);
|
Map<String, Ref> refs = repo.getRefDatabase().getRefs(refPrefix);
|
||||||
if (!refs.isEmpty()) {
|
if (!refs.isEmpty()) {
|
||||||
|
|||||||
@@ -2016,7 +2016,8 @@ public class ReceiveCommits {
|
|||||||
if (changeCtl.isPatchSetLocked(db)) {
|
if (changeCtl.isPatchSetLocked(db)) {
|
||||||
locked = ". Change is patch set locked.";
|
locked = ". Change is patch set locked.";
|
||||||
}
|
}
|
||||||
reject(inputCommand, "cannot replace " + ontoChange + locked);
|
reject(inputCommand, "cannot add patch set to "
|
||||||
|
+ ontoChange + locked);
|
||||||
return false;
|
return false;
|
||||||
} else if (notes.getChange().getStatus().isClosed()) {
|
} else if (notes.getChange().getStatus().isClosed()) {
|
||||||
reject(inputCommand, "change " + ontoChange + " closed");
|
reject(inputCommand, "change " + ontoChange + " closed");
|
||||||
|
|||||||
@@ -314,11 +314,16 @@ public class ChangeControl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Can this user add a patch set to this change? */
|
/** Can this user add a patch set to this change? */
|
||||||
public boolean canAddPatchSet(ReviewDb db)
|
public boolean canAddPatchSet(ReviewDb db) throws OrmException {
|
||||||
throws OrmException {
|
if (!getRefControl().canUpload()
|
||||||
return getRefControl().canUpload()
|
|| isPatchSetLocked(db)
|
||||||
&& !isPatchSetLocked(db)
|
|| !isPatchVisible(patchSetUtil.current(db, notes), db)) {
|
||||||
&& isPatchVisible(patchSetUtil.current(db, notes), db);
|
return false;
|
||||||
|
}
|
||||||
|
if (isOwner()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return getRefControl().canAddPatchSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Is the current patch set locked against state changes? */
|
/** Is the current patch set locked against state changes? */
|
||||||
|
|||||||
@@ -150,6 +150,13 @@ public class RefControl {
|
|||||||
&& canWrite();
|
&& canWrite();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return true if this user can add a new patch set to this ref */
|
||||||
|
public boolean canAddPatchSet() {
|
||||||
|
return projectControl.controlForRef("refs/for/" + getRefName())
|
||||||
|
.canPerform(Permission.ADD_PATCH_SET)
|
||||||
|
&& canWrite();
|
||||||
|
}
|
||||||
|
|
||||||
/** @return true if this user can submit merge patch sets to this ref */
|
/** @return true if this user can submit merge patch sets to this ref */
|
||||||
public boolean canUploadMerges() {
|
public boolean canUploadMerges() {
|
||||||
return projectControl.controlForRef("refs/for/" + getRefName())
|
return projectControl.controlForRef("refs/for/" + getRefName())
|
||||||
|
|||||||
@@ -138,10 +138,12 @@ public class AllProjectsCreator {
|
|||||||
AccessSection heads = config.getAccessSection(AccessSection.HEADS, true);
|
AccessSection heads = config.getAccessSection(AccessSection.HEADS, true);
|
||||||
AccessSection tags = config.getAccessSection("refs/tags/*", true);
|
AccessSection tags = config.getAccessSection("refs/tags/*", true);
|
||||||
AccessSection meta = config.getAccessSection(RefNames.REFS_CONFIG, true);
|
AccessSection meta = config.getAccessSection(RefNames.REFS_CONFIG, true);
|
||||||
|
AccessSection refsFor = config.getAccessSection("refs/for/*", true);
|
||||||
AccessSection magic = config.getAccessSection("refs/for/" + AccessSection.ALL, true);
|
AccessSection magic = config.getAccessSection("refs/for/" + AccessSection.ALL, true);
|
||||||
|
|
||||||
grant(config, cap, GlobalCapability.ADMINISTRATE_SERVER, admin);
|
grant(config, cap, GlobalCapability.ADMINISTRATE_SERVER, admin);
|
||||||
grant(config, all, Permission.READ, admin, anonymous);
|
grant(config, all, Permission.READ, admin, anonymous);
|
||||||
|
grant(config, refsFor, Permission.ADD_PATCH_SET, registered);
|
||||||
|
|
||||||
if (batch != null) {
|
if (batch != null) {
|
||||||
Permission priority = cap.getPermission(GlobalCapability.PRIORITY, true);
|
Permission priority = cap.getPermission(GlobalCapability.PRIORITY, true);
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import java.util.List;
|
|||||||
/** A version of the database schema. */
|
/** A version of the database schema. */
|
||||||
public abstract class SchemaVersion {
|
public abstract class SchemaVersion {
|
||||||
/** The current schema version. */
|
/** The current schema version. */
|
||||||
public static final Class<Schema_127> C = Schema_127.class;
|
public static final Class<Schema_128> C = Schema_128.class;
|
||||||
|
|
||||||
public static int getBinaryVersion() {
|
public static int getBinaryVersion() {
|
||||||
return guessVersion(C);
|
return guessVersion(C);
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
// Copyright (C) 2016 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.google.gerrit.server.schema;
|
||||||
|
|
||||||
|
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
|
||||||
|
import static com.google.gerrit.server.schema.AclUtil.grant;
|
||||||
|
|
||||||
|
import com.google.gerrit.common.data.AccessSection;
|
||||||
|
import com.google.gerrit.common.data.GroupReference;
|
||||||
|
import com.google.gerrit.common.data.Permission;
|
||||||
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
|
import com.google.gerrit.server.GerritPersonIdent;
|
||||||
|
import com.google.gerrit.server.config.AllProjectsName;
|
||||||
|
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||||
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||||
|
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||||
|
import com.google.gerrit.server.git.ProjectConfig;
|
||||||
|
import com.google.gerrit.server.group.SystemGroupBackend;
|
||||||
|
import com.google.gwtorm.server.OrmException;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class Schema_128 extends SchemaVersion {
|
||||||
|
private static final String COMMIT_MSG =
|
||||||
|
"Add addPatchSet permission to all projects";
|
||||||
|
|
||||||
|
private final GitRepositoryManager repoManager;
|
||||||
|
private final AllProjectsName allProjectsName;
|
||||||
|
private final PersonIdent serverUser;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
Schema_128(Provider<Schema_126> prior,
|
||||||
|
GitRepositoryManager repoManager,
|
||||||
|
AllProjectsName allProjectsName,
|
||||||
|
@GerritPersonIdent PersonIdent serverUser) {
|
||||||
|
super(prior);
|
||||||
|
this.repoManager = repoManager;
|
||||||
|
this.allProjectsName = allProjectsName;
|
||||||
|
this.serverUser = serverUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
|
||||||
|
try (Repository git = repoManager.openRepository(allProjectsName);
|
||||||
|
MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED,
|
||||||
|
allProjectsName, git)) {
|
||||||
|
ProjectConfig config = ProjectConfig.read(md);
|
||||||
|
|
||||||
|
GroupReference registered = SystemGroupBackend.getGroup(REGISTERED_USERS);
|
||||||
|
AccessSection refsFor = config.getAccessSection("refs/for/*", true);
|
||||||
|
grant(config, refsFor, Permission.ADD_PATCH_SET,
|
||||||
|
false, false, registered);
|
||||||
|
|
||||||
|
md.getCommitBuilder().setAuthor(serverUser);
|
||||||
|
md.getCommitBuilder().setCommitter(serverUser);
|
||||||
|
md.setMessage(COMMIT_MSG);
|
||||||
|
config.commit(md);
|
||||||
|
} catch (ConfigInvalidException | IOException ex) {
|
||||||
|
throw new OrmException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user