Allow per-branch OWN +1 to delegate branch ownership
We now correctly honor OWN +1 on reference names other than refs/* to delegate ownership (aka access control management) to a subnamespace within the larger reference namespace of the project. This permits a project owner to hand out control over a particular namespace to another group, e.g. refs/heads/qa/* can be delegated fully to the QA group, allowing its members to manage the Submit and Push Branch access control rules only within that subspace. The client UI is still really crude here. For example, it allows you to try to change global project settings, but then throws back the server exception when the server rejects your edit. It also allows you to try to delete access rights which you can't delete, and again just throws back the server error message. We really could be smarter here in the future, but the client doesn't have the RefControl processing logic required to understand which rows it can change, and which it cannot. Change-Id: I83c607eaea92d9fd7e40ed93f38b2c4478ec842f Signed-off-by: Shawn O. Pearce <sop@google.com> Reviewed-by: Nico Sallembien <nsallembien@google.com>
This commit is contained in:
parent
5aa47fee31
commit
d9d7fe02b0
@ -18,6 +18,7 @@ import com.google.gerrit.common.errors.CorruptEntityException;
|
|||||||
import com.google.gerrit.common.errors.NoSuchEntityException;
|
import com.google.gerrit.common.errors.NoSuchEntityException;
|
||||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||||
import com.google.gerrit.server.project.NoSuchProjectException;
|
import com.google.gerrit.server.project.NoSuchProjectException;
|
||||||
|
import com.google.gerrit.server.project.NoSuchRefException;
|
||||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||||
import com.google.gwtjsonrpc.client.VoidResult;
|
import com.google.gwtjsonrpc.client.VoidResult;
|
||||||
import com.google.gwtorm.client.OrmException;
|
import com.google.gwtorm.client.OrmException;
|
||||||
@ -68,6 +69,9 @@ public abstract class Handler<T> implements Callable<T> {
|
|||||||
} catch (NoSuchProjectException e) {
|
} catch (NoSuchProjectException e) {
|
||||||
callback.onFailure(new NoSuchEntityException());
|
callback.onFailure(new NoSuchEntityException());
|
||||||
|
|
||||||
|
} catch (NoSuchRefException e) {
|
||||||
|
callback.onFailure(new NoSuchEntityException());
|
||||||
|
|
||||||
} catch (NoSuchChangeException e) {
|
} catch (NoSuchChangeException e) {
|
||||||
callback.onFailure(new NoSuchEntityException());
|
callback.onFailure(new NoSuchEntityException());
|
||||||
|
|
||||||
|
@ -88,8 +88,7 @@ class AddBranch extends Handler<List<Branch>> {
|
|||||||
public List<Branch> call() throws NoSuchProjectException,
|
public List<Branch> call() throws NoSuchProjectException,
|
||||||
InvalidNameException, InvalidRevisionException, IOException {
|
InvalidNameException, InvalidRevisionException, IOException {
|
||||||
final ProjectControl projectControl =
|
final ProjectControl projectControl =
|
||||||
projectControlFactory.validateFor(projectName, ProjectControl.OWNER
|
projectControlFactory.controlFor(projectName);
|
||||||
| ProjectControl.VISIBLE);
|
|
||||||
|
|
||||||
String refname = branchName;
|
String refname = branchName;
|
||||||
while (refname.startsWith("/")) {
|
while (refname.startsWith("/")) {
|
||||||
|
@ -27,8 +27,10 @@ import com.google.gerrit.reviewdb.ReviewDb;
|
|||||||
import com.google.gerrit.server.account.GroupCache;
|
import com.google.gerrit.server.account.GroupCache;
|
||||||
import com.google.gerrit.server.account.NoSuchGroupException;
|
import com.google.gerrit.server.account.NoSuchGroupException;
|
||||||
import com.google.gerrit.server.project.NoSuchProjectException;
|
import com.google.gerrit.server.project.NoSuchProjectException;
|
||||||
|
import com.google.gerrit.server.project.NoSuchRefException;
|
||||||
import com.google.gerrit.server.project.ProjectCache;
|
import com.google.gerrit.server.project.ProjectCache;
|
||||||
import com.google.gerrit.server.project.ProjectControl;
|
import com.google.gerrit.server.project.ProjectControl;
|
||||||
|
import com.google.gerrit.server.project.RefControl;
|
||||||
import com.google.gwtorm.client.OrmException;
|
import com.google.gwtorm.client.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.assistedinject.Assisted;
|
import com.google.inject.assistedinject.Assisted;
|
||||||
@ -97,9 +99,9 @@ class AddRefRight extends Handler<ProjectDetail> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ProjectDetail call() throws NoSuchProjectException, OrmException,
|
public ProjectDetail call() throws NoSuchProjectException, OrmException,
|
||||||
NoSuchGroupException, InvalidNameException {
|
NoSuchGroupException, InvalidNameException, NoSuchRefException {
|
||||||
final ProjectControl projectControl =
|
final ProjectControl projectControl =
|
||||||
projectControlFactory.ownerFor(projectName);
|
projectControlFactory.controlFor(projectName);
|
||||||
|
|
||||||
final ApprovalType at = approvalTypes.getApprovalType(categoryId);
|
final ApprovalType at = approvalTypes.getApprovalType(categoryId);
|
||||||
if (at == null || at.getValue(min) == null || at.getValue(max) == null) {
|
if (at == null || at.getValue(min) == null || at.getValue(max) == null) {
|
||||||
@ -150,6 +152,10 @@ class AddRefRight extends Handler<ProjectDetail> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!controlForRef(projectControl, refPattern).isOwner()) {
|
||||||
|
throw new NoSuchRefException(refPattern);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO Support per-branch READ access.
|
// TODO Support per-branch READ access.
|
||||||
if (ApprovalCategory.READ.equals(categoryId)
|
if (ApprovalCategory.READ.equals(categoryId)
|
||||||
&& !refPattern.equals("refs/*")) {
|
&& !refPattern.equals("refs/*")) {
|
||||||
@ -178,4 +184,11 @@ class AddRefRight extends Handler<ProjectDetail> {
|
|||||||
projectCache.evict(projectControl.getProject());
|
projectCache.evict(projectControl.getProject());
|
||||||
return projectDetailFactory.create(projectName).call();
|
return projectDetailFactory.create(projectName).call();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RefControl controlForRef(ProjectControl p, String ref) {
|
||||||
|
if (ref.endsWith("/*")) {
|
||||||
|
ref = ref.substring(0, ref.length() - 1);
|
||||||
|
}
|
||||||
|
return p.controlForRef(ref);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,8 +68,7 @@ class DeleteBranches extends Handler<Set<Branch.NameKey>> {
|
|||||||
public Set<Branch.NameKey> call() throws NoSuchProjectException,
|
public Set<Branch.NameKey> call() throws NoSuchProjectException,
|
||||||
RepositoryNotFoundException {
|
RepositoryNotFoundException {
|
||||||
final ProjectControl projectControl =
|
final ProjectControl projectControl =
|
||||||
projectControlFactory.validateFor(projectName, ProjectControl.OWNER
|
projectControlFactory.controlFor(projectName);
|
||||||
| ProjectControl.VISIBLE);
|
|
||||||
|
|
||||||
for (Branch.NameKey k : toRemove) {
|
for (Branch.NameKey k : toRemove) {
|
||||||
if (!projectName.equals(k.getParentKey())) {
|
if (!projectName.equals(k.getParentKey())) {
|
||||||
|
@ -19,8 +19,10 @@ import com.google.gerrit.reviewdb.Project;
|
|||||||
import com.google.gerrit.reviewdb.RefRight;
|
import com.google.gerrit.reviewdb.RefRight;
|
||||||
import com.google.gerrit.reviewdb.ReviewDb;
|
import com.google.gerrit.reviewdb.ReviewDb;
|
||||||
import com.google.gerrit.server.project.NoSuchProjectException;
|
import com.google.gerrit.server.project.NoSuchProjectException;
|
||||||
|
import com.google.gerrit.server.project.NoSuchRefException;
|
||||||
import com.google.gerrit.server.project.ProjectCache;
|
import com.google.gerrit.server.project.ProjectCache;
|
||||||
import com.google.gerrit.server.project.ProjectControl;
|
import com.google.gerrit.server.project.ProjectControl;
|
||||||
|
import com.google.gerrit.server.project.RefControl;
|
||||||
import com.google.gwtjsonrpc.client.VoidResult;
|
import com.google.gwtjsonrpc.client.VoidResult;
|
||||||
import com.google.gwtorm.client.OrmException;
|
import com.google.gwtorm.client.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
@ -57,14 +59,18 @@ class DeleteRefRights extends Handler<VoidResult> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VoidResult call() throws NoSuchProjectException, OrmException {
|
public VoidResult call() throws NoSuchProjectException, OrmException,
|
||||||
|
NoSuchRefException {
|
||||||
final ProjectControl projectControl =
|
final ProjectControl projectControl =
|
||||||
projectControlFactory.ownerFor(projectName);
|
projectControlFactory.controlFor(projectName);
|
||||||
|
|
||||||
for (final RefRight.Key k : toRemove) {
|
for (final RefRight.Key k : toRemove) {
|
||||||
if (!projectName.equals(k.getProjectNameKey())) {
|
if (!projectName.equals(k.getProjectNameKey())) {
|
||||||
throw new IllegalArgumentException("All keys must be from same project");
|
throw new IllegalArgumentException("All keys must be from same project");
|
||||||
}
|
}
|
||||||
|
if (!controlForRef(projectControl, k.getRefPattern()).isOwner()) {
|
||||||
|
throw new NoSuchRefException(k.getRefPattern());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final RefRight.Key k : toRemove) {
|
for (final RefRight.Key k : toRemove) {
|
||||||
@ -76,4 +82,11 @@ class DeleteRefRights extends Handler<VoidResult> {
|
|||||||
projectCache.evict(projectControl.getProject());
|
projectCache.evict(projectControl.getProject());
|
||||||
return VoidResult.INSTANCE;
|
return VoidResult.INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RefControl controlForRef(ProjectControl p, String ref) {
|
||||||
|
if (ref.endsWith("/*")) {
|
||||||
|
ref = ref.substring(0, ref.length() - 1);
|
||||||
|
}
|
||||||
|
return p.controlForRef(ref);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,8 +66,10 @@ class OwnedProjects extends Handler<List<Project>> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
ProjectControl c = projectControlFactory.ownerFor(name);
|
ProjectControl c = projectControlFactory.controlFor(name);
|
||||||
result.add(c.getProject());
|
if (c.isOwnerAnyRef()) {
|
||||||
|
result.add(c.getProject());
|
||||||
|
}
|
||||||
} catch (NoSuchProjectException e) {
|
} catch (NoSuchProjectException e) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,10 @@ public final class RefRight {
|
|||||||
return projectName;
|
return projectName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getRefPattern() {
|
||||||
|
return refPattern.get();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public com.google.gwtorm.client.Key<?>[] members() {
|
public com.google.gwtorm.client.Key<?>[] members() {
|
||||||
return new com.google.gwtorm.client.Key<?>[] {refPattern, categoryId,
|
return new com.google.gwtorm.client.Key<?>[] {refPattern, categoryId,
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
// Copyright (C) 2010 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.project;
|
||||||
|
|
||||||
|
/** Indicates the reference does not exist. */
|
||||||
|
public class NoSuchRefException extends Exception {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public NoSuchRefException(final String ref) {
|
||||||
|
this(ref, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NoSuchRefException(final String ref, final Throwable why) {
|
||||||
|
super(ref, why);
|
||||||
|
}
|
||||||
|
}
|
@ -124,6 +124,12 @@ public class ProjectControl {
|
|||||||
|| getCurrentUser().isAdministrator();
|
|| getCurrentUser().isAdministrator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Does this user have ownership on at least one reference name? */
|
||||||
|
public boolean isOwnerAnyRef() {
|
||||||
|
return canPerformOnAnyRef(ApprovalCategory.OWN, (short) 1)
|
||||||
|
|| getCurrentUser().isAdministrator();
|
||||||
|
}
|
||||||
|
|
||||||
/** @return true if the user can upload to at least one reference */
|
/** @return true if the user can upload to at least one reference */
|
||||||
public boolean canUploadToAtLeastOneRef() {
|
public boolean canUploadToAtLeastOneRef() {
|
||||||
return canPerformOnAnyRef(ApprovalCategory.READ, (short) 2);
|
return canPerformOnAnyRef(ApprovalCategory.READ, (short) 2);
|
||||||
|
Loading…
Reference in New Issue
Block a user