Allow users to save update of parent project for review
Only Gerrit administrators are allowed to change the parent of a project. Now normal users can change the parent project in the WebUI and save the modification for review. This creates a change for the refs/meta/config branch that can be approved and submitted by administrators. Since the administrators are the only ones who can submit this change they are automatically added as reviewer. This way we have an integrated workflow for requesting and approving changes of parent projects, instead of having requests by email outside of Gerrit. Change-Id: I080649990f80edf78fd6eb465cef25fb8012352d Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
This commit is contained in:
@@ -112,7 +112,7 @@ public class ProjectAccessEditor extends Composite implements
|
||||
parentProject.setTargetHistoryToken( //
|
||||
Dispatcher.toProjectAdmin(parent, ProjectScreen.ACCESS));
|
||||
|
||||
parentProjectBox.setVisible(editing && value.canChangeParent());
|
||||
parentProjectBox.setVisible(editing);
|
||||
parentProjectBox.setProject(value.getProjectName());
|
||||
parentProjectBox.setParentProject(value.getInheritsFrom());
|
||||
parentProject.setVisible(!parentProjectBox.isVisible());
|
||||
|
||||
@@ -71,7 +71,8 @@ class ChangeProjectAccess extends ProjectAccessHandler<ProjectAccess> {
|
||||
|
||||
@Override
|
||||
protected ProjectAccess updateProjectConfig(ProjectConfig config,
|
||||
MetaDataUpdate md) throws IOException, NoSuchProjectException, ConfigInvalidException {
|
||||
MetaDataUpdate md, boolean parentProjectUpdate) throws IOException,
|
||||
NoSuchProjectException, ConfigInvalidException {
|
||||
config.commit(md);
|
||||
projectCache.evict(config.getProject());
|
||||
return projectAccessFactory.create(projectName).call();
|
||||
|
||||
@@ -132,12 +132,18 @@ public abstract class ProjectAccessHandler<T> extends Handler<T> {
|
||||
}
|
||||
}
|
||||
|
||||
boolean parentProjectUpdate = false;
|
||||
if (!config.getProject().getNameKey().equals(allProjects.get()) &&
|
||||
!config.getProject().getParent(allProjects.get()).equals(parentProjectName)) {
|
||||
parentProjectUpdate = true;
|
||||
try {
|
||||
setParent.get().validateParentUpdate(projectControl, parentProjectName.get());
|
||||
setParent.get().validateParentUpdate(projectControl,
|
||||
parentProjectName.get(), checkIfOwner);
|
||||
} catch (AuthException e) {
|
||||
throw new UpdateParentFailedException(e.getMessage(), e);
|
||||
throw new UpdateParentFailedException(
|
||||
"You are not allowed to change the parent project since you are "
|
||||
+ "not an administrator. You may save the modifications for review "
|
||||
+ "so that an administrator can approve them.", e);
|
||||
} catch (ResourceConflictException e) {
|
||||
throw new UpdateParentFailedException(e.getMessage(), e);
|
||||
} catch (UnprocessableEntityException e) {
|
||||
@@ -155,15 +161,15 @@ public abstract class ProjectAccessHandler<T> extends Handler<T> {
|
||||
md.setMessage("Modify access rules\n");
|
||||
}
|
||||
|
||||
return updateProjectConfig(config, md);
|
||||
return updateProjectConfig(config, md, parentProjectUpdate);
|
||||
} finally {
|
||||
md.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract T updateProjectConfig(ProjectConfig config,
|
||||
MetaDataUpdate md) throws IOException, NoSuchProjectException,
|
||||
ConfigInvalidException, OrmException;
|
||||
MetaDataUpdate md, boolean parentProjectUpdate) throws IOException,
|
||||
NoSuchProjectException, ConfigInvalidException, OrmException;
|
||||
|
||||
private void replace(ProjectConfig config, Set<String> toDelete,
|
||||
AccessSection section) throws NoSuchGroupException {
|
||||
|
||||
@@ -17,6 +17,8 @@ package com.google.gerrit.httpd.rpc.project;
|
||||
import com.google.gerrit.common.ChangeHooks;
|
||||
import com.google.gerrit.common.Nullable;
|
||||
import com.google.gerrit.common.data.AccessSection;
|
||||
import com.google.gerrit.common.data.GlobalCapability;
|
||||
import com.google.gerrit.common.data.PermissionRule;
|
||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||
import com.google.gerrit.reviewdb.client.Branch;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
@@ -39,6 +41,7 @@ import com.google.gerrit.server.index.ChangeIndexer;
|
||||
import com.google.gerrit.server.mail.CreateChangeSender;
|
||||
import com.google.gerrit.server.patch.PatchSetInfoFactory;
|
||||
import com.google.gerrit.server.project.ChangeControl;
|
||||
import com.google.gerrit.server.project.ProjectCache;
|
||||
import com.google.gerrit.server.project.ProjectControl;
|
||||
import com.google.gerrit.server.project.SetParent;
|
||||
import com.google.gerrit.server.util.TimeUtil;
|
||||
@@ -78,6 +81,7 @@ public class ReviewProjectAccess extends ProjectAccessHandler<Change.Id> {
|
||||
private final ChangeIndexer indexer;
|
||||
private final ChangeHooks hooks;
|
||||
private final CreateChangeSender.Factory createChangeSenderFactory;
|
||||
private final ProjectCache projectCache;
|
||||
|
||||
@Inject
|
||||
ReviewProjectAccess(final ProjectControl.Factory projectControlFactory,
|
||||
@@ -88,6 +92,7 @@ public class ReviewProjectAccess extends ProjectAccessHandler<Change.Id> {
|
||||
ChangeControl.GenericFactory changeFactory,
|
||||
ChangeIndexer indexer, ChangeHooks hooks,
|
||||
CreateChangeSender.Factory createChangeSenderFactory,
|
||||
ProjectCache projectCache,
|
||||
AllProjectsNameProvider allProjects,
|
||||
Provider<SetParent> setParent,
|
||||
|
||||
@@ -107,11 +112,13 @@ public class ReviewProjectAccess extends ProjectAccessHandler<Change.Id> {
|
||||
this.indexer = indexer;
|
||||
this.hooks = hooks;
|
||||
this.createChangeSenderFactory = createChangeSenderFactory;
|
||||
this.projectCache = projectCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Change.Id updateProjectConfig(ProjectConfig config, MetaDataUpdate md)
|
||||
throws IOException, OrmException {
|
||||
protected Change.Id updateProjectConfig(ProjectConfig config,
|
||||
MetaDataUpdate md, boolean parentProjectUpdate) throws IOException,
|
||||
OrmException {
|
||||
Change.Id changeId = new Change.Id(db.nextChangeId());
|
||||
PatchSet ps =
|
||||
new PatchSet(new PatchSet.Id(changeId, Change.INITIAL_PATCH_SET_ID));
|
||||
@@ -158,6 +165,9 @@ public class ReviewProjectAccess extends ProjectAccessHandler<Change.Id> {
|
||||
log.error("Cannot send email for new change " + change.getId(), err);
|
||||
}
|
||||
addProjectOwnersAsReviewers(change);
|
||||
if (parentProjectUpdate) {
|
||||
addAdministratorsAsReviewers(change);
|
||||
}
|
||||
return changeId;
|
||||
}
|
||||
|
||||
@@ -175,7 +185,7 @@ public class ReviewProjectAccess extends ProjectAccessHandler<Change.Id> {
|
||||
db.patchSetAncestors().insert(toInsert);
|
||||
}
|
||||
|
||||
private void addProjectOwnersAsReviewers(final Change change) {
|
||||
private void addProjectOwnersAsReviewers(Change change) {
|
||||
final String projectOwners =
|
||||
groupBackend.get(AccountGroup.PROJECT_OWNERS).getName();
|
||||
try {
|
||||
@@ -189,4 +199,22 @@ public class ReviewProjectAccess extends ProjectAccessHandler<Change.Id> {
|
||||
// can't be added as reviewer
|
||||
}
|
||||
}
|
||||
|
||||
private void addAdministratorsAsReviewers(Change change) {
|
||||
List<PermissionRule> adminRules =
|
||||
projectCache.getAllProjects().getConfig()
|
||||
.getAccessSection(AccessSection.GLOBAL_CAPABILITIES)
|
||||
.getPermission(GlobalCapability.ADMINISTRATE_SERVER).getRules();
|
||||
for (PermissionRule r : adminRules) {
|
||||
try {
|
||||
ChangeResource rsrc =
|
||||
new ChangeResource(changeFactory.controlFor(change, user));
|
||||
PostReviewers.Input input = new PostReviewers.Input();
|
||||
input.reviewer = r.getGroup().getUUID().get();
|
||||
reviewersProvider.get().apply(rsrc, input);
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ public class SetParent implements RestModifyView<ProjectResource, Input> {
|
||||
throws AuthException, ResourceConflictException,
|
||||
ResourceNotFoundException, UnprocessableEntityException, IOException {
|
||||
ProjectControl ctl = rsrc.getControl();
|
||||
validateParentUpdate(ctl, input.parent);
|
||||
validateParentUpdate(ctl, input.parent, true);
|
||||
IdentifiedUser user = (IdentifiedUser) ctl.getCurrentUser();
|
||||
try {
|
||||
MetaDataUpdate md = updateFactory.create(rsrc.getNameKey());
|
||||
@@ -97,11 +97,11 @@ public class SetParent implements RestModifyView<ProjectResource, Input> {
|
||||
}
|
||||
}
|
||||
|
||||
public void validateParentUpdate(final ProjectControl ctl, String newParent)
|
||||
throws AuthException, ResourceConflictException,
|
||||
public void validateParentUpdate(final ProjectControl ctl, String newParent,
|
||||
boolean checkIfAdmin) throws AuthException, ResourceConflictException,
|
||||
UnprocessableEntityException {
|
||||
IdentifiedUser user = (IdentifiedUser) ctl.getCurrentUser();
|
||||
if (!user.getCapabilities().canAdministrateServer()) {
|
||||
if (checkIfAdmin && !user.getCapabilities().canAdministrateServer()) {
|
||||
throw new AuthException("not administrator");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user