| // Copyright (C) 2014 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.acceptance.git; |
| |
| import static com.google.common.collect.ImmutableList.toImmutableList; |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.common.truth.Truth.assertWithMessage; |
| import static com.google.common.truth.TruthJUnit.assume; |
| import static com.google.gerrit.acceptance.GitUtil.fetch; |
| import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow; |
| import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability; |
| import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.deny; |
| import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.permissionKey; |
| import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS; |
| import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction; |
| import static java.util.stream.Collectors.toList; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.gerrit.acceptance.AbstractDaemonTest; |
| import com.google.gerrit.acceptance.NoHttpd; |
| import com.google.gerrit.acceptance.PushOneCommit; |
| import com.google.gerrit.acceptance.TestAccount; |
| import com.google.gerrit.acceptance.config.GerritConfig; |
| import com.google.gerrit.acceptance.testsuite.change.IndexOperations; |
| import com.google.gerrit.acceptance.testsuite.project.ProjectOperations; |
| import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations; |
| import com.google.gerrit.common.Nullable; |
| import com.google.gerrit.common.data.GlobalCapability; |
| import com.google.gerrit.entities.AccessSection; |
| import com.google.gerrit.entities.AccountGroup; |
| import com.google.gerrit.entities.Patch; |
| import com.google.gerrit.entities.PatchSet; |
| import com.google.gerrit.entities.Permission; |
| import com.google.gerrit.entities.Project; |
| import com.google.gerrit.entities.RefNames; |
| import com.google.gerrit.extensions.api.changes.DraftInput; |
| import com.google.gerrit.extensions.api.groups.GroupInput; |
| import com.google.gerrit.extensions.api.projects.BranchInput; |
| import com.google.gerrit.extensions.restapi.RestApiException; |
| import com.google.gerrit.server.Sequence; |
| import com.google.gerrit.server.account.ServiceUserClassifier; |
| import com.google.gerrit.server.config.AllUsersName; |
| import com.google.gerrit.server.git.receive.ReceiveCommitsAdvertiseRefsHookChain; |
| import com.google.gerrit.server.git.receive.testing.TestRefAdvertiser; |
| import com.google.gerrit.server.permissions.PermissionBackend; |
| import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions; |
| import com.google.gerrit.server.query.change.ChangeData; |
| import com.google.gerrit.testing.ConfigSuite; |
| import com.google.inject.Inject; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.function.Predicate; |
| import org.eclipse.jgit.api.Git; |
| import org.eclipse.jgit.junit.TestRepository; |
| import org.eclipse.jgit.lib.Config; |
| 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.RevCommit; |
| import org.eclipse.jgit.transport.AdvertiseRefsHook; |
| import org.eclipse.jgit.transport.ReceivePack; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| @NoHttpd |
| public class RefAdvertisementIT extends AbstractDaemonTest { |
| @Inject private AllUsersName allUsersName; |
| @Inject private PermissionBackend permissionBackend; |
| @Inject private ProjectOperations projectOperations; |
| @Inject private RequestScopeOperations requestScopeOperations; |
| @Inject private IndexOperations.Change changeIndexOperations; |
| |
| private AccountGroup.UUID admins; |
| private AccountGroup.UUID nonInteractiveUsers; |
| |
| private RevCommit rcMaster; |
| private RevCommit rcBranch; |
| |
| private ChangeData cd1; |
| private String psRef1; |
| private String metaRef1; |
| |
| private ChangeData cd2; |
| private String psRef2; |
| private String metaRef2; |
| |
| private ChangeData cd3; |
| private String psRef3; |
| private String metaRef3; |
| |
| private ChangeData cd4; |
| private String psRef4; |
| private String metaRef4; |
| |
| @ConfigSuite.Config |
| public static Config enableFullRefEvaluation() { |
| Config cfg = new Config(); |
| cfg.setBoolean("auth", null, "skipFullRefEvaluationIfAllRefsAreVisible", false); |
| return cfg; |
| } |
| |
| @Before |
| public void setUp() throws Exception { |
| admins = adminGroupUuid(); |
| nonInteractiveUsers = groupUuid(ServiceUserClassifier.SERVICE_USERS); |
| setUpPermissions(); |
| setUpChanges(); |
| } |
| |
| // This method is idempotent, so it is safe to call it on every test setup. |
| private void setUpPermissions() throws Exception { |
| // Remove read permissions for all users besides admin. |
| try (ProjectConfigUpdate u = updateProject(allProjects)) { |
| |
| for (AccessSection sec : u.getConfig().getAccessSections()) { |
| u.getConfig() |
| .upsertAccessSection( |
| sec.getName(), |
| updatedSec -> { |
| updatedSec.removePermission(Permission.READ); |
| }); |
| } |
| u.save(); |
| } |
| projectOperations |
| .allProjectsForUpdate() |
| .add(allow(Permission.READ).ref("refs/*").group(admins)) |
| .update(); |
| |
| // Remove all read permissions on All-Users. |
| try (ProjectConfigUpdate u = updateProject(allUsers)) { |
| for (AccessSection sec : u.getConfig().getAccessSections()) { |
| u.getConfig() |
| .upsertAccessSection( |
| sec.getName(), |
| updatedSec -> { |
| updatedSec.removePermission(Permission.READ); |
| }); |
| } |
| u.save(); |
| } |
| } |
| |
| // Building the following: |
| // rcMaster (c1 master master-tag) <-- rcBranch (c2 branch branch-tag) |
| // \ \ |
| // (c3_open) (c4_open) |
| // |
| private void setUpChanges() throws Exception { |
| gApi.projects().name(project.get()).branch("branch").create(new BranchInput()); |
| |
| // First 2 changes are merged, which means the tags pointing to them are |
| // visible. |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.SUBMIT).ref("refs/for/refs/heads/*").group(admins)) |
| .update(); |
| |
| // rcMaster (c1 master) |
| PushOneCommit.Result mr = |
| pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master%submit"); |
| mr.assertOkStatus(); |
| cd1 = mr.getChange(); |
| rcMaster = mr.getCommit(); |
| psRef1 = cd1.currentPatchSet().id().toRefName(); |
| metaRef1 = RefNames.changeMetaRef(cd1.getId()); |
| |
| // rcMaster (c1 master) <-- rcBranch (c2 branch) |
| PushOneCommit.Result br = |
| pushFactory.create(admin.newIdent(), testRepo).to("refs/for/branch%submit"); |
| br.assertOkStatus(); |
| cd2 = br.getChange(); |
| rcBranch = br.getCommit(); |
| psRef2 = cd2.currentPatchSet().id().toRefName(); |
| metaRef2 = RefNames.changeMetaRef(cd2.getId()); |
| |
| // Second 2 changes are unmerged. |
| // rcMaster (c1 master) <-- rcBranch (c2 branch) |
| // \ |
| // (c3_open) |
| // |
| mr = pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master"); |
| mr.assertOkStatus(); |
| cd3 = mr.getChange(); |
| psRef3 = cd3.currentPatchSet().id().toRefName(); |
| metaRef3 = RefNames.changeMetaRef(cd3.getId()); |
| |
| // rcMaster (c1 master) <-- rcBranch (c2 branch) |
| // \ \ |
| // (c3_open) (c4_open) |
| br = pushFactory.create(admin.newIdent(), testRepo).to("refs/for/branch"); |
| br.assertOkStatus(); |
| cd4 = br.getChange(); |
| psRef4 = cd4.currentPatchSet().id().toRefName(); |
| metaRef4 = RefNames.changeMetaRef(cd4.getId()); |
| |
| try (Repository repo = repoManager.openRepository(project)) { |
| // rcMaster (c1 master master-tag) <-- rcBranch (c2 branch) |
| // \ \ |
| // (c3_open) (c4_open) |
| RefUpdate mtu = repo.updateRef("refs/tags/master-tag"); |
| mtu.setExpectedOldObjectId(ObjectId.zeroId()); |
| mtu.setNewObjectId(repo.exactRef("refs/heads/master").getObjectId()); |
| testRefAction(() -> assertThat(mtu.update()).isEqualTo(RefUpdate.Result.NEW)); |
| |
| // rcMaster (c1 master master-tag) <-- rcBranch (c2 branch branch-tag) |
| // \ \ |
| // (c3_open) (c4_open) |
| RefUpdate btu = repo.updateRef("refs/tags/branch-tag"); |
| btu.setExpectedOldObjectId(ObjectId.zeroId()); |
| btu.setNewObjectId(repo.exactRef("refs/heads/branch").getObjectId()); |
| testRefAction(() -> assertThat(btu.update()).isEqualTo(RefUpdate.Result.NEW)); |
| |
| // Create a tag for the tree of the commit on 'master' |
| // tree-tag -> master.tree |
| RefUpdate ttu = repo.updateRef("refs/tags/tree-tag"); |
| ttu.setExpectedOldObjectId(ObjectId.zeroId()); |
| ttu.setNewObjectId(rcMaster.getTree().toObjectId()); |
| testRefAction(() -> assertThat(ttu.update()).isEqualTo(RefUpdate.Result.NEW)); |
| } |
| } |
| |
| @Test |
| @GerritConfig(name = "auth.skipFullRefEvaluationIfAllRefsAreVisible", value = "false") |
| public void uploadPackAllRefsVisibleNoRefsMetaConfig() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/*").group(REGISTERED_USERS)) |
| .add(allow(Permission.READ).ref(RefNames.REFS_CONFIG).group(admins)) |
| .setExclusiveGroup(permissionKey(Permission.READ).ref(RefNames.REFS_CONFIG), true) |
| .update(); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| assertUploadPackRefs( |
| "HEAD", |
| psRef1, |
| metaRef1, |
| psRef2, |
| metaRef2, |
| psRef3, |
| metaRef3, |
| psRef4, |
| metaRef4, |
| "refs/heads/branch", |
| "refs/heads/master", |
| "refs/tags/branch-tag", |
| "refs/tags/master-tag"); |
| // tree-tag not visible. See comment in subsetOfBranchesVisibleIncludingHead. |
| } |
| |
| @Test |
| @GerritConfig(name = "auth.skipFullRefEvaluationIfAllRefsAreVisible", value = "true") |
| public void uploadPackAllRefsVisibleNoRefsMetaConfigSkipFullRefEval() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/*").group(REGISTERED_USERS)) |
| .add(allow(Permission.READ).ref(RefNames.REFS_CONFIG).group(admins)) |
| .setExclusiveGroup(permissionKey(Permission.READ).ref(RefNames.REFS_CONFIG), true) |
| .update(); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| assertUploadPackRefs( |
| "HEAD", |
| psRef1, |
| metaRef1, |
| psRef2, |
| metaRef2, |
| psRef3, |
| metaRef3, |
| psRef4, |
| metaRef4, |
| "refs/heads/branch", |
| "refs/heads/master", |
| "refs/tags/branch-tag", |
| "refs/tags/master-tag", |
| "refs/tags/tree-tag"); |
| } |
| |
| @Test |
| public void uploadPackAllRefsVisibleWithRefsMetaConfig() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/*").group(REGISTERED_USERS)) |
| .add(allow(Permission.READ).ref(RefNames.REFS_CONFIG).group(REGISTERED_USERS)) |
| .update(); |
| |
| assertUploadPackRefs( |
| "HEAD", |
| psRef1, |
| metaRef1, |
| psRef2, |
| metaRef2, |
| psRef3, |
| metaRef3, |
| psRef4, |
| metaRef4, |
| "refs/heads/branch", |
| "refs/heads/master", |
| RefNames.REFS_CONFIG, |
| "refs/tags/branch-tag", |
| "refs/tags/master-tag", |
| "refs/tags/tree-tag"); |
| } |
| |
| @Test |
| public void grantReadOnRefsTagsIsNoOp() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/tags/*").group(REGISTERED_USERS)) |
| .update(); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| assertUploadPackRefs(); // We expect no refs returned |
| } |
| |
| @Test |
| public void uploadPackSubsetOfBranchesVisibleIncludingHead() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/heads/master").group(REGISTERED_USERS)) |
| .add(deny(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS)) |
| .update(); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| assertUploadPackRefs( |
| "HEAD", psRef1, metaRef1, psRef3, metaRef3, "refs/heads/master", "refs/tags/master-tag"); |
| // tree-tag is not visible because we don't look at trees reachable from |
| // refs |
| } |
| |
| @Test |
| public void uploadPackSubsetOfBranchesVisibleNotIncludingHead() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(deny(Permission.READ).ref("refs/heads/master").group(REGISTERED_USERS)) |
| .add(allow(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS)) |
| .update(); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| assertUploadPackRefs( |
| psRef2, |
| metaRef2, |
| psRef4, |
| metaRef4, |
| "refs/heads/branch", |
| "refs/tags/branch-tag", |
| // master branch is not visible but master-tag is reachable from branch |
| // (since PushOneCommit always bases changes on each other). |
| "refs/tags/master-tag"); |
| // tree-tag not visible. See comment in subsetOfBranchesVisibleIncludingHead. |
| } |
| |
| @Test |
| public void uploadPackSubsetOfBranchesVisibleWithEdit() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/heads/master").group(REGISTERED_USERS)) |
| .update(); |
| |
| // Admin's edit is not visible. |
| requestScopeOperations.setApiUser(admin.id()); |
| gApi.changes().id(cd3.getId().get()).edit().create(); |
| |
| // User's edit is visible. |
| requestScopeOperations.setApiUser(user.id()); |
| gApi.changes().id(cd3.getId().get()).edit().create(); |
| |
| assertUploadPackRefs( |
| "HEAD", |
| psRef1, |
| metaRef1, |
| psRef3, |
| metaRef3, |
| "refs/heads/master", |
| "refs/tags/master-tag", |
| "refs/users/01/1000001/edit-" + cd3.getId() + "/1"); |
| // tree-tag not visible. See comment in subsetOfBranchesVisibleIncludingHead. |
| } |
| |
| @Test |
| public void uploadPackSubsetOfBranchesVisibleAllPatchsets() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/heads/master").group(REGISTERED_USERS)) |
| .update(); |
| |
| PushOneCommit.Result pushResult = |
| pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master"); |
| pushResult.assertOkStatus(); |
| String firstPatchSetRef = RefNames.patchSetRef(pushResult.getPatchSetId()); |
| |
| pushResult = amendChange(pushResult.getChangeId()); |
| pushResult.assertOkStatus(); |
| |
| String secondPatchSetRef = RefNames.patchSetRef(pushResult.getPatchSetId()); |
| |
| assertUploadPackRefs( |
| "HEAD", |
| psRef1, |
| metaRef1, |
| psRef3, |
| metaRef3, |
| RefNames.changeMetaRef(pushResult.getChange().getId()), |
| firstPatchSetRef, |
| // include all patchsets of the visible changes |
| secondPatchSetRef, |
| "refs/heads/master", |
| "refs/tags/master-tag"); |
| // tree-tag not visible. See comment in subsetOfBranchesVisibleIncludingHead. |
| } |
| |
| @Test |
| public void uploadPackSubsetOfBranchesAndEditsVisibleWithViewPrivateChanges() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/heads/master").group(REGISTERED_USERS)) |
| .add(allow(Permission.VIEW_PRIVATE_CHANGES).ref("refs/*").group(REGISTERED_USERS)) |
| .update(); |
| |
| // Admin's edit on change3 is visible. |
| requestScopeOperations.setApiUser(admin.id()); |
| gApi.changes().id(cd3.getId().get()).edit().create(); |
| |
| // Admin's edit on change4 is not visible since user cannot see the change. |
| gApi.changes().id(cd4.getId().get()).edit().create(); |
| |
| // User's edit is visible. |
| requestScopeOperations.setApiUser(user.id()); |
| gApi.changes().id(cd3.getId().get()).edit().create(); |
| |
| assertUploadPackRefs( |
| "HEAD", |
| psRef1, |
| metaRef1, |
| psRef3, |
| metaRef3, |
| "refs/heads/master", |
| "refs/tags/master-tag", |
| "refs/users/00/1000000/edit-" + cd3.getId() + "/1", |
| "refs/users/01/1000001/edit-" + cd3.getId() + "/1"); |
| // tree-tag not visible. See comment in subsetOfBranchesVisibleIncludingHead. |
| } |
| |
| @Test |
| public void uploadPackSubsetOfRefsVisibleWithAccessDatabase() throws Exception { |
| projectOperations |
| .allProjectsForUpdate() |
| .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS)) |
| .update(); |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(deny(Permission.READ).ref("refs/heads/master").group(REGISTERED_USERS)) |
| .add(allow(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS)) |
| .update(); |
| |
| requestScopeOperations.setApiUser(admin.id()); |
| gApi.changes().id(cd3.getId().get()).edit().create(); |
| requestScopeOperations.setApiUser(user.id()); |
| |
| assertUploadPackRefs( |
| // Change 1 is visible due to accessDatabase capability, even though |
| // refs/heads/master is not. |
| psRef1, |
| metaRef1, |
| psRef2, |
| metaRef2, |
| psRef3, |
| metaRef3, |
| psRef4, |
| metaRef4, |
| "refs/heads/branch", |
| "refs/tags/branch-tag", |
| // See comment in subsetOfBranchesVisibleNotIncludingHead. |
| "refs/tags/master-tag", |
| // All edits are visible due to accessDatabase capability. |
| "refs/users/00/1000000/edit-" + cd3.getId() + "/1"); |
| // tree-tag not visible. See comment in subsetOfBranchesVisibleIncludingHead. |
| } |
| |
| @Test |
| public void uploadPackNoSearchingChangeCacheImplMaster() throws Exception { |
| uploadPackNoSearchingChangeCacheImpl(); |
| } |
| |
| @Test |
| @GerritConfig(name = "container.slave", value = "true") |
| public void uploadPackNoSearchingChangeCacheImplSlave() throws Exception { |
| uploadPackNoSearchingChangeCacheImpl(); |
| } |
| |
| private void uploadPackNoSearchingChangeCacheImpl() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/heads/*").group(REGISTERED_USERS)) |
| .update(); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| assertRefs( |
| project, |
| user, |
| // Can't use stored values from the index so DB must be enabled. |
| false, |
| "HEAD", |
| psRef1, |
| metaRef1, |
| psRef2, |
| metaRef2, |
| psRef3, |
| metaRef3, |
| psRef4, |
| metaRef4, |
| "refs/heads/branch", |
| "refs/heads/master", |
| "refs/tags/branch-tag", |
| "refs/tags/master-tag"); |
| // tree-tag not visible. See comment in subsetOfBranchesVisibleIncludingHead. |
| } |
| |
| @Test |
| public void uploadPackSequencesWithAccessDatabase() throws Exception { |
| assertRefs(allProjects, user, true); |
| |
| projectOperations |
| .allProjectsForUpdate() |
| .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS)) |
| .update(); |
| assertRefs(allProjects, user, true, "refs/sequences/changes"); |
| } |
| |
| @Test |
| public void uploadPackAllRefsAreVisibleOrphanedTag() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/*").group(REGISTERED_USERS)) |
| .update(); |
| // Delete the pending change on 'branch' and 'branch' itself so that the tag gets orphaned |
| gApi.changes().id(cd4.getId().get()).delete(); |
| gApi.projects().name(project.get()).branch("refs/heads/branch").delete(); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| assertUploadPackRefs( |
| "HEAD", |
| "refs/meta/config", |
| psRef1, |
| metaRef1, |
| psRef2, |
| metaRef2, |
| psRef3, |
| metaRef3, |
| "refs/heads/master", |
| "refs/tags/branch-tag", |
| "refs/tags/master-tag", |
| "refs/tags/tree-tag"); |
| } |
| |
| @Test |
| public void uploadPackSubsetRefsVisibleOrphanedTagInvisible() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS)) |
| .update(); |
| testRefAction( |
| () -> { |
| // Create a tag for the pending change on 'branch' so that the tag is orphaned |
| try (Repository repo = repoManager.openRepository(project)) { |
| // change4-tag -> psRef4 |
| RefUpdate ctu = repo.updateRef("refs/tags/change4-tag"); |
| ctu.setExpectedOldObjectId(ObjectId.zeroId()); |
| ctu.setNewObjectId(repo.exactRef(psRef4).getObjectId()); |
| assertThat(ctu.update()).isEqualTo(RefUpdate.Result.NEW); |
| } |
| }); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| assertUploadPackRefs( |
| psRef2, |
| metaRef2, |
| psRef4, |
| metaRef4, |
| "refs/heads/branch", |
| "refs/tags/branch-tag", |
| // See comment in subsetOfBranchesVisibleNotIncludingHead. |
| "refs/tags/master-tag"); |
| } |
| |
| // first ls-remote: rcMaster (c1 master) |
| // second ls-remote: rcMaster (c1 master) <- newchange1 (master-newtag) |
| @Test |
| public void uploadPackNewCommitOrphanTagInvisible() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS)) |
| .update(); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| |
| // rcMaster (c1 master) |
| assertUploadPackRefs( |
| psRef2, |
| metaRef2, |
| psRef4, |
| metaRef4, |
| "refs/heads/branch", |
| "refs/tags/branch-tag", |
| // See comment in subsetOfBranchesVisibleNotIncludingHead. |
| "refs/tags/master-tag"); |
| |
| try (Repository repo = repoManager.openRepository(project)) { |
| PushOneCommit.Result r = |
| pushFactory.create(admin.newIdent(), testRepo).setParent(rcMaster).to("refs/for/master"); |
| r.assertOkStatus(); |
| |
| // rcMaster (c1 master) <- newchange1 (master-newtag) |
| RefUpdate btu = repo.updateRef("refs/tags/master-newtag"); |
| btu.setExpectedOldObjectId(ObjectId.zeroId()); |
| btu.setNewObjectId(r.getCommit()); |
| testRefAction(() -> assertThat(btu.update()).isEqualTo(RefUpdate.Result.NEW)); |
| } |
| |
| assertUploadPackRefs( |
| psRef2, |
| metaRef2, |
| psRef4, |
| metaRef4, |
| "refs/heads/branch", |
| "refs/tags/branch-tag", |
| // See comment in subsetOfBranchesVisibleNotIncludingHead. |
| "refs/tags/master-tag"); |
| } |
| |
| // first ls-remote: rcBranch (c2) <- newcommit1 <- newcommit2 (branch) |
| // second ls-remote: rcBranch (c2) <- newcommit1 (branch-newtag) <- newcommit2 (branch) |
| @Test |
| public void uploadPackNewReachableTagVisible() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS)) |
| .update(); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| |
| try (Repository repo = repoManager.openRepository(project)) { |
| // c2 <- newcommit1 (branch) |
| PushOneCommit.Result r = |
| pushFactory |
| .create(admin.newIdent(), testRepo) |
| .setParent(rcBranch) |
| .to("refs/heads/branch"); |
| r.assertOkStatus(); |
| RevCommit tagRc = r.getCommit(); |
| |
| // c2 <- newcommit1 <- newcommit2 (branch) |
| r = pushFactory.create(admin.newIdent(), testRepo).setParent(tagRc).to("refs/heads/branch"); |
| r.assertOkStatus(); |
| |
| assertUploadPackRefs( |
| psRef2, |
| metaRef2, |
| psRef4, |
| metaRef4, |
| "refs/heads/branch", |
| "refs/tags/branch-tag", |
| // See comment in subsetOfBranchesVisibleNotIncludingHead. |
| "refs/tags/master-tag"); |
| |
| // c2 <- newcommit1 (branch-newtag) <- newcommit2 (branch) |
| RefUpdate btu = repo.updateRef("refs/tags/branch-newtag"); |
| btu.setExpectedOldObjectId(ObjectId.zeroId()); |
| btu.setNewObjectId(tagRc); |
| testRefAction(() -> assertThat(btu.update()).isEqualTo(RefUpdate.Result.NEW)); |
| } |
| |
| assertUploadPackRefs( |
| psRef2, |
| metaRef2, |
| psRef4, |
| metaRef4, |
| "refs/heads/branch", |
| "refs/tags/branch-tag", |
| "refs/tags/branch-newtag", |
| // See comment in subsetOfBranchesVisibleNotIncludingHead. |
| "refs/tags/master-tag"); |
| } |
| |
| // first ls-remote: rcBranch (c2) <- newcommit1 (branch) |
| // second ls-remote: rcBranch (c2) <- newcommit1 <- newcommit2 (branch) |
| // third ls-remote: rcBranch (c2) <- newcommit1 (branch-newtag) <- newcommit2 (branch) |
| @Test |
| public void uploadPackBranchFFNewTagOldBranchVisible() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS)) |
| .update(); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| |
| try (Repository repo = repoManager.openRepository(project)) { |
| // rcBranch (c2) <- newcommit1 (branch) |
| PushOneCommit.Result r = |
| pushFactory |
| .create(admin.newIdent(), testRepo) |
| .setParent(rcBranch) |
| .to("refs/heads/branch"); |
| r.assertOkStatus(); |
| RevCommit tagRc = r.getCommit(); |
| |
| assertUploadPackRefs( |
| psRef2, |
| metaRef2, |
| psRef4, |
| metaRef4, |
| "refs/heads/branch", |
| "refs/tags/branch-tag", |
| // See comment in subsetOfBranchesVisibleNotIncludingHead. |
| "refs/tags/master-tag"); |
| |
| // rcBranch (c2) <- newcommit1 <- newcommit2 (branch) |
| r = pushFactory.create(admin.newIdent(), testRepo).setParent(tagRc).to("refs/heads/branch"); |
| r.assertOkStatus(); |
| |
| // rcBranch (c2) <- newcommit1 (branch-newtag) <- newcommit2 (branch) |
| RefUpdate btu = repo.updateRef("refs/tags/branch-newtag"); |
| btu.setExpectedOldObjectId(ObjectId.zeroId()); |
| btu.setNewObjectId(tagRc); |
| testRefAction(() -> assertThat(btu.update()).isEqualTo(RefUpdate.Result.NEW)); |
| } |
| |
| assertUploadPackRefs( |
| psRef2, |
| metaRef2, |
| psRef4, |
| metaRef4, |
| "refs/heads/branch", |
| "refs/tags/branch-tag", |
| "refs/tags/branch-newtag", |
| // See comment in subsetOfBranchesVisibleNotIncludingHead. |
| "refs/tags/master-tag"); |
| } |
| |
| // first ls-remote: rcBranch (c2) <- newcommit1 (branch-oldtag) <- newcommit2 (branch) |
| // second ls-remote: rcBranch (c2 branch) <- newcommit1 (branch-oldtag) |
| @Test |
| public void uploadPackBranchRewindMakeTagUnreachableInVisible() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS)) |
| .update(); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| |
| try (Repository repo = repoManager.openRepository(project)) { |
| // rcBranch (c2) <- newcommit1 (branch) |
| PushOneCommit.Result r = |
| pushFactory |
| .create(admin.newIdent(), testRepo) |
| .setParent(rcBranch) |
| .to("refs/heads/branch"); |
| r.assertOkStatus(); |
| RevCommit tagRc = r.getCommit(); |
| |
| // rcBranch (c2) <- newcommit1 <- newcommit2 (branch) |
| r = pushFactory.create(admin.newIdent(), testRepo).setParent(tagRc).to("refs/heads/branch"); |
| r.assertOkStatus(); |
| RevCommit bRc = r.getCommit(); |
| |
| // rcBranch (c2) <- newcommit1 (branch-oldtag) <- newcommit2 (branch) |
| RefUpdate btu1 = repo.updateRef("refs/tags/branch-oldtag"); |
| |
| btu1.setExpectedOldObjectId(ObjectId.zeroId()); |
| btu1.setNewObjectId(tagRc); |
| testRefAction(() -> assertThat(btu1.update()).isEqualTo(RefUpdate.Result.NEW)); |
| |
| assertUploadPackRefs( |
| psRef2, |
| metaRef2, |
| psRef4, |
| metaRef4, |
| "refs/heads/branch", |
| "refs/tags/branch-tag", |
| "refs/tags/branch-oldtag", |
| // See comment in subsetOfBranchesVisibleNotIncludingHead. |
| "refs/tags/master-tag"); |
| |
| // rcBranch (c2 branch) <- newcommit1 (branch-oldtag) <- newcommit2 |
| RefUpdate btu2 = repo.updateRef("refs/heads/branch"); |
| btu2.setExpectedOldObjectId(bRc); |
| btu2.setNewObjectId(rcBranch); |
| btu2.setForceUpdate(true); |
| testRefAction(() -> assertThat(btu2.update()).isEqualTo(RefUpdate.Result.FORCED)); |
| } |
| |
| assertUploadPackRefs( |
| psRef2, |
| metaRef2, |
| psRef4, |
| metaRef4, |
| "refs/heads/branch", |
| "refs/tags/branch-tag", |
| // See comment in subsetOfBranchesVisibleNotIncludingHead. |
| "refs/tags/master-tag"); |
| } |
| |
| // first ls-remote: rcBranch (c2 branch) <- newcommit1 (new-tag) |
| // second ls-remote: rcBranch (c2 branch) <- newcommit1 (new-tag) <- newcommit2 (new-branch) |
| @Test |
| public void uploadPackCreateBranchTagReachableVisible() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/heads/new-branch").group(REGISTERED_USERS)) |
| .add(allow(Permission.PUSH).ref("refs/tags/*").group(REGISTERED_USERS)) |
| .update(); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| |
| try (Repository repo = repoManager.openRepository(project)) { |
| // rcBranch (c2 branch) <- newcommit1 (branch-newtag) |
| PushOneCommit.Result r = |
| pushFactory |
| .create(admin.newIdent(), testRepo) |
| .setParent(rcBranch) |
| .to("refs/tags/new-tag"); |
| r.assertOkStatus(); |
| RevCommit tagRc = r.getCommit(); |
| |
| assertUploadPackRefs(); |
| |
| // rcBranch (c2) <- newcommit1 (branch-newtag) <- newcommit2 (branch) |
| r = |
| pushFactory |
| .create(admin.newIdent(), testRepo) |
| .setParent(tagRc) |
| .to("refs/heads/new-branch"); |
| r.assertOkStatus(); |
| } |
| |
| assertUploadPackRefs( |
| "refs/heads/new-branch", |
| "refs/tags/branch-tag", |
| "refs/tags/master-tag", |
| "refs/tags/new-tag"); |
| } |
| |
| // rcMaster (c1 master master-tag) <- rcBranch (c2 branch branch-tag) <- rcBranch (c2 branch) <- |
| // newcommit1 <- newcommit2 (new-branch) |
| @Test |
| public void uploadPackReachableTagVisibleFromLeafBranch() throws Exception { |
| try (Repository repo = repoManager.openRepository(project)) { |
| // rcBranch (c2 branch) <- newcommit1 (new-branch) |
| PushOneCommit.Result r = |
| pushFactory |
| .create(admin.newIdent(), testRepo) |
| .setParent(rcBranch) |
| .to("refs/heads/new-branch"); |
| r.assertOkStatus(); |
| RevCommit branchRc = r.getCommit(); |
| |
| // rcBranch (c2) <- newcommit1 <- newcommit2 (new-branch) |
| r = |
| pushFactory |
| .create(admin.newIdent(), testRepo) |
| .setParent(branchRc) |
| .to("refs/heads/new-branch"); |
| r.assertOkStatus(); |
| } |
| |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(deny(Permission.READ).ref("refs/heads/master").group(REGISTERED_USERS)) |
| .add(deny(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS)) |
| .add(allow(Permission.READ).ref("refs/heads/new-branch").group(REGISTERED_USERS)) |
| .update(); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| assertUploadPackRefs( |
| "refs/heads/new-branch", |
| // 'master' and 'branch' branches are not visible but 'master-tag' and 'branch-tag' are |
| // reachable from new-branch (since PushOneCommit always bases changes on each other). |
| "refs/tags/branch-tag", |
| "refs/tags/master-tag"); |
| // tree-tag not visible. See comment in subsetOfBranchesVisibleIncludingHead. |
| } |
| |
| // first ls-remote: rcBranch (c2 branch) <- newcommit1 (updated-tag) |
| // second ls-remote: rcBranch (c2 branch updated-tag) |
| @Test |
| public void uploadPackTagUpdatedReachableVisible() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS)) |
| .add(allow(Permission.PUSH).ref("refs/tags/*").group(REGISTERED_USERS)) |
| .update(); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| |
| try (Repository repo = repoManager.openRepository(project)) { |
| // rcBranch (c2 branch) <- newcommit1 (updated-tag) |
| PushOneCommit.Result r = |
| pushFactory |
| .create(admin.newIdent(), testRepo) |
| .setParent(rcBranch) |
| .to("refs/tags/updated-tag"); |
| r.assertOkStatus(); |
| RevCommit tagRc = r.getCommit(); |
| |
| assertUploadPackRefs( |
| psRef2, |
| metaRef2, |
| psRef4, |
| metaRef4, |
| "refs/heads/branch", |
| "refs/tags/branch-tag", |
| "refs/tags/master-tag"); |
| |
| // rcBranch (c2 branch updated-tag) |
| RefUpdate btu = repo.updateRef("refs/tags/updated-tag"); |
| btu.setExpectedOldObjectId(tagRc); |
| btu.setNewObjectId(rcBranch); |
| btu.setForceUpdate(true); |
| testRefAction(() -> assertThat(btu.update()).isEqualTo(RefUpdate.Result.FORCED)); |
| } |
| |
| assertUploadPackRefs( |
| psRef2, |
| metaRef2, |
| psRef4, |
| metaRef4, |
| "refs/heads/branch", |
| "refs/tags/branch-tag", |
| "refs/tags/master-tag", |
| "refs/tags/updated-tag"); |
| } |
| |
| // first ls-remote: rcBranch (c2 branch updated-tag) |
| // second ls-remote: rcBranch (c2 branch) <- newcommit1 (updated-tag) |
| @Test |
| public void uploadPackTagUpdatedUnreachableInvisible() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS)) |
| .add(allow(Permission.PUSH).ref("refs/tags/*").group(REGISTERED_USERS)) |
| .update(); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| |
| try (Repository repo = repoManager.openRepository(project)) { |
| // rcBranch (c2 branch updated-tag) |
| RefUpdate btu = repo.updateRef("refs/tags/updated-tag"); |
| btu.setExpectedOldObjectId(ObjectId.zeroId()); |
| btu.setNewObjectId(rcBranch); |
| testRefAction(() -> assertThat(btu.update()).isEqualTo(RefUpdate.Result.NEW)); |
| |
| assertUploadPackRefs( |
| psRef2, |
| metaRef2, |
| psRef4, |
| metaRef4, |
| "refs/heads/branch", |
| "refs/tags/branch-tag", |
| "refs/tags/master-tag", |
| "refs/tags/updated-tag"); |
| |
| // rcBranch (c2 branch) <- newcommit1 (updated-tag) |
| PushOneCommit.Result r = |
| pushFactory |
| .create(admin.newIdent(), testRepo) |
| .setParent(rcBranch) |
| .to("refs/tags/updated-tag"); |
| r.assertOkStatus(); |
| } |
| |
| assertUploadPackRefs( |
| psRef2, |
| metaRef2, |
| psRef4, |
| metaRef4, |
| "refs/heads/branch", |
| "refs/tags/branch-tag", |
| "refs/tags/master-tag"); |
| } |
| |
| // first ls-remote: rcBranch (c2 branch branch-tag) |
| // second ls-remote: rcBranch (c2 branch) |
| @Test |
| public void uploadPackTagDeleted() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS)) |
| .add(allow(Permission.DELETE).ref("refs/tags/branch-tag").group(REGISTERED_USERS)) |
| .add(allow(Permission.PUSH).ref("refs/tags/branch-tag").group(REGISTERED_USERS)) |
| .update(); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| |
| // rcBranch (c2 branch branch-tag) |
| assertUploadPackRefs( |
| psRef2, |
| metaRef2, |
| psRef4, |
| metaRef4, |
| "refs/heads/branch", |
| "refs/tags/branch-tag", |
| "refs/tags/master-tag"); |
| |
| // rcBranch (c2 branch) |
| testRefAction( |
| () -> { |
| try (Repository repo = repoManager.openRepository(project)) { |
| RefUpdate btu = repo.updateRef("refs/tags/branch-tag"); |
| btu.setExpectedOldObjectId(rcBranch); |
| btu.setNewObjectId(ObjectId.zeroId()); |
| btu.setForceUpdate(true); |
| assertThat(btu.delete()).isEqualTo(RefUpdate.Result.FORCED); |
| } |
| }); |
| |
| assertUploadPackRefs( |
| psRef2, metaRef2, psRef4, metaRef4, "refs/heads/branch", "refs/tags/master-tag"); |
| } |
| |
| // first ls-remote: rcBranch (c2 branch) <- newcommit1 (new-tag) <- newcommit2 (new-branch) |
| // second ls-remote: rcBranch (c2 branch) <- newcommit1 (new-tag) |
| @Test |
| public void uploadPackBranchDeleteTagUnreachableInvisible() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS)) |
| .add(allow(Permission.READ).ref("refs/heads/new-branch").group(REGISTERED_USERS)) |
| .add(allow(Permission.DELETE).ref("refs/heads/new-branch").group(REGISTERED_USERS)) |
| .add(allow(Permission.PUSH).ref("refs/tags/*").group(REGISTERED_USERS)) |
| .update(); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| |
| try (Repository repo = repoManager.openRepository(project)) { |
| // rcBranch (branch) <- newcommit1 (new-tag) |
| PushOneCommit.Result r = |
| pushFactory |
| .create(admin.newIdent(), testRepo) |
| .setParent(rcBranch) |
| .to("refs/tags/new-tag"); |
| r.assertOkStatus(); |
| RevCommit tagRc = r.getCommit(); |
| |
| // rcBranch (c2 branch) <- newcommit1 (new-tag) <- newcommit2 (new-branch) |
| r = |
| pushFactory |
| .create(admin.newIdent(), testRepo) |
| .setParent(tagRc) |
| .to("refs/heads/new-branch"); |
| r.assertOkStatus(); |
| } |
| |
| assertUploadPackRefs( |
| psRef2, |
| metaRef2, |
| psRef4, |
| metaRef4, |
| "refs/heads/branch", |
| "refs/tags/branch-tag", |
| "refs/heads/new-branch", |
| "refs/tags/new-tag", |
| "refs/tags/master-tag"); |
| |
| // rcBranch (c2 branch) <- newcommit1 (new-tag) |
| gApi.projects().name(project.get()).branch("refs/heads/new-branch").delete(); |
| |
| assertUploadPackRefs( |
| psRef2, |
| metaRef2, |
| psRef4, |
| metaRef4, |
| "refs/heads/branch", |
| "refs/tags/branch-tag", |
| "refs/tags/master-tag"); |
| } |
| |
| @Test |
| public void receivePackListsOpenChangesAsAdditionalHaves() throws Exception { |
| TestRefAdvertiser.Result r = getReceivePackRefs(); |
| assertThat(r.allRefs().keySet()) |
| .containsExactly( |
| // meta refs are excluded |
| "refs/heads/branch", |
| "refs/heads/master", |
| "refs/meta/config", |
| "refs/tags/branch-tag", |
| "refs/tags/master-tag", |
| "refs/tags/tree-tag"); |
| assertThat(r.additionalHaves()).containsExactly(obj(cd3, 1), obj(cd4, 1)); |
| } |
| |
| @Test |
| public void receivePackRespectsVisibilityOfOpenChanges() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/heads/master").group(REGISTERED_USERS)) |
| .add(deny(Permission.READ).ref("refs/heads/branch").group(REGISTERED_USERS)) |
| .update(); |
| requestScopeOperations.setApiUser(user.id()); |
| |
| assertThat(getReceivePackRefs().additionalHaves()).containsExactly(obj(cd3, 1)); |
| } |
| |
| @Test |
| public void receivePackListsOnlyLatestPatchSet() throws Exception { |
| testRepo.reset(obj(cd3, 1)); |
| PushOneCommit.Result r = amendChange(cd3.change().getKey().get()); |
| r.assertOkStatus(); |
| cd3 = r.getChange(); |
| assertThat(getReceivePackRefs().additionalHaves()).containsExactly(obj(cd3, 2), obj(cd4, 1)); |
| } |
| |
| @Test |
| public void receivePackOmitsMissingObject() throws Exception { |
| try (Repository repo = repoManager.openRepository(project); |
| TestRepository<Repository> tr = new TestRepository<>(repo)) { |
| PatchSet.Id psId = PatchSet.id(cd3.getId(), 1); |
| tr.delete(psId.toRefName()); |
| } |
| |
| assertThat(getReceivePackRefs().additionalHaves()).containsExactly(obj(cd4, 1)); |
| } |
| |
| @Test |
| public void advertisedReferencesDontShowUserBranchWithoutRead() throws Exception { |
| TestRepository<?> userTestRepository = cloneProject(allUsers, user); |
| try (Git git = userTestRepository.git()) { |
| assertThat(getUserRefs(git)).isEmpty(); |
| } |
| } |
| |
| @Test |
| public void advertisedReferencesOmitUserBranchesOfOtherUsers() throws Exception { |
| projectOperations |
| .project(allUsersName) |
| .forUpdate() |
| .add(allow(Permission.READ).ref(RefNames.REFS_USERS + "*").group(REGISTERED_USERS)) |
| .update(); |
| TestRepository<?> userTestRepository = cloneProject(allUsers, user); |
| try (Git git = userTestRepository.git()) { |
| assertThat(getUserRefs(git)) |
| .containsExactly(RefNames.REFS_USERS_SELF, RefNames.refsUsers(user.id())); |
| } |
| } |
| |
| @Test |
| public void advertisedReferencesIncludeAllUserBranchesWithAccessDatabase() throws Exception { |
| projectOperations |
| .allProjectsForUpdate() |
| .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS)) |
| .update(); |
| TestRepository<?> userTestRepository = cloneProject(allUsers, user); |
| try (Git git = userTestRepository.git()) { |
| assertThat(getUserRefs(git)) |
| .containsExactly( |
| RefNames.REFS_USERS_SELF, |
| RefNames.refsUsers(user.id()), |
| RefNames.refsUsers(admin.id())); |
| } |
| } |
| |
| @Test |
| public void advertisedReferencesDontShowGroupBranchToOwnerWithoutRead() throws Exception { |
| createSelfOwnedGroup("Foos", user); |
| TestRepository<?> userTestRepository = cloneProject(allUsers, user); |
| try (Git git = userTestRepository.git()) { |
| assertThat(getGroupRefs(git)).isEmpty(); |
| } |
| } |
| |
| @Test |
| public void advertisedReferencesOmitGroupBranchesOfNonOwnedGroups() throws Exception { |
| projectOperations |
| .project(allUsersName) |
| .forUpdate() |
| .add(allow(Permission.READ).ref(RefNames.REFS_GROUPS + "*").group(REGISTERED_USERS)) |
| .update(); |
| AccountGroup.UUID users = createGroup("Users", admins, user); |
| AccountGroup.UUID foos = createGroup("Foos", users); |
| AccountGroup.UUID bars = createSelfOwnedGroup("Bars", user); |
| TestRepository<?> userTestRepository = cloneProject(allUsers, user); |
| try (Git git = userTestRepository.git()) { |
| assertThat(getGroupRefs(git)) |
| .containsExactly(RefNames.refsGroups(foos), RefNames.refsGroups(bars)); |
| } |
| } |
| |
| @Test |
| public void advertisedReferencesIncludeAllGroupBranchesWithAccessDatabase() throws Exception { |
| projectOperations |
| .allProjectsForUpdate() |
| .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS)) |
| .update(); |
| AccountGroup.UUID users = createGroup("Users", admins); |
| TestRepository<?> userTestRepository = cloneProject(allUsers, user); |
| try (Git git = userTestRepository.git()) { |
| assertThat(getGroupRefs(git)) |
| .containsExactly( |
| RefNames.refsGroups(admins), |
| RefNames.refsGroups(nonInteractiveUsers), |
| RefNames.refsGroups(users)); |
| } |
| } |
| |
| @Test |
| public void advertisedReferencesIncludeAllGroupBranchesForAdmins() throws Exception { |
| projectOperations |
| .project(allUsersName) |
| .forUpdate() |
| .add(allow(Permission.READ).ref(RefNames.REFS_GROUPS + "*").group(REGISTERED_USERS)) |
| .update(); |
| projectOperations |
| .allProjectsForUpdate() |
| .add(allowCapability(GlobalCapability.ADMINISTRATE_SERVER).group(REGISTERED_USERS)) |
| .update(); |
| AccountGroup.UUID users = createGroup("Users", admins); |
| TestRepository<?> userTestRepository = cloneProject(allUsers, user); |
| try (Git git = userTestRepository.git()) { |
| assertThat(getGroupRefs(git)) |
| .containsExactly( |
| RefNames.refsGroups(admins), |
| RefNames.refsGroups(nonInteractiveUsers), |
| RefNames.refsGroups(users)); |
| } |
| } |
| |
| @Test |
| public void advertisedReferencesOmitNoteDbNotesBranches() throws Exception { |
| projectOperations |
| .project(allUsersName) |
| .forUpdate() |
| .add(allow(Permission.READ).ref(RefNames.REFS + "*").group(REGISTERED_USERS)) |
| .update(); |
| TestRepository<?> userTestRepository = cloneProject(allUsers, user); |
| try (Git git = userTestRepository.git()) { |
| assertThat(getRefs(git)).containsNoneOf(RefNames.REFS_EXTERNAL_IDS, RefNames.REFS_GROUPNAMES); |
| } |
| } |
| |
| @Test |
| public void advertisedReferencesOmitPrivateChangesOfOtherUsers() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/heads/master").group(REGISTERED_USERS)) |
| .update(); |
| |
| TestRepository<?> userTestRepository = cloneProject(project, user); |
| try (Git git = userTestRepository.git()) { |
| String change3RefName = cd3.currentPatchSet().refName(); |
| assertWithMessage("Precondition violated").that(getRefs(git)).contains(change3RefName); |
| |
| gApi.changes().id(cd3.getId().get()).setPrivate(true, null); |
| assertThat(getRefs(git)).doesNotContain(change3RefName); |
| } |
| } |
| |
| @Test |
| public void advertisedReferencesIncludePrivateChangesWhenAllRefsMayBeRead() throws Exception { |
| assume() |
| .that(baseConfig.getBoolean("auth", "skipFullRefEvaluationIfAllRefsAreVisible", true)) |
| .isTrue(); |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/*").group(REGISTERED_USERS)) |
| .update(); |
| |
| TestRepository<?> userTestRepository = cloneProject(project, user); |
| try (Git git = userTestRepository.git()) { |
| String change3RefName = cd3.currentPatchSet().refName(); |
| assertWithMessage("Precondition violated").that(getRefs(git)).contains(change3RefName); |
| |
| gApi.changes().id(cd3.getId().get()).setPrivate(true, null); |
| assertThat(getRefs(git)).contains(change3RefName); |
| } |
| } |
| |
| @Test |
| @GerritConfig(name = "auth.skipFullRefEvaluationIfAllRefsAreVisible", value = "false") |
| public void advertisedReferencesOmitPrivateChangesOfOtherUsersWhenShortcutDisabled() |
| throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/*").group(REGISTERED_USERS)) |
| .update(); |
| |
| TestRepository<?> userTestRepository = cloneProject(project, user); |
| try (Git git = userTestRepository.git()) { |
| String change3RefName = cd3.currentPatchSet().refName(); |
| assertWithMessage("Precondition violated").that(getRefs(git)).contains(change3RefName); |
| |
| gApi.changes().id(cd3.getId().get()).setPrivate(true, null); |
| assertThat(getRefs(git)).doesNotContain(change3RefName); |
| } |
| } |
| |
| @Test |
| public void advertisedReferencesOmitDraftCommentRefsOfOtherUsers() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/*").group(REGISTERED_USERS)) |
| .update(); |
| projectOperations |
| .project(allUsersName) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/*").group(REGISTERED_USERS)) |
| .update(); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| DraftInput draftInput = new DraftInput(); |
| draftInput.line = 1; |
| draftInput.message = "nit: trailing whitespace"; |
| draftInput.path = Patch.COMMIT_MSG; |
| gApi.changes().id(cd3.getId().get()).current().createDraft(draftInput); |
| String draftCommentRef = RefNames.refsDraftComments(cd3.getId(), user.id()); |
| |
| // user can see the draft comment ref of the own draft comment |
| assertThat(lsRemote(allUsersName, user)).contains(draftCommentRef); |
| |
| // user2 can't see the draft comment ref of user's draft comment |
| assertThat(lsRemote(allUsersName, accountCreator.user2())).doesNotContain(draftCommentRef); |
| } |
| |
| @Test |
| public void advertisedReferencesOmitStarredChangesRefsOfOtherUsers() throws Exception { |
| projectOperations |
| .project(project) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/*").group(REGISTERED_USERS)) |
| .update(); |
| projectOperations |
| .project(allUsersName) |
| .forUpdate() |
| .add(allow(Permission.READ).ref("refs/*").group(REGISTERED_USERS)) |
| .update(); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| gApi.accounts().self().starChange(cd3.getId().toString()); |
| String starredChangesRef = RefNames.refsStarredChanges(cd3.getId(), user.id()); |
| |
| // user can see the starred changes ref of the own star |
| assertThat(lsRemote(allUsersName, user)).contains(starredChangesRef); |
| |
| // user2 can't see the starred changes ref of admin's star |
| assertThat(lsRemote(allUsersName, accountCreator.user2())).doesNotContain(starredChangesRef); |
| } |
| |
| @Test |
| public void hideMetadata() throws Exception { |
| projectOperations |
| .allProjectsForUpdate() |
| .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS)) |
| .update(); |
| // create change |
| TestRepository<?> allUsersRepo = cloneProject(allUsers); |
| fetch(allUsersRepo, RefNames.REFS_USERS_SELF + ":userRef"); |
| allUsersRepo.reset("userRef"); |
| PushOneCommit.Result mr = |
| pushFactory |
| .create(admin.newIdent(), allUsersRepo) |
| .to("refs/for/" + RefNames.REFS_USERS_SELF); |
| mr.assertOkStatus(); |
| |
| List<String> expectedNonMetaRefs = |
| ImmutableList.of( |
| RefNames.refsUsers(admin.id()), |
| RefNames.refsUsers(user.id()), |
| RefNames.REFS_EXTERNAL_IDS, |
| RefNames.REFS_GROUPNAMES, |
| RefNames.refsGroups(admins), |
| RefNames.refsGroups(nonInteractiveUsers), |
| RefNames.REFS_SEQUENCES + Sequence.NAME_ACCOUNTS, |
| RefNames.REFS_SEQUENCES + Sequence.NAME_GROUPS, |
| RefNames.REFS_CONFIG, |
| Constants.HEAD); |
| |
| List<String> expectedMetaRefs = |
| new ArrayList<>(ImmutableList.of(mr.getPatchSetId().toRefName())); |
| expectedMetaRefs.add(RefNames.changeMetaRef(mr.getChange().getId())); |
| |
| List<String> expectedAllRefs = new ArrayList<>(expectedNonMetaRefs); |
| expectedAllRefs.addAll(expectedMetaRefs); |
| |
| try (Repository repo = repoManager.openRepository(allUsers)) { |
| PermissionBackend.ForProject forProject = newFilter(allUsers, admin); |
| assertThat( |
| names( |
| forProject.filter( |
| repo.getRefDatabase().getRefs(), repo, RefFilterOptions.defaults()))) |
| .containsExactlyElementsIn(expectedAllRefs); |
| assertThat( |
| names( |
| forProject.filter( |
| repo.getRefDatabase().getRefs(), |
| repo, |
| RefFilterOptions.builder().setFilterMeta(true).build()))) |
| .containsExactlyElementsIn(expectedNonMetaRefs); |
| } |
| } |
| |
| @Test |
| public void fetchSingleChangeWithoutIndexAccess() throws Exception { |
| PushOneCommit.Result change = createChange(); |
| String patchSetRef = change.getPatchSetId().toRefName(); |
| try (AutoCloseable ignored = changeIndexOperations.disableReadsAndWrites(); |
| Repository repo = repoManager.openRepository(project)) { |
| ImmutableList<Ref> singleRef = ImmutableList.of(repo.exactRef(patchSetRef)); |
| ImmutableList<Ref> filteredRefs = |
| ImmutableList.copyOf( |
| permissionBackend |
| .user(user(admin)) |
| .project(project) |
| .filter(singleRef, repo, RefFilterOptions.defaults())); |
| assertThat(filteredRefs).isEqualTo(singleRef); |
| } |
| } |
| |
| private List<String> lsRemote(Project.NameKey p, TestAccount a) throws Exception { |
| TestRepository<?> testRepository = cloneProject(p, a); |
| try (Git git = testRepository.git()) { |
| return git.lsRemote().call().stream().map(Ref::getName).collect(toList()); |
| } |
| } |
| |
| private List<String> getRefs(Git git) throws Exception { |
| return getRefs(git, x -> true); |
| } |
| |
| private List<String> getUserRefs(Git git) throws Exception { |
| return getRefs(git, RefNames::isRefsUsers); |
| } |
| |
| private List<String> getGroupRefs(Git git) throws Exception { |
| return getRefs(git, RefNames::isRefsGroups); |
| } |
| |
| private List<String> getRefs(Git git, Predicate<String> predicate) throws Exception { |
| return git.lsRemote().call().stream().map(Ref::getName).filter(predicate).collect(toList()); |
| } |
| |
| /** |
| * Assert that refs seen by a non-admin user match the expected refs. |
| * |
| * @param expectedRefs expected refs. |
| */ |
| private void assertUploadPackRefs(String... expectedRefs) throws Exception { |
| assertRefs(project, user, true, expectedRefs); |
| } |
| |
| private void assertRefs( |
| Project.NameKey project, TestAccount user, boolean disableDb, String... expectedRefs) |
| throws Exception { |
| AutoCloseable ctx = null; |
| if (disableDb) { |
| ctx = disableNoteDb(); |
| } |
| try { |
| assertThat(lsRemote(project, user)).containsExactlyElementsIn(expectedRefs); |
| } finally { |
| if (disableDb) { |
| ctx.close(); |
| } |
| } |
| } |
| |
| private TestRefAdvertiser.Result getReceivePackRefs() throws Exception { |
| try (Repository repo = repoManager.openRepository(project)) { |
| AdvertiseRefsHook adv = |
| ReceiveCommitsAdvertiseRefsHookChain.createForTest( |
| queryProvider, project, identifiedUserFactory.create(admin.id())); |
| ReceivePack rp = new ReceivePack(repo); |
| rp.setAdvertiseRefsHook(adv); |
| TestRefAdvertiser advertiser = new TestRefAdvertiser(repo); |
| rp.sendAdvertisedRefs(advertiser); |
| return advertiser.result(); |
| } |
| } |
| |
| private PermissionBackend.ForProject newFilter(Project.NameKey project, TestAccount u) { |
| return permissionBackend.user(user(u)).project(project); |
| } |
| |
| private static ObjectId obj(ChangeData cd, int psNum) throws Exception { |
| PatchSet.Id psId = PatchSet.id(cd.getId(), psNum); |
| PatchSet ps = cd.patchSet(psId); |
| assertWithMessage("%s not found in %s", psId, cd.patchSets()).that(ps).isNotNull(); |
| return ps.commitId(); |
| } |
| |
| private AccountGroup.UUID createSelfOwnedGroup(String name, TestAccount... members) |
| throws RestApiException { |
| return createGroup(name, null, members); |
| } |
| |
| private AccountGroup.UUID createGroup( |
| String name, @Nullable AccountGroup.UUID ownerGroup, TestAccount... members) |
| throws RestApiException { |
| GroupInput groupInput = new GroupInput(); |
| groupInput.name = name(name); |
| groupInput.ownerId = ownerGroup != null ? ownerGroup.get() : null; |
| groupInput.members = |
| Arrays.stream(members).map(m -> String.valueOf(m.id().get())).collect(toList()); |
| return AccountGroup.uuid(gApi.groups().create(groupInput).get().id); |
| } |
| |
| private static Collection<String> names(Collection<Ref> refs) { |
| return refs.stream().map(Ref::getName).collect(toImmutableList()); |
| } |
| } |