// 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.gerrit.reviewdb.ApprovalCategory.OWN; import static com.google.gerrit.reviewdb.ApprovalCategory.READ; import static com.google.gerrit.reviewdb.ApprovalCategory.SUBMIT; import com.google.gerrit.reviewdb.AccountGroup; import com.google.gerrit.reviewdb.AccountProjectWatch; import com.google.gerrit.reviewdb.ApprovalCategory; import com.google.gerrit.reviewdb.Change; import com.google.gerrit.reviewdb.Project; import com.google.gerrit.reviewdb.RefRight; import com.google.gerrit.reviewdb.SystemConfig; import com.google.gerrit.reviewdb.RefRight.RefPattern; import com.google.gerrit.server.AccessPath; import com.google.gerrit.server.AnonymousUser; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.config.AuthConfig; import com.google.gerrit.server.config.GerritServerConfig; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; import junit.framework.TestCase; import org.apache.commons.codec.binary.Base64; import org.eclipse.jgit.lib.Config; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; public class RefControlTest extends TestCase { public void testOwnerProject() { grant(local, OWN, admin, "refs/*", 1); ProjectControl uBlah = user(devs); ProjectControl uAdmin = user(devs, admin); assertFalse("not owner", uBlah.isOwner()); assertTrue("is owner", uAdmin.isOwner()); } public void testBranchDelegation1() { grant(local, OWN, admin, "refs/*", 1); grant(local, OWN, devs, "refs/heads/x/*", 1); ProjectControl uDev = user(devs); assertFalse("not owner", uDev.isOwner()); assertTrue("owns ref", uDev.isOwnerAnyRef()); 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); } public void testBranchDelegation2() { grant(local, OWN, admin, "refs/*", 1); grant(local, OWN, devs, "refs/heads/x/*", 1); grant(local, OWN, fixers, "-refs/heads/x/y/*", 1); ProjectControl uDev = user(devs); assertFalse("not owner", uDev.isOwner()); assertTrue("owns ref", uDev.isOwnerAnyRef()); 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(fixers); assertFalse("not owner", uFix.isOwner()); assertTrue("owns ref", uFix.isOwnerAnyRef()); 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); } public void testInheritRead_SingleBranchDeniesUpload() { grant(parent, READ, registered, "refs/*", 1, 2); grant(local, READ, registered, "-refs/heads/foobar", 1); ProjectControl u = user(); assertTrue("can upload", u.canPushToAtLeastOneRef()); assertTrue("can upload refs/heads/master", // u.controlForRef("refs/heads/master").canUpload()); assertFalse("deny refs/heads/foobar", // u.controlForRef("refs/heads/foobar").canUpload()); } public void testInheritRead_SingleBranchDoesNotOverrideInherited() { grant(parent, READ, registered, "refs/*", 1, 2); grant(local, READ, registered, "refs/heads/foobar", 1); ProjectControl u = user(); assertTrue("can upload", u.canPushToAtLeastOneRef()); assertTrue("can upload refs/heads/master", // u.controlForRef("refs/heads/master").canUpload()); assertTrue("can upload refs/heads/foobar", // u.controlForRef("refs/heads/foobar").canUpload()); } public void testInheritRead_OverrideWithDeny() { grant(parent, READ, registered, "refs/*", 1); grant(local, READ, registered, "refs/*", 0); ProjectControl u = user(); assertFalse("can't read", u.isVisible()); } public void testInheritRead_AppendWithDenyOfRef() { grant(parent, READ, registered, "refs/*", 1); grant(local, READ, registered, "refs/heads/*", 0); ProjectControl u = user(); assertTrue("can read", u.isVisible()); assertTrue("can read", u.controlForRef("refs/master").isVisible()); assertTrue("can read", u.controlForRef("refs/tags/foobar").isVisible()); assertTrue("no master", u.controlForRef("refs/heads/master").isVisible()); } public void testInheritRead_OverridesAndDeniesOfRef() { grant(parent, READ, registered, "refs/*", 1); grant(local, READ, registered, "refs/*", 0); grant(local, READ, registered, "refs/heads/*", -1, 1); ProjectControl u = user(); assertTrue("can read", u.isVisible()); assertFalse("can't read", u.controlForRef("refs/foobar").isVisible()); assertFalse("can't read", u.controlForRef("refs/tags/foobar").isVisible()); assertTrue("can read", u.controlForRef("refs/heads/foobar").isVisible()); } public void testInheritSubmit_OverridesAndDeniesOfRef() { grant(parent, SUBMIT, registered, "refs/*", 1); grant(local, SUBMIT, registered, "refs/*", 0); grant(local, SUBMIT, registered, "refs/heads/*", -1, 1); ProjectControl u = user(); assertFalse("can't submit", u.controlForRef("refs/foobar").canSubmit()); assertFalse("can't submit", u.controlForRef("refs/tags/foobar").canSubmit()); assertTrue("can submit", u.controlForRef("refs/heads/foobar").canSubmit()); } public void testCannotUploadToAnyRef() { grant(parent, READ, registered, "refs/*", 1); grant(local, READ, devs, "refs/heads/*", 1, 2); ProjectControl u = user(); assertFalse("cannot upload", u.canPushToAtLeastOneRef()); assertFalse("cannot upload refs/heads/master", // u.controlForRef("refs/heads/master").canUpload()); } // ----------------------------------------------------------------------- private final Project.NameKey local = new Project.NameKey("test"); private final Project.NameKey parent = new Project.NameKey("parent"); private final AccountGroup.Id admin = new AccountGroup.Id(1); private final AccountGroup.Id anonymous = new AccountGroup.Id(2); private final AccountGroup.Id registered = new AccountGroup.Id(3); private final AccountGroup.Id owners = new AccountGroup.Id(4); private final AccountGroup.Id devs = new AccountGroup.Id(5); private final AccountGroup.Id fixers = new AccountGroup.Id(6); private final SystemConfig systemConfig; private final AuthConfig authConfig; private final AnonymousUser anonymousUser; public RefControlTest() { systemConfig = SystemConfig.create(); systemConfig.adminGroupId = admin; systemConfig.anonymousGroupId = anonymous; systemConfig.registeredGroupId = registered; systemConfig.ownerGroupId = owners; systemConfig.batchUsersGroupId = anonymous; try { byte[] bin = "abcdefghijklmnopqrstuvwxyz".getBytes("UTF-8"); systemConfig.registerEmailPrivateKey = Base64.encodeBase64String(bin); } catch (UnsupportedEncodingException err) { throw new RuntimeException("Cannot encode key", err); } Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(Config.class) // .annotatedWith(GerritServerConfig.class) // .toInstance(new Config()); bind(SystemConfig.class).toInstance(systemConfig); bind(AuthConfig.class); bind(AnonymousUser.class); } }); authConfig = injector.getInstance(AuthConfig.class); anonymousUser = injector.getInstance(AnonymousUser.class); } private List<RefRight> localRights; private List<RefRight> inheritedRights; @Override protected void setUp() throws Exception { super.setUp(); localRights = new ArrayList<RefRight>(); inheritedRights = new ArrayList<RefRight>(); } private static void assertOwner(String ref, ProjectControl u) { assertTrue("OWN " + ref, u.controlForRef(ref).isOwner()); } private static void assertNotOwner(String ref, ProjectControl u) { assertFalse("NOT OWN " + ref, u.controlForRef(ref).isOwner()); } private void grant(Project.NameKey project, ApprovalCategory.Id categoryId, AccountGroup.Id group, String ref, int maxValue) { grant(project, categoryId, group, ref, maxValue, maxValue); } private void grant(Project.NameKey project, ApprovalCategory.Id categoryId, AccountGroup.Id group, String ref, int minValue, int maxValue) { RefRight right = new RefRight(new RefRight.Key(project, new RefPattern(ref), categoryId, group)); right.setMinValue((short) minValue); right.setMaxValue((short) maxValue); if (project == parent) { inheritedRights.add(right); } else if (project == local) { localRights.add(right); } else { fail("Unknown project key: " + project); } } private ProjectControl user(AccountGroup.Id... memberOf) { RefControl.Factory refControlFactory = new RefControl.Factory() { @Override public RefControl create(final ProjectControl projectControl, final String ref) { return new RefControl(systemConfig, projectControl, ref); } }; return new ProjectControl(Collections.<AccountGroup.Id> emptySet(), Collections.<AccountGroup.Id> emptySet(), refControlFactory, new MockUser(memberOf), newProjectState()); } private ProjectState newProjectState() { ProjectCache projectCache = null; Project.NameKey wildProject = new Project.NameKey("-- All Projects --"); ProjectControl.AssistedFactory projectControlFactory = null; ProjectState ps = new ProjectState(anonymousUser, projectCache, wildProject, projectControlFactory, new Project(parent), localRights); ps.setInheritedRights(inheritedRights); return ps; } private class MockUser extends CurrentUser { private final Set<AccountGroup.Id> groups; MockUser(AccountGroup.Id[] groupId) { super(AccessPath.UNKNOWN, RefControlTest.this.authConfig); groups = new HashSet<AccountGroup.Id>(Arrays.asList(groupId)); groups.add(registered); groups.add(anonymous); } @Override public Set<AccountGroup.Id> getEffectiveGroups() { return groups; } @Override public Set<Change.Id> getStarredChanges() { return Collections.emptySet(); } @Override public Collection<AccountProjectWatch> getNotificationFilters() { return Collections.emptySet(); } } }