Convert update and forceUpdate to PermissionBackend

Change-Id: Ic2d4d879f73541585f80be40cbcc8b670f23d151
This commit is contained in:
Shawn Pearce
2017-02-24 00:28:39 -08:00
parent 7d5d23ab8f
commit b18b2b75c3
4 changed files with 75 additions and 19 deletions

View File

@@ -107,6 +107,9 @@ import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.permissions.GlobalPermission;
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;
@@ -295,6 +298,7 @@ public class ReceiveCommits {
private final ChangeNotes.Factory notesFactory;
private final AccountResolver accountResolver;
private final PermissionBackend permissionBackend;
private final PermissionBackend.ForProject permissions;
private final CmdLineParser.Factory optionParserFactory;
private final GitReferenceUpdated gitRefUpdated;
private final PatchSetInfoFactory patchSetInfoFactory;
@@ -393,7 +397,7 @@ public class ReceiveCommits {
SetHashtagsOp.Factory hashtagsFactory,
ReplaceOp.Factory replaceOpFactory,
MergedByPushOp.Factory mergedByPushOpFactory)
throws IOException {
throws IOException, PermissionBackendException {
this.user = projectControl.getUser().asIdentifiedUser();
this.db = db;
this.seq = seq;
@@ -468,9 +472,15 @@ public class ReceiveCommits {
}
});
if (!projectControl.allRefsAreVisible()) {
permissions = permissionBackend.user(user).project(project.getNameKey());
// If the user lacks READ permission, some references may be filtered and hidden from view.
// Check objects mentioned inside the incoming pack file are reachable from visible refs.
try {
permissions.check(ProjectPermission.READ);
} catch (AuthException e) {
rp.setCheckReferencedObjectsAreReachable(receiveConfig.checkReferencedObjectsAreReachable);
}
rp.setAdvertiseRefsHook(
new VisibleRefFilter(tagCache, notesFactory, changeCache, repo, projectControl, db, false));
List<AdvertiseRefsHook> advHooks = new ArrayList<>(3);
@@ -584,7 +594,16 @@ public class ReceiveCommits {
batch.setRefLogIdent(rp.getRefLogIdent());
batch.setRefLogMessage("push", true);
parseCommands(commands);
try {
parseCommands(commands);
} catch (PermissionBackendException err) {
for (ReceiveCommand cmd : batch.getCommands()) {
if (cmd.getResult() == NOT_ATTEMPTED) {
cmd.setResult(REJECTED_OTHER_REASON, "internal server error");
}
}
logError(String.format("Failed to process refs in %s", project.getName()), err);
}
if (magicBranch != null && magicBranch.cmd.getResult() == NOT_ATTEMPTED) {
selectNewAndReplacedChangesFromMagicBranch();
}
@@ -928,7 +947,8 @@ public class ReceiveCommits {
return displayName;
}
private void parseCommands(Collection<ReceiveCommand> commands) {
private void parseCommands(Collection<ReceiveCommand> commands)
throws PermissionBackendException {
List<String> optionList = rp.getPushOptions();
if (optionList != null) {
for (String option : optionList) {
@@ -1162,24 +1182,29 @@ public class ReceiveCommits {
}
}
private void parseUpdate(ReceiveCommand cmd) {
private void parseUpdate(ReceiveCommand cmd) throws PermissionBackendException {
logDebug("Updating {}", cmd);
RefControl ctl = projectControl.controlForRef(cmd.getRefName());
if (ctl.canUpdate()) {
boolean ok;
try {
permissions.ref(cmd.getRefName()).check(RefPermission.UPDATE);
ok = true;
} catch (AuthException err) {
ok = false;
}
if (ok) {
if (isHead(cmd) && !isCommit(cmd)) {
return;
}
if (!validRefOperation(cmd)) {
return;
}
validateNewCommits(ctl, cmd);
validateNewCommits(projectControl.controlForRef(cmd.getRefName()), cmd);
batch.addCommand(cmd);
} else {
if (RefNames.REFS_CONFIG.equals(ctl.getRefName())) {
if (RefNames.REFS_CONFIG.equals(cmd.getRefName())) {
errors.put(Error.CONFIG_UPDATE, RefNames.REFS_CONFIG);
} else {
errors.put(Error.UPDATE, ctl.getRefName());
errors.put(Error.UPDATE, cmd.getRefName());
}
reject(cmd, "prohibited by Gerrit: ref update access denied");
}
@@ -1223,7 +1248,7 @@ public class ReceiveCommits {
}
}
private void parseRewind(ReceiveCommand cmd) {
private void parseRewind(ReceiveCommand cmd) throws PermissionBackendException {
RevCommit newObject;
try {
newObject = rp.getRevWalk().parseCommit(cmd.getNewId());
@@ -1246,7 +1271,14 @@ public class ReceiveCommits {
}
}
if (ctl.canForceUpdate()) {
boolean ok;
try {
permissions.ref(cmd.getRefName()).check(RefPermission.FORCE_UPDATE);
ok = true;
} catch (AuthException err) {
ok = false;
}
if (ok) {
if (!validRefOperation(cmd)) {
return;
}

View File

@@ -261,6 +261,25 @@ public abstract class PermissionBackend {
public boolean test(RefPermission perm) throws PermissionBackendException {
return test(EnumSet.of(perm)).contains(perm);
}
/**
* Test if user may be able to perform the permission.
*
* <p>Similar to {@link #test(RefPermission)} except this method returns {@code false} instead
* of throwing an exception.
*
* @param perm the permission to test.
* @return true if the user might be able to perform the permission; false if the user may be
* missing the necessary grants or state, or if the backend threw an exception.
*/
public boolean testOrFalse(RefPermission perm) {
try {
return test(perm);
} catch (PermissionBackendException e) {
logger.warn("Cannot test " + perm + "; assuming false", e);
return false;
}
}
}
/** PermissionBackend scoped to a user, project, reference and change. */

View File

@@ -188,7 +188,7 @@ public class RefControl {
}
/** @return true if the user can update the reference as a fast-forward. */
public boolean canUpdate() {
private boolean canUpdate() {
if (RefNames.REFS_CONFIG.equals(refName) && !projectControl.isOwner()) {
// Pushing requires being at least project owner, in addition to push.
// Pushing configuration changes modifies the access control
@@ -208,7 +208,7 @@ public class RefControl {
}
/** @return true if the user can rewind (force push) the reference. */
public boolean canForceUpdate() {
private boolean canForceUpdate() {
if (!canWrite()) {
return false;
}