Factor RefControl#canCreate(Repository, RevCommit) out
This commit factors this method out into its own class and delegates all RefPermission checks to the PermissionBackend. Change-Id: I005ac6c5b68ba4b3887cfba09ecf89567d98c45f
This commit is contained in:
@@ -128,7 +128,9 @@ import com.google.gerrit.server.permissions.GlobalPermission;
|
|||||||
import com.google.gerrit.server.permissions.PermissionBackend;
|
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||||
import com.google.gerrit.server.permissions.RefPermission;
|
import com.google.gerrit.server.permissions.RefPermission;
|
||||||
|
import com.google.gerrit.server.project.CreateRefControl;
|
||||||
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.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.ProjectState;
|
import com.google.gerrit.server.project.ProjectState;
|
||||||
@@ -320,6 +322,7 @@ class ReceiveCommits {
|
|||||||
private final String canonicalWebUrl;
|
private final String canonicalWebUrl;
|
||||||
private final SubmoduleOp.Factory subOpFactory;
|
private final SubmoduleOp.Factory subOpFactory;
|
||||||
private final TagCache tagCache;
|
private final TagCache tagCache;
|
||||||
|
private final CreateRefControl createRefControl;
|
||||||
|
|
||||||
// Assisted injected fields.
|
// Assisted injected fields.
|
||||||
private final AllRefsWatcher allRefsWatcher;
|
private final AllRefsWatcher allRefsWatcher;
|
||||||
@@ -402,6 +405,7 @@ class ReceiveCommits {
|
|||||||
SshInfo sshInfo,
|
SshInfo sshInfo,
|
||||||
SubmoduleOp.Factory subOpFactory,
|
SubmoduleOp.Factory subOpFactory,
|
||||||
TagCache tagCache,
|
TagCache tagCache,
|
||||||
|
CreateRefControl createRefControl,
|
||||||
@Assisted ProjectControl projectControl,
|
@Assisted ProjectControl projectControl,
|
||||||
@Assisted ReceivePack rp,
|
@Assisted ReceivePack rp,
|
||||||
@Assisted AllRefsWatcher allRefsWatcher,
|
@Assisted AllRefsWatcher allRefsWatcher,
|
||||||
@@ -440,6 +444,7 @@ class ReceiveCommits {
|
|||||||
this.sshInfo = sshInfo;
|
this.sshInfo = sshInfo;
|
||||||
this.subOpFactory = subOpFactory;
|
this.subOpFactory = subOpFactory;
|
||||||
this.tagCache = tagCache;
|
this.tagCache = tagCache;
|
||||||
|
this.createRefControl = createRefControl;
|
||||||
|
|
||||||
// Assisted injected fields.
|
// Assisted injected fields.
|
||||||
this.allRefsWatcher = allRefsWatcher;
|
this.allRefsWatcher = allRefsWatcher;
|
||||||
@@ -524,7 +529,7 @@ class ReceiveCommits {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
parseCommands(commands);
|
parseCommands(commands);
|
||||||
} catch (PermissionBackendException err) {
|
} catch (PermissionBackendException | NoSuchProjectException | IOException err) {
|
||||||
for (ReceiveCommand cmd : actualCommands) {
|
for (ReceiveCommand cmd : actualCommands) {
|
||||||
if (cmd.getResult() == NOT_ATTEMPTED) {
|
if (cmd.getResult() == NOT_ATTEMPTED) {
|
||||||
cmd.setResult(REJECTED_OTHER_REASON, "internal server error");
|
cmd.setResult(REJECTED_OTHER_REASON, "internal server error");
|
||||||
@@ -772,7 +777,7 @@ class ReceiveCommits {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void parseCommands(Collection<ReceiveCommand> commands)
|
private void parseCommands(Collection<ReceiveCommand> commands)
|
||||||
throws PermissionBackendException {
|
throws PermissionBackendException, NoSuchProjectException, IOException {
|
||||||
List<String> optionList = rp.getPushOptions();
|
List<String> optionList = rp.getPushOptions();
|
||||||
if (optionList != null) {
|
if (optionList != null) {
|
||||||
for (String option : optionList) {
|
for (String option : optionList) {
|
||||||
@@ -977,7 +982,8 @@ class ReceiveCommits {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseCreate(ReceiveCommand cmd) throws PermissionBackendException {
|
private void parseCreate(ReceiveCommand cmd)
|
||||||
|
throws PermissionBackendException, NoSuchProjectException, IOException {
|
||||||
RevObject obj;
|
RevObject obj;
|
||||||
try {
|
try {
|
||||||
obj = rp.getRevWalk().parseAny(cmd.getNewId());
|
obj = rp.getRevWalk().parseAny(cmd.getNewId());
|
||||||
@@ -994,8 +1000,8 @@ class ReceiveCommits {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RefControl ctl = projectControl.controlForRef(cmd.getRefName());
|
Branch.NameKey branch = new Branch.NameKey(project.getName(), cmd.getRefName());
|
||||||
String rejectReason = ctl.canCreate(rp.getRepository(), obj);
|
String rejectReason = createRefControl.canCreateRef(rp.getRepository(), obj, user, branch);
|
||||||
if (rejectReason != null) {
|
if (rejectReason != null) {
|
||||||
reject(cmd, "prohibited by Gerrit: " + rejectReason);
|
reject(cmd, "prohibited by Gerrit: " + rejectReason);
|
||||||
return;
|
return;
|
||||||
@@ -1006,6 +1012,7 @@ class ReceiveCommits {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RefControl ctl = projectControl.controlForRef(cmd.getRefName());
|
||||||
validateNewCommits(ctl, cmd);
|
validateNewCommits(ctl, cmd);
|
||||||
actualCommands.add(cmd);
|
actualCommands.add(cmd);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import com.google.gerrit.server.IdentifiedUser;
|
|||||||
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||||
import com.google.gerrit.server.permissions.PermissionBackend;
|
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.permissions.RefPermission;
|
||||||
import com.google.gerrit.server.util.MagicBranch;
|
import com.google.gerrit.server.util.MagicBranch;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
@@ -55,6 +56,7 @@ public class CreateBranch implements RestModifyView<ProjectResource, BranchInput
|
|||||||
private final GitRepositoryManager repoManager;
|
private final GitRepositoryManager repoManager;
|
||||||
private final GitReferenceUpdated referenceUpdated;
|
private final GitReferenceUpdated referenceUpdated;
|
||||||
private final RefValidationHelper refCreationValidator;
|
private final RefValidationHelper refCreationValidator;
|
||||||
|
private final CreateRefControl createRefControl;
|
||||||
private String ref;
|
private String ref;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@@ -64,18 +66,21 @@ public class CreateBranch implements RestModifyView<ProjectResource, BranchInput
|
|||||||
GitRepositoryManager repoManager,
|
GitRepositoryManager repoManager,
|
||||||
GitReferenceUpdated referenceUpdated,
|
GitReferenceUpdated referenceUpdated,
|
||||||
RefValidationHelper.Factory refHelperFactory,
|
RefValidationHelper.Factory refHelperFactory,
|
||||||
|
CreateRefControl createRefControl,
|
||||||
@Assisted String ref) {
|
@Assisted String ref) {
|
||||||
this.identifiedUser = identifiedUser;
|
this.identifiedUser = identifiedUser;
|
||||||
this.permissionBackend = permissionBackend;
|
this.permissionBackend = permissionBackend;
|
||||||
this.repoManager = repoManager;
|
this.repoManager = repoManager;
|
||||||
this.referenceUpdated = referenceUpdated;
|
this.referenceUpdated = referenceUpdated;
|
||||||
this.refCreationValidator = refHelperFactory.create(ReceiveCommand.Type.CREATE);
|
this.refCreationValidator = refHelperFactory.create(ReceiveCommand.Type.CREATE);
|
||||||
|
this.createRefControl = createRefControl;
|
||||||
this.ref = ref;
|
this.ref = ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BranchInfo apply(ProjectResource rsrc, BranchInput input)
|
public BranchInfo apply(ProjectResource rsrc, BranchInput input)
|
||||||
throws BadRequestException, AuthException, ResourceConflictException, IOException {
|
throws BadRequestException, AuthException, ResourceConflictException, IOException,
|
||||||
|
PermissionBackendException, NoSuchProjectException {
|
||||||
if (input == null) {
|
if (input == null) {
|
||||||
input = new BranchInput();
|
input = new BranchInput();
|
||||||
}
|
}
|
||||||
@@ -100,7 +105,6 @@ public class CreateBranch implements RestModifyView<ProjectResource, BranchInput
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Branch.NameKey name = new Branch.NameKey(rsrc.getNameKey(), ref);
|
final Branch.NameKey name = new Branch.NameKey(rsrc.getNameKey(), ref);
|
||||||
final RefControl refControl = rsrc.getControl().controlForRef(name);
|
|
||||||
try (Repository repo = repoManager.openRepository(rsrc.getNameKey())) {
|
try (Repository repo = repoManager.openRepository(rsrc.getNameKey())) {
|
||||||
ObjectId revid = RefUtil.parseBaseRevision(repo, rsrc.getNameKey(), input.revision);
|
ObjectId revid = RefUtil.parseBaseRevision(repo, rsrc.getNameKey(), input.revision);
|
||||||
RevWalk rw = RefUtil.verifyConnected(repo, revid);
|
RevWalk rw = RefUtil.verifyConnected(repo, revid);
|
||||||
@@ -117,7 +121,7 @@ public class CreateBranch implements RestModifyView<ProjectResource, BranchInput
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String rejectReason = refControl.canCreate(repo, object);
|
String rejectReason = createRefControl.canCreateRef(repo, object, identifiedUser.get(), name);
|
||||||
if (rejectReason != null) {
|
if (rejectReason != null) {
|
||||||
throw new AuthException("Cannot create \"" + ref + "\": " + rejectReason);
|
throw new AuthException("Cannot create \"" + ref + "\": " + rejectReason);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,179 @@
|
|||||||
|
// Copyright (C) 2017 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;
|
||||||
|
|
||||||
|
import com.google.gerrit.common.Nullable;
|
||||||
|
import com.google.gerrit.common.data.Permission;
|
||||||
|
import com.google.gerrit.extensions.restapi.AuthException;
|
||||||
|
import com.google.gerrit.reviewdb.client.Branch;
|
||||||
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
|
import com.google.gerrit.server.permissions.PermissionBackend;
|
||||||
|
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||||
|
import com.google.gerrit.server.permissions.RefPermission;
|
||||||
|
import java.io.IOException;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
|
import org.eclipse.jgit.revwalk.RevObject;
|
||||||
|
import org.eclipse.jgit.revwalk.RevTag;
|
||||||
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/** Manages access control for creating Git references (aka branches, tags). */
|
||||||
|
@Singleton
|
||||||
|
public class CreateRefControl {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(CreateRefControl.class);
|
||||||
|
|
||||||
|
private final PermissionBackend permissionBackend;
|
||||||
|
private final ProjectCache projectCache;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CreateRefControl(PermissionBackend permissionBackend, ProjectCache projectCache) {
|
||||||
|
this.permissionBackend = permissionBackend;
|
||||||
|
this.projectCache = projectCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the user can create a new Git ref.
|
||||||
|
*
|
||||||
|
* @param repo repository on which user want to create
|
||||||
|
* @param object the object the user will start the reference with
|
||||||
|
* @param user the current identified user
|
||||||
|
* @param branch the branch the new {@link RevObject} should be created on
|
||||||
|
* @return {@code null} if the user specified can create a new Git ref, or a String describing why
|
||||||
|
* the creation is not allowed.
|
||||||
|
* @throws PermissionBackendException on failure of permission checks
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public String canCreateRef(
|
||||||
|
Repository repo, RevObject object, IdentifiedUser user, Branch.NameKey branch)
|
||||||
|
throws PermissionBackendException, NoSuchProjectException, IOException {
|
||||||
|
ProjectState ps = projectCache.checkedGet(branch.getParentKey());
|
||||||
|
if (ps == null) {
|
||||||
|
throw new NoSuchProjectException(branch.getParentKey());
|
||||||
|
}
|
||||||
|
if (!ps.getProject()
|
||||||
|
.getState()
|
||||||
|
.equals(com.google.gerrit.extensions.client.ProjectState.ACTIVE)) {
|
||||||
|
return "project state does not permit write";
|
||||||
|
}
|
||||||
|
|
||||||
|
PermissionBackend.ForRef perm = permissionBackend.user(user).ref(branch);
|
||||||
|
if (object instanceof RevCommit) {
|
||||||
|
if (!testAuditLogged(perm, RefPermission.CREATE)) {
|
||||||
|
return user.getAccountId() + " lacks permission: " + Permission.CREATE;
|
||||||
|
}
|
||||||
|
return canCreateCommit(repo, (RevCommit) object, ps, user, perm);
|
||||||
|
} else if (object instanceof RevTag) {
|
||||||
|
final RevTag tag = (RevTag) object;
|
||||||
|
try (RevWalk rw = new RevWalk(repo)) {
|
||||||
|
rw.parseBody(tag);
|
||||||
|
} catch (IOException e) {
|
||||||
|
String msg =
|
||||||
|
String.format("RevWalk(%s) for pushing tag %s:", branch.getParentKey(), tag.name());
|
||||||
|
log.error(msg, e);
|
||||||
|
|
||||||
|
return "I/O exception for revwalk";
|
||||||
|
}
|
||||||
|
|
||||||
|
// If tagger is present, require it matches the user's email.
|
||||||
|
//
|
||||||
|
final PersonIdent tagger = tag.getTaggerIdent();
|
||||||
|
if (tagger != null) {
|
||||||
|
boolean valid;
|
||||||
|
if (user.isIdentifiedUser()) {
|
||||||
|
final String addr = tagger.getEmailAddress();
|
||||||
|
valid = user.asIdentifiedUser().hasEmailAddress(addr);
|
||||||
|
} else {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
if (!valid && !testAuditLogged(perm, RefPermission.FORGE_COMMITTER)) {
|
||||||
|
return user.getAccountId() + " lacks permission: " + Permission.FORGE_COMMITTER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RevObject tagObject = tag.getObject();
|
||||||
|
if (tagObject instanceof RevCommit) {
|
||||||
|
String rejectReason = canCreateCommit(repo, (RevCommit) tagObject, ps, user, perm);
|
||||||
|
if (rejectReason != null) {
|
||||||
|
return rejectReason;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String rejectReason = canCreateRef(repo, tagObject, user, branch);
|
||||||
|
if (rejectReason != null) {
|
||||||
|
return rejectReason;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the tag has a PGP signature, allow a lower level of permission
|
||||||
|
// than if it doesn't have a PGP signature.
|
||||||
|
//
|
||||||
|
RefControl refControl = ps.controlFor(user).controlForRef(branch);
|
||||||
|
if (tag.getFullMessage().contains("-----BEGIN PGP SIGNATURE-----\n")) {
|
||||||
|
return refControl.canPerform(Permission.CREATE_SIGNED_TAG)
|
||||||
|
? null
|
||||||
|
: user.getAccountId() + " lacks permission: " + Permission.CREATE_SIGNED_TAG;
|
||||||
|
}
|
||||||
|
return refControl.canPerform(Permission.CREATE_TAG)
|
||||||
|
? null
|
||||||
|
: user.getAccountId() + " lacks permission " + Permission.CREATE_TAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the user is allowed to create a new commit object if this introduces a new commit to
|
||||||
|
* the project. If not allowed, returns a string describing why it's not allowed. The userId
|
||||||
|
* argument is only used for the error message.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
private String canCreateCommit(
|
||||||
|
Repository repo,
|
||||||
|
RevCommit commit,
|
||||||
|
ProjectState projectState,
|
||||||
|
IdentifiedUser user,
|
||||||
|
PermissionBackend.ForRef forRef)
|
||||||
|
throws PermissionBackendException {
|
||||||
|
if (projectState.controlFor(user).isReachableFromHeadsOrTags(repo, commit)) {
|
||||||
|
// If the user has no push permissions, check whether the object is
|
||||||
|
// merged into a branch or tag readable by this user. If so, they are
|
||||||
|
// not effectively "pushing" more objects, so they can create the ref
|
||||||
|
// even if they don't have push permission.
|
||||||
|
return null;
|
||||||
|
} else if (testAuditLogged(forRef, RefPermission.UPDATE)) {
|
||||||
|
// If the user has push permissions, they can create the ref regardless
|
||||||
|
// of whether they are pushing any new objects along with the create.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return user.getAccountId()
|
||||||
|
+ " lacks permission "
|
||||||
|
+ Permission.PUSH
|
||||||
|
+ " for creating new commit object";
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean testAuditLogged(PermissionBackend.ForRef forRef, RefPermission p)
|
||||||
|
throws PermissionBackendException {
|
||||||
|
try {
|
||||||
|
forRef.check(p);
|
||||||
|
} catch (AuthException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,6 @@ package com.google.gerrit.server.project;
|
|||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
|
||||||
import com.google.gerrit.common.Nullable;
|
|
||||||
import com.google.gerrit.common.data.Permission;
|
import com.google.gerrit.common.data.Permission;
|
||||||
import com.google.gerrit.common.data.PermissionRange;
|
import com.google.gerrit.common.data.PermissionRange;
|
||||||
import com.google.gerrit.common.data.PermissionRule;
|
import com.google.gerrit.common.data.PermissionRule;
|
||||||
@@ -34,7 +33,6 @@ import com.google.gerrit.server.permissions.PermissionBackendException;
|
|||||||
import com.google.gerrit.server.permissions.RefPermission;
|
import com.google.gerrit.server.permissions.RefPermission;
|
||||||
import com.google.gerrit.server.query.change.ChangeData;
|
import com.google.gerrit.server.query.change.ChangeData;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -44,19 +42,9 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.eclipse.jgit.lib.PersonIdent;
|
|
||||||
import org.eclipse.jgit.lib.Repository;
|
|
||||||
import org.eclipse.jgit.revwalk.RevCommit;
|
|
||||||
import org.eclipse.jgit.revwalk.RevObject;
|
|
||||||
import org.eclipse.jgit.revwalk.RevTag;
|
|
||||||
import org.eclipse.jgit.revwalk.RevWalk;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/** Manages access control for Git references (aka branches, tags). */
|
/** Manages access control for Git references (aka branches, tags). */
|
||||||
public class RefControl {
|
public class RefControl {
|
||||||
private static final Logger log = LoggerFactory.getLogger(RefControl.class);
|
|
||||||
|
|
||||||
private final ProjectControl projectControl;
|
private final ProjectControl projectControl;
|
||||||
private final String refName;
|
private final String refName;
|
||||||
|
|
||||||
@@ -230,108 +218,6 @@ public class RefControl {
|
|||||||
return canForcePerform(Permission.PUSH);
|
return canForcePerform(Permission.PUSH);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines whether the user can create a new Git ref.
|
|
||||||
*
|
|
||||||
* @param repo repository on which user want to create
|
|
||||||
* @param object the object the user will start the reference with.
|
|
||||||
* @return {@code null} if the user specified can create a new Git ref, or a String describing why
|
|
||||||
* the creation is not allowed.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public String canCreate(Repository repo, RevObject object) {
|
|
||||||
if (!isProjectStatePermittingWrite()) {
|
|
||||||
return "project state does not permit write";
|
|
||||||
}
|
|
||||||
|
|
||||||
String userId =
|
|
||||||
getUser().isIdentifiedUser() ? "account " + getUser().getAccountId() : "anonymous user";
|
|
||||||
|
|
||||||
if (object instanceof RevCommit) {
|
|
||||||
if (!canPerform(Permission.CREATE)) {
|
|
||||||
return userId + " lacks permission: " + Permission.CREATE;
|
|
||||||
}
|
|
||||||
return canCreateCommit(repo, (RevCommit) object, userId);
|
|
||||||
} else if (object instanceof RevTag) {
|
|
||||||
final RevTag tag = (RevTag) object;
|
|
||||||
try (RevWalk rw = new RevWalk(repo)) {
|
|
||||||
rw.parseBody(tag);
|
|
||||||
} catch (IOException e) {
|
|
||||||
String msg =
|
|
||||||
String.format(
|
|
||||||
"RevWalk(%s) for pushing tag %s:",
|
|
||||||
projectControl.getProject().getNameKey(), tag.name());
|
|
||||||
log.error(msg, e);
|
|
||||||
|
|
||||||
return "I/O exception for revwalk";
|
|
||||||
}
|
|
||||||
|
|
||||||
// If tagger is present, require it matches the user's email.
|
|
||||||
//
|
|
||||||
final PersonIdent tagger = tag.getTaggerIdent();
|
|
||||||
if (tagger != null) {
|
|
||||||
boolean valid;
|
|
||||||
if (getUser().isIdentifiedUser()) {
|
|
||||||
final String addr = tagger.getEmailAddress();
|
|
||||||
valid = getUser().asIdentifiedUser().hasEmailAddress(addr);
|
|
||||||
} else {
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
if (!valid && !canForgeCommitter()) {
|
|
||||||
return userId + " lacks permission: " + Permission.FORGE_COMMITTER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RevObject tagObject = tag.getObject();
|
|
||||||
if (tagObject instanceof RevCommit) {
|
|
||||||
String rejectReason = canCreateCommit(repo, (RevCommit) tagObject, userId);
|
|
||||||
if (rejectReason != null) {
|
|
||||||
return rejectReason;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
String rejectReason = canCreate(repo, tagObject);
|
|
||||||
if (rejectReason != null) {
|
|
||||||
return rejectReason;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the tag has a PGP signature, allow a lower level of permission
|
|
||||||
// than if it doesn't have a PGP signature.
|
|
||||||
//
|
|
||||||
if (tag.getFullMessage().contains("-----BEGIN PGP SIGNATURE-----\n")) {
|
|
||||||
return canPerform(Permission.CREATE_SIGNED_TAG)
|
|
||||||
? null
|
|
||||||
: userId + " lacks permission: " + Permission.CREATE_SIGNED_TAG;
|
|
||||||
}
|
|
||||||
return canPerform(Permission.CREATE_TAG)
|
|
||||||
? null
|
|
||||||
: userId + " lacks permission " + Permission.CREATE_TAG;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the user is allowed to create a new commit object if this introduces a new commit to
|
|
||||||
* the project. If not allowed, returns a string describing why it's not allowed. The userId
|
|
||||||
* argument is only used for the error message.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
private String canCreateCommit(Repository repo, RevCommit commit, String userId) {
|
|
||||||
if (canUpdate()) {
|
|
||||||
// If the user has push permissions, they can create the ref regardless
|
|
||||||
// of whether they are pushing any new objects along with the create.
|
|
||||||
return null;
|
|
||||||
} else if (projectControl.isReachableFromHeadsOrTags(repo, commit)) {
|
|
||||||
// If the user has no push permissions, check whether the object is
|
|
||||||
// merged into a branch or tag readable by this user. If so, they are
|
|
||||||
// not effectively "pushing" more objects, so they can create the ref
|
|
||||||
// even if they don't have push permission.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return userId + " lacks permission " + Permission.PUSH + " for creating new commit object";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether the user can delete the Git ref controlled by this object.
|
* Determines whether the user can delete the Git ref controlled by this object.
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user