Merge changes from topic 'permission-backend'
* changes: Replace canUpload with CREATE_CHANGE Convert GetAccess and canAddRefs to PermissionBackend Convert forge author, committer, server to PermissionBackend Push option bypass-review disables validation
This commit is contained in:
commit
64e43c24ef
|
@ -42,9 +42,9 @@ import com.google.gerrit.extensions.common.ChangeMessageInfo;
|
|||
import com.google.gerrit.extensions.common.CommitInfo;
|
||||
import com.google.gerrit.extensions.common.MergeInput;
|
||||
import com.google.gerrit.extensions.common.RevisionInfo;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
|
||||
import com.google.gerrit.reviewdb.client.Branch;
|
||||
|
@ -192,7 +192,7 @@ public class CreateChangeIT extends AbstractDaemonTest {
|
|||
|
||||
ChangeInput in = newChangeInput(ChangeStatus.NEW);
|
||||
in.branch = "invisible-branch";
|
||||
assertCreateFails(in, AuthException.class, "cannot upload review");
|
||||
assertCreateFails(in, ResourceNotFoundException.class, "");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -14,6 +14,10 @@
|
|||
|
||||
package com.google.gerrit.httpd.rpc.project;
|
||||
|
||||
import static com.google.gerrit.server.permissions.GlobalPermission.ADMINISTRATE_SERVER;
|
||||
import static com.google.gerrit.server.permissions.RefPermission.CREATE_CHANGE;
|
||||
import static com.google.gerrit.server.permissions.RefPermission.READ;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.gerrit.common.data.AccessSection;
|
||||
import com.google.gerrit.common.data.GroupDescription;
|
||||
|
@ -39,10 +43,10 @@ import com.google.gerrit.server.git.ProjectConfig;
|
|||
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.permissions.ProjectPermission;
|
||||
import com.google.gerrit.server.permissions.RefPermission;
|
||||
import com.google.gerrit.server.project.NoSuchProjectException;
|
||||
import com.google.gerrit.server.project.ProjectCache;
|
||||
import com.google.gerrit.server.project.ProjectControl;
|
||||
import com.google.gerrit.server.project.RefControl;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
|
@ -122,10 +126,11 @@ class ProjectAccessFactory extends Handler<ProjectAccess> {
|
|||
}
|
||||
}
|
||||
|
||||
final RefControl metaConfigControl = pc.controlForRef(RefNames.REFS_CONFIG);
|
||||
List<AccessSection> local = new ArrayList<>();
|
||||
Set<String> ownerOf = new HashSet<>();
|
||||
Map<AccountGroup.UUID, Boolean> visibleGroups = new HashMap<>();
|
||||
PermissionBackend.ForProject perm = permissionBackend.user(user).project(projectName);
|
||||
boolean checkReadConfig = check(perm, RefNames.REFS_CONFIG, READ);
|
||||
|
||||
for (AccessSection section : config.getAccessSections()) {
|
||||
String name = section.getName();
|
||||
|
@ -134,20 +139,19 @@ class ProjectAccessFactory extends Handler<ProjectAccess> {
|
|||
local.add(section);
|
||||
ownerOf.add(name);
|
||||
|
||||
} else if (metaConfigControl.isVisible()) {
|
||||
} else if (checkReadConfig) {
|
||||
local.add(section);
|
||||
}
|
||||
|
||||
} else if (RefConfigSection.isValid(name)) {
|
||||
RefControl rc = pc.controlForRef(name);
|
||||
if (rc.isOwner()) {
|
||||
if (pc.controlForRef(name).isOwner()) {
|
||||
local.add(section);
|
||||
ownerOf.add(name);
|
||||
|
||||
} else if (metaConfigControl.isVisible()) {
|
||||
} else if (checkReadConfig) {
|
||||
local.add(section);
|
||||
|
||||
} else if (rc.isVisible()) {
|
||||
} else if (check(perm, name, READ)) {
|
||||
// Filter the section to only add rules describing groups that
|
||||
// are visible to the current-user. This includes any group the
|
||||
// user is a member of, as well as groups they own or that
|
||||
|
@ -205,17 +209,17 @@ class ProjectAccessFactory extends Handler<ProjectAccess> {
|
|||
|
||||
detail.setInheritsFrom(config.getProject().getParent(allProjectsName));
|
||||
|
||||
if (projectName.equals(allProjectsName)) {
|
||||
if (pc.isOwner()) {
|
||||
ownerOf.add(AccessSection.GLOBAL_CAPABILITIES);
|
||||
}
|
||||
if (projectName.equals(allProjectsName)
|
||||
&& permissionBackend.user(user).testOrFalse(ADMINISTRATE_SERVER)) {
|
||||
ownerOf.add(AccessSection.GLOBAL_CAPABILITIES);
|
||||
}
|
||||
|
||||
detail.setLocal(local);
|
||||
detail.setOwnerOf(ownerOf);
|
||||
detail.setCanUpload(
|
||||
metaConfigControl.isVisible() && (pc.isOwner() || metaConfigControl.canUpload()));
|
||||
detail.setConfigVisible(pc.isOwner() || metaConfigControl.isVisible());
|
||||
pc.isOwner()
|
||||
|| (checkReadConfig && perm.ref(RefNames.REFS_CONFIG).testOrFalse(CREATE_CHANGE)));
|
||||
detail.setConfigVisible(pc.isOwner() || checkReadConfig);
|
||||
detail.setGroupInfo(buildGroupInfo(local));
|
||||
detail.setLabelTypes(pc.getLabelTypes());
|
||||
detail.setFileHistoryLinks(getConfigFileLogLinks(projectName.get()));
|
||||
|
@ -257,4 +261,14 @@ class ProjectAccessFactory extends Handler<ProjectAccess> {
|
|||
}
|
||||
return pc;
|
||||
}
|
||||
|
||||
private static boolean check(PermissionBackend.ForProject ctx, String ref, RefPermission perm)
|
||||
throws PermissionBackendException {
|
||||
try {
|
||||
ctx.ref(ref).check(perm);
|
||||
return true;
|
||||
} catch (AuthException denied) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import com.google.gerrit.common.data.GlobalCapability;
|
|||
import com.google.gerrit.common.data.PermissionRule;
|
||||
import com.google.gerrit.common.errors.PermissionDeniedException;
|
||||
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
|
@ -39,9 +40,11 @@ import com.google.gerrit.server.config.AllProjectsName;
|
|||
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||
import com.google.gerrit.server.git.ProjectConfig;
|
||||
import com.google.gerrit.server.group.SystemGroupBackend;
|
||||
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.permissions.RefPermission;
|
||||
import com.google.gerrit.server.project.ProjectCache;
|
||||
import com.google.gerrit.server.project.ProjectControl;
|
||||
import com.google.gerrit.server.project.RefControl;
|
||||
import com.google.gerrit.server.project.SetParent;
|
||||
import com.google.gerrit.server.update.BatchUpdate;
|
||||
import com.google.gerrit.server.update.UpdateException;
|
||||
|
@ -68,6 +71,7 @@ public class ReviewProjectAccess extends ProjectAccessHandler<Change.Id> {
|
|||
}
|
||||
|
||||
private final ReviewDb db;
|
||||
private final PermissionBackend permissionBackend;
|
||||
private final Sequences seq;
|
||||
private final Provider<PostReviewers> reviewersProvider;
|
||||
private final ProjectCache projectCache;
|
||||
|
@ -78,6 +82,7 @@ public class ReviewProjectAccess extends ProjectAccessHandler<Change.Id> {
|
|||
@Inject
|
||||
ReviewProjectAccess(
|
||||
final ProjectControl.Factory projectControlFactory,
|
||||
PermissionBackend permissionBackend,
|
||||
GroupBackend groupBackend,
|
||||
MetaDataUpdate.User metaDataUpdateFactory,
|
||||
ReviewDb db,
|
||||
|
@ -107,6 +112,7 @@ public class ReviewProjectAccess extends ProjectAccessHandler<Change.Id> {
|
|||
message,
|
||||
false);
|
||||
this.db = db;
|
||||
this.permissionBackend = permissionBackend;
|
||||
this.seq = seq;
|
||||
this.reviewersProvider = reviewersProvider;
|
||||
this.projectCache = projectCache;
|
||||
|
@ -124,13 +130,23 @@ public class ReviewProjectAccess extends ProjectAccessHandler<Change.Id> {
|
|||
ProjectConfig config,
|
||||
MetaDataUpdate md,
|
||||
boolean parentProjectUpdate)
|
||||
throws IOException, OrmException, PermissionDeniedException {
|
||||
RefControl refsMetaConfigControl = projectControl.controlForRef(RefNames.REFS_CONFIG);
|
||||
if (!refsMetaConfigControl.isVisible()) {
|
||||
throws IOException, OrmException, PermissionDeniedException, PermissionBackendException {
|
||||
PermissionBackend.ForRef metaRef =
|
||||
permissionBackend
|
||||
.user(projectControl.getUser())
|
||||
.project(projectControl.getProject().getNameKey())
|
||||
.ref(RefNames.REFS_CONFIG);
|
||||
try {
|
||||
metaRef.check(RefPermission.READ);
|
||||
} catch (AuthException denied) {
|
||||
throw new PermissionDeniedException(RefNames.REFS_CONFIG + " not visible");
|
||||
}
|
||||
if (!projectControl.isOwner() && !refsMetaConfigControl.canUpload()) {
|
||||
throw new PermissionDeniedException("cannot upload to " + RefNames.REFS_CONFIG);
|
||||
if (!projectControl.isOwner()) {
|
||||
try {
|
||||
metaRef.check(RefPermission.CREATE_CHANGE);
|
||||
} catch (AuthException denied) {
|
||||
throw new PermissionDeniedException("cannot create change for " + RefNames.REFS_CONFIG);
|
||||
}
|
||||
}
|
||||
|
||||
md.setInsertChangeId(true);
|
||||
|
|
|
@ -20,6 +20,7 @@ import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
|||
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||
import com.google.gerrit.extensions.restapi.TopLevelResource;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.project.GetAccess;
|
||||
import com.google.inject.Inject;
|
||||
import java.io.IOException;
|
||||
|
@ -48,11 +49,11 @@ public class ListAccess implements RestReadView<TopLevelResource> {
|
|||
|
||||
@Override
|
||||
public Map<String, ProjectAccessInfo> apply(TopLevelResource resource)
|
||||
throws ResourceNotFoundException, ResourceConflictException, IOException {
|
||||
throws ResourceNotFoundException, ResourceConflictException, IOException,
|
||||
PermissionBackendException {
|
||||
Map<String, ProjectAccessInfo> access = new TreeMap<>();
|
||||
for (String p : projects) {
|
||||
Project.NameKey projectName = new Project.NameKey(p);
|
||||
access.put(p, getAccess.apply(projectName));
|
||||
access.put(p, getAccess.apply(new Project.NameKey(p)));
|
||||
}
|
||||
return access;
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ import com.google.gerrit.server.notedb.ChangeNotes;
|
|||
import com.google.gerrit.server.notedb.ChangeUpdate;
|
||||
import com.google.gerrit.server.notedb.NotesMigration;
|
||||
import com.google.gerrit.server.patch.PatchSetInfoFactory;
|
||||
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||
import com.google.gerrit.server.project.ChangeControl;
|
||||
import com.google.gerrit.server.project.NoSuchProjectException;
|
||||
import com.google.gerrit.server.project.ProjectControl;
|
||||
|
@ -90,6 +91,7 @@ public class ChangeInserter implements InsertChangeOp {
|
|||
|
||||
private static final Logger log = LoggerFactory.getLogger(ChangeInserter.class);
|
||||
|
||||
private final PermissionBackend permissionBackend;
|
||||
private final ProjectControl.GenericFactory projectControlFactory;
|
||||
private final IdentifiedUser.GenericFactory userFactory;
|
||||
private final ChangeControl.GenericFactory changeControlFactory;
|
||||
|
@ -138,6 +140,7 @@ public class ChangeInserter implements InsertChangeOp {
|
|||
|
||||
@Inject
|
||||
ChangeInserter(
|
||||
PermissionBackend permissionBackend,
|
||||
ProjectControl.GenericFactory projectControlFactory,
|
||||
IdentifiedUser.GenericFactory userFactory,
|
||||
ChangeControl.GenericFactory changeControlFactory,
|
||||
|
@ -154,6 +157,7 @@ public class ChangeInserter implements InsertChangeOp {
|
|||
@Assisted Change.Id changeId,
|
||||
@Assisted ObjectId commitId,
|
||||
@Assisted String refName) {
|
||||
this.permissionBackend = permissionBackend;
|
||||
this.projectControlFactory = projectControlFactory;
|
||||
this.userFactory = userFactory;
|
||||
this.changeControlFactory = changeControlFactory;
|
||||
|
@ -543,6 +547,8 @@ public class ChangeInserter implements InsertChangeOp {
|
|||
return;
|
||||
}
|
||||
|
||||
PermissionBackend.ForRef perm =
|
||||
permissionBackend.user(ctx.getUser()).project(ctx.getProject()).ref(refName);
|
||||
try {
|
||||
RefControl refControl =
|
||||
projectControlFactory.controlFor(ctx.getProject(), ctx.getUser()).controlForRef(refName);
|
||||
|
@ -555,7 +561,7 @@ public class ChangeInserter implements InsertChangeOp {
|
|||
commitId,
|
||||
ctx.getIdentifiedUser())) {
|
||||
commitValidatorsFactory
|
||||
.forGerritCommits(refControl, new NoSshInfo(), ctx.getRevWalk())
|
||||
.forGerritCommits(perm, refControl, new NoSshInfo(), ctx.getRevWalk())
|
||||
.validate(event);
|
||||
}
|
||||
} catch (CommitValidationException e) {
|
||||
|
|
|
@ -14,23 +14,21 @@
|
|||
|
||||
package com.google.gerrit.server.change;
|
||||
|
||||
import com.google.gerrit.common.data.Capable;
|
||||
import com.google.gerrit.extensions.api.changes.CherryPickInput;
|
||||
import com.google.gerrit.extensions.common.ChangeInfo;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
import com.google.gerrit.extensions.webui.UiAction;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.client.RefNames;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.git.IntegrationException;
|
||||
import com.google.gerrit.server.project.ChangeControl;
|
||||
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.permissions.RefPermission;
|
||||
import com.google.gerrit.server.project.InvalidChangeOperationException;
|
||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||
import com.google.gerrit.server.project.ProjectControl;
|
||||
import com.google.gerrit.server.project.RefControl;
|
||||
import com.google.gerrit.server.update.BatchUpdate;
|
||||
import com.google.gerrit.server.update.RetryHelper;
|
||||
import com.google.gerrit.server.update.RetryingRestModifyView;
|
||||
|
@ -45,59 +43,54 @@ import java.io.IOException;
|
|||
public class CherryPick
|
||||
extends RetryingRestModifyView<RevisionResource, CherryPickInput, ChangeInfo>
|
||||
implements UiAction<RevisionResource> {
|
||||
private final Provider<ReviewDb> dbProvider;
|
||||
private final PermissionBackend permissionBackend;
|
||||
private final Provider<CurrentUser> user;
|
||||
private final CherryPickChange cherryPickChange;
|
||||
private final ChangeJson.Factory json;
|
||||
|
||||
@Inject
|
||||
CherryPick(
|
||||
PermissionBackend permissionBackend,
|
||||
Provider<CurrentUser> user,
|
||||
RetryHelper retryHelper,
|
||||
Provider<ReviewDb> dbProvider,
|
||||
CherryPickChange cherryPickChange,
|
||||
ChangeJson.Factory json) {
|
||||
super(retryHelper);
|
||||
this.dbProvider = dbProvider;
|
||||
this.permissionBackend = permissionBackend;
|
||||
this.user = user;
|
||||
this.cherryPickChange = cherryPickChange;
|
||||
this.json = json;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ChangeInfo applyImpl(
|
||||
BatchUpdate.Factory updateFactory, RevisionResource revision, CherryPickInput input)
|
||||
throws OrmException, IOException, UpdateException, RestApiException {
|
||||
final ChangeControl control = revision.getControl();
|
||||
public ChangeInfo applyImpl(
|
||||
BatchUpdate.Factory updateFactory, RevisionResource rsrc, CherryPickInput input)
|
||||
throws OrmException, IOException, UpdateException, RestApiException,
|
||||
PermissionBackendException {
|
||||
input.parent = input.parent == null ? 1 : input.parent;
|
||||
|
||||
if (input.message == null || input.message.trim().isEmpty()) {
|
||||
throw new BadRequestException("message must be non-empty");
|
||||
} else if (input.destination == null || input.destination.trim().isEmpty()) {
|
||||
throw new BadRequestException("destination must be non-empty");
|
||||
}
|
||||
|
||||
if (!control.isVisible(dbProvider.get())) {
|
||||
throw new AuthException("Cherry pick not permitted");
|
||||
}
|
||||
|
||||
ProjectControl projectControl = control.getProjectControl();
|
||||
Capable capable = projectControl.canPushToAtLeastOneRef();
|
||||
if (capable != Capable.OK) {
|
||||
throw new AuthException(capable.getMessage());
|
||||
}
|
||||
|
||||
RefControl refControl = projectControl.controlForRef(RefNames.fullName(input.destination));
|
||||
if (!refControl.canUpload()) {
|
||||
throw new AuthException(
|
||||
"Not allowed to cherry pick "
|
||||
+ revision.getChange().getId().toString()
|
||||
+ " to "
|
||||
+ input.destination);
|
||||
}
|
||||
String refName = RefNames.fullName(input.destination);
|
||||
CreateChange.checkValidCLA(rsrc.getControl().getProjectControl());
|
||||
permissionBackend
|
||||
.user(user)
|
||||
.project(rsrc.getChange().getProject())
|
||||
.ref(refName)
|
||||
.check(RefPermission.CREATE_CHANGE);
|
||||
|
||||
try {
|
||||
Change.Id cherryPickedChangeId =
|
||||
cherryPickChange.cherryPick(
|
||||
updateFactory, revision.getChange(), revision.getPatchSet(), input, refControl);
|
||||
return json.noOptions().format(revision.getProject(), cherryPickedChangeId);
|
||||
updateFactory,
|
||||
rsrc.getChange(),
|
||||
rsrc.getPatchSet(),
|
||||
input,
|
||||
rsrc.getControl().getProjectControl().controlForRef(refName));
|
||||
return json.noOptions().format(rsrc.getProject(), cherryPickedChangeId);
|
||||
} catch (InvalidChangeOperationException e) {
|
||||
throw new BadRequestException(e.getMessage());
|
||||
} catch (IntegrationException | NoSuchChangeException e) {
|
||||
|
|
|
@ -15,27 +15,28 @@
|
|||
package com.google.gerrit.server.change;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.gerrit.common.data.Capable;
|
||||
import com.google.gerrit.extensions.api.changes.CherryPickInput;
|
||||
import com.google.gerrit.extensions.common.ChangeInfo;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.BadRequestException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gerrit.reviewdb.client.RefNames;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.git.IntegrationException;
|
||||
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.permissions.RefPermission;
|
||||
import com.google.gerrit.server.project.CommitResource;
|
||||
import com.google.gerrit.server.project.InvalidChangeOperationException;
|
||||
import com.google.gerrit.server.project.ProjectControl;
|
||||
import com.google.gerrit.server.project.RefControl;
|
||||
import com.google.gerrit.server.update.BatchUpdate;
|
||||
import com.google.gerrit.server.update.RetryHelper;
|
||||
import com.google.gerrit.server.update.RetryingRestModifyView;
|
||||
import com.google.gerrit.server.update.UpdateException;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
|
@ -43,14 +44,21 @@ import org.eclipse.jgit.revwalk.RevCommit;
|
|||
@Singleton
|
||||
public class CherryPickCommit
|
||||
extends RetryingRestModifyView<CommitResource, CherryPickInput, ChangeInfo> {
|
||||
|
||||
private final PermissionBackend permissionBackend;
|
||||
private final Provider<CurrentUser> user;
|
||||
private final CherryPickChange cherryPickChange;
|
||||
private final ChangeJson.Factory json;
|
||||
|
||||
@Inject
|
||||
CherryPickCommit(
|
||||
RetryHelper retryHelper, CherryPickChange cherryPickChange, ChangeJson.Factory json) {
|
||||
RetryHelper retryHelper,
|
||||
Provider<CurrentUser> user,
|
||||
CherryPickChange cherryPickChange,
|
||||
ChangeJson.Factory json,
|
||||
PermissionBackend permissionBackend) {
|
||||
super(retryHelper);
|
||||
this.permissionBackend = permissionBackend;
|
||||
this.user = user;
|
||||
this.cherryPickChange = cherryPickChange;
|
||||
this.json = json;
|
||||
}
|
||||
|
@ -58,35 +66,40 @@ public class CherryPickCommit
|
|||
@Override
|
||||
public ChangeInfo applyImpl(
|
||||
BatchUpdate.Factory updateFactory, CommitResource rsrc, CherryPickInput input)
|
||||
throws OrmException, IOException, UpdateException, RestApiException {
|
||||
throws OrmException, IOException, UpdateException, RestApiException,
|
||||
PermissionBackendException {
|
||||
RevCommit commit = rsrc.getCommit();
|
||||
String message = Strings.nullToEmpty(input.message).trim();
|
||||
input.message = message.isEmpty() ? commit.getFullMessage() : message;
|
||||
String destination = Strings.nullToEmpty(input.destination).trim();
|
||||
input.parent = input.parent == null ? 1 : input.parent;
|
||||
Project.NameKey projectName = rsrc.getProject().getProject().getNameKey();
|
||||
|
||||
if (destination.isEmpty()) {
|
||||
throw new BadRequestException("destination must be non-empty");
|
||||
}
|
||||
|
||||
ProjectControl projectControl = rsrc.getProject();
|
||||
Capable capable = projectControl.canPushToAtLeastOneRef();
|
||||
if (capable != Capable.OK) {
|
||||
throw new AuthException(capable.getMessage());
|
||||
}
|
||||
|
||||
String refName = RefNames.fullName(destination);
|
||||
RefControl refControl = projectControl.controlForRef(refName);
|
||||
if (!refControl.canUpload()) {
|
||||
throw new AuthException("Not allowed to cherry pick " + commit + " to " + destination);
|
||||
}
|
||||
CreateChange.checkValidCLA(rsrc.getProject());
|
||||
permissionBackend
|
||||
.user(user)
|
||||
.project(projectName)
|
||||
.ref(refName)
|
||||
.check(RefPermission.CREATE_CHANGE);
|
||||
|
||||
Project.NameKey project = projectControl.getProject().getNameKey();
|
||||
try {
|
||||
Change.Id cherryPickedChangeId =
|
||||
cherryPickChange.cherryPick(
|
||||
updateFactory, null, null, null, null, project, commit, input, refControl);
|
||||
return json.noOptions().format(project, cherryPickedChangeId);
|
||||
updateFactory,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
projectName,
|
||||
commit,
|
||||
input,
|
||||
rsrc.getProject().controlForRef(refName));
|
||||
return json.noOptions().format(projectName, cherryPickedChangeId);
|
||||
} catch (InvalidChangeOperationException e) {
|
||||
throw new BadRequestException(e.getMessage());
|
||||
} catch (IntegrationException e) {
|
||||
|
|
|
@ -52,13 +52,14 @@ import com.google.gerrit.server.config.AnonymousCowardName;
|
|||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.git.MergeUtil;
|
||||
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.permissions.RefPermission;
|
||||
import com.google.gerrit.server.project.ChangeControl;
|
||||
import com.google.gerrit.server.project.InvalidChangeOperationException;
|
||||
import com.google.gerrit.server.project.ProjectControl;
|
||||
import com.google.gerrit.server.project.ProjectResource;
|
||||
import com.google.gerrit.server.project.ProjectsCollection;
|
||||
import com.google.gerrit.server.project.RefControl;
|
||||
import com.google.gerrit.server.update.BatchUpdate;
|
||||
import com.google.gerrit.server.update.RetryHelper;
|
||||
import com.google.gerrit.server.update.RetryingRestModifyView;
|
||||
|
@ -89,13 +90,13 @@ import org.eclipse.jgit.util.ChangeIdUtil;
|
|||
@Singleton
|
||||
public class CreateChange
|
||||
extends RetryingRestModifyView<TopLevelResource, ChangeInput, Response<ChangeInfo>> {
|
||||
|
||||
private final String anonymousCowardName;
|
||||
private final Provider<ReviewDb> db;
|
||||
private final GitRepositoryManager gitManager;
|
||||
private final AccountCache accountCache;
|
||||
private final Sequences seq;
|
||||
private final TimeZone serverTimeZone;
|
||||
private final PermissionBackend permissionBackend;
|
||||
private final Provider<CurrentUser> user;
|
||||
private final ProjectsCollection projectsCollection;
|
||||
private final ChangeInserter.Factory changeInserterFactory;
|
||||
|
@ -115,6 +116,7 @@ public class CreateChange
|
|||
AccountCache accountCache,
|
||||
Sequences seq,
|
||||
@GerritPersonIdent PersonIdent myIdent,
|
||||
PermissionBackend permissionBackend,
|
||||
Provider<CurrentUser> user,
|
||||
ProjectsCollection projectsCollection,
|
||||
ChangeInserter.Factory changeInserterFactory,
|
||||
|
@ -132,6 +134,7 @@ public class CreateChange
|
|||
this.accountCache = accountCache;
|
||||
this.seq = seq;
|
||||
this.serverTimeZone = myIdent.getTimeZone();
|
||||
this.permissionBackend = permissionBackend;
|
||||
this.user = user;
|
||||
this.projectsCollection = projectsCollection;
|
||||
this.changeInserterFactory = changeInserterFactory;
|
||||
|
@ -165,26 +168,18 @@ public class CreateChange
|
|||
if (input.status != ChangeStatus.NEW && input.status != ChangeStatus.DRAFT) {
|
||||
throw new BadRequestException("unsupported change status");
|
||||
}
|
||||
|
||||
if (!allowDrafts && input.status == ChangeStatus.DRAFT) {
|
||||
throw new MethodNotAllowedException("draft workflow is disabled");
|
||||
}
|
||||
}
|
||||
|
||||
String refName = RefNames.fullName(input.branch);
|
||||
ProjectResource rsrc = projectsCollection.parse(input.project);
|
||||
|
||||
Capable r = rsrc.getControl().canPushToAtLeastOneRef();
|
||||
if (r != Capable.OK) {
|
||||
throw new AuthException(r.getMessage());
|
||||
}
|
||||
|
||||
RefControl refControl = rsrc.getControl().controlForRef(refName);
|
||||
if (!refControl.canUpload() || !refControl.isVisible()) {
|
||||
throw new AuthException("cannot upload review");
|
||||
}
|
||||
checkValidCLA(rsrc.getControl());
|
||||
|
||||
Project.NameKey project = rsrc.getNameKey();
|
||||
String refName = RefNames.fullName(input.branch);
|
||||
permissionBackend.user(user).project(project).ref(refName).check(RefPermission.CREATE_CHANGE);
|
||||
|
||||
try (Repository git = gitManager.openRepository(project);
|
||||
ObjectInserter oi = git.newObjectInserter();
|
||||
ObjectReader reader = oi.newReader();
|
||||
|
@ -345,4 +340,11 @@ public class CreateChange
|
|||
private static ObjectId emptyTreeId(ObjectInserter inserter) throws IOException {
|
||||
return inserter.insert(new TreeFormatter());
|
||||
}
|
||||
|
||||
static void checkValidCLA(ProjectControl ctl) throws AuthException {
|
||||
Capable capable = ctl.canPushToAtLeastOneRef();
|
||||
if (capable != Capable.OK) {
|
||||
throw new AuthException(capable.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -320,6 +320,9 @@ public class PatchSetInserter implements BatchUpdateOp {
|
|||
return;
|
||||
}
|
||||
|
||||
PermissionBackend.ForRef perm =
|
||||
permissionBackend.user(ctx.getUser()).ref(origCtl.getChange().getDest());
|
||||
|
||||
String refName = getPatchSetId().toRefName();
|
||||
try (CommitReceivedEvent event =
|
||||
new CommitReceivedEvent(
|
||||
|
@ -333,7 +336,7 @@ public class PatchSetInserter implements BatchUpdateOp {
|
|||
commitId,
|
||||
ctx.getIdentifiedUser())) {
|
||||
commitValidatorsFactory
|
||||
.forGerritCommits(origCtl.getRefControl(), new NoSshInfo(), ctx.getRevWalk())
|
||||
.forGerritCommits(perm, origCtl.getRefControl(), new NoSshInfo(), ctx.getRevWalk())
|
||||
.validate(event);
|
||||
} catch (CommitValidationException e) {
|
||||
throw new ResourceConflictException(e.getFullMessage());
|
||||
|
|
|
@ -14,11 +14,9 @@
|
|||
|
||||
package com.google.gerrit.server.change;
|
||||
|
||||
import com.google.gerrit.common.data.Capable;
|
||||
import com.google.gerrit.extensions.api.changes.PublishChangeEditInput;
|
||||
import com.google.gerrit.extensions.registration.DynamicMap;
|
||||
import com.google.gerrit.extensions.restapi.AcceptsPost;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.ChildCollection;
|
||||
import com.google.gerrit.extensions.restapi.IdString;
|
||||
import com.google.gerrit.extensions.restapi.NotImplementedException;
|
||||
|
@ -88,11 +86,7 @@ public class PublishChangeEdit
|
|||
protected Response<?> applyImpl(
|
||||
BatchUpdate.Factory updateFactory, ChangeResource rsrc, PublishChangeEditInput in)
|
||||
throws IOException, OrmException, RestApiException, UpdateException {
|
||||
Capable r = rsrc.getControl().getProjectControl().canPushToAtLeastOneRef();
|
||||
if (r != Capable.OK) {
|
||||
throw new AuthException(r.getMessage());
|
||||
}
|
||||
|
||||
CreateChange.checkValidCLA(rsrc.getControl().getProjectControl());
|
||||
Optional<ChangeEdit> edit = editUtil.byChange(rsrc.getChange());
|
||||
if (!edit.isPresent()) {
|
||||
throw new ResourceConflictException(
|
||||
|
|
|
@ -14,19 +14,18 @@
|
|||
|
||||
package com.google.gerrit.server.change;
|
||||
|
||||
import static com.google.gerrit.server.permissions.RefPermission.CREATE_CHANGE;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.gerrit.common.TimeUtil;
|
||||
import com.google.gerrit.common.data.Capable;
|
||||
import com.google.gerrit.extensions.api.changes.RevertInput;
|
||||
import com.google.gerrit.extensions.common.ChangeInfo;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
import com.google.gerrit.extensions.restapi.RestApiException;
|
||||
import com.google.gerrit.extensions.webui.UiAction;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.client.Change.Status;
|
||||
import com.google.gerrit.reviewdb.client.ChangeMessage;
|
||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
|
@ -43,10 +42,10 @@ import com.google.gerrit.server.extensions.events.ChangeReverted;
|
|||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.mail.send.RevertedSender;
|
||||
import com.google.gerrit.server.notedb.ReviewerStateInternal;
|
||||
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.project.ChangeControl;
|
||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||
import com.google.gerrit.server.project.ProjectControl;
|
||||
import com.google.gerrit.server.project.RefControl;
|
||||
import com.google.gerrit.server.update.BatchUpdate;
|
||||
import com.google.gerrit.server.update.BatchUpdateOp;
|
||||
import com.google.gerrit.server.update.ChangeContext;
|
||||
|
@ -82,6 +81,8 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
|
|||
private static final Logger log = LoggerFactory.getLogger(Revert.class);
|
||||
|
||||
private final Provider<ReviewDb> db;
|
||||
private final PermissionBackend permissionBackend;
|
||||
private final Provider<CurrentUser> user;
|
||||
private final GitRepositoryManager repoManager;
|
||||
private final ChangeInserter.Factory changeInserterFactory;
|
||||
private final ChangeMessagesUtil cmUtil;
|
||||
|
@ -96,6 +97,8 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
|
|||
@Inject
|
||||
Revert(
|
||||
Provider<ReviewDb> db,
|
||||
PermissionBackend permissionBackend,
|
||||
Provider<CurrentUser> user,
|
||||
GitRepositoryManager repoManager,
|
||||
ChangeInserter.Factory changeInserterFactory,
|
||||
ChangeMessagesUtil cmUtil,
|
||||
|
@ -109,6 +112,8 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
|
|||
ChangeReverted changeReverted) {
|
||||
super(retryHelper);
|
||||
this.db = db;
|
||||
this.permissionBackend = permissionBackend;
|
||||
this.user = user;
|
||||
this.repoManager = repoManager;
|
||||
this.changeInserterFactory = changeInserterFactory;
|
||||
this.cmUtil = cmUtil;
|
||||
|
@ -122,27 +127,21 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
|
|||
}
|
||||
|
||||
@Override
|
||||
protected ChangeInfo applyImpl(
|
||||
BatchUpdate.Factory updateFactory, ChangeResource req, RevertInput input)
|
||||
throws IOException, OrmException, RestApiException, UpdateException, NoSuchChangeException {
|
||||
RefControl refControl = req.getControl().getRefControl();
|
||||
ProjectControl projectControl = req.getControl().getProjectControl();
|
||||
|
||||
Capable capable = projectControl.canPushToAtLeastOneRef();
|
||||
if (capable != Capable.OK) {
|
||||
throw new AuthException(capable.getMessage());
|
||||
}
|
||||
|
||||
Change change = req.getChange();
|
||||
if (!refControl.canUpload()) {
|
||||
throw new AuthException("revert not permitted");
|
||||
} else if (change.getStatus() != Status.MERGED) {
|
||||
public ChangeInfo applyImpl(
|
||||
BatchUpdate.Factory updateFactory, ChangeResource rsrc, RevertInput input)
|
||||
throws IOException, OrmException, RestApiException, UpdateException, NoSuchChangeException,
|
||||
PermissionBackendException {
|
||||
Change change = rsrc.getChange();
|
||||
if (change.getStatus() != Change.Status.MERGED) {
|
||||
throw new ResourceConflictException("change is " + ChangeUtil.status(change));
|
||||
}
|
||||
|
||||
Change.Id revertedChangeId =
|
||||
revert(updateFactory, req.getControl(), Strings.emptyToNull(input.message));
|
||||
return json.noOptions().format(req.getProject(), revertedChangeId);
|
||||
CreateChange.checkValidCLA(rsrc.getControl().getProjectControl());
|
||||
permissionBackend.user(user).ref(change.getDest()).check(CREATE_CHANGE);
|
||||
|
||||
Change.Id revertId =
|
||||
revert(updateFactory, rsrc.getControl(), Strings.emptyToNull(input.message));
|
||||
return json.noOptions().format(rsrc.getProject(), revertId);
|
||||
}
|
||||
|
||||
private Change.Id revert(BatchUpdate.Factory updateFactory, ChangeControl ctl, String message)
|
||||
|
@ -234,13 +233,14 @@ public class Revert extends RetryingRestModifyView<ChangeResource, RevertInput,
|
|||
}
|
||||
|
||||
@Override
|
||||
public UiAction.Description getDescription(ChangeResource resource) {
|
||||
public UiAction.Description getDescription(ChangeResource rsrc) {
|
||||
Change change = rsrc.getChange();
|
||||
return new UiAction.Description()
|
||||
.setLabel("Revert")
|
||||
.setTitle("Revert the change")
|
||||
.setVisible(
|
||||
resource.getChange().getStatus() == Status.MERGED
|
||||
&& resource.getControl().getRefControl().canUpload());
|
||||
change.getStatus() == Change.Status.MERGED
|
||||
&& permissionBackend.user(user).ref(change.getDest()).testOrFalse(CREATE_CHANGE));
|
||||
}
|
||||
|
||||
private class NotifyOp implements BatchUpdateOp {
|
||||
|
|
|
@ -112,7 +112,6 @@ import com.google.gerrit.server.permissions.PermissionBackend;
|
|||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.permissions.ProjectPermission;
|
||||
import com.google.gerrit.server.permissions.RefPermission;
|
||||
import com.google.gerrit.server.project.ChangeControl;
|
||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||
import com.google.gerrit.server.project.ProjectCache;
|
||||
import com.google.gerrit.server.project.ProjectControl;
|
||||
|
@ -188,6 +187,7 @@ import org.slf4j.LoggerFactory;
|
|||
/** Receives change upload using the Git receive-pack protocol. */
|
||||
public class ReceiveCommits {
|
||||
private static final Logger log = LoggerFactory.getLogger(ReceiveCommits.class);
|
||||
private static final String BYPASS_REVIEW = "bypass-review";
|
||||
|
||||
public static final Pattern NEW_PATCHSET =
|
||||
Pattern.compile("^" + REFS_CHANGES + "(?:[0-9][0-9]/)?([1-9][0-9]*)(?:/new)?$");
|
||||
|
@ -1051,7 +1051,7 @@ public class ReceiveCommits {
|
|||
}
|
||||
}
|
||||
|
||||
private void parseCreate(ReceiveCommand cmd) {
|
||||
private void parseCreate(ReceiveCommand cmd) throws PermissionBackendException {
|
||||
RevObject obj;
|
||||
try {
|
||||
obj = rp.getRevWalk().parseAny(cmd.getNewId());
|
||||
|
@ -1069,7 +1069,14 @@ public class ReceiveCommits {
|
|||
}
|
||||
|
||||
RefControl ctl = projectControl.controlForRef(cmd.getRefName());
|
||||
if (ctl.canCreate(db, rp.getRepository(), obj)) {
|
||||
boolean ok;
|
||||
try {
|
||||
permissions.ref(cmd.getRefName()).check(RefPermission.CREATE);
|
||||
ok = true;
|
||||
} catch (AuthException err) {
|
||||
ok = false;
|
||||
}
|
||||
if (ok && ctl.canCreate(db, rp.getRepository(), obj)) {
|
||||
if (!validRefOperation(cmd)) {
|
||||
return;
|
||||
}
|
||||
|
@ -1202,6 +1209,7 @@ public class ReceiveCommits {
|
|||
private final boolean defaultPublishComments;
|
||||
Branch.NameKey dest;
|
||||
RefControl ctl;
|
||||
PermissionBackend.ForRef perm;
|
||||
Set<Account.Id> reviewer = Sets.newLinkedHashSet();
|
||||
Set<Account.Id> cc = Sets.newLinkedHashSet();
|
||||
Map<String, Short> labels = new HashMap<>();
|
||||
|
@ -1437,7 +1445,7 @@ public class ReceiveCommits {
|
|||
return ImmutableListMultimap.copyOf(pushOptions);
|
||||
}
|
||||
|
||||
private void parseMagicBranch(ReceiveCommand cmd) {
|
||||
private void parseMagicBranch(ReceiveCommand cmd) throws PermissionBackendException {
|
||||
// Permit exactly one new change request per push.
|
||||
if (magicBranch != null) {
|
||||
reject(cmd, "duplicate request");
|
||||
|
@ -1488,6 +1496,7 @@ public class ReceiveCommits {
|
|||
|
||||
magicBranch.dest = new Branch.NameKey(project.getNameKey(), ref);
|
||||
magicBranch.ctl = projectControl.controlForRef(ref);
|
||||
magicBranch.perm = permissions.ref(ref);
|
||||
if (projectControl.getProject().getState()
|
||||
!= com.google.gerrit.extensions.client.ProjectState.ACTIVE) {
|
||||
reject(cmd, "project is read only");
|
||||
|
@ -1508,9 +1517,11 @@ public class ReceiveCommits {
|
|||
}
|
||||
}
|
||||
|
||||
if (!magicBranch.ctl.canUpload()) {
|
||||
try {
|
||||
magicBranch.perm.check(RefPermission.CREATE_CHANGE);
|
||||
} catch (AuthException denied) {
|
||||
errors.put(Error.CODE_REVIEW, ref);
|
||||
reject(cmd, "cannot upload review");
|
||||
reject(cmd, denied.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1829,7 +1840,7 @@ public class ReceiveCommits {
|
|||
logDebug("Creating new change for {} even though it is already tracked", name);
|
||||
}
|
||||
|
||||
if (!validCommit(rp.getRevWalk(), magicBranch.ctl, magicBranch.cmd, c)) {
|
||||
if (!validCommit(rp.getRevWalk(), magicBranch.perm, magicBranch.ctl, magicBranch.cmd, c)) {
|
||||
// Not a change the user can propose? Abort as early as possible.
|
||||
newChanges = Collections.emptyList();
|
||||
logDebug("Aborting early due to invalid commit");
|
||||
|
@ -2407,8 +2418,9 @@ public class ReceiveCommits {
|
|||
}
|
||||
}
|
||||
|
||||
ChangeControl changeCtl = projectControl.controlFor(notes);
|
||||
if (!validCommit(rp.getRevWalk(), changeCtl.getRefControl(), inputCommand, newCommit)) {
|
||||
PermissionBackend.ForRef perm = permissions.ref(change.getDest().get());
|
||||
RefControl refctl = projectControl.controlForRef(change.getDest());
|
||||
if (!validCommit(rp.getRevWalk(), perm, refctl, inputCommand, newCommit)) {
|
||||
return false;
|
||||
}
|
||||
rp.getRevWalk().parseBody(priorCommit);
|
||||
|
@ -2704,17 +2716,22 @@ public class ReceiveCommits {
|
|||
return true;
|
||||
}
|
||||
|
||||
private void validateNewCommits(RefControl ctl, ReceiveCommand cmd) {
|
||||
if (ctl.canForgeAuthor()
|
||||
&& ctl.canForgeCommitter()
|
||||
&& ctl.canForgeGerritServerIdentity()
|
||||
&& ctl.canUploadMerges()
|
||||
&& !projectControl.getProjectState().isUseSignedOffBy()
|
||||
&& Iterables.isEmpty(rejectCommits)
|
||||
&& !RefNames.REFS_CONFIG.equals(ctl.getRefName())
|
||||
private void validateNewCommits(RefControl ctl, ReceiveCommand cmd)
|
||||
throws PermissionBackendException {
|
||||
PermissionBackend.ForRef perm = permissions.ref(ctl.getRefName());
|
||||
if (!RefNames.REFS_CONFIG.equals(cmd.getRefName())
|
||||
&& !(MagicBranch.isMagicBranch(cmd.getRefName())
|
||||
|| NEW_PATCHSET.matcher(cmd.getRefName()).matches())) {
|
||||
logDebug("Short-circuiting new commit validation");
|
||||
|| NEW_PATCHSET.matcher(cmd.getRefName()).matches())
|
||||
&& pushOptions.containsKey(BYPASS_REVIEW)) {
|
||||
try {
|
||||
perm.check(RefPermission.BYPASS_REVIEW);
|
||||
if (!Iterables.isEmpty(rejectCommits)) {
|
||||
throw new AuthException("reject-commits prevents " + BYPASS_REVIEW);
|
||||
}
|
||||
logDebug("Short-circuiting new commit validation");
|
||||
} catch (AuthException denied) {
|
||||
reject(cmd, denied.getMessage());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2735,7 +2752,7 @@ public class ReceiveCommits {
|
|||
i++;
|
||||
if (existing.keySet().contains(c)) {
|
||||
continue;
|
||||
} else if (!validCommit(walk, ctl, cmd, c)) {
|
||||
} else if (!validCommit(walk, perm, ctl, cmd, c)) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2761,7 +2778,8 @@ public class ReceiveCommits {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean validCommit(RevWalk rw, RefControl ctl, ReceiveCommand cmd, ObjectId id)
|
||||
private boolean validCommit(
|
||||
RevWalk rw, PermissionBackend.ForRef perm, RefControl ctl, ReceiveCommand cmd, ObjectId id)
|
||||
throws IOException {
|
||||
|
||||
if (validCommits.contains(id)) {
|
||||
|
@ -2779,8 +2797,8 @@ public class ReceiveCommits {
|
|||
&& magicBranch.merged;
|
||||
CommitValidators validators =
|
||||
isMerged
|
||||
? commitValidatorsFactory.forMergedCommits(ctl)
|
||||
: commitValidatorsFactory.forReceiveCommits(ctl, sshInfo, repo, rw);
|
||||
? commitValidatorsFactory.forMergedCommits(perm, ctl)
|
||||
: commitValidatorsFactory.forReceiveCommits(perm, ctl, sshInfo, repo, rw);
|
||||
messages.addAll(validators.validate(receiveEvent));
|
||||
} catch (CommitValidationException e) {
|
||||
logDebug("Commit validation failed on {}", c.name());
|
||||
|
|
|
@ -22,6 +22,11 @@ public class CommitValidationException extends ValidationException {
|
|||
private static final long serialVersionUID = 1L;
|
||||
private final ImmutableList<CommitValidationMessage> messages;
|
||||
|
||||
public CommitValidationException(String reason, CommitValidationMessage message) {
|
||||
super(reason);
|
||||
this.messages = ImmutableList.of(message);
|
||||
}
|
||||
|
||||
public CommitValidationException(String reason, List<CommitValidationMessage> messages) {
|
||||
super(reason);
|
||||
this.messages = ImmutableList.copyOf(messages);
|
||||
|
|
|
@ -26,6 +26,7 @@ import com.google.gerrit.common.Nullable;
|
|||
import com.google.gerrit.common.PageLinks;
|
||||
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo;
|
||||
import com.google.gerrit.extensions.registration.DynamicSet;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.RefNames;
|
||||
import com.google.gerrit.server.GerritPersonIdent;
|
||||
|
@ -39,7 +40,11 @@ import com.google.gerrit.server.events.CommitReceivedEvent;
|
|||
import com.google.gerrit.server.git.BanCommit;
|
||||
import com.google.gerrit.server.git.ProjectConfig;
|
||||
import com.google.gerrit.server.git.ValidationError;
|
||||
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.permissions.RefPermission;
|
||||
import com.google.gerrit.server.project.ProjectControl;
|
||||
import com.google.gerrit.server.project.ProjectState;
|
||||
import com.google.gerrit.server.project.RefControl;
|
||||
import com.google.gerrit.server.ssh.SshInfo;
|
||||
import com.google.gerrit.server.util.MagicBranch;
|
||||
|
@ -96,38 +101,45 @@ public class CommitValidators {
|
|||
}
|
||||
|
||||
public CommitValidators forReceiveCommits(
|
||||
RefControl refControl, SshInfo sshInfo, Repository repo, RevWalk rw) throws IOException {
|
||||
PermissionBackend.ForRef perm,
|
||||
RefControl refctl,
|
||||
SshInfo sshInfo,
|
||||
Repository repo,
|
||||
RevWalk rw)
|
||||
throws IOException {
|
||||
NoteMap rejectCommits = BanCommit.loadRejectCommitsMap(repo, rw);
|
||||
IdentifiedUser user = refctl.getUser().asIdentifiedUser();
|
||||
return new CommitValidators(
|
||||
ImmutableList.of(
|
||||
new UploadMergesPermissionValidator(refControl),
|
||||
new AmendedGerritMergeCommitValidationListener(refControl, gerritIdent),
|
||||
new AuthorUploaderValidator(refControl, canonicalWebUrl),
|
||||
new CommitterUploaderValidator(refControl, canonicalWebUrl),
|
||||
new SignedOffByValidator(refControl),
|
||||
new ChangeIdValidator(
|
||||
refControl, canonicalWebUrl, installCommitMsgHookCommand, sshInfo),
|
||||
new ConfigValidator(refControl, rw, allUsers),
|
||||
new UploadMergesPermissionValidator(refctl),
|
||||
new AmendedGerritMergeCommitValidationListener(perm, gerritIdent),
|
||||
new AuthorUploaderValidator(user, perm, canonicalWebUrl),
|
||||
new CommitterUploaderValidator(user, perm, canonicalWebUrl),
|
||||
new SignedOffByValidator(user, perm, refctl.getProjectControl().getProjectState()),
|
||||
new ChangeIdValidator(refctl, canonicalWebUrl, installCommitMsgHookCommand, sshInfo),
|
||||
new ConfigValidator(refctl, rw, allUsers),
|
||||
new BannedCommitsValidator(rejectCommits),
|
||||
new PluginCommitValidationListener(pluginValidators),
|
||||
new ExternalIdUpdateListener(allUsers, externalIdsConsistencyChecker)));
|
||||
}
|
||||
|
||||
public CommitValidators forGerritCommits(RefControl refControl, SshInfo sshInfo, RevWalk rw) {
|
||||
public CommitValidators forGerritCommits(
|
||||
PermissionBackend.ForRef perm, RefControl refctl, SshInfo sshInfo, RevWalk rw) {
|
||||
IdentifiedUser user = refctl.getUser().asIdentifiedUser();
|
||||
return new CommitValidators(
|
||||
ImmutableList.of(
|
||||
new UploadMergesPermissionValidator(refControl),
|
||||
new AmendedGerritMergeCommitValidationListener(refControl, gerritIdent),
|
||||
new AuthorUploaderValidator(refControl, canonicalWebUrl),
|
||||
new SignedOffByValidator(refControl),
|
||||
new ChangeIdValidator(
|
||||
refControl, canonicalWebUrl, installCommitMsgHookCommand, sshInfo),
|
||||
new ConfigValidator(refControl, rw, allUsers),
|
||||
new UploadMergesPermissionValidator(refctl),
|
||||
new AmendedGerritMergeCommitValidationListener(perm, gerritIdent),
|
||||
new AuthorUploaderValidator(user, perm, canonicalWebUrl),
|
||||
new SignedOffByValidator(user, perm, refctl.getProjectControl().getProjectState()),
|
||||
new ChangeIdValidator(refctl, canonicalWebUrl, installCommitMsgHookCommand, sshInfo),
|
||||
new ConfigValidator(refctl, rw, allUsers),
|
||||
new PluginCommitValidationListener(pluginValidators),
|
||||
new ExternalIdUpdateListener(allUsers, externalIdsConsistencyChecker)));
|
||||
}
|
||||
|
||||
public CommitValidators forMergedCommits(RefControl refControl) {
|
||||
public CommitValidators forMergedCommits(PermissionBackend.ForRef perm, RefControl refControl) {
|
||||
IdentifiedUser user = refControl.getUser().asIdentifiedUser();
|
||||
// Generally only include validators that are based on permissions of the
|
||||
// user creating a change for a merged commit; generally exclude
|
||||
// validators that would require amending the change in order to correct.
|
||||
|
@ -144,8 +156,8 @@ public class CommitValidators {
|
|||
return new CommitValidators(
|
||||
ImmutableList.of(
|
||||
new UploadMergesPermissionValidator(refControl),
|
||||
new AuthorUploaderValidator(refControl, canonicalWebUrl),
|
||||
new CommitterUploaderValidator(refControl, canonicalWebUrl)));
|
||||
new AuthorUploaderValidator(user, perm, canonicalWebUrl),
|
||||
new CommitterUploaderValidator(user, perm, canonicalWebUrl)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -441,37 +453,50 @@ public class CommitValidators {
|
|||
}
|
||||
|
||||
public static class SignedOffByValidator implements CommitValidationListener {
|
||||
private final RefControl refControl;
|
||||
private final IdentifiedUser user;
|
||||
private final PermissionBackend.ForRef perm;
|
||||
private final ProjectState state;
|
||||
|
||||
public SignedOffByValidator(RefControl refControl) {
|
||||
this.refControl = refControl;
|
||||
public SignedOffByValidator(
|
||||
IdentifiedUser user, PermissionBackend.ForRef perm, ProjectState state) {
|
||||
this.user = user;
|
||||
this.perm = perm;
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommitValidationMessage> onCommitReceived(CommitReceivedEvent receiveEvent)
|
||||
throws CommitValidationException {
|
||||
IdentifiedUser currentUser = refControl.getUser().asIdentifiedUser();
|
||||
final PersonIdent committer = receiveEvent.commit.getCommitterIdent();
|
||||
final PersonIdent author = receiveEvent.commit.getAuthorIdent();
|
||||
final ProjectControl projectControl = refControl.getProjectControl();
|
||||
if (!state.isUseSignedOffBy()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (projectControl.getProjectState().isUseSignedOffBy()) {
|
||||
boolean sboAuthor = false;
|
||||
boolean sboCommitter = false;
|
||||
boolean sboMe = false;
|
||||
for (final FooterLine footer : receiveEvent.commit.getFooterLines()) {
|
||||
if (footer.matches(FooterKey.SIGNED_OFF_BY)) {
|
||||
final String e = footer.getEmailAddress();
|
||||
if (e != null) {
|
||||
sboAuthor |= author.getEmailAddress().equals(e);
|
||||
sboCommitter |= committer.getEmailAddress().equals(e);
|
||||
sboMe |= currentUser.hasEmailAddress(e);
|
||||
}
|
||||
RevCommit commit = receiveEvent.commit;
|
||||
PersonIdent committer = commit.getCommitterIdent();
|
||||
PersonIdent author = commit.getAuthorIdent();
|
||||
|
||||
boolean sboAuthor = false;
|
||||
boolean sboCommitter = false;
|
||||
boolean sboMe = false;
|
||||
for (FooterLine footer : commit.getFooterLines()) {
|
||||
if (footer.matches(FooterKey.SIGNED_OFF_BY)) {
|
||||
String e = footer.getEmailAddress();
|
||||
if (e != null) {
|
||||
sboAuthor |= author.getEmailAddress().equals(e);
|
||||
sboCommitter |= committer.getEmailAddress().equals(e);
|
||||
sboMe |= user.hasEmailAddress(e);
|
||||
}
|
||||
}
|
||||
if (!sboAuthor && !sboCommitter && !sboMe && !refControl.canForgeCommitter()) {
|
||||
}
|
||||
if (!sboAuthor && !sboCommitter && !sboMe) {
|
||||
try {
|
||||
perm.check(RefPermission.FORGE_COMMITTER);
|
||||
} catch (AuthException denied) {
|
||||
throw new CommitValidationException(
|
||||
"not Signed-off-by author/committer/uploader in commit message footer");
|
||||
} catch (PermissionBackendException e) {
|
||||
log.error("cannot check FORGE_COMMITTER", e);
|
||||
throw new CommitValidationException("internal auth error");
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
|
@ -480,56 +505,69 @@ public class CommitValidators {
|
|||
|
||||
/** Require that author matches the uploader. */
|
||||
public static class AuthorUploaderValidator implements CommitValidationListener {
|
||||
private final RefControl refControl;
|
||||
private final IdentifiedUser user;
|
||||
private final PermissionBackend.ForRef perm;
|
||||
private final String canonicalWebUrl;
|
||||
|
||||
public AuthorUploaderValidator(RefControl refControl, String canonicalWebUrl) {
|
||||
this.refControl = refControl;
|
||||
public AuthorUploaderValidator(
|
||||
IdentifiedUser user, PermissionBackend.ForRef perm, String canonicalWebUrl) {
|
||||
this.user = user;
|
||||
this.perm = perm;
|
||||
this.canonicalWebUrl = canonicalWebUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommitValidationMessage> onCommitReceived(CommitReceivedEvent receiveEvent)
|
||||
throws CommitValidationException {
|
||||
IdentifiedUser currentUser = refControl.getUser().asIdentifiedUser();
|
||||
final PersonIdent author = receiveEvent.commit.getAuthorIdent();
|
||||
|
||||
if (!currentUser.hasEmailAddress(author.getEmailAddress()) && !refControl.canForgeAuthor()) {
|
||||
List<CommitValidationMessage> messages = new ArrayList<>();
|
||||
|
||||
messages.add(
|
||||
getInvalidEmailError(
|
||||
receiveEvent.commit, "author", author, currentUser, canonicalWebUrl));
|
||||
throw new CommitValidationException("invalid author", messages);
|
||||
PersonIdent author = receiveEvent.commit.getAuthorIdent();
|
||||
if (user.hasEmailAddress(author.getEmailAddress())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
perm.check(RefPermission.FORGE_AUTHOR);
|
||||
return Collections.emptyList();
|
||||
} catch (AuthException e) {
|
||||
throw new CommitValidationException(
|
||||
"invalid author",
|
||||
invalidEmail(receiveEvent.commit, "author", author, user, canonicalWebUrl));
|
||||
} catch (PermissionBackendException e) {
|
||||
log.error("cannot check FORGE_AUTHOR", e);
|
||||
throw new CommitValidationException("internal auth error");
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
/** Require that committer matches the uploader. */
|
||||
public static class CommitterUploaderValidator implements CommitValidationListener {
|
||||
private final RefControl refControl;
|
||||
private final IdentifiedUser user;
|
||||
private final PermissionBackend.ForRef perm;
|
||||
private final String canonicalWebUrl;
|
||||
|
||||
public CommitterUploaderValidator(RefControl refControl, String canonicalWebUrl) {
|
||||
this.refControl = refControl;
|
||||
public CommitterUploaderValidator(
|
||||
IdentifiedUser user, PermissionBackend.ForRef perm, String canonicalWebUrl) {
|
||||
this.user = user;
|
||||
this.perm = perm;
|
||||
this.canonicalWebUrl = canonicalWebUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommitValidationMessage> onCommitReceived(CommitReceivedEvent receiveEvent)
|
||||
throws CommitValidationException {
|
||||
IdentifiedUser currentUser = refControl.getUser().asIdentifiedUser();
|
||||
final PersonIdent committer = receiveEvent.commit.getCommitterIdent();
|
||||
if (!currentUser.hasEmailAddress(committer.getEmailAddress())
|
||||
&& !refControl.canForgeCommitter()) {
|
||||
List<CommitValidationMessage> messages = new ArrayList<>();
|
||||
messages.add(
|
||||
getInvalidEmailError(
|
||||
receiveEvent.commit, "committer", committer, currentUser, canonicalWebUrl));
|
||||
throw new CommitValidationException("invalid committer", messages);
|
||||
PersonIdent committer = receiveEvent.commit.getCommitterIdent();
|
||||
if (user.hasEmailAddress(committer.getEmailAddress())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
perm.check(RefPermission.FORGE_COMMITTER);
|
||||
return Collections.emptyList();
|
||||
} catch (AuthException e) {
|
||||
throw new CommitValidationException(
|
||||
"invalid committer",
|
||||
invalidEmail(receiveEvent.commit, "committer", committer, user, canonicalWebUrl));
|
||||
} catch (PermissionBackendException e) {
|
||||
log.error("cannot check FORGE_COMMITTER", e);
|
||||
throw new CommitValidationException("internal auth error");
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -539,25 +577,30 @@ public class CommitValidators {
|
|||
*/
|
||||
public static class AmendedGerritMergeCommitValidationListener
|
||||
implements CommitValidationListener {
|
||||
private final PermissionBackend.ForRef perm;
|
||||
private final PersonIdent gerritIdent;
|
||||
private final RefControl refControl;
|
||||
|
||||
public AmendedGerritMergeCommitValidationListener(
|
||||
final RefControl refControl, final PersonIdent gerritIdent) {
|
||||
this.refControl = refControl;
|
||||
PermissionBackend.ForRef perm, PersonIdent gerritIdent) {
|
||||
this.perm = perm;
|
||||
this.gerritIdent = gerritIdent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommitValidationMessage> onCommitReceived(CommitReceivedEvent receiveEvent)
|
||||
throws CommitValidationException {
|
||||
final PersonIdent author = receiveEvent.commit.getAuthorIdent();
|
||||
|
||||
PersonIdent author = receiveEvent.commit.getAuthorIdent();
|
||||
if (receiveEvent.commit.getParentCount() > 1
|
||||
&& author.getName().equals(gerritIdent.getName())
|
||||
&& author.getEmailAddress().equals(gerritIdent.getEmailAddress())
|
||||
&& !refControl.canForgeGerritServerIdentity()) {
|
||||
throw new CommitValidationException("do not amend merges not made by you");
|
||||
&& author.getEmailAddress().equals(gerritIdent.getEmailAddress())) {
|
||||
try {
|
||||
perm.check(RefPermission.FORGE_SERVER);
|
||||
} catch (AuthException denied) {
|
||||
throw new CommitValidationException("do not amend merges not made by you");
|
||||
} catch (PermissionBackendException e) {
|
||||
log.error("cannot check FORGE_SERVER", e);
|
||||
throw new CommitValidationException("internal auth error");
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
@ -629,7 +672,7 @@ public class CommitValidators {
|
|||
}
|
||||
}
|
||||
|
||||
private static CommitValidationMessage getInvalidEmailError(
|
||||
private static CommitValidationMessage invalidEmail(
|
||||
RevCommit c,
|
||||
String type,
|
||||
PersonIdent who,
|
||||
|
|
|
@ -32,7 +32,22 @@ public enum ProjectPermission {
|
|||
*
|
||||
* <p>This is a stronger form of {@link #ACCESS} where no filtering is required.
|
||||
*/
|
||||
READ(Permission.READ);
|
||||
READ(Permission.READ),
|
||||
|
||||
/**
|
||||
* Can create at least one reference in the project.
|
||||
*
|
||||
* <p>This project level permission only validates the user may create some type of reference
|
||||
* within the project. The exact reference name must be checked at creation:
|
||||
*
|
||||
* <pre>permissionBackend
|
||||
* .user(user)
|
||||
* .project(proj)
|
||||
* .ref(ref)
|
||||
* .check(RefPermission.CREATE);
|
||||
* </pre>
|
||||
*/
|
||||
CREATE_REF;
|
||||
|
||||
private final String name;
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ public enum RefPermission {
|
|||
FORGE_AUTHOR(Permission.FORGE_AUTHOR),
|
||||
FORGE_COMMITTER(Permission.FORGE_COMMITTER),
|
||||
FORGE_SERVER(Permission.FORGE_SERVER),
|
||||
BYPASS_REVIEW,
|
||||
|
||||
CREATE_CHANGE;
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ import com.google.gerrit.server.permissions.ChangePermissionOrLabel;
|
|||
import com.google.gerrit.server.permissions.LabelPermission;
|
||||
import com.google.gerrit.server.permissions.PermissionBackend.ForChange;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.permissions.RefPermission;
|
||||
import com.google.gerrit.server.query.change.ChangeData;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
|
@ -281,14 +282,14 @@ public class ChangeControl {
|
|||
/** Can this user rebase this change? */
|
||||
private boolean canRebase(ReviewDb db) throws OrmException {
|
||||
return (isOwner() || getRefControl().canSubmit(isOwner()) || getRefControl().canRebase())
|
||||
&& getRefControl().canUpload()
|
||||
&& refControl.asForRef().testOrFalse(RefPermission.CREATE_CHANGE)
|
||||
&& !isPatchSetLocked(db);
|
||||
}
|
||||
|
||||
/** Can this user restore this change? */
|
||||
private boolean canRestore(ReviewDb db) throws OrmException {
|
||||
return canAbandon(db) // Anyone who can abandon the change can restore it back
|
||||
&& getRefControl().canUpload(); // as long as you can upload too
|
||||
// Anyone who can abandon the change can restore it, as long as they can create changes.
|
||||
return canAbandon(db) && refControl.asForRef().testOrFalse(RefPermission.CREATE_CHANGE);
|
||||
}
|
||||
|
||||
/** All available label types for this change. */
|
||||
|
@ -326,7 +327,7 @@ public class ChangeControl {
|
|||
|
||||
/** Can this user add a patch set to this change? */
|
||||
private boolean canAddPatchSet(ReviewDb db) throws OrmException {
|
||||
if (!getRefControl().canUpload()
|
||||
if (!refControl.asForRef().testOrFalse(RefPermission.CREATE_CHANGE)
|
||||
|| isPatchSetLocked(db)
|
||||
|| !isPatchVisible(patchSetUtil.current(db, notes), db)) {
|
||||
return false;
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
|
||||
package com.google.gerrit.server.project;
|
||||
|
||||
import static com.google.gerrit.server.permissions.GlobalPermission.ADMINISTRATE_SERVER;
|
||||
import static com.google.gerrit.server.permissions.ProjectPermission.CREATE_REF;
|
||||
import static com.google.gerrit.server.permissions.RefPermission.CREATE_CHANGE;
|
||||
import static com.google.gerrit.server.permissions.RefPermission.READ;
|
||||
|
||||
import com.google.common.collect.ImmutableBiMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.gerrit.common.data.AccessSection;
|
||||
|
@ -25,6 +30,7 @@ import com.google.gerrit.extensions.api.access.AccessSectionInfo;
|
|||
import com.google.gerrit.extensions.api.access.PermissionInfo;
|
||||
import com.google.gerrit.extensions.api.access.PermissionRuleInfo;
|
||||
import com.google.gerrit.extensions.api.access.ProjectAccessInfo;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
|
||||
import com.google.gerrit.extensions.restapi.RestReadView;
|
||||
|
@ -37,6 +43,9 @@ import com.google.gerrit.server.account.GroupControl;
|
|||
import com.google.gerrit.server.config.AllProjectsName;
|
||||
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||
import com.google.gerrit.server.git.ProjectConfig;
|
||||
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||
import com.google.gerrit.server.permissions.RefPermission;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
|
@ -63,7 +72,8 @@ public class GetAccess implements RestReadView<ProjectResource> {
|
|||
PermissionRule.Action.INTERACTIVE,
|
||||
PermissionRuleInfo.Action.INTERACTIVE);
|
||||
|
||||
private final Provider<CurrentUser> self;
|
||||
private final Provider<CurrentUser> user;
|
||||
private final PermissionBackend permissionBackend;
|
||||
private final GroupControl.Factory groupControlFactory;
|
||||
private final AllProjectsName allProjectsName;
|
||||
private final ProjectJson projectJson;
|
||||
|
@ -75,6 +85,7 @@ public class GetAccess implements RestReadView<ProjectResource> {
|
|||
@Inject
|
||||
public GetAccess(
|
||||
Provider<CurrentUser> self,
|
||||
PermissionBackend permissionBackend,
|
||||
GroupControl.Factory groupControlFactory,
|
||||
AllProjectsName allProjectsName,
|
||||
ProjectCache projectCache,
|
||||
|
@ -82,7 +93,8 @@ public class GetAccess implements RestReadView<ProjectResource> {
|
|||
ProjectJson projectJson,
|
||||
ProjectControl.GenericFactory projectControlFactory,
|
||||
GroupBackend groupBackend) {
|
||||
this.self = self;
|
||||
this.user = self;
|
||||
this.permissionBackend = permissionBackend;
|
||||
this.groupControlFactory = groupControlFactory;
|
||||
this.allProjectsName = allProjectsName;
|
||||
this.projectJson = projectJson;
|
||||
|
@ -93,9 +105,10 @@ public class GetAccess implements RestReadView<ProjectResource> {
|
|||
}
|
||||
|
||||
public ProjectAccessInfo apply(Project.NameKey nameKey)
|
||||
throws ResourceNotFoundException, ResourceConflictException, IOException {
|
||||
throws ResourceNotFoundException, ResourceConflictException, IOException,
|
||||
PermissionBackendException {
|
||||
try {
|
||||
return apply(new ProjectResource(projectControlFactory.controlFor(nameKey, self.get())));
|
||||
return apply(new ProjectResource(projectControlFactory.controlFor(nameKey, user.get())));
|
||||
} catch (NoSuchProjectException e) {
|
||||
throw new ResourceNotFoundException(nameKey.get());
|
||||
}
|
||||
|
@ -103,16 +116,18 @@ public class GetAccess implements RestReadView<ProjectResource> {
|
|||
|
||||
@Override
|
||||
public ProjectAccessInfo apply(ProjectResource rsrc)
|
||||
throws ResourceNotFoundException, ResourceConflictException, IOException {
|
||||
throws ResourceNotFoundException, ResourceConflictException, IOException,
|
||||
PermissionBackendException {
|
||||
// Load the current configuration from the repository, ensuring it's the most
|
||||
// recent version available. If it differs from what was in the project
|
||||
// state, force a cache flush now.
|
||||
//
|
||||
|
||||
Project.NameKey projectName = rsrc.getNameKey();
|
||||
ProjectAccessInfo info = new ProjectAccessInfo();
|
||||
ProjectConfig config;
|
||||
ProjectControl pc = createProjectControl(projectName);
|
||||
RefControl metaConfigControl = pc.controlForRef(RefNames.REFS_CONFIG);
|
||||
PermissionBackend.ForProject perm = permissionBackend.user(user).project(projectName);
|
||||
|
||||
ProjectConfig config;
|
||||
try (MetaDataUpdate md = metaDataUpdateFactory.create(projectName)) {
|
||||
config = ProjectConfig.read(md);
|
||||
|
||||
|
@ -121,10 +136,12 @@ public class GetAccess implements RestReadView<ProjectResource> {
|
|||
config.commit(md);
|
||||
projectCache.evict(config.getProject());
|
||||
pc = createProjectControl(projectName);
|
||||
perm = permissionBackend.user(user).project(projectName);
|
||||
} else if (config.getRevision() != null
|
||||
&& !config.getRevision().equals(pc.getProjectState().getConfig().getRevision())) {
|
||||
projectCache.evict(config.getProject());
|
||||
pc = createProjectControl(projectName);
|
||||
perm = permissionBackend.user(user).project(projectName);
|
||||
}
|
||||
} catch (ConfigInvalidException e) {
|
||||
throw new ResourceConflictException(e.getMessage());
|
||||
|
@ -135,6 +152,7 @@ public class GetAccess implements RestReadView<ProjectResource> {
|
|||
info.local = new HashMap<>();
|
||||
info.ownerOf = new HashSet<>();
|
||||
Map<AccountGroup.UUID, Boolean> visibleGroups = new HashMap<>();
|
||||
boolean checkReadConfig = check(perm, RefNames.REFS_CONFIG, READ);
|
||||
|
||||
for (AccessSection section : config.getAccessSections()) {
|
||||
String name = section.getName();
|
||||
|
@ -143,20 +161,19 @@ public class GetAccess implements RestReadView<ProjectResource> {
|
|||
info.local.put(name, createAccessSection(section));
|
||||
info.ownerOf.add(name);
|
||||
|
||||
} else if (metaConfigControl.isVisible()) {
|
||||
} else if (checkReadConfig) {
|
||||
info.local.put(section.getName(), createAccessSection(section));
|
||||
}
|
||||
|
||||
} else if (RefConfigSection.isValid(name)) {
|
||||
RefControl rc = pc.controlForRef(name);
|
||||
if (rc.isOwner()) {
|
||||
if (pc.controlForRef(name).isOwner()) {
|
||||
info.local.put(name, createAccessSection(section));
|
||||
info.ownerOf.add(name);
|
||||
|
||||
} else if (metaConfigControl.isVisible()) {
|
||||
} else if (checkReadConfig) {
|
||||
info.local.put(name, createAccessSection(section));
|
||||
|
||||
} else if (rc.isVisible()) {
|
||||
} else if (check(perm, name, READ)) {
|
||||
// Filter the section to only add rules describing groups that
|
||||
// are visible to the current-user. This includes any group the
|
||||
// user is a member of, as well as groups they own or that
|
||||
|
@ -214,21 +231,32 @@ public class GetAccess implements RestReadView<ProjectResource> {
|
|||
info.inheritsFrom = projectJson.format(parent.getProject());
|
||||
}
|
||||
|
||||
if (pc.getProject().getNameKey().equals(allProjectsName)) {
|
||||
if (pc.isOwner()) {
|
||||
info.ownerOf.add(AccessSection.GLOBAL_CAPABILITIES);
|
||||
}
|
||||
if (projectName.equals(allProjectsName)
|
||||
&& permissionBackend.user(user).testOrFalse(ADMINISTRATE_SERVER)) {
|
||||
info.ownerOf.add(AccessSection.GLOBAL_CAPABILITIES);
|
||||
}
|
||||
|
||||
info.isOwner = toBoolean(pc.isOwner());
|
||||
info.canUpload =
|
||||
toBoolean(pc.isOwner() || (metaConfigControl.isVisible() && metaConfigControl.canUpload()));
|
||||
info.canAdd = toBoolean(pc.canAddRefs());
|
||||
info.configVisible = pc.isOwner() || metaConfigControl.isVisible();
|
||||
toBoolean(
|
||||
pc.isOwner()
|
||||
|| (checkReadConfig && perm.ref(RefNames.REFS_CONFIG).testOrFalse(CREATE_CHANGE)));
|
||||
info.canAdd = toBoolean(perm.testOrFalse(CREATE_REF));
|
||||
info.configVisible = checkReadConfig || pc.isOwner();
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
private static boolean check(PermissionBackend.ForProject ctx, String ref, RefPermission perm)
|
||||
throws PermissionBackendException {
|
||||
try {
|
||||
ctx.ref(ref).check(perm);
|
||||
return true;
|
||||
} catch (AuthException denied) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private AccessSectionInfo createAccessSection(AccessSection section) {
|
||||
AccessSectionInfo accessSectionInfo = new AccessSectionInfo();
|
||||
accessSectionInfo.permissions = new HashMap<>();
|
||||
|
@ -255,7 +283,7 @@ public class GetAccess implements RestReadView<ProjectResource> {
|
|||
private ProjectControl createProjectControl(Project.NameKey projectName)
|
||||
throws IOException, ResourceNotFoundException {
|
||||
try {
|
||||
return projectControlFactory.controlFor(projectName, self.get());
|
||||
return projectControlFactory.controlFor(projectName, user.get());
|
||||
} catch (NoSuchProjectException e) {
|
||||
throw new ResourceNotFoundException(projectName.get());
|
||||
}
|
||||
|
|
|
@ -249,7 +249,7 @@ public class ProjectControl {
|
|||
return getProject().getState().equals(com.google.gerrit.extensions.client.ProjectState.HIDDEN);
|
||||
}
|
||||
|
||||
public boolean canAddRefs() {
|
||||
private boolean canAddRefs() {
|
||||
return (canPerformOnAnyRef(Permission.CREATE) || isOwnerAnyRef());
|
||||
}
|
||||
|
||||
|
@ -588,6 +588,9 @@ public class ProjectControl {
|
|||
|
||||
case READ:
|
||||
return !isHidden() && allRefsAreVisible(Collections.emptySet());
|
||||
|
||||
case CREATE_REF:
|
||||
return canAddRefs();
|
||||
}
|
||||
throw new PermissionBackendException(perm + " unsupported");
|
||||
}
|
||||
|
|
|
@ -151,12 +151,7 @@ public class RefControl {
|
|||
return blocks.isEmpty() && !allows.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the user can upload a change to the ref controlled by this object.
|
||||
*
|
||||
* @return {@code true} if the user specified can upload a change to the Git ref
|
||||
*/
|
||||
public boolean canUpload() {
|
||||
private boolean canUpload() {
|
||||
return projectControl.controlForRef("refs/for/" + getRefName()).canPerform(Permission.PUSH)
|
||||
&& isProjectStatePermittingWrite();
|
||||
}
|
||||
|
@ -391,7 +386,7 @@ public class RefControl {
|
|||
}
|
||||
|
||||
/** @return true if this user can forge the author line in a commit. */
|
||||
public boolean canForgeAuthor() {
|
||||
private boolean canForgeAuthor() {
|
||||
if (canForgeAuthor == null) {
|
||||
canForgeAuthor = canPerform(Permission.FORGE_AUTHOR);
|
||||
}
|
||||
|
@ -399,7 +394,7 @@ public class RefControl {
|
|||
}
|
||||
|
||||
/** @return true if this user can forge the committer line in a commit. */
|
||||
public boolean canForgeCommitter() {
|
||||
private boolean canForgeCommitter() {
|
||||
if (canForgeCommitter == null) {
|
||||
canForgeCommitter = canPerform(Permission.FORGE_COMMITTER);
|
||||
}
|
||||
|
@ -407,7 +402,7 @@ public class RefControl {
|
|||
}
|
||||
|
||||
/** @return true if this user can forge the server on the committer line. */
|
||||
public boolean canForgeGerritServerIdentity() {
|
||||
private boolean canForgeGerritServerIdentity() {
|
||||
return canPerform(Permission.FORGE_SERVER);
|
||||
}
|
||||
|
||||
|
@ -738,6 +733,13 @@ public class RefControl {
|
|||
return canForgeGerritServerIdentity();
|
||||
case CREATE_CHANGE:
|
||||
return canUpload();
|
||||
|
||||
case BYPASS_REVIEW:
|
||||
return canForgeAuthor()
|
||||
&& canForgeCommitter()
|
||||
&& canForgeGerritServerIdentity()
|
||||
&& canUploadMerges()
|
||||
&& !projectControl.getProjectState().isUseSignedOffBy();
|
||||
}
|
||||
throw new PermissionBackendException(perm + " unsupported");
|
||||
}
|
||||
|
|
|
@ -141,16 +141,18 @@ public class RefControlTest {
|
|||
assertThat(u.canPushToAtLeastOneRef()).named("can upload").isEqualTo(Capable.OK);
|
||||
}
|
||||
|
||||
private void assertCanUpload(String ref, ProjectControl u) {
|
||||
assertThat(u.controlForRef(ref).canUpload()).named("can upload " + ref).isTrue();
|
||||
private void assertCreateChange(String ref, ProjectControl u) {
|
||||
boolean create = u.asForProject().ref(ref).testOrFalse(RefPermission.CREATE_CHANGE);
|
||||
assertThat(create).named("can create change " + ref).isTrue();
|
||||
}
|
||||
|
||||
private void assertCannotUpload(ProjectControl u) {
|
||||
assertThat(u.canPushToAtLeastOneRef()).named("cannot upload").isNotEqualTo(Capable.OK);
|
||||
}
|
||||
|
||||
private void assertCannotUpload(String ref, ProjectControl u) {
|
||||
assertThat(u.controlForRef(ref).canUpload()).named("cannot upload " + ref).isFalse();
|
||||
private void assertCannotCreateChange(String ref, ProjectControl u) {
|
||||
boolean create = u.asForProject().ref(ref).testOrFalse(RefPermission.CREATE_CHANGE);
|
||||
assertThat(create).named("cannot create change " + ref).isFalse();
|
||||
}
|
||||
|
||||
private void assertBlocked(String p, String ref, ProjectControl u) {
|
||||
|
@ -405,8 +407,8 @@ public class RefControlTest {
|
|||
|
||||
ProjectControl u = user(local);
|
||||
assertCanUpload(u);
|
||||
assertCanUpload("refs/heads/master", u);
|
||||
assertCannotUpload("refs/heads/foobar", u);
|
||||
assertCreateChange("refs/heads/master", u);
|
||||
assertCannotCreateChange("refs/heads/foobar", u);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -415,7 +417,7 @@ public class RefControlTest {
|
|||
block(parent, PUSH, ANONYMOUS_USERS, "refs/drafts/*");
|
||||
|
||||
ProjectControl u = user(local);
|
||||
assertCanUpload("refs/heads/master", u);
|
||||
assertCreateChange("refs/heads/master", u);
|
||||
assertBlocked(PUSH, "refs/drafts/refs/heads/master", u);
|
||||
}
|
||||
|
||||
|
@ -438,8 +440,8 @@ public class RefControlTest {
|
|||
|
||||
ProjectControl u = user(local);
|
||||
assertCanUpload(u);
|
||||
assertCanUpload("refs/heads/master", u);
|
||||
assertCanUpload("refs/heads/foobar", u);
|
||||
assertCreateChange("refs/heads/master", u);
|
||||
assertCreateChange("refs/heads/foobar", u);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -508,7 +510,7 @@ public class RefControlTest {
|
|||
|
||||
ProjectControl u = user(local);
|
||||
assertCannotUpload(u);
|
||||
assertCannotUpload("refs/heads/master", u);
|
||||
assertCannotCreateChange("refs/heads/master", u);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in New Issue