| // 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.Util.ADMIN; |
| import static com.google.gerrit.server.project.Util.DEVS; |
| import static com.google.gerrit.server.project.Util.allow; |
| import static com.google.gerrit.server.project.Util.block; |
| import static com.google.gerrit.server.project.Util.deny; |
| import static com.google.gerrit.server.project.Util.doNotInherit; |
| import static com.google.gerrit.testutil.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.Capable; |
| 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.rules.PrologEnvironment; |
| import com.google.gerrit.rules.RulesCache; |
| 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.schema.SchemaCreator; |
| import com.google.gerrit.server.util.RequestContext; |
| import com.google.gerrit.server.util.ThreadLocalRequestContext; |
| import com.google.gerrit.testutil.InMemoryDatabase; |
| import com.google.gerrit.testutil.InMemoryModule; |
| import com.google.gerrit.testutil.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").isEqualTo(Capable.OK); |
| } |
| |
| 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").isNotEqualTo(Capable.OK); |
| } |
| |
| 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; |
| } |
| } |
| } |