909 lines
30 KiB
Java
909 lines
30 KiB
Java
// Copyright (C) 2010 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package com.google.gerrit.server.project;
|
|
|
|
import static com.google.common.truth.Truth.assertThat;
|
|
import static com.google.gerrit.common.data.Permission.EDIT_TOPIC_NAME;
|
|
import static com.google.gerrit.common.data.Permission.LABEL;
|
|
import static com.google.gerrit.common.data.Permission.OWNER;
|
|
import static com.google.gerrit.common.data.Permission.PUSH;
|
|
import static com.google.gerrit.common.data.Permission.READ;
|
|
import static com.google.gerrit.common.data.Permission.SUBMIT;
|
|
import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
|
|
import static com.google.gerrit.server.group.SystemGroupBackend.CHANGE_OWNER;
|
|
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
|
|
import static com.google.gerrit.server.project.testing.Util.ADMIN;
|
|
import static com.google.gerrit.server.project.testing.Util.DEVS;
|
|
import static com.google.gerrit.server.project.testing.Util.allow;
|
|
import static com.google.gerrit.server.project.testing.Util.block;
|
|
import static com.google.gerrit.server.project.testing.Util.deny;
|
|
import static com.google.gerrit.server.project.testing.Util.doNotInherit;
|
|
import static com.google.gerrit.testing.InMemoryRepositoryManager.newRepository;
|
|
|
|
import com.google.common.cache.Cache;
|
|
import com.google.common.cache.CacheBuilder;
|
|
import com.google.common.collect.Lists;
|
|
import com.google.gerrit.common.data.LabelType;
|
|
import com.google.gerrit.common.data.PermissionRange;
|
|
import com.google.gerrit.common.data.PermissionRule;
|
|
import com.google.gerrit.common.errors.InvalidNameException;
|
|
import com.google.gerrit.extensions.api.projects.CommentLinkInfo;
|
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
|
import com.google.gerrit.reviewdb.client.Project;
|
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
|
import com.google.gerrit.server.CurrentUser;
|
|
import com.google.gerrit.server.account.CapabilityCollection;
|
|
import com.google.gerrit.server.account.GroupMembership;
|
|
import com.google.gerrit.server.account.ListGroupMembership;
|
|
import com.google.gerrit.server.config.AllProjectsName;
|
|
import com.google.gerrit.server.config.AllProjectsNameProvider;
|
|
import com.google.gerrit.server.config.AllUsersName;
|
|
import com.google.gerrit.server.config.AllUsersNameProvider;
|
|
import com.google.gerrit.server.config.SitePaths;
|
|
import com.google.gerrit.server.git.ProjectConfig;
|
|
import com.google.gerrit.server.index.SingleVersionModule.SingleVersionListener;
|
|
import com.google.gerrit.server.permissions.PermissionBackend;
|
|
import com.google.gerrit.server.permissions.ProjectPermission;
|
|
import com.google.gerrit.server.permissions.RefPermission;
|
|
import com.google.gerrit.server.project.testing.Util;
|
|
import com.google.gerrit.server.rules.PrologEnvironment;
|
|
import com.google.gerrit.server.rules.RulesCache;
|
|
import com.google.gerrit.server.schema.SchemaCreator;
|
|
import com.google.gerrit.server.util.RequestContext;
|
|
import com.google.gerrit.server.util.ThreadLocalRequestContext;
|
|
import com.google.gerrit.testing.InMemoryDatabase;
|
|
import com.google.gerrit.testing.InMemoryModule;
|
|
import com.google.gerrit.testing.InMemoryRepositoryManager;
|
|
import com.google.inject.Guice;
|
|
import com.google.inject.Inject;
|
|
import com.google.inject.Injector;
|
|
import com.google.inject.Provider;
|
|
import com.google.inject.util.Providers;
|
|
import java.io.IOException;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
|
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
|
|
import org.eclipse.jgit.lib.Repository;
|
|
import org.junit.After;
|
|
import org.junit.Before;
|
|
import org.junit.Test;
|
|
|
|
public class RefControlTest {
|
|
private void assertAdminsAreOwnersAndDevsAreNot() {
|
|
ProjectControl uBlah = user(local, DEVS);
|
|
ProjectControl uAdmin = user(local, DEVS, ADMIN);
|
|
|
|
assertThat(uBlah.isOwner()).named("not owner").isFalse();
|
|
assertThat(uAdmin.isOwner()).named("is owner").isTrue();
|
|
}
|
|
|
|
private void assertOwner(String ref, ProjectControl u) {
|
|
assertThat(u.controlForRef(ref).isOwner()).named("OWN " + ref).isTrue();
|
|
}
|
|
|
|
private void assertNotOwner(ProjectControl u) {
|
|
assertThat(u.isOwner()).named("not owner").isFalse();
|
|
}
|
|
|
|
private void assertNotOwner(String ref, ProjectControl u) {
|
|
assertThat(u.controlForRef(ref).isOwner()).named("NOT OWN " + ref).isFalse();
|
|
}
|
|
|
|
private void assertCanAccess(ProjectControl u) {
|
|
boolean access = u.asForProject().testOrFalse(ProjectPermission.ACCESS);
|
|
assertThat(access).named("can access").isTrue();
|
|
}
|
|
|
|
private void assertAccessDenied(ProjectControl u) {
|
|
boolean access = u.asForProject().testOrFalse(ProjectPermission.ACCESS);
|
|
assertThat(access).named("cannot access").isFalse();
|
|
}
|
|
|
|
private void assertCanRead(String ref, ProjectControl u) {
|
|
assertThat(u.controlForRef(ref).isVisible()).named("can read " + ref).isTrue();
|
|
}
|
|
|
|
private void assertCannotRead(String ref, ProjectControl u) {
|
|
assertThat(u.controlForRef(ref).isVisible()).named("cannot read " + ref).isFalse();
|
|
}
|
|
|
|
private void assertCanSubmit(String ref, ProjectControl u) {
|
|
assertThat(u.controlForRef(ref).canSubmit(false)).named("can submit " + ref).isTrue();
|
|
}
|
|
|
|
private void assertCannotSubmit(String ref, ProjectControl u) {
|
|
assertThat(u.controlForRef(ref).canSubmit(false)).named("can submit " + ref).isFalse();
|
|
}
|
|
|
|
private void assertCanUpload(ProjectControl u) {
|
|
assertThat(u.canPushToAtLeastOneRef()).named("can upload").isTrue();
|
|
}
|
|
|
|
private void assertCreateChange(String ref, ProjectControl u) {
|
|
boolean create = u.asForProject().ref(ref).testOrFalse(RefPermission.CREATE_CHANGE);
|
|
assertThat(create).named("can create change " + ref).isTrue();
|
|
}
|
|
|
|
private void assertCannotUpload(ProjectControl u) {
|
|
assertThat(u.canPushToAtLeastOneRef()).named("cannot upload").isFalse();
|
|
}
|
|
|
|
private void assertCannotCreateChange(String ref, ProjectControl u) {
|
|
boolean create = u.asForProject().ref(ref).testOrFalse(RefPermission.CREATE_CHANGE);
|
|
assertThat(create).named("cannot create change " + ref).isFalse();
|
|
}
|
|
|
|
private void assertBlocked(String p, String ref, ProjectControl u) {
|
|
assertThat(u.controlForRef(ref).isBlocked(p)).named(p + " is blocked for " + ref).isTrue();
|
|
}
|
|
|
|
private void assertNotBlocked(String p, String ref, ProjectControl u) {
|
|
assertThat(u.controlForRef(ref).isBlocked(p)).named(p + " is blocked for " + ref).isFalse();
|
|
}
|
|
|
|
private void assertCanUpdate(String ref, ProjectControl u) {
|
|
boolean update = u.asForProject().ref(ref).testOrFalse(RefPermission.UPDATE);
|
|
assertThat(update).named("can update " + ref).isTrue();
|
|
}
|
|
|
|
private void assertCannotUpdate(String ref, ProjectControl u) {
|
|
boolean update = u.asForProject().ref(ref).testOrFalse(RefPermission.UPDATE);
|
|
assertThat(update).named("cannot update " + ref).isFalse();
|
|
}
|
|
|
|
private void assertCanForceUpdate(String ref, ProjectControl u) {
|
|
boolean update = u.asForProject().ref(ref).testOrFalse(RefPermission.FORCE_UPDATE);
|
|
assertThat(update).named("can force push " + ref).isTrue();
|
|
}
|
|
|
|
private void assertCannotForceUpdate(String ref, ProjectControl u) {
|
|
boolean update = u.asForProject().ref(ref).testOrFalse(RefPermission.FORCE_UPDATE);
|
|
assertThat(update).named("cannot force push " + ref).isFalse();
|
|
}
|
|
|
|
private void assertCanVote(int score, PermissionRange range) {
|
|
assertThat(range.contains(score)).named("can vote " + score).isTrue();
|
|
}
|
|
|
|
private void assertCannotVote(int score, PermissionRange range) {
|
|
assertThat(range.contains(score)).named("cannot vote " + score).isFalse();
|
|
}
|
|
|
|
private final AllProjectsName allProjectsName =
|
|
new AllProjectsName(AllProjectsNameProvider.DEFAULT);
|
|
private final AllUsersName allUsersName = new AllUsersName(AllUsersNameProvider.DEFAULT);
|
|
private final AccountGroup.UUID fixers = new AccountGroup.UUID("test.fixers");
|
|
private final Map<Project.NameKey, ProjectState> all = new HashMap<>();
|
|
private Project.NameKey localKey = new Project.NameKey("local");
|
|
private ProjectConfig local;
|
|
private Project.NameKey parentKey = new Project.NameKey("parent");
|
|
private ProjectConfig parent;
|
|
private InMemoryRepositoryManager repoManager;
|
|
private ProjectCache projectCache;
|
|
private PermissionCollection.Factory sectionSorter;
|
|
private ChangeControl.Factory changeControlFactory;
|
|
private ReviewDb db;
|
|
|
|
@Inject private PermissionBackend permissionBackend;
|
|
@Inject private CapabilityCollection.Factory capabilityCollectionFactory;
|
|
@Inject private SchemaCreator schemaCreator;
|
|
@Inject private SingleVersionListener singleVersionListener;
|
|
@Inject private InMemoryDatabase schemaFactory;
|
|
@Inject private ThreadLocalRequestContext requestContext;
|
|
|
|
@Before
|
|
public void setUp() throws Exception {
|
|
repoManager = new InMemoryRepositoryManager();
|
|
projectCache =
|
|
new ProjectCache() {
|
|
@Override
|
|
public ProjectState getAllProjects() {
|
|
return get(allProjectsName);
|
|
}
|
|
|
|
@Override
|
|
public ProjectState getAllUsers() {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public ProjectState get(Project.NameKey projectName) {
|
|
return all.get(projectName);
|
|
}
|
|
|
|
@Override
|
|
public void evict(Project p) {}
|
|
|
|
@Override
|
|
public void remove(Project p) {}
|
|
|
|
@Override
|
|
public Iterable<Project.NameKey> all() {
|
|
return Collections.emptySet();
|
|
}
|
|
|
|
@Override
|
|
public Iterable<Project.NameKey> byName(String prefix) {
|
|
return Collections.emptySet();
|
|
}
|
|
|
|
@Override
|
|
public void onCreateProject(Project.NameKey newProjectName) {}
|
|
|
|
@Override
|
|
public Set<AccountGroup.UUID> guessRelevantGroupUUIDs() {
|
|
return Collections.emptySet();
|
|
}
|
|
|
|
@Override
|
|
public ProjectState checkedGet(Project.NameKey projectName) throws IOException {
|
|
return all.get(projectName);
|
|
}
|
|
|
|
@Override
|
|
public void evict(Project.NameKey p) {}
|
|
};
|
|
|
|
Injector injector = Guice.createInjector(new InMemoryModule());
|
|
injector.injectMembers(this);
|
|
|
|
try {
|
|
Repository repo = repoManager.createRepository(allProjectsName);
|
|
ProjectConfig allProjects = new ProjectConfig(new Project.NameKey(allProjectsName.get()));
|
|
allProjects.load(repo);
|
|
LabelType cr = Util.codeReview();
|
|
allProjects.getLabelSections().put(cr.getName(), cr);
|
|
add(allProjects);
|
|
} catch (IOException | ConfigInvalidException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
|
|
db = schemaFactory.open();
|
|
singleVersionListener.start();
|
|
try {
|
|
schemaCreator.create(db);
|
|
} finally {
|
|
singleVersionListener.stop();
|
|
}
|
|
|
|
Cache<SectionSortCache.EntryKey, SectionSortCache.EntryVal> c =
|
|
CacheBuilder.newBuilder().build();
|
|
sectionSorter = new PermissionCollection.Factory(new SectionSortCache(c));
|
|
|
|
parent = new ProjectConfig(parentKey);
|
|
parent.load(newRepository(parentKey));
|
|
add(parent);
|
|
|
|
local = new ProjectConfig(localKey);
|
|
local.load(newRepository(localKey));
|
|
add(local);
|
|
local.getProject().setParentName(parentKey);
|
|
|
|
requestContext.setContext(
|
|
new RequestContext() {
|
|
@Override
|
|
public CurrentUser getUser() {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public Provider<ReviewDb> getReviewDbProvider() {
|
|
return Providers.of(db);
|
|
}
|
|
});
|
|
|
|
changeControlFactory = injector.getInstance(ChangeControl.Factory.class);
|
|
}
|
|
|
|
@After
|
|
public void tearDown() {
|
|
requestContext.setContext(null);
|
|
if (db != null) {
|
|
db.close();
|
|
}
|
|
InMemoryDatabase.drop(schemaFactory);
|
|
}
|
|
|
|
@Test
|
|
public void ownerProject() {
|
|
allow(local, OWNER, ADMIN, "refs/*");
|
|
|
|
assertAdminsAreOwnersAndDevsAreNot();
|
|
}
|
|
|
|
@Test
|
|
public void denyOwnerProject() {
|
|
allow(local, OWNER, ADMIN, "refs/*");
|
|
deny(local, OWNER, DEVS, "refs/*");
|
|
|
|
assertAdminsAreOwnersAndDevsAreNot();
|
|
}
|
|
|
|
@Test
|
|
public void blockOwnerProject() {
|
|
allow(local, OWNER, ADMIN, "refs/*");
|
|
block(local, OWNER, DEVS, "refs/*");
|
|
|
|
assertAdminsAreOwnersAndDevsAreNot();
|
|
}
|
|
|
|
@Test
|
|
public void branchDelegation1() {
|
|
allow(local, OWNER, ADMIN, "refs/*");
|
|
allow(local, OWNER, DEVS, "refs/heads/x/*");
|
|
|
|
ProjectControl uDev = user(local, DEVS);
|
|
assertNotOwner(uDev);
|
|
|
|
assertOwner("refs/heads/x/*", uDev);
|
|
assertOwner("refs/heads/x/y", uDev);
|
|
assertOwner("refs/heads/x/y/*", uDev);
|
|
|
|
assertNotOwner("refs/*", uDev);
|
|
assertNotOwner("refs/heads/master", uDev);
|
|
}
|
|
|
|
@Test
|
|
public void branchDelegation2() {
|
|
allow(local, OWNER, ADMIN, "refs/*");
|
|
allow(local, OWNER, DEVS, "refs/heads/x/*");
|
|
allow(local, OWNER, fixers, "refs/heads/x/y/*");
|
|
doNotInherit(local, OWNER, "refs/heads/x/y/*");
|
|
|
|
ProjectControl uDev = user(local, DEVS);
|
|
assertNotOwner(uDev);
|
|
|
|
assertOwner("refs/heads/x/*", uDev);
|
|
assertOwner("refs/heads/x/y", uDev);
|
|
assertOwner("refs/heads/x/y/*", uDev);
|
|
assertNotOwner("refs/*", uDev);
|
|
assertNotOwner("refs/heads/master", uDev);
|
|
|
|
ProjectControl uFix = user(local, fixers);
|
|
assertNotOwner(uFix);
|
|
|
|
assertOwner("refs/heads/x/y/*", uFix);
|
|
assertOwner("refs/heads/x/y/bar", uFix);
|
|
assertNotOwner("refs/heads/x/*", uFix);
|
|
assertNotOwner("refs/heads/x/y", uFix);
|
|
assertNotOwner("refs/*", uFix);
|
|
assertNotOwner("refs/heads/master", uFix);
|
|
}
|
|
|
|
@Test
|
|
public void inheritRead_SingleBranchDeniesUpload() {
|
|
allow(parent, READ, REGISTERED_USERS, "refs/*");
|
|
allow(parent, PUSH, REGISTERED_USERS, "refs/for/refs/*");
|
|
allow(local, READ, REGISTERED_USERS, "refs/heads/foobar");
|
|
doNotInherit(local, READ, "refs/heads/foobar");
|
|
doNotInherit(local, PUSH, "refs/for/refs/heads/foobar");
|
|
|
|
ProjectControl u = user(local);
|
|
assertCanUpload(u);
|
|
assertCreateChange("refs/heads/master", u);
|
|
assertCannotCreateChange("refs/heads/foobar", u);
|
|
}
|
|
|
|
@Test
|
|
public void blockPushDrafts() {
|
|
allow(parent, PUSH, REGISTERED_USERS, "refs/for/refs/*");
|
|
block(parent, PUSH, ANONYMOUS_USERS, "refs/drafts/*");
|
|
|
|
ProjectControl u = user(local);
|
|
assertCreateChange("refs/heads/master", u);
|
|
assertBlocked(PUSH, "refs/drafts/refs/heads/master", u);
|
|
}
|
|
|
|
@Test
|
|
public void blockPushDraftsUnblockAdmin() {
|
|
block(parent, PUSH, ANONYMOUS_USERS, "refs/drafts/*");
|
|
allow(parent, PUSH, ADMIN, "refs/drafts/*");
|
|
|
|
ProjectControl u = user(local);
|
|
ProjectControl a = user(local, "a", ADMIN);
|
|
assertBlocked(PUSH, "refs/drafts/refs/heads/master", u);
|
|
assertNotBlocked(PUSH, "refs/drafts/refs/heads/master", a);
|
|
}
|
|
|
|
@Test
|
|
public void inheritRead_SingleBranchDoesNotOverrideInherited() {
|
|
allow(parent, READ, REGISTERED_USERS, "refs/*");
|
|
allow(parent, PUSH, REGISTERED_USERS, "refs/for/refs/*");
|
|
allow(local, READ, REGISTERED_USERS, "refs/heads/foobar");
|
|
|
|
ProjectControl u = user(local);
|
|
assertCanUpload(u);
|
|
assertCreateChange("refs/heads/master", u);
|
|
assertCreateChange("refs/heads/foobar", u);
|
|
}
|
|
|
|
@Test
|
|
public void inheritDuplicateSections() throws Exception {
|
|
allow(parent, READ, ADMIN, "refs/*");
|
|
allow(local, READ, DEVS, "refs/heads/*");
|
|
assertCanAccess(user(local, "a", ADMIN));
|
|
|
|
local = new ProjectConfig(localKey);
|
|
local.load(newRepository(localKey));
|
|
local.getProject().setParentName(parentKey);
|
|
allow(local, READ, DEVS, "refs/*");
|
|
assertCanAccess(user(local, "d", DEVS));
|
|
}
|
|
|
|
@Test
|
|
public void inheritRead_OverrideWithDeny() {
|
|
allow(parent, READ, REGISTERED_USERS, "refs/*");
|
|
deny(local, READ, REGISTERED_USERS, "refs/*");
|
|
|
|
assertAccessDenied(user(local));
|
|
}
|
|
|
|
@Test
|
|
public void inheritRead_AppendWithDenyOfRef() {
|
|
allow(parent, READ, REGISTERED_USERS, "refs/*");
|
|
deny(local, READ, REGISTERED_USERS, "refs/heads/*");
|
|
|
|
ProjectControl u = user(local);
|
|
assertCanAccess(u);
|
|
assertCanRead("refs/master", u);
|
|
assertCanRead("refs/tags/foobar", u);
|
|
assertCanRead("refs/heads/master", u);
|
|
}
|
|
|
|
@Test
|
|
public void inheritRead_OverridesAndDeniesOfRef() {
|
|
allow(parent, READ, REGISTERED_USERS, "refs/*");
|
|
deny(local, READ, REGISTERED_USERS, "refs/*");
|
|
allow(local, READ, REGISTERED_USERS, "refs/heads/*");
|
|
|
|
ProjectControl u = user(local);
|
|
assertCanAccess(u);
|
|
assertCannotRead("refs/foobar", u);
|
|
assertCannotRead("refs/tags/foobar", u);
|
|
assertCanRead("refs/heads/foobar", u);
|
|
}
|
|
|
|
@Test
|
|
public void inheritSubmit_OverridesAndDeniesOfRef() {
|
|
allow(parent, SUBMIT, REGISTERED_USERS, "refs/*");
|
|
deny(local, SUBMIT, REGISTERED_USERS, "refs/*");
|
|
allow(local, SUBMIT, REGISTERED_USERS, "refs/heads/*");
|
|
|
|
ProjectControl u = user(local);
|
|
assertCannotSubmit("refs/foobar", u);
|
|
assertCannotSubmit("refs/tags/foobar", u);
|
|
assertCanSubmit("refs/heads/foobar", u);
|
|
}
|
|
|
|
@Test
|
|
public void cannotUploadToAnyRef() {
|
|
allow(parent, READ, REGISTERED_USERS, "refs/*");
|
|
allow(local, READ, DEVS, "refs/heads/*");
|
|
allow(local, PUSH, DEVS, "refs/for/refs/heads/*");
|
|
|
|
ProjectControl u = user(local);
|
|
assertCannotUpload(u);
|
|
assertCannotCreateChange("refs/heads/master", u);
|
|
}
|
|
|
|
@Test
|
|
public void usernamePatternCanUploadToAnyRef() {
|
|
allow(local, PUSH, REGISTERED_USERS, "refs/heads/users/${username}/*");
|
|
ProjectControl u = user(local, "a-registered-user");
|
|
assertCanUpload(u);
|
|
}
|
|
|
|
@Test
|
|
public void usernamePatternNonRegex() {
|
|
allow(local, READ, DEVS, "refs/sb/${username}/heads/*");
|
|
|
|
ProjectControl u = user(local, "u", DEVS);
|
|
ProjectControl d = user(local, "d", DEVS);
|
|
assertCannotRead("refs/sb/d/heads/foobar", u);
|
|
assertCanRead("refs/sb/d/heads/foobar", d);
|
|
}
|
|
|
|
@Test
|
|
public void usernamePatternWithRegex() {
|
|
allow(local, READ, DEVS, "^refs/sb/${username}/heads/.*");
|
|
|
|
ProjectControl u = user(local, "d.v", DEVS);
|
|
ProjectControl d = user(local, "dev", DEVS);
|
|
assertCannotRead("refs/sb/dev/heads/foobar", u);
|
|
assertCanRead("refs/sb/dev/heads/foobar", d);
|
|
}
|
|
|
|
@Test
|
|
public void usernameEmailPatternWithRegex() {
|
|
allow(local, READ, DEVS, "^refs/sb/${username}/heads/.*");
|
|
|
|
ProjectControl u = user(local, "d.v@ger-rit.org", DEVS);
|
|
ProjectControl d = user(local, "dev@ger-rit.org", DEVS);
|
|
assertCannotRead("refs/sb/dev@ger-rit.org/heads/foobar", u);
|
|
assertCanRead("refs/sb/dev@ger-rit.org/heads/foobar", d);
|
|
}
|
|
|
|
@Test
|
|
public void sortWithRegex() {
|
|
allow(local, READ, DEVS, "^refs/heads/.*");
|
|
allow(parent, READ, ANONYMOUS_USERS, "^refs/heads/.*-QA-.*");
|
|
|
|
ProjectControl u = user(local, DEVS);
|
|
ProjectControl d = user(local, DEVS);
|
|
assertCanRead("refs/heads/foo-QA-bar", u);
|
|
assertCanRead("refs/heads/foo-QA-bar", d);
|
|
}
|
|
|
|
@Test
|
|
public void blockRule_ParentBlocksChild() {
|
|
allow(local, PUSH, DEVS, "refs/tags/*");
|
|
block(parent, PUSH, ANONYMOUS_USERS, "refs/tags/*");
|
|
ProjectControl u = user(local, DEVS);
|
|
assertCannotUpdate("refs/tags/V10", u);
|
|
}
|
|
|
|
@Test
|
|
public void blockRule_ParentBlocksChildEvenIfAlreadyBlockedInChild() {
|
|
allow(local, PUSH, DEVS, "refs/tags/*");
|
|
block(local, PUSH, ANONYMOUS_USERS, "refs/tags/*");
|
|
block(parent, PUSH, ANONYMOUS_USERS, "refs/tags/*");
|
|
|
|
ProjectControl u = user(local, DEVS);
|
|
assertCannotUpdate("refs/tags/V10", u);
|
|
}
|
|
|
|
@Test
|
|
public void blockLabelRange_ParentBlocksChild() {
|
|
allow(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
|
|
block(parent, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
|
|
|
|
ProjectControl u = user(local, DEVS);
|
|
|
|
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
|
|
assertCanVote(-1, range);
|
|
assertCanVote(1, range);
|
|
assertCannotVote(-2, range);
|
|
assertCannotVote(2, range);
|
|
}
|
|
|
|
@Test
|
|
public void blockLabelRange_ParentBlocksChildEvenIfAlreadyBlockedInChild() {
|
|
allow(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
|
|
block(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
|
|
block(parent, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
|
|
|
|
ProjectControl u = user(local, DEVS);
|
|
|
|
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
|
|
assertCanVote(-1, range);
|
|
assertCanVote(1, range);
|
|
assertCannotVote(-2, range);
|
|
assertCannotVote(2, range);
|
|
}
|
|
|
|
@Test
|
|
public void inheritSubmit_AllowInChildDoesntAffectUnblockInParent() {
|
|
block(parent, SUBMIT, ANONYMOUS_USERS, "refs/heads/*");
|
|
allow(parent, SUBMIT, REGISTERED_USERS, "refs/heads/*");
|
|
allow(local, SUBMIT, REGISTERED_USERS, "refs/heads/*");
|
|
|
|
ProjectControl u = user(local);
|
|
assertNotBlocked(SUBMIT, "refs/heads/master", u);
|
|
}
|
|
|
|
@Test
|
|
public void unblockNoForce() {
|
|
block(local, PUSH, ANONYMOUS_USERS, "refs/heads/*");
|
|
allow(local, PUSH, DEVS, "refs/heads/*");
|
|
|
|
ProjectControl u = user(local, DEVS);
|
|
assertCanUpdate("refs/heads/master", u);
|
|
}
|
|
|
|
@Test
|
|
public void unblockForce() {
|
|
PermissionRule r = block(local, PUSH, ANONYMOUS_USERS, "refs/heads/*");
|
|
r.setForce(true);
|
|
allow(local, PUSH, DEVS, "refs/heads/*").setForce(true);
|
|
|
|
ProjectControl u = user(local, DEVS);
|
|
assertCanForceUpdate("refs/heads/master", u);
|
|
}
|
|
|
|
@Test
|
|
public void unblockForceWithAllowNoForce_NotPossible() {
|
|
PermissionRule r = block(local, PUSH, ANONYMOUS_USERS, "refs/heads/*");
|
|
r.setForce(true);
|
|
allow(local, PUSH, DEVS, "refs/heads/*");
|
|
|
|
ProjectControl u = user(local, DEVS);
|
|
assertCannotForceUpdate("refs/heads/master", u);
|
|
}
|
|
|
|
@Test
|
|
public void unblockMoreSpecificRef_Fails() {
|
|
block(local, PUSH, ANONYMOUS_USERS, "refs/heads/*");
|
|
allow(local, PUSH, DEVS, "refs/heads/master");
|
|
|
|
ProjectControl u = user(local, DEVS);
|
|
assertCannotUpdate("refs/heads/master", u);
|
|
}
|
|
|
|
@Test
|
|
public void unblockMoreSpecificRefInLocal_Fails() {
|
|
block(parent, PUSH, ANONYMOUS_USERS, "refs/heads/*");
|
|
allow(local, PUSH, DEVS, "refs/heads/master");
|
|
|
|
ProjectControl u = user(local, DEVS);
|
|
assertCannotUpdate("refs/heads/master", u);
|
|
}
|
|
|
|
@Test
|
|
public void unblockMoreSpecificRefWithExclusiveFlag() {
|
|
block(local, PUSH, ANONYMOUS_USERS, "refs/heads/*");
|
|
allow(local, PUSH, DEVS, "refs/heads/master", true);
|
|
|
|
ProjectControl u = user(local, DEVS);
|
|
assertCanUpdate("refs/heads/master", u);
|
|
}
|
|
|
|
@Test
|
|
public void unblockMoreSpecificRefInLocalWithExclusiveFlag_Fails() {
|
|
block(parent, PUSH, ANONYMOUS_USERS, "refs/heads/*");
|
|
allow(local, PUSH, DEVS, "refs/heads/master", true);
|
|
|
|
ProjectControl u = user(local, DEVS);
|
|
assertCannotUpdate("refs/heads/master", u);
|
|
}
|
|
|
|
@Test
|
|
public void unblockOtherPermissionWithMoreSpecificRefAndExclusiveFlag_Fails() {
|
|
block(local, PUSH, ANONYMOUS_USERS, "refs/heads/*");
|
|
allow(local, PUSH, DEVS, "refs/heads/master");
|
|
allow(local, SUBMIT, DEVS, "refs/heads/master", true);
|
|
|
|
ProjectControl u = user(local, DEVS);
|
|
assertCannotUpdate("refs/heads/master", u);
|
|
}
|
|
|
|
@Test
|
|
public void unblockLargerScope_Fails() {
|
|
block(local, PUSH, ANONYMOUS_USERS, "refs/heads/master");
|
|
allow(local, PUSH, DEVS, "refs/heads/*");
|
|
|
|
ProjectControl u = user(local, DEVS);
|
|
assertCannotUpdate("refs/heads/master", u);
|
|
}
|
|
|
|
@Test
|
|
public void unblockInLocal_Fails() {
|
|
block(parent, PUSH, ANONYMOUS_USERS, "refs/heads/*");
|
|
allow(local, PUSH, fixers, "refs/heads/*");
|
|
|
|
ProjectControl f = user(local, fixers);
|
|
assertCannotUpdate("refs/heads/master", f);
|
|
}
|
|
|
|
@Test
|
|
public void unblockInParentBlockInLocal() {
|
|
block(parent, PUSH, ANONYMOUS_USERS, "refs/heads/*");
|
|
allow(parent, PUSH, DEVS, "refs/heads/*");
|
|
block(local, PUSH, DEVS, "refs/heads/*");
|
|
|
|
ProjectControl d = user(local, DEVS);
|
|
assertCannotUpdate("refs/heads/master", d);
|
|
}
|
|
|
|
@Test
|
|
public void unblockForceEditTopicName() {
|
|
block(local, EDIT_TOPIC_NAME, ANONYMOUS_USERS, "refs/heads/*");
|
|
allow(local, EDIT_TOPIC_NAME, DEVS, "refs/heads/*").setForce(true);
|
|
|
|
ProjectControl u = user(local, DEVS);
|
|
assertThat(u.controlForRef("refs/heads/master").canForceEditTopicName())
|
|
.named("u can edit topic name")
|
|
.isTrue();
|
|
}
|
|
|
|
@Test
|
|
public void unblockInLocalForceEditTopicName_Fails() {
|
|
block(parent, EDIT_TOPIC_NAME, ANONYMOUS_USERS, "refs/heads/*");
|
|
allow(local, EDIT_TOPIC_NAME, DEVS, "refs/heads/*").setForce(true);
|
|
|
|
ProjectControl u = user(local, REGISTERED_USERS);
|
|
assertThat(u.controlForRef("refs/heads/master").canForceEditTopicName())
|
|
.named("u can't edit topic name")
|
|
.isFalse();
|
|
}
|
|
|
|
@Test
|
|
public void unblockRange() {
|
|
block(local, LABEL + "Code-Review", -1, +1, ANONYMOUS_USERS, "refs/heads/*");
|
|
allow(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
|
|
|
|
ProjectControl u = user(local, DEVS);
|
|
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
|
|
assertCanVote(-2, range);
|
|
assertCanVote(2, range);
|
|
}
|
|
|
|
@Test
|
|
public void unblockRangeOnMoreSpecificRef_Fails() {
|
|
block(local, LABEL + "Code-Review", -1, +1, ANONYMOUS_USERS, "refs/heads/*");
|
|
allow(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/master");
|
|
|
|
ProjectControl u = user(local, DEVS);
|
|
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
|
|
assertCannotVote(-2, range);
|
|
assertCannotVote(2, range);
|
|
}
|
|
|
|
@Test
|
|
public void unblockRangeOnLargerScope_Fails() {
|
|
block(local, LABEL + "Code-Review", -1, +1, ANONYMOUS_USERS, "refs/heads/master");
|
|
allow(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
|
|
|
|
ProjectControl u = user(local, DEVS);
|
|
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
|
|
assertCannotVote(-2, range);
|
|
assertCannotVote(2, range);
|
|
}
|
|
|
|
@Test
|
|
public void unblockInLocalRange_Fails() {
|
|
block(parent, LABEL + "Code-Review", -1, 1, ANONYMOUS_USERS, "refs/heads/*");
|
|
allow(local, LABEL + "Code-Review", -2, +2, DEVS, "refs/heads/*");
|
|
|
|
ProjectControl u = user(local, DEVS);
|
|
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
|
|
assertCannotVote(-2, range);
|
|
assertCannotVote(2, range);
|
|
}
|
|
|
|
@Test
|
|
public void unblockRangeForChangeOwner() {
|
|
allow(local, LABEL + "Code-Review", -2, +2, CHANGE_OWNER, "refs/heads/*");
|
|
|
|
ProjectControl u = user(local, DEVS);
|
|
PermissionRange range =
|
|
u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review", true);
|
|
assertCanVote(-2, range);
|
|
assertCanVote(2, range);
|
|
}
|
|
|
|
@Test
|
|
public void unblockRangeForNotChangeOwner() {
|
|
allow(local, LABEL + "Code-Review", -2, +2, CHANGE_OWNER, "refs/heads/*");
|
|
|
|
ProjectControl u = user(local, DEVS);
|
|
PermissionRange range = u.controlForRef("refs/heads/master").getRange(LABEL + "Code-Review");
|
|
assertCannotVote(-2, range);
|
|
assertCannotVote(2, range);
|
|
}
|
|
|
|
@Test
|
|
public void blockOwner() {
|
|
block(parent, OWNER, ANONYMOUS_USERS, "refs/*");
|
|
allow(local, OWNER, DEVS, "refs/*");
|
|
|
|
assertThat(user(local, DEVS).isOwner()).isFalse();
|
|
}
|
|
|
|
@Test
|
|
public void validateRefPatternsOK() throws Exception {
|
|
RefPattern.validate("refs/*");
|
|
RefPattern.validate("^refs/heads/*");
|
|
RefPattern.validate("^refs/tags/[0-9a-zA-Z-_.]+");
|
|
RefPattern.validate("refs/heads/review/${username}/*");
|
|
}
|
|
|
|
@Test(expected = InvalidNameException.class)
|
|
public void testValidateBadRefPatternDoubleCaret() throws Exception {
|
|
RefPattern.validate("^^refs/*");
|
|
}
|
|
|
|
@Test(expected = InvalidNameException.class)
|
|
public void testValidateBadRefPatternDanglingCharacter() throws Exception {
|
|
RefPattern.validate("^refs/heads/tmp/sdk/[0-9]{3,3}_R[1-9][A-Z][0-9]{3,3}*");
|
|
}
|
|
|
|
@Test
|
|
public void validateRefPatternNoDanglingCharacter() throws Exception {
|
|
RefPattern.validate("^refs/heads/tmp/sdk/[0-9]{3,3}_R[1-9][A-Z][0-9]{3,3}");
|
|
}
|
|
|
|
private InMemoryRepository add(ProjectConfig pc) {
|
|
PrologEnvironment.Factory envFactory = null;
|
|
ProjectControl.AssistedFactory projectControlFactory = null;
|
|
RulesCache rulesCache = null;
|
|
SitePaths sitePaths = null;
|
|
List<CommentLinkInfo> commentLinks = null;
|
|
|
|
InMemoryRepository repo;
|
|
try {
|
|
repo = repoManager.createRepository(pc.getName());
|
|
if (pc.getProject() == null) {
|
|
pc.load(repo);
|
|
}
|
|
} catch (IOException | ConfigInvalidException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
all.put(
|
|
pc.getName(),
|
|
new ProjectState(
|
|
sitePaths,
|
|
projectCache,
|
|
allProjectsName,
|
|
allUsersName,
|
|
projectControlFactory,
|
|
envFactory,
|
|
repoManager,
|
|
rulesCache,
|
|
commentLinks,
|
|
capabilityCollectionFactory,
|
|
pc));
|
|
return repo;
|
|
}
|
|
|
|
private ProjectControl user(ProjectConfig local, AccountGroup.UUID... memberOf) {
|
|
return user(local, null, memberOf);
|
|
}
|
|
|
|
private ProjectControl user(ProjectConfig local, String name, AccountGroup.UUID... memberOf) {
|
|
return new ProjectControl(
|
|
Collections.<AccountGroup.UUID>emptySet(),
|
|
Collections.<AccountGroup.UUID>emptySet(),
|
|
sectionSorter,
|
|
null, // commitsCollection
|
|
changeControlFactory,
|
|
permissionBackend,
|
|
new MockUser(name, memberOf),
|
|
newProjectState(local));
|
|
}
|
|
|
|
private ProjectState newProjectState(ProjectConfig local) {
|
|
add(local);
|
|
return all.get(local.getProject().getNameKey());
|
|
}
|
|
|
|
private class MockUser extends CurrentUser {
|
|
private final String username;
|
|
private final GroupMembership groups;
|
|
|
|
MockUser(String name, AccountGroup.UUID[] groupId) {
|
|
username = name;
|
|
ArrayList<AccountGroup.UUID> groupIds = Lists.newArrayList(groupId);
|
|
groupIds.add(REGISTERED_USERS);
|
|
groupIds.add(ANONYMOUS_USERS);
|
|
groups = new ListGroupMembership(groupIds);
|
|
}
|
|
|
|
@Override
|
|
public GroupMembership getEffectiveGroups() {
|
|
return groups;
|
|
}
|
|
|
|
@Override
|
|
public String getUserName() {
|
|
return username;
|
|
}
|
|
}
|
|
}
|