commit a82123a638c80e29023be96063bb3d51d5503b2a Author: Patrick Hiesel Date: Mon Oct 12 14:20:02 2020 +0200 GroupBackedUser: Provide tests for checking change and ref permissions In this commit, we add tests around GroupBackedUser that ensure that we are able to use this entity for checking read permissions on changes and refs. We use this entity for checking the visibility of changes when trying to see if a change is visible to a certain group. The newly added test makes sure this usage doesn't break in the future. Ref checks are tested as well. Change-Id: I93f7b7c9a1efb0f53d2540d47ac4af6a770041bd diff --git a/javatests/com/google/gerrit/acceptance/server/permissions/BUILD b/javatests/com/google/gerrit/acceptance/server/permissions/BUILD new file mode 100644 index 0000000..e89e8d1 --- /dev/null +++ b/javatests/com/google/gerrit/acceptance/server/permissions/BUILD @@ -0,0 +1,7 @@ +load("//javatests/com/google/gerrit/acceptance:tests.bzl", "acceptance_tests") + +acceptance_tests( + srcs = glob(["*IT.java"]), + group = "server_permissions", + labels = ["server"], +) diff --git a/javatests/com/google/gerrit/acceptance/server/permissions/GroupBackedUserPermissionIT.java b/javatests/com/google/gerrit/acceptance/server/permissions/GroupBackedUserPermissionIT.java new file mode 100644 index 0000000..d68d681 --- /dev/null +++ b/javatests/com/google/gerrit/acceptance/server/permissions/GroupBackedUserPermissionIT.java @@ -0,0 +1,184 @@ +// 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 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()); + } + } +}