summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPrudhvi Akhil Alahari <prudhvi.alahari@linaro.org>2022-06-23 17:05:59 +0530
committerPrudhvi Akhil Alahari <prudhvi.alahari@linaro.org>2022-10-06 09:22:17 +0530
commita527652cdfb05d2d21c3f28796ad0ab007cd3a73 (patch)
tree4683d239bb228e59ea6c3d2d609a56663390e5ce
parent14adad6c38324f7ae4572291d802c80c19614941 (diff)
CreateRefControl: Check permissions on source refs
Optionally check user permissions on a given list of source refs instead of looking through all refs. Create branch read validation is more restrictive when user inputs a ref. This change improves the performance of create branch REST API when user inputs a ref. Test project stats: Total refs: 1222552 Total tags: 344763 Total commits: 16770216 Site setup: Index: Lucene git data: local disk Performance of create branch REST API [1] (5 samples): before: 2m55s, 2m52s, 2m49s, 2m49s, 2m50s after: 1m20s, 1m22s, 1m22s, 1m22s, 1m22s [1] curl --netrc --location --request POST \ 'http://<server>/a/projects/test_project/branches/test_branch' \ --header 'Content-Type: application/json' \ --data-raw '{"revision" : "refs/heads/branch1"}' Release-Notes: performance of create branch REST API is improved Change-Id: Ifaefc3bd1e0e73f0e676487bed5edfcb2701e06d
-rw-r--r--java/com/google/gerrit/server/project/CreateRefControl.java40
-rw-r--r--java/com/google/gerrit/server/restapi/project/CreateBranch.java17
2 files changed, 47 insertions, 10 deletions
diff --git a/java/com/google/gerrit/server/project/CreateRefControl.java b/java/com/google/gerrit/server/project/CreateRefControl.java
index 54ab628ccb..ab134b50ce 100644
--- a/java/com/google/gerrit/server/project/CreateRefControl.java
+++ b/java/com/google/gerrit/server/project/CreateRefControl.java
@@ -69,8 +69,9 @@ public class CreateRefControl {
*
* @param user the user performing the operation
* @param repo repository on which user want to create
- * @param branch the branch the new {@link RevObject} should be created on
+ * @param destBranch the branch the new {@link RevObject} should be created on
* @param object the object the user will start the reference with
+ * @param sourceBranches the source ref from which the new ref is created from
* @throws AuthException if creation is denied; the message explains the denial.
* @throws PermissionBackendException on failure of permission checks.
* @throws ResourceConflictException if the project state does not permit the operation
@@ -78,25 +79,46 @@ public class CreateRefControl {
public void checkCreateRef(
Provider<? extends CurrentUser> user,
Repository repo,
- BranchNameKey branch,
+ BranchNameKey destBranch,
RevObject object,
- boolean forPush)
+ boolean forPush,
+ BranchNameKey... sourceBranches)
throws AuthException, PermissionBackendException, NoSuchProjectException, IOException,
ResourceConflictException {
ProjectState ps =
- projectCache.get(branch.project()).orElseThrow(noSuchProject(branch.project()));
+ projectCache.get(destBranch.project()).orElseThrow(noSuchProject(destBranch.project()));
ps.checkStatePermitsWrite();
- PermissionBackend.ForRef perm = permissionBackend.user(user.get()).ref(branch);
+ PermissionBackend.ForRef perm = permissionBackend.user(user.get()).ref(destBranch);
if (object instanceof RevCommit) {
perm.check(RefPermission.CREATE);
- checkCreateCommit(user, repo, (RevCommit) object, ps.getNameKey(), perm, forPush);
+ if (sourceBranches.length == 0) {
+ checkCreateCommit(user, repo, (RevCommit) object, ps.getNameKey(), perm, forPush);
+ } else {
+ for (BranchNameKey src : sourceBranches) {
+ PermissionBackend.ForRef forRef = permissionBackend.user(user.get()).ref(src);
+ if (forRef.testOrFalse(RefPermission.READ)) {
+ return;
+ }
+ }
+ AuthException e =
+ new AuthException(
+ String.format(
+ "must have %s on existing ref to create new ref from it",
+ RefPermission.READ.describeForException()));
+ e.setAdvice(
+ String.format(
+ "use an existing ref visible to you, or get %s permission on the ref",
+ RefPermission.READ.describeForException()));
+ throw e;
+ }
} else if (object instanceof RevTag) {
RevTag tag = (RevTag) object;
try (RevWalk rw = new RevWalk(repo)) {
rw.parseBody(tag);
} catch (IOException e) {
- logger.atSevere().withCause(e).log("RevWalk(%s) parsing %s:", branch.project(), tag.name());
+ logger.atSevere().withCause(e).log(
+ "RevWalk(%s) parsing %s:", destBranch.project(), tag.name());
throw e;
}
@@ -112,12 +134,12 @@ public class CreateRefControl {
if (target instanceof RevCommit) {
checkCreateCommit(user, repo, (RevCommit) target, ps.getNameKey(), perm, forPush);
} else {
- checkCreateRef(user, repo, branch, target, forPush);
+ checkCreateRef(user, repo, destBranch, target, forPush);
}
// If the tag has a PGP signature, allow a lower level of permission
// than if it doesn't have a PGP signature.
- PermissionBackend.ForRef forRef = permissionBackend.user(user.get()).ref(branch);
+ PermissionBackend.ForRef forRef = permissionBackend.user(user.get()).ref(destBranch);
if (tag.getRawGpgSignature() != null) {
forRef.check(RefPermission.CREATE_SIGNED_TAG);
} else {
diff --git a/java/com/google/gerrit/server/restapi/project/CreateBranch.java b/java/com/google/gerrit/server/restapi/project/CreateBranch.java
index ba3a5be21a..33549fe669 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateBranch.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateBranch.java
@@ -47,6 +47,7 @@ import com.google.inject.Singleton;
import java.io.IOException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevObject;
@@ -129,7 +130,21 @@ public class CreateBranch
object = rw.parseCommit(object);
}
- createRefControl.checkCreateRef(identifiedUser, repo, name, object, /* forPush= */ false);
+ Ref sourceRef = repo.exactRef(input.revision);
+ if (sourceRef == null) {
+ createRefControl.checkCreateRef(identifiedUser, repo, name, object, /* forPush= */ false);
+ } else {
+ if (sourceRef.isSymbolic()) {
+ sourceRef = sourceRef.getTarget();
+ }
+ createRefControl.checkCreateRef(
+ identifiedUser,
+ repo,
+ name,
+ object,
+ /* forPush= */ false,
+ BranchNameKey.create(rsrc.getNameKey(), sourceRef.getName()));
+ }
RefUpdate u = repo.updateRef(ref);
u.setExpectedOldObjectId(ObjectId.zeroId());