blob: 7108a98a45525c8c5fb67b3c72c4fb0d2f1e8d89 [file] [log] [blame]
// 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.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.server.group.SystemGroupBackend.REGISTERED_USERS;
import static java.util.stream.Collectors.toList;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.AcceptanceTestRequestScope;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.ProjectResetter;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.Permission;
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.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.Sequences;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.git.receive.ReceiveCommitsAdvertiseRefsHook;
import com.google.gerrit.server.notedb.ChangeNoteUtil;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions;
import com.google.gerrit.server.project.testing.Util;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.junit.Before;
import org.junit.Test;
@NoHttpd
public class RefAdvertisementIT extends AbstractDaemonTest {
@Inject private PermissionBackend permissionBackend;
@Inject private ChangeNoteUtil noteUtil;
@Inject @AnonymousCowardName private String anonymousCowardName;
@Inject private AllUsersName allUsersName;
private AccountGroup.UUID admins;
private AccountGroup.UUID nonInteractiveUsers;
private ChangeData c1;
private ChangeData c2;
private ChangeData c3;
private ChangeData c4;
private String r1;
private String r2;
private String r3;
private String r4;
@Before
public void setUp() throws Exception {
admins = adminGroupUuid();
nonInteractiveUsers = groupUuid("Non-Interactive Users");
setUpPermissions();
setUpChanges();
}
private void setUpPermissions() throws Exception {
// Remove read permissions for all users besides admin. This method is idempotent, so is safe
// to call on every test setup.
try (ProjectConfigUpdate u = updateProject(allProjects)) {
for (AccessSection sec : u.getConfig().getAccessSections()) {
sec.removePermission(Permission.READ);
}
Util.allow(u.getConfig(), Permission.READ, admins, "refs/*");
u.save();
}
// Remove all read permissions on All-Users. This method is idempotent, so is safe to call on
// every test setup.
try (ProjectConfigUpdate u = updateProject(allUsers)) {
for (AccessSection sec : u.getConfig().getAccessSections()) {
sec.removePermission(Permission.READ);
}
u.save();
}
}
private static String changeRefPrefix(Change.Id id) {
String ps = new PatchSet.Id(id, 1).toRefName();
return ps.substring(0, ps.length() - 1);
}
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.
allow("refs/for/refs/heads/*", Permission.SUBMIT, admins);
PushOneCommit.Result mr =
pushFactory.create(db, admin.getIdent(), testRepo).to("refs/for/master%submit");
mr.assertOkStatus();
c1 = mr.getChange();
r1 = changeRefPrefix(c1.getId());
PushOneCommit.Result br =
pushFactory.create(db, admin.getIdent(), testRepo).to("refs/for/branch%submit");
br.assertOkStatus();
c2 = br.getChange();
r2 = changeRefPrefix(c2.getId());
// Second 2 changes are unmerged.
mr = pushFactory.create(db, admin.getIdent(), testRepo).to("refs/for/master");
mr.assertOkStatus();
c3 = mr.getChange();
r3 = changeRefPrefix(c3.getId());
br = pushFactory.create(db, admin.getIdent(), testRepo).to("refs/for/branch");
br.assertOkStatus();
c4 = br.getChange();
r4 = changeRefPrefix(c4.getId());
try (Repository repo = repoManager.openRepository(project)) {
// master-tag -> master
RefUpdate mtu = repo.updateRef("refs/tags/master-tag");
mtu.setExpectedOldObjectId(ObjectId.zeroId());
mtu.setNewObjectId(repo.exactRef("refs/heads/master").getObjectId());
assertThat(mtu.update()).isEqualTo(RefUpdate.Result.NEW);
// branch-tag -> branch
RefUpdate btu = repo.updateRef("refs/tags/branch-tag");
btu.setExpectedOldObjectId(ObjectId.zeroId());
btu.setNewObjectId(repo.exactRef("refs/heads/branch").getObjectId());
assertThat(btu.update()).isEqualTo(RefUpdate.Result.NEW);
}
}
@Test
public void uploadPackAllRefsVisibleNoRefsMetaConfig() throws Exception {
try (ProjectConfigUpdate u = updateProject(project)) {
Util.allow(u.getConfig(), Permission.READ, REGISTERED_USERS, "refs/*");
Util.allow(u.getConfig(), Permission.READ, admins, RefNames.REFS_CONFIG);
Util.doNotInherit(u.getConfig(), Permission.READ, RefNames.REFS_CONFIG);
u.save();
}
setApiUser(user);
assertUploadPackRefs(
"HEAD",
r1 + "1",
r1 + "meta",
r2 + "1",
r2 + "meta",
r3 + "1",
r3 + "meta",
r4 + "1",
r4 + "meta",
"refs/heads/branch",
"refs/heads/master",
"refs/tags/branch-tag",
"refs/tags/master-tag");
}
@Test
public void uploadPackAllRefsVisibleWithRefsMetaConfig() throws Exception {
allow("refs/*", Permission.READ, REGISTERED_USERS);
allow(RefNames.REFS_CONFIG, Permission.READ, REGISTERED_USERS);
assertUploadPackRefs(
"HEAD",
r1 + "1",
r1 + "meta",
r2 + "1",
r2 + "meta",
r3 + "1",
r3 + "meta",
r4 + "1",
r4 + "meta",
"refs/heads/branch",
"refs/heads/master",
RefNames.REFS_CONFIG,
"refs/tags/branch-tag",
"refs/tags/master-tag");
}
@Test
public void uploadPackSubsetOfBranchesVisibleIncludingHead() throws Exception {
allow("refs/heads/master", Permission.READ, REGISTERED_USERS);
deny("refs/heads/branch", Permission.READ, REGISTERED_USERS);
setApiUser(user);
assertUploadPackRefs(
"HEAD",
r1 + "1",
r1 + "meta",
r3 + "1",
r3 + "meta",
"refs/heads/master",
"refs/tags/master-tag");
}
@Test
public void uploadPackSubsetOfBranchesVisibleNotIncludingHead() throws Exception {
deny("refs/heads/master", Permission.READ, REGISTERED_USERS);
allow("refs/heads/branch", Permission.READ, REGISTERED_USERS);
setApiUser(user);
assertUploadPackRefs(
r2 + "1",
r2 + "meta",
r4 + "1",
r4 + "meta",
"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");
}
@Test
public void uploadPackSubsetOfBranchesVisibleWithEdit() throws Exception {
allow("refs/heads/master", Permission.READ, REGISTERED_USERS);
Change c = notesFactory.createChecked(db, project, c3.getId()).getChange();
String changeId = c.getKey().get();
// Admin's edit is not visible.
setApiUser(admin);
gApi.changes().id(changeId).edit().create();
// User's edit is visible.
setApiUser(user);
gApi.changes().id(changeId).edit().create();
assertUploadPackRefs(
"HEAD",
r1 + "1",
r1 + "meta",
r3 + "1",
r3 + "meta",
"refs/heads/master",
"refs/tags/master-tag",
"refs/tags/branch-tag",
"refs/users/01/1000001/edit-" + c3.getId() + "/1");
}
@Test
public void uploadPackSubsetOfBranchesAndEditsVisibleWithViewPrivateChanges() throws Exception {
allow("refs/heads/master", Permission.READ, REGISTERED_USERS);
allow("refs/*", Permission.VIEW_PRIVATE_CHANGES, REGISTERED_USERS);
Change change3 = notesFactory.createChecked(db, project, c3.getId()).getChange();
String changeId3 = change3.getKey().get();
Change change4 = notesFactory.createChecked(db, project, c4.getId()).getChange();
String changeId4 = change4.getKey().get();
// Admin's edit on change3 is visible.
setApiUser(admin);
gApi.changes().id(changeId3).edit().create();
// Admin's edit on change4 is not visible since user cannot see the change.
gApi.changes().id(changeId4).edit().create();
// User's edit is visible.
setApiUser(user);
gApi.changes().id(changeId3).edit().create();
assertUploadPackRefs(
"HEAD",
r1 + "1",
r1 + "meta",
r3 + "1",
r3 + "meta",
"refs/heads/master",
"refs/tags/master-tag",
"refs/tags/branch-tag",
"refs/users/00/1000000/edit-" + c3.getId() + "/1",
"refs/users/01/1000001/edit-" + c3.getId() + "/1");
}
@Test
public void uploadPackSubsetOfRefsVisibleWithAccessDatabase() throws Exception {
allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
deny("refs/heads/master", Permission.READ, REGISTERED_USERS);
allow("refs/heads/branch", Permission.READ, REGISTERED_USERS);
String changeId = c3.change().getKey().get();
setApiUser(admin);
gApi.changes().id(changeId).edit().create();
setApiUser(user);
assertUploadPackRefs(
// Change 1 is visible due to accessDatabase capability, even though
// refs/heads/master is not.
r1 + "1",
r1 + "meta",
r2 + "1",
r2 + "meta",
r3 + "1",
r3 + "meta",
r4 + "1",
r4 + "meta",
"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-" + c3.getId() + "/1");
}
@Test
public void uploadPackNoSearchingChangeCacheImpl() throws Exception {
allow("refs/heads/*", Permission.READ, REGISTERED_USERS);
setApiUser(user);
try (Repository repo = repoManager.openRepository(project)) {
assertRefs(
repo,
permissionBackend.user(user(user)).project(project),
// Can't use stored values from the index so DB must be enabled.
false,
"HEAD",
r1 + "1",
r1 + "meta",
r2 + "1",
r2 + "meta",
r3 + "1",
r3 + "meta",
r4 + "1",
r4 + "meta",
"refs/heads/branch",
"refs/heads/master",
"refs/tags/branch-tag",
"refs/tags/master-tag");
}
}
@Test
public void uploadPackSequencesWithAccessDatabase() throws Exception {
try (Repository repo = repoManager.openRepository(allProjects)) {
assertRefs(repo, newFilter(allProjects, user), true);
allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
assertRefs(repo, newFilter(allProjects, user), true, "refs/sequences/changes");
}
}
@Test
public void receivePackListsOpenChangesAsAdditionalHaves() throws Exception {
ReceiveCommitsAdvertiseRefsHook.Result r = getReceivePackRefs();
assertThat(r.allRefs().keySet())
.containsExactly(
// meta refs are excluded even when NoteDb is enabled.
"HEAD",
"refs/heads/branch",
"refs/heads/master",
"refs/meta/config",
"refs/tags/branch-tag",
"refs/tags/master-tag");
assertThat(r.additionalHaves()).containsExactly(obj(c3, 1), obj(c4, 1));
}
@Test
public void receivePackRespectsVisibilityOfOpenChanges() throws Exception {
allow("refs/heads/master", Permission.READ, REGISTERED_USERS);
deny("refs/heads/branch", Permission.READ, REGISTERED_USERS);
setApiUser(user);
assertThat(getReceivePackRefs().additionalHaves()).containsExactly(obj(c3, 1));
}
@Test
public void receivePackListsOnlyLatestPatchSet() throws Exception {
testRepo.reset(obj(c3, 1));
PushOneCommit.Result r = amendChange(c3.change().getKey().get());
r.assertOkStatus();
c3 = r.getChange();
assertThat(getReceivePackRefs().additionalHaves()).containsExactly(obj(c3, 2), obj(c4, 1));
}
@Test
public void receivePackOmitsMissingObject() throws Exception {
String rev = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
try (Repository repo = repoManager.openRepository(project)) {
TestRepository<?> tr = new TestRepository<>(repo);
String subject = "Subject for missing commit";
Change c = new Change(c3.change());
PatchSet.Id psId = new PatchSet.Id(c3.getId(), 2);
c.setCurrentPatchSet(psId, subject, c.getOriginalSubject());
if (notesMigration.commitChangeWrites()) {
PersonIdent committer = serverIdent.get();
PersonIdent author =
noteUtil.newIdent(getAccount(admin.getId()), committer.getWhen(), committer);
tr.branch(RefNames.changeMetaRef(c3.getId()))
.commit()
.author(author)
.committer(committer)
.message(
"Update patch set "
+ psId.get()
+ "\n"
+ "\n"
+ "Patch-set: "
+ psId.get()
+ "\n"
+ "Commit: "
+ rev
+ "\n"
+ "Subject: "
+ subject
+ "\n")
.create();
}
indexer.index(db, c.getProject(), c.getId());
}
assertThat(getReceivePackRefs().additionalHaves()).containsExactly(obj(c4, 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 {
allow(allUsersName, RefNames.REFS_USERS + "*", Permission.READ, REGISTERED_USERS);
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 {
allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
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
@GerritConfig(name = "noteDb.groups.write", value = "true")
public void advertisedReferencesDontShowGroupBranchToOwnerWithoutRead() throws Exception {
try (ProjectResetter resetter = resetGroups()) {
createSelfOwnedGroup("Foos", user);
TestRepository<?> userTestRepository = cloneProject(allUsers, user);
try (Git git = userTestRepository.git()) {
assertThat(getGroupRefs(git)).isEmpty();
}
}
}
@Test
@GerritConfig(name = "noteDb.groups.write", value = "true")
public void advertisedReferencesOmitGroupBranchesOfNonOwnedGroups() throws Exception {
try (ProjectResetter resetter = resetGroups()) {
allow(allUsersName, RefNames.REFS_GROUPS + "*", Permission.READ, REGISTERED_USERS);
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
@GerritConfig(name = "noteDb.groups.write", value = "true")
public void advertisedReferencesIncludeAllGroupBranchesWithAccessDatabase() throws Exception {
try (ProjectResetter resetter = resetGroups()) {
allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
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
@GerritConfig(name = "noteDb.groups.write", value = "true")
public void advertisedReferencesIncludeAllGroupBranchesForAdmins() throws Exception {
allow(allUsersName, RefNames.REFS_GROUPS + "*", Permission.READ, REGISTERED_USERS);
allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ADMINISTRATE_SERVER);
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
@GerritConfig(name = "noteDb.groups.write", value = "true")
public void advertisedReferencesOmitNoteDbNotesBranches() throws Exception {
allow(allUsersName, RefNames.REFS + "*", Permission.READ, REGISTERED_USERS);
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 {
allow("refs/heads/master", Permission.READ, REGISTERED_USERS);
TestRepository<?> userTestRepository = cloneProject(project, user);
try (Git git = userTestRepository.git()) {
String change3RefName = c3.currentPatchSet().getRefName();
assertWithMessage("Precondition violated").that(getRefs(git)).contains(change3RefName);
gApi.changes().id(c3.getId().get()).setPrivate(true, null);
assertThat(getRefs(git)).doesNotContain(change3RefName);
}
}
@Test
public void advertisedReferencesIncludePrivateChangesWhenAllRefsMayBeRead() throws Exception {
allow("refs/*", Permission.READ, REGISTERED_USERS);
TestRepository<?> userTestRepository = cloneProject(project, user);
try (Git git = userTestRepository.git()) {
String change3RefName = c3.currentPatchSet().getRefName();
assertWithMessage("Precondition violated").that(getRefs(git)).contains(change3RefName);
gApi.changes().id(c3.getId().get()).setPrivate(true, null);
assertThat(getRefs(git)).contains(change3RefName);
}
}
@Test
@GerritConfig(name = "auth.skipFullRefEvaluationIfAllRefsAreVisible", value = "false")
public void advertisedReferencesOmitPrivateChangesOfOtherUsersWhenShortcutDisabled()
throws Exception {
allow("refs/*", Permission.READ, REGISTERED_USERS);
TestRepository<?> userTestRepository = cloneProject(project, user);
try (Git git = userTestRepository.git()) {
String change3RefName = c3.currentPatchSet().getRefName();
assertWithMessage("Precondition violated").that(getRefs(git)).contains(change3RefName);
gApi.changes().id(c3.getId().get()).setPrivate(true, null);
assertThat(getRefs(git)).doesNotContain(change3RefName);
}
}
@Test
public void advertisedReferencesOmitDraftCommentRefsOfOtherUsers() throws Exception {
assume().that(notesMigration.commitChangeWrites()).isTrue();
allow(project, "refs/*", Permission.READ, REGISTERED_USERS);
allow(allUsersName, "refs/*", Permission.READ, REGISTERED_USERS);
setApiUser(user);
DraftInput draftInput = new DraftInput();
draftInput.line = 1;
draftInput.message = "nit: trailing whitespace";
draftInput.path = Patch.COMMIT_MSG;
gApi.changes().id(c3.getId().get()).current().createDraft(draftInput);
String draftCommentRef = RefNames.refsDraftComments(c3.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 {
assume().that(notesMigration.commitChangeWrites()).isTrue();
allow(project, "refs/*", Permission.READ, REGISTERED_USERS);
allow(allUsersName, "refs/*", Permission.READ, REGISTERED_USERS);
setApiUser(user);
gApi.accounts().self().starChange(c3.getId().toString());
String starredChangesRef = RefNames.refsStarredChanges(c3.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
@GerritConfig(name = "noteDb.groups.write", value = "true")
public void hideMetadata() throws Exception {
allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
// create change
TestRepository<?> allUsersRepo = cloneProject(allUsers);
fetch(allUsersRepo, RefNames.REFS_USERS_SELF + ":userRef");
allUsersRepo.reset("userRef");
PushOneCommit.Result mr =
pushFactory
.create(db, admin.getIdent(), allUsersRepo)
.to("refs/for/" + RefNames.REFS_USERS_SELF);
mr.assertOkStatus();
List<String> expectedNonMetaRefs =
ImmutableList.of(
RefNames.REFS_USERS_SELF,
RefNames.refsUsers(admin.id),
RefNames.refsUsers(user.id),
RefNames.REFS_EXTERNAL_IDS,
RefNames.REFS_GROUPNAMES,
RefNames.refsGroups(admins),
RefNames.refsGroups(nonInteractiveUsers),
RefNames.REFS_SEQUENCES + Sequences.NAME_ACCOUNTS,
RefNames.REFS_SEQUENCES + Sequences.NAME_GROUPS,
RefNames.REFS_CONFIG,
Constants.HEAD);
List<String> expectedMetaRefs =
new ArrayList<>(ImmutableList.of(mr.getPatchSetId().toRefName()));
expectedMetaRefs.add(changeRefPrefix(mr.getChange().getId()) + "meta");
List<String> expectedAllRefs = new ArrayList<>(expectedNonMetaRefs);
expectedAllRefs.addAll(expectedMetaRefs);
try (Repository repo = repoManager.openRepository(allUsers)) {
Map<String, Ref> all = repo.getAllRefs();
PermissionBackend.ForProject forProject = newFilter(allUsers, admin);
assertThat(forProject.filter(all, repo, RefFilterOptions.defaults()).keySet())
.containsExactlyElementsIn(expectedAllRefs);
assertThat(
forProject
.filter(all, repo, RefFilterOptions.builder().setFilterMeta(true).build())
.keySet())
.containsExactlyElementsIn(expectedNonMetaRefs);
}
}
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, Predicates.alwaysTrue());
}
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 expected.
*
* @param expectedWithMeta expected refs, in order. If NoteDb is disabled by the configuration,
* any NoteDb refs (i.e. ending in "/meta") are removed from the expected list before
* comparing to the actual results.
* @throws Exception
*/
private void assertUploadPackRefs(String... expectedWithMeta) throws Exception {
try (Repository repo = repoManager.openRepository(project)) {
assertRefs(repo, permissionBackend.user(user(user)).project(project), true, expectedWithMeta);
}
}
private void assertRefs(
Repository repo,
PermissionBackend.ForProject forProject,
boolean disableDb,
String... expectedWithMeta)
throws Exception {
List<String> expected = new ArrayList<>(expectedWithMeta.length);
for (String r : expectedWithMeta) {
if (notesMigration.commitChangeWrites() || !r.endsWith(RefNames.META_SUFFIX)) {
expected.add(r);
}
}
AcceptanceTestRequestScope.Context ctx = null;
if (disableDb) {
ctx = disableDb();
}
try {
Map<String, Ref> all = repo.getAllRefs();
assertThat(forProject.filter(all, repo, RefFilterOptions.defaults()).keySet())
.containsExactlyElementsIn(expected);
} finally {
if (disableDb) {
enableDb(ctx);
}
}
}
private ReceiveCommitsAdvertiseRefsHook.Result getReceivePackRefs() throws Exception {
ReceiveCommitsAdvertiseRefsHook hook =
new ReceiveCommitsAdvertiseRefsHook(queryProvider, project);
try (Repository repo = repoManager.openRepository(project)) {
return hook.advertiseRefs(repo.getAllRefs());
}
}
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 = new PatchSet.Id(cd.getId(), psNum);
PatchSet ps = cd.patchSet(psId);
assertWithMessage("%s not found in %s", psId, cd.patchSets()).that(ps).isNotNull();
return ObjectId.fromString(ps.getRevision().get());
}
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 new AccountGroup.UUID(gApi.groups().create(groupInput).get().id);
}
/**
* Create a resetter to reset the group branches in All-Users. This makes the group data between
* ReviewDb and NoteDb inconsistent, but in the context of this test class we only care about refs
* and hence this is not an issue. Once groups are no longer in ReviewDb and {@link
* AbstractDaemonTest#resetProjects} takes care to reset group branches we no longer need this
* method.
*/
private ProjectResetter resetGroups() throws IOException {
return projectResetter
.builder()
.build(
new ProjectResetter.Config()
.reset(allUsers, RefNames.REFS_GROUPS + "*", RefNames.REFS_GROUPNAMES));
}
}