diff options
author | Prudhvi Akhil Alahari <prudhvi.alahari@linaro.org> | 2022-06-23 17:05:59 +0530 |
---|---|---|
committer | Prudhvi Akhil Alahari <prudhvi.alahari@linaro.org> | 2022-10-06 09:22:17 +0530 |
commit | a527652cdfb05d2d21c3f28796ad0ab007cd3a73 (patch) | |
tree | 4683d239bb228e59ea6c3d2d609a56663390e5ce | |
parent | 14adad6c38324f7ae4572291d802c80c19614941 (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.java | 40 | ||||
-rw-r--r-- | java/com/google/gerrit/server/restapi/project/CreateBranch.java | 17 |
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()); |