Add on_behalf_of to /review REST API handler

The on_behalf_of field allows the caller to request the review to be
processed as a different user than the authenticated user.  This
allows role accounts to edit scores, such as removing a Verified+1 or
copying Code-Review+2 to a more recent revision.

In order to use the on_behalf_of field the caller must have been
granted the new labelAs-Verified permission, and the target user
must also have permission to use the label.

This is a least-privileges version of X-Gerrit-RunAs or suexec.  I
suspect many installs needing to use suexec are doing so to modify or
copy existing label score information.  Instead of granting full
permission to all user accounts administrators can now grant role
accounts only sufficient permission for automatically updating labels.

Permissions are per project, branch, and label.  This can be used to
prevent a role account from setting Code-Review+2 on a secure branch
such as refs/meta/config.

Change-Id: I24a9eab6d69962043d60936bd65b9a89b0a59ebb
This commit is contained in:
Shawn Pearce
2013-06-11 18:18:03 -07:00
parent 665c2a88d3
commit 9d78312c6c
13 changed files with 117 additions and 25 deletions

View File

@@ -29,6 +29,7 @@ public class Permission implements Comparable<Permission> {
public static final String FORGE_COMMITTER = "forgeCommitter";
public static final String FORGE_SERVER = "forgeServerAsCommitter";
public static final String LABEL = "label-";
public static final String LABEL_AS = "labelAs-";
public static final String OWNER = "owner";
public static final String PUBLISH_DRAFTS = "publishDrafts";
public static final String PUSH = "push";
@@ -43,6 +44,7 @@ public class Permission implements Comparable<Permission> {
private static final List<String> NAMES_LC;
private static final int labelIndex;
private static final int labelAsIndex;
static {
NAMES_LC = new ArrayList<String>();
@@ -58,6 +60,7 @@ public class Permission implements Comparable<Permission> {
NAMES_LC.add(PUSH_TAG.toLowerCase());
NAMES_LC.add(PUSH_SIGNED_TAG.toLowerCase());
NAMES_LC.add(LABEL.toLowerCase());
NAMES_LC.add(LABEL_AS.toLowerCase());
NAMES_LC.add(REBASE.toLowerCase());
NAMES_LC.add(REMOVE_REVIEWER.toLowerCase());
NAMES_LC.add(SUBMIT.toLowerCase());
@@ -67,15 +70,18 @@ public class Permission implements Comparable<Permission> {
NAMES_LC.add(PUBLISH_DRAFTS.toLowerCase());
labelIndex = NAMES_LC.indexOf(Permission.LABEL);
labelAsIndex = NAMES_LC.indexOf(Permission.LABEL_AS.toLowerCase());
}
/** @return true if the name is recognized as a permission name. */
public static boolean isPermission(String varName) {
String lc = varName.toLowerCase();
if (lc.startsWith(LABEL)) {
return LABEL.length() < lc.length();
}
return NAMES_LC.contains(lc);
return isLabel(varName)
|| isLabelAs(varName)
|| NAMES_LC.contains(varName.toLowerCase());
}
public static boolean hasRange(String varName) {
return isLabel(varName) || isLabelAs(varName);
}
/** @return true if the permission name is actually for a review label. */
@@ -83,11 +89,30 @@ public class Permission implements Comparable<Permission> {
return varName.startsWith(LABEL) && LABEL.length() < varName.length();
}
/** @return true if the permission is for impersonated review labels. */
public static boolean isLabelAs(String var) {
return var.startsWith(LABEL_AS) && LABEL_AS.length() < var.length();
}
/** @return permission name for the given review label. */
public static String forLabel(String labelName) {
return LABEL + labelName;
}
/** @return permission name to apply a label for another user. */
public static String forLabelAs(String labelName) {
return LABEL_AS + labelName;
}
public static String extractLabel(String varName) {
if (isLabel(varName)) {
return varName.substring(LABEL.length());
} else if (isLabelAs(varName)) {
return varName.substring(LABEL_AS.length());
}
return null;
}
public static boolean canBeOnAllProjects(String ref, String permissionName) {
if (AccessSection.ALL.equals(ref)) {
return !OWNER.equals(permissionName);
@@ -110,15 +135,8 @@ public class Permission implements Comparable<Permission> {
return name;
}
public boolean isLabel() {
return isLabel(getName());
}
public String getLabel() {
if (isLabel()) {
return getName().substring(LABEL.length());
}
return null;
return extractLabel(getName());
}
public Boolean getExclusiveGroup() {
@@ -223,8 +241,10 @@ public class Permission implements Comparable<Permission> {
}
private static int index(Permission a) {
if (a.isLabel()) {
if (isLabel(a.getName())) {
return labelIndex;
} else if (isLabelAs(a.getName())) {
return labelAsIndex;
}
int index = NAMES_LC.indexOf(a.getName().toLowerCase());

View File

@@ -86,7 +86,7 @@ public class PermissionRange implements Comparable<PermissionRange> {
}
public String getLabel() {
return isLabel() ? getName().substring(Permission.LABEL.length()) : null;
return Permission.extractLabel(getName());
}
public int getMin() {