blob: d68d681354f7db96fc664704e6195668780b49df [file] [log] [blame]
// Copyright (C) 2020 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.server.permissions;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.permissionKey;
import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.testsuite.change.ChangeOperations;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.entities.AccountGroup;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.entities.Permission;
import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.group.testing.TestGroupBackend;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.query.change.GroupBackedUser;
import com.google.inject.AbstractModule;
import com.google.inject.Module;
import javax.inject.Inject;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.junit.Before;
import org.junit.Test;
/** Tests that permission logic used by {@link GroupBackedUser} works as expected. */
public class GroupBackedUserPermissionIT extends AbstractDaemonTest {
@Inject private ChangeOperations changeOperations;
@Inject private ProjectOperations projectOperations;
@Inject private PermissionBackend permissionBackend;
@Inject private ChangeNotes.Factory changeNotesFactory;
private final TestGroupBackend testGroupBackend = new TestGroupBackend();
private final AccountGroup.UUID externalGroup = AccountGroup.uuid("testbackend:test");
@Before
public void setUp() {
// Allow only read on refs/heads/master by default
projectOperations
.project(allProjects)
.forUpdate()
.remove(permissionKey(Permission.READ).ref("refs/*").group(ANONYMOUS_USERS))
.add(allow(Permission.READ).ref("refs/heads/master").group(ANONYMOUS_USERS))
.update();
}
@Override
public Module createModule() {
/** Binding a {@link TestGroupBackend} to test adding external groups * */
return new AbstractModule() {
@Override
protected void configure() {
DynamicSet.bind(binder(), GroupBackend.class).toInstance(testGroupBackend);
}
};
}
@Test
public void defaultRefFilter_changeVisibilityIsAgnosticOfProvidedGroups() throws Exception {
GroupBackedUser user =
new GroupBackedUser(ImmutableSet.of(ANONYMOUS_USERS, REGISTERED_USERS, externalGroup));
Change.Id changeOnMaster = changeOperations.newChange().project(project).create();
Change.Id changeOnRefsMetaConfig =
changeOperations.newChange().project(project).branch("refs/meta/config").create();
// Check that only the change on the default branch is visible
assertThat(getVisibleRefNames(user))
.containsExactly(
"HEAD",
"refs/heads/master",
RefNames.changeMetaRef(changeOnMaster),
RefNames.patchSetRef(PatchSet.id(changeOnMaster, 1)));
// Grant access
projectOperations
.project(project)
.forUpdate()
.add(allow(Permission.READ).ref("refs/meta/config").group(externalGroup))
.update();
// Check that both changes are visible now
assertThat(getVisibleRefNames(user))
.containsExactly(
"HEAD",
"refs/heads/master",
"refs/meta/config",
RefNames.changeMetaRef(changeOnMaster),
RefNames.patchSetRef(PatchSet.id(changeOnMaster, 1)),
RefNames.changeMetaRef(changeOnRefsMetaConfig),
RefNames.patchSetRef(PatchSet.id(changeOnRefsMetaConfig, 1)));
}
@Test
public void defaultRefFilter_refVisibilityIsAgnosticOfProvidedGroups() throws Exception {
GroupBackedUser user =
new GroupBackedUser(ImmutableSet.of(ANONYMOUS_USERS, REGISTERED_USERS, externalGroup));
// Check that refs/meta/config isn't visible by default
assertThat(getVisibleRefNames(user)).containsExactly("HEAD", "refs/heads/master");
// Grant access
projectOperations
.project(project)
.forUpdate()
.add(allow(Permission.READ).ref("refs/meta/config").group(externalGroup))
.update();
// Check that refs/meta/config became visible
assertThat(getVisibleRefNames(user))
.containsExactly("HEAD", "refs/heads/master", "refs/meta/config");
}
@Test
public void changeVisibility_changeOnInvisibleBranchNotVisible() throws Exception {
// Create a change that is not visible to members of 'externalGroup'
Change.Id invisibleChange =
changeOperations.newChange().project(project).branch("refs/meta/config").create();
GroupBackedUser user =
new GroupBackedUser(ImmutableSet.of(ANONYMOUS_USERS, REGISTERED_USERS, externalGroup));
AuthException thrown =
assertThrows(
AuthException.class,
() ->
permissionBackend
.user(user)
.change(changeNotesFactory.create(project, invisibleChange))
.check(ChangePermission.READ));
assertThat(thrown).hasMessageThat().isEqualTo("read not permitted");
}
@Test
public void changeVisibility_changeOnBranchVisibleToAnonymousIsVisible() throws Exception {
Change.Id changeId = changeOperations.newChange().project(project).create();
GroupBackedUser user =
new GroupBackedUser(ImmutableSet.of(ANONYMOUS_USERS, REGISTERED_USERS, externalGroup));
permissionBackend
.user(user)
.change(changeNotesFactory.create(project, changeId))
.check(ChangePermission.READ);
}
@Test
public void changeVisibility_changeOnBranchVisibleToRegisteredUsersIsVisible() throws Exception {
Change.Id changeId = changeOperations.newChange().project(project).create();
GroupBackedUser user =
new GroupBackedUser(ImmutableSet.of(ANONYMOUS_USERS, REGISTERED_USERS, externalGroup));
blockAnonymousRead();
permissionBackend
.user(user)
.change(changeNotesFactory.create(project, changeId))
.check(ChangePermission.READ);
}
private ImmutableList<String> getVisibleRefNames(CurrentUser user) throws Exception {
try (Repository repo = repoManager.openRepository(project)) {
return permissionBackend.user(user).project(project)
.filter(
repo.getRefDatabase().getRefs(), repo, PermissionBackend.RefFilterOptions.defaults())
.stream()
.map(Ref::getName)
.collect(toImmutableList());
}
}
}