| // Copyright (C) 2016 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.api.project; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow; |
| import static com.google.gerrit.extensions.client.ListChangesOption.MESSAGES; |
| 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.server.schema.AclUtil.grant; |
| import static com.google.gerrit.testing.GerritJUnit.assertThrows; |
| import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction; |
| import static com.google.gerrit.truth.ConfigSubject.assertThat; |
| import static com.google.gerrit.truth.MapSubject.assertThatMap; |
| import static java.util.Arrays.asList; |
| import static org.junit.Assert.assertEquals; |
| |
| import com.google.gerrit.acceptance.AbstractDaemonTest; |
| import com.google.gerrit.acceptance.ExtensionRegistry; |
| import com.google.gerrit.acceptance.ExtensionRegistry.Registration; |
| import com.google.gerrit.acceptance.GitUtil; |
| import com.google.gerrit.acceptance.PushOneCommit; |
| import com.google.gerrit.acceptance.testsuite.project.ProjectOperations; |
| import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations; |
| import com.google.gerrit.common.data.GlobalCapability; |
| import com.google.gerrit.entities.AccessSection; |
| import com.google.gerrit.entities.GroupDescription; |
| import com.google.gerrit.entities.GroupReference; |
| import com.google.gerrit.entities.LabelId; |
| import com.google.gerrit.entities.Permission; |
| import com.google.gerrit.entities.Project; |
| import com.google.gerrit.entities.RefNames; |
| import com.google.gerrit.extensions.api.access.AccessSectionInfo; |
| import com.google.gerrit.extensions.api.access.PermissionInfo; |
| import com.google.gerrit.extensions.api.access.PermissionRuleInfo; |
| import com.google.gerrit.extensions.api.access.ProjectAccessInfo; |
| import com.google.gerrit.extensions.api.access.ProjectAccessInput; |
| import com.google.gerrit.extensions.api.changes.ReviewInput; |
| import com.google.gerrit.extensions.api.projects.ProjectApi; |
| import com.google.gerrit.extensions.client.ChangeStatus; |
| import com.google.gerrit.extensions.common.ChangeInfo; |
| import com.google.gerrit.extensions.common.GroupInfo; |
| import com.google.gerrit.extensions.common.WebLinkInfo; |
| import com.google.gerrit.extensions.config.CapabilityDefinition; |
| import com.google.gerrit.extensions.config.PluginProjectPermissionDefinition; |
| import com.google.gerrit.extensions.restapi.AuthException; |
| import com.google.gerrit.extensions.restapi.BadRequestException; |
| import com.google.gerrit.extensions.restapi.ResourceNotFoundException; |
| import com.google.gerrit.extensions.webui.FileHistoryWebLink; |
| import com.google.gerrit.server.config.AllProjectsNameProvider; |
| import com.google.gerrit.server.extensions.events.GitReferenceUpdated; |
| import com.google.gerrit.server.git.meta.MetaDataUpdate; |
| import com.google.gerrit.server.group.SystemGroupBackend; |
| import com.google.gerrit.server.group.testing.TestGroupBackend; |
| import com.google.gerrit.server.project.ProjectConfig; |
| import com.google.gerrit.server.schema.GrantRevertPermission; |
| import com.google.inject.Inject; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; |
| import org.eclipse.jgit.junit.TestRepository; |
| import org.eclipse.jgit.lib.Config; |
| import org.eclipse.jgit.lib.Constants; |
| import org.eclipse.jgit.lib.ObjectId; |
| import org.eclipse.jgit.lib.RefUpdate; |
| import org.eclipse.jgit.lib.RefUpdate.Result; |
| import org.eclipse.jgit.lib.Repository; |
| import org.eclipse.jgit.revwalk.RevCommit; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| public class AccessIT extends AbstractDaemonTest { |
| |
| private static final String REFS_ALL = Constants.R_REFS + "*"; |
| private static final String REFS_HEADS = Constants.R_HEADS + "*"; |
| private static final String REFS_META_VERSION = "refs/meta/version"; |
| private static final String REFS_DRAFTS = "refs/draft-comments/*"; |
| private static final String REFS_STARRED_CHANGES = "refs/starred-changes/*"; |
| |
| @Inject private ProjectOperations projectOperations; |
| @Inject private RequestScopeOperations requestScopeOperations; |
| @Inject private ExtensionRegistry extensionRegistry; |
| @Inject private GrantRevertPermission grantRevertPermission; |
| |
| private Project.NameKey newProjectName; |
| |
| @Before |
| public void setUp() throws Exception { |
| newProjectName = projectOperations.newProject().create(); |
| } |
| |
| @Test |
| public void grantRevertPermission() throws Exception { |
| String ref = "refs/*"; |
| String groupId = "global:Registered-Users"; |
| |
| grantRevertPermission.execute(newProjectName); |
| |
| ProjectAccessInfo info = pApi().access(); |
| assertThat(info.local.containsKey(ref)).isTrue(); |
| AccessSectionInfo accessSectionInfo = info.local.get(ref); |
| assertThat(accessSectionInfo.permissions.containsKey(Permission.REVERT)).isTrue(); |
| PermissionInfo permissionInfo = accessSectionInfo.permissions.get(Permission.REVERT); |
| assertThat(permissionInfo.rules.containsKey(groupId)).isTrue(); |
| PermissionRuleInfo permissionRuleInfo = permissionInfo.rules.get(groupId); |
| assertThat(permissionRuleInfo.action).isEqualTo(PermissionRuleInfo.Action.ALLOW); |
| } |
| |
| @Test |
| public void grantRevertPermissionByOnNewRefAndDeletingOnOldRef() throws Exception { |
| String refsHeads = "refs/heads/*"; |
| String refsStar = "refs/*"; |
| String groupId = "global:Registered-Users"; |
| GroupReference registeredUsers = systemGroupBackend.getGroup(REGISTERED_USERS); |
| |
| try (Repository repo = repoManager.openRepository(newProjectName)) { |
| MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, newProjectName, repo); |
| ProjectConfig projectConfig = projectConfigFactory.read(md); |
| projectConfig.upsertAccessSection( |
| AccessSection.HEADS, |
| heads -> { |
| grant(projectConfig, heads, Permission.REVERT, registeredUsers); |
| }); |
| md.getCommitBuilder().setAuthor(admin.newIdent()); |
| md.getCommitBuilder().setCommitter(admin.newIdent()); |
| md.setMessage("Add revert permission for all registered users\n"); |
| |
| projectConfig.commit(md); |
| } |
| grantRevertPermission.execute(newProjectName); |
| |
| ProjectAccessInfo info = pApi().access(); |
| |
| // Revert permission is removed on refs/heads/*. |
| assertThat(info.local.containsKey(refsHeads)).isTrue(); |
| AccessSectionInfo accessSectionInfo = info.local.get(refsHeads); |
| assertThat(accessSectionInfo.permissions.containsKey(Permission.REVERT)).isFalse(); |
| |
| // new permission is added on refs/* with Registered-Users. |
| assertThat(info.local.containsKey(refsStar)).isTrue(); |
| accessSectionInfo = info.local.get(refsStar); |
| assertThat(accessSectionInfo.permissions.containsKey(Permission.REVERT)).isTrue(); |
| PermissionInfo permissionInfo = accessSectionInfo.permissions.get(Permission.REVERT); |
| assertThat(permissionInfo.rules.containsKey(groupId)).isTrue(); |
| PermissionRuleInfo permissionRuleInfo = permissionInfo.rules.get(groupId); |
| assertThat(permissionRuleInfo.action).isEqualTo(PermissionRuleInfo.Action.ALLOW); |
| } |
| |
| @Test |
| public void grantRevertPermissionDoesntDeleteAdminsPreferences() throws Exception { |
| GroupReference registeredUsers = systemGroupBackend.getGroup(REGISTERED_USERS); |
| GroupReference otherGroup = systemGroupBackend.getGroup(ANONYMOUS_USERS); |
| |
| try (Repository repo = repoManager.openRepository(newProjectName)) { |
| MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, newProjectName, repo); |
| ProjectConfig projectConfig = projectConfigFactory.read(md); |
| projectConfig.upsertAccessSection( |
| AccessSection.HEADS, |
| heads -> { |
| grant(projectConfig, heads, Permission.REVERT, registeredUsers); |
| grant(projectConfig, heads, Permission.REVERT, otherGroup); |
| }); |
| md.getCommitBuilder().setAuthor(admin.newIdent()); |
| md.getCommitBuilder().setCommitter(admin.newIdent()); |
| md.setMessage("Add revert permission for all registered users\n"); |
| |
| projectConfig.commit(md); |
| } |
| projectCache.evict(newProjectName); |
| ProjectAccessInfo expected = pApi().access(); |
| |
| grantRevertPermission.execute(newProjectName); |
| projectCache.evict(newProjectName); |
| ProjectAccessInfo actual = pApi().access(); |
| // Permissions don't change |
| assertThat(actual.local).isEqualTo(expected.local); |
| } |
| |
| @Test |
| public void grantRevertPermissionOnlyWorksOnce() throws Exception { |
| grantRevertPermission.execute(newProjectName); |
| grantRevertPermission.execute(newProjectName); |
| |
| try (Repository repo = repoManager.openRepository(newProjectName)) { |
| MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, newProjectName, repo); |
| ProjectConfig projectConfig = projectConfigFactory.read(md); |
| AccessSection all = projectConfig.getAccessSection(AccessSection.ALL); |
| |
| Permission permission = all.getPermission(Permission.REVERT); |
| assertThat(permission.getRules()).hasSize(1); |
| } |
| } |
| |
| @Test |
| public void getDefaultInheritance() throws Exception { |
| String inheritedName = pApi().access().inheritsFrom.name; |
| assertThat(inheritedName).isEqualTo(AllProjectsNameProvider.DEFAULT); |
| } |
| |
| private Registration newFileHistoryWebLink() { |
| FileHistoryWebLink weblink = |
| new FileHistoryWebLink() { |
| @Override |
| public WebLinkInfo getFileHistoryWebLink( |
| String projectName, String revision, String fileName) { |
| return new WebLinkInfo( |
| "name", "imageURL", "http://view/" + projectName + "/" + fileName); |
| } |
| }; |
| return extensionRegistry.newRegistration().add(weblink); |
| } |
| |
| @Test |
| public void webLink() throws Exception { |
| try (Registration registration = newFileHistoryWebLink()) { |
| ProjectAccessInfo info = pApi().access(); |
| assertThat(info.configWebLinks).hasSize(1); |
| assertThat(info.configWebLinks.get(0).url) |
| .isEqualTo("http://view/" + newProjectName + "/project.config"); |
| } |
| } |
| |
| @Test |
| public void webLinkNoRefsMetaConfig() throws Exception { |
| try (Repository repo = repoManager.openRepository(newProjectName); |
| Registration registration = newFileHistoryWebLink()) { |
| RefUpdate u = repo.updateRef(RefNames.REFS_CONFIG); |
| u.setForceUpdate(true); |
| testRefAction(() -> assertThat(u.delete()).isEqualTo(Result.FORCED)); |
| |
| // This should not crash. |
| @SuppressWarnings("unused") |
| var unused = pApi().access(); |
| } |
| } |
| |
| @Test |
| public void addAccessSection() throws Exception { |
| RevCommit initialHead = projectOperations.project(newProjectName).getHead(RefNames.REFS_CONFIG); |
| |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = createDefaultAccessSectionInfo(); |
| |
| accessInput.add.put(REFS_HEADS, accessSectionInfo); |
| pApi().access(accessInput); |
| |
| assertThat(pApi().access().local).isEqualTo(accessInput.add); |
| |
| RevCommit updatedHead = projectOperations.project(newProjectName).getHead(RefNames.REFS_CONFIG); |
| eventRecorder.assertRefUpdatedEvents( |
| newProjectName.get(), RefNames.REFS_CONFIG, null, initialHead, initialHead, updatedHead); |
| } |
| |
| @Test |
| public void addDuplicatedAccessSection_doesNotAddDuplicateEntry() throws Exception { |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = createDefaultAccessSectionInfo(); |
| |
| // Update project config. Record the file content and the refs_config object ID |
| accessInput.add.put(REFS_HEADS, accessSectionInfo); |
| pApi().access(accessInput); |
| ObjectId refsConfigId = |
| projectOperations.project(newProjectName).getHead(RefNames.REFS_CONFIG).getId(); |
| List<String> projectConfigLines = |
| Arrays.asList(projectOperations.project(newProjectName).getConfig().toText().split("\n")); |
| assertThat(projectConfigLines) |
| .containsExactly( |
| "[submit]", |
| "\taction = inherit", |
| "[access \"refs/heads/*\"]", |
| "\tlabel-Code-Review = deny group Registered Users", |
| "\tlabel-Code-Review = -1..+1 group Project Owners", |
| "\tpush = group Registered Users"); |
| |
| // Apply the same update once more. Make sure that the file content and the ref did not change |
| pApi().access(accessInput); |
| |
| List<String> newProjectConfigLines = |
| Arrays.asList(projectOperations.project(newProjectName).getConfig().toText().split("\n")); |
| ObjectId newRefsConfigId = |
| projectOperations.project(newProjectName).getHead(RefNames.REFS_CONFIG).getId(); |
| assertThat(projectConfigLines).isEqualTo(newProjectConfigLines); |
| assertThat(refsConfigId).isEqualTo(newRefsConfigId); |
| } |
| |
| @Test |
| public void addAccessSectionForPluginPermission() throws Exception { |
| try (Registration registration = |
| extensionRegistry |
| .newRegistration() |
| .add( |
| new PluginProjectPermissionDefinition() { |
| @Override |
| public String getDescription() { |
| return "A Plugin Project Permission"; |
| } |
| }, |
| "fooPermission")) { |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = newAccessSectionInfo(); |
| |
| PermissionInfo foo = newPermissionInfo(); |
| PermissionRuleInfo pri = new PermissionRuleInfo(PermissionRuleInfo.Action.ALLOW, false); |
| foo.rules.put(SystemGroupBackend.REGISTERED_USERS.get(), pri); |
| accessSectionInfo.permissions.put( |
| "plugin-" + ExtensionRegistry.PLUGIN_NAME + "-fooPermission", foo); |
| |
| accessInput.add.put(REFS_HEADS, accessSectionInfo); |
| ProjectAccessInfo updatedAccessSectionInfo = pApi().access(accessInput); |
| assertThat(updatedAccessSectionInfo.local).isEqualTo(accessInput.add); |
| |
| assertThat(pApi().access().local).isEqualTo(accessInput.add); |
| } |
| } |
| |
| @Test |
| public void addAccessSectionWithInvalidPermission() throws Exception { |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = createDefaultAccessSectionInfo(); |
| |
| PermissionInfo push = newPermissionInfo(); |
| PermissionRuleInfo pri = new PermissionRuleInfo(PermissionRuleInfo.Action.ALLOW, false); |
| push.rules.put(SystemGroupBackend.REGISTERED_USERS.get(), pri); |
| accessSectionInfo.permissions.put("Invalid Permission", push); |
| |
| accessInput.add.put(REFS_HEADS, accessSectionInfo); |
| BadRequestException ex = |
| assertThrows(BadRequestException.class, () -> pApi().access(accessInput)); |
| assertThat(ex).hasMessageThat().isEqualTo("Unknown permission: Invalid Permission"); |
| } |
| |
| @Test |
| public void addAccessSectionWithInvalidLabelPermission() throws Exception { |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = createDefaultAccessSectionInfo(); |
| |
| PermissionInfo push = newPermissionInfo(); |
| PermissionRuleInfo pri = new PermissionRuleInfo(PermissionRuleInfo.Action.ALLOW, false); |
| push.rules.put(SystemGroupBackend.REGISTERED_USERS.get(), pri); |
| accessSectionInfo.permissions.put("label-Invalid Permission", push); |
| |
| accessInput.add.put(REFS_HEADS, accessSectionInfo); |
| BadRequestException ex = |
| assertThrows(BadRequestException.class, () -> pApi().access(accessInput)); |
| assertThat(ex).hasMessageThat().isEqualTo("Unknown permission: label-Invalid Permission"); |
| } |
| |
| @Test |
| public void addAccessSectionWithInvalidLabelRange_minGreaterThanMax() throws Exception { |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = createDefaultAccessSectionInfo(); |
| |
| PermissionInfo permissionInfo = newPermissionInfo(); |
| PermissionRuleInfo permissionRuleInfo = |
| new PermissionRuleInfo(PermissionRuleInfo.Action.ALLOW, false); |
| permissionInfo.rules.put(SystemGroupBackend.REGISTERED_USERS.get(), permissionRuleInfo); |
| permissionRuleInfo.min = 1; |
| permissionRuleInfo.max = -1; |
| accessSectionInfo.permissions.put("label-Code-Review", permissionInfo); |
| |
| accessInput.add.put(REFS_HEADS, accessSectionInfo); |
| BadRequestException ex = |
| assertThrows(BadRequestException.class, () -> pApi().access(accessInput)); |
| assertThat(ex) |
| .hasMessageThat() |
| .isEqualTo( |
| String.format( |
| "Invalid range for permission rule that assigns label-Code-Review to group %s" |
| + " on ref refs/heads/*: 1..-1 (min must be <= max)", |
| SystemGroupBackend.REGISTERED_USERS.get())); |
| } |
| |
| @Test |
| public void addAccessSectionWithInvalidLabelRange_minSetMaxMissing() throws Exception { |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = createDefaultAccessSectionInfo(); |
| |
| PermissionInfo permissionInfo = newPermissionInfo(); |
| PermissionRuleInfo permissionRuleInfo = |
| new PermissionRuleInfo(PermissionRuleInfo.Action.ALLOW, false); |
| permissionInfo.rules.put(SystemGroupBackend.REGISTERED_USERS.get(), permissionRuleInfo); |
| permissionRuleInfo.min = -1; |
| accessSectionInfo.permissions.put("label-Code-Review", permissionInfo); |
| |
| accessInput.add.put(REFS_HEADS, accessSectionInfo); |
| BadRequestException ex = |
| assertThrows(BadRequestException.class, () -> pApi().access(accessInput)); |
| assertThat(ex) |
| .hasMessageThat() |
| .isEqualTo( |
| String.format( |
| "Invalid range for permission rule that assigns label-Code-Review to group %s" |
| + " on ref refs/heads/*: -1.. (max is required if min is set)", |
| SystemGroupBackend.REGISTERED_USERS.get())); |
| } |
| |
| @Test |
| public void addAccessSectionWithInvalidLabelRange_maxSetMinMissing() throws Exception { |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = createDefaultAccessSectionInfo(); |
| |
| PermissionInfo permissionInfo = newPermissionInfo(); |
| PermissionRuleInfo permissionRuleInfo = |
| new PermissionRuleInfo(PermissionRuleInfo.Action.ALLOW, false); |
| permissionInfo.rules.put(SystemGroupBackend.REGISTERED_USERS.get(), permissionRuleInfo); |
| permissionRuleInfo.max = 1; |
| accessSectionInfo.permissions.put("label-Code-Review", permissionInfo); |
| |
| accessInput.add.put(REFS_HEADS, accessSectionInfo); |
| BadRequestException ex = |
| assertThrows(BadRequestException.class, () -> pApi().access(accessInput)); |
| assertThat(ex) |
| .hasMessageThat() |
| .isEqualTo( |
| String.format( |
| "Invalid range for permission rule that assigns label-Code-Review to group %s" |
| + " on ref refs/heads/*: ..1 (min is required if max is set)", |
| SystemGroupBackend.REGISTERED_USERS.get())); |
| } |
| |
| @Test |
| public void createAccessChangeNop() throws Exception { |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| assertThrows(BadRequestException.class, () -> pApi().accessChange(accessInput)); |
| } |
| |
| @Test |
| public void createAccessChangeEmptyConfig() throws Exception { |
| try (Repository repo = repoManager.openRepository(newProjectName)) { |
| RefUpdate ru = repo.updateRef(RefNames.REFS_CONFIG); |
| ru.setForceUpdate(true); |
| testRefAction(() -> assertThat(ru.delete()).isEqualTo(Result.FORCED)); |
| |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSection = newAccessSectionInfo(); |
| PermissionInfo read = newPermissionInfo(); |
| PermissionRuleInfo pri = new PermissionRuleInfo(PermissionRuleInfo.Action.BLOCK, false); |
| read.rules.put(SystemGroupBackend.REGISTERED_USERS.get(), pri); |
| accessSection.permissions.put(Permission.READ, read); |
| accessInput.add.put(REFS_HEADS, accessSection); |
| |
| ChangeInfo out = pApi().accessChange(accessInput); |
| assertThat(out.status).isEqualTo(ChangeStatus.NEW); |
| } |
| } |
| |
| @Test |
| public void createAccessChange() throws Exception { |
| projectOperations |
| .project(newProjectName) |
| .forUpdate() |
| .add(allow(Permission.READ).ref(RefNames.REFS_CONFIG).group(REGISTERED_USERS)) |
| .update(); |
| |
| // User can see the branch |
| requestScopeOperations.setApiUser(user.id()); |
| @SuppressWarnings("unused") |
| var unused = pApi().branch("refs/heads/master").get(); |
| |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| |
| AccessSectionInfo accessSection = newAccessSectionInfo(); |
| |
| // Deny read to registered users. |
| PermissionInfo read = newPermissionInfo(); |
| PermissionRuleInfo pri = new PermissionRuleInfo(PermissionRuleInfo.Action.DENY, false); |
| read.rules.put(SystemGroupBackend.REGISTERED_USERS.get(), pri); |
| read.exclusive = true; |
| accessSection.permissions.put(Permission.READ, read); |
| accessInput.add.put(REFS_HEADS, accessSection); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| ChangeInfo out = pApi().accessChange(accessInput); |
| |
| assertThat(out.project).isEqualTo(newProjectName.get()); |
| assertThat(out.branch).isEqualTo(RefNames.REFS_CONFIG); |
| assertThat(out.status).isEqualTo(ChangeStatus.NEW); |
| assertThat(out.submitted).isNull(); |
| |
| requestScopeOperations.setApiUser(admin.id()); |
| |
| ChangeInfo c = gApi.changes().id(out._number).get(MESSAGES); |
| assertThat(c.messages.stream().map(m -> m.message)).containsExactly("Uploaded patch set 1"); |
| |
| ReviewInput reviewIn = new ReviewInput(); |
| reviewIn.label("Code-Review", (short) 2); |
| gApi.changes().id(out._number).current().review(reviewIn); |
| gApi.changes().id(out._number).current().submit(); |
| |
| // check that the change took effect. |
| requestScopeOperations.setApiUser(user.id()); |
| assertThrows(ResourceNotFoundException.class, () -> pApi().branch("refs/heads/master").get()); |
| |
| // Restore. |
| accessInput.add.clear(); |
| accessInput.remove.put(REFS_HEADS, accessSection); |
| requestScopeOperations.setApiUser(user.id()); |
| |
| requestScopeOperations.setApiUser(admin.id()); |
| out = pApi().accessChange(accessInput); |
| |
| gApi.changes().id(out._number).current().review(reviewIn); |
| gApi.changes().id(out._number).current().submit(); |
| |
| // Now it works again. |
| requestScopeOperations.setApiUser(user.id()); |
| unused = pApi().branch("refs/heads/master").get(); |
| } |
| |
| @Test |
| public void removePermission() throws Exception { |
| // Add initial permission set |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = createDefaultAccessSectionInfo(); |
| |
| accessInput.add.put(REFS_HEADS, accessSectionInfo); |
| pApi().access(accessInput); |
| |
| // Remove specific permission |
| AccessSectionInfo accessSectionToRemove = newAccessSectionInfo(); |
| accessSectionToRemove.permissions.put( |
| Permission.LABEL + LabelId.CODE_REVIEW, newPermissionInfo()); |
| ProjectAccessInput removal = newProjectAccessInput(); |
| removal.remove.put(REFS_HEADS, accessSectionToRemove); |
| pApi().access(removal); |
| |
| // Remove locally |
| accessInput.add.get(REFS_HEADS).permissions.remove(Permission.LABEL + LabelId.CODE_REVIEW); |
| |
| // Check |
| assertThat(pApi().access().local).isEqualTo(accessInput.add); |
| } |
| |
| @Test |
| public void removePermissionRule() throws Exception { |
| // Add initial permission set |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = createDefaultAccessSectionInfo(); |
| |
| accessInput.add.put(REFS_HEADS, accessSectionInfo); |
| pApi().access(accessInput); |
| |
| // Remove specific permission rule |
| AccessSectionInfo accessSectionToRemove = newAccessSectionInfo(); |
| PermissionInfo codeReview = newPermissionInfo(); |
| codeReview.label = LabelId.CODE_REVIEW; |
| PermissionRuleInfo pri = new PermissionRuleInfo(PermissionRuleInfo.Action.DENY, false); |
| codeReview.rules.put(SystemGroupBackend.REGISTERED_USERS.get(), pri); |
| accessSectionToRemove.permissions.put(Permission.LABEL + LabelId.CODE_REVIEW, codeReview); |
| ProjectAccessInput removal = newProjectAccessInput(); |
| removal.remove.put(REFS_HEADS, accessSectionToRemove); |
| pApi().access(removal); |
| |
| // Remove locally |
| accessInput |
| .add |
| .get(REFS_HEADS) |
| .permissions |
| .get(Permission.LABEL + LabelId.CODE_REVIEW) |
| .rules |
| .remove(SystemGroupBackend.REGISTERED_USERS.get()); |
| |
| // Check |
| assertThat(pApi().access().local).isEqualTo(accessInput.add); |
| } |
| |
| @Test |
| public void removePermissionRuleForNonExistingeExternalGroup() throws Exception { |
| // Register a group backend with an external group |
| TestGroupBackend testGroupBackend = new TestGroupBackend(); |
| GroupDescription.Basic externalGroup = testGroupBackend.create("External Group"); |
| try (ExtensionRegistry.Registration registration = |
| extensionRegistry.newRegistration().add(testGroupBackend)) { |
| // Add a permission for the external group. |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = newAccessSectionInfo(); |
| PermissionInfo push = newPermissionInfo(); |
| PermissionRuleInfo pri = new PermissionRuleInfo(PermissionRuleInfo.Action.ALLOW, false); |
| push.rules.put(externalGroup.getGroupUUID().get(), pri); |
| accessSectionInfo.permissions.put(Permission.PUSH, push); |
| accessInput.add.put(REFS_HEADS, accessSectionInfo); |
| pApi().access(accessInput); |
| assertThat(pApi().access().local).isNotEmpty(); |
| |
| // Remove the external group. |
| testGroupBackend.remove(externalGroup.getGroupUUID()); |
| |
| // Remove the permission rule for the external group that no longer exists. |
| AccessSectionInfo accessSectionToRemove = newAccessSectionInfo(); |
| push = newPermissionInfo(); |
| pri = new PermissionRuleInfo(PermissionRuleInfo.Action.ALLOW, false); |
| push.rules.put(externalGroup.getGroupUUID().get(), pri); |
| accessSectionToRemove.permissions.put(Permission.PUSH, push); |
| ProjectAccessInput removal = newProjectAccessInput(); |
| removal.remove.put(REFS_HEADS, accessSectionToRemove); |
| pApi().access(removal); |
| assertThat(pApi().access().local).isEmpty(); |
| } |
| } |
| |
| @Test |
| public void removePermissionRulesAndCleanupEmptyEntries() throws Exception { |
| // Add initial permission set |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = createDefaultAccessSectionInfo(); |
| |
| accessInput.add.put(REFS_HEADS, accessSectionInfo); |
| pApi().access(accessInput); |
| |
| // Remove specific permission rules |
| AccessSectionInfo accessSectionToRemove = newAccessSectionInfo(); |
| PermissionInfo codeReview = newPermissionInfo(); |
| codeReview.label = LabelId.CODE_REVIEW; |
| PermissionRuleInfo pri = new PermissionRuleInfo(PermissionRuleInfo.Action.DENY, false); |
| codeReview.rules.put(SystemGroupBackend.REGISTERED_USERS.get(), pri); |
| pri = new PermissionRuleInfo(PermissionRuleInfo.Action.DENY, false); |
| codeReview.rules.put(SystemGroupBackend.PROJECT_OWNERS.get(), pri); |
| accessSectionToRemove.permissions.put(Permission.LABEL + LabelId.CODE_REVIEW, codeReview); |
| ProjectAccessInput removal = newProjectAccessInput(); |
| removal.remove.put(REFS_HEADS, accessSectionToRemove); |
| pApi().access(removal); |
| |
| // Remove locally |
| accessInput.add.get(REFS_HEADS).permissions.remove(Permission.LABEL + LabelId.CODE_REVIEW); |
| |
| // Check |
| assertThat(pApi().access().local).isEqualTo(accessInput.add); |
| } |
| |
| @Test |
| public void getPermissionsWithDisallowedUser() throws Exception { |
| // Add initial permission set |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = createAccessSectionInfoDenyAll(); |
| |
| // Disallow READ |
| accessInput.add.put(REFS_META_VERSION, accessSectionInfo); |
| accessInput.add.put(REFS_DRAFTS, accessSectionInfo); |
| accessInput.add.put(REFS_STARRED_CHANGES, accessSectionInfo); |
| accessInput.add.put(REFS_HEADS, accessSectionInfo); |
| pApi().access(accessInput); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| assertThrows(ResourceNotFoundException.class, () -> pApi().access()); |
| } |
| |
| @Test |
| public void setPermissionsWithDisallowedUser() throws Exception { |
| // Add initial permission set |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = createAccessSectionInfoDenyAll(); |
| |
| // Disallow READ |
| accessInput.add.put(REFS_META_VERSION, accessSectionInfo); |
| accessInput.add.put(REFS_DRAFTS, accessSectionInfo); |
| accessInput.add.put(REFS_STARRED_CHANGES, accessSectionInfo); |
| accessInput.add.put(REFS_HEADS, accessSectionInfo); |
| pApi().access(accessInput); |
| |
| // Create a change to apply |
| ProjectAccessInput accessInfoToApply = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfoToApply = createDefaultAccessSectionInfo(); |
| accessInfoToApply.add.put(REFS_HEADS, accessSectionInfoToApply); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| assertThrows(ResourceNotFoundException.class, () -> pApi().access()); |
| } |
| |
| @Test |
| public void permissionsGroupMap() throws Exception { |
| // Add initial permission set |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSection = newAccessSectionInfo(); |
| |
| PermissionInfo push = newPermissionInfo(); |
| PermissionRuleInfo pri = new PermissionRuleInfo(PermissionRuleInfo.Action.ALLOW, false); |
| push.rules.put(SystemGroupBackend.PROJECT_OWNERS.get(), pri); |
| accessSection.permissions.put(Permission.PUSH, push); |
| |
| PermissionInfo read = newPermissionInfo(); |
| pri = new PermissionRuleInfo(PermissionRuleInfo.Action.ALLOW, false); |
| read.rules.put(SystemGroupBackend.ANONYMOUS_USERS.get(), pri); |
| accessSection.permissions.put(Permission.READ, read); |
| |
| accessInput.add.put(REFS_ALL, accessSection); |
| ProjectAccessInfo result = pApi().access(accessInput); |
| assertThatMap(result.groups) |
| .keys() |
| .containsExactly( |
| SystemGroupBackend.PROJECT_OWNERS.get(), SystemGroupBackend.ANONYMOUS_USERS.get()); |
| |
| // Check the name, which is what the UI cares about; exhaustive |
| // coverage of GroupInfo should be in groups REST API tests. |
| assertThat(result.groups.get(SystemGroupBackend.PROJECT_OWNERS.get()).name) |
| .isEqualTo("Project Owners"); |
| // Strip the ID, since it is in the key. |
| assertThat(result.groups.get(SystemGroupBackend.PROJECT_OWNERS.get()).id).isNull(); |
| |
| // Get call returns groups too. |
| ProjectAccessInfo loggedInResult = pApi().access(); |
| assertThatMap(loggedInResult.groups) |
| .keys() |
| .containsExactly( |
| SystemGroupBackend.PROJECT_OWNERS.get(), SystemGroupBackend.ANONYMOUS_USERS.get()); |
| |
| GroupInfo owners = loggedInResult.groups.get(SystemGroupBackend.PROJECT_OWNERS.get()); |
| assertThat(owners.name).isEqualTo("Project Owners"); |
| assertThat(owners.id).isNull(); |
| assertThat(owners.members).isNull(); |
| assertThat(owners.includes).isNull(); |
| |
| // PROJECT_OWNERS is invisible to anonymous user, but GetAccess disregards visibility. |
| requestScopeOperations.setApiUserAnonymous(); |
| ProjectAccessInfo anonResult = pApi().access(); |
| assertThatMap(anonResult.groups) |
| .keys() |
| .containsExactly( |
| SystemGroupBackend.PROJECT_OWNERS.get(), SystemGroupBackend.ANONYMOUS_USERS.get()); |
| } |
| |
| @Test |
| public void updateParentAsUser() throws Exception { |
| // Create child |
| String newParentProjectName = projectOperations.newProject().create().get(); |
| |
| // Set new parent |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| accessInput.parent = newParentProjectName; |
| |
| requestScopeOperations.setApiUser(user.id()); |
| AuthException thrown = assertThrows(AuthException.class, () -> pApi().access(accessInput)); |
| assertThat(thrown).hasMessageThat().contains("administrate server not permitted"); |
| } |
| |
| @Test |
| public void updateParentAsAdministrator() throws Exception { |
| // Create parent |
| String newParentProjectName = projectOperations.newProject().create().get(); |
| |
| // Set new parent |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| accessInput.parent = newParentProjectName; |
| |
| pApi().access(accessInput); |
| |
| assertThat(pApi().access().inheritsFrom.name).isEqualTo(newParentProjectName); |
| } |
| |
| @Test |
| public void addGlobalCapabilityAsUser() throws Exception { |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = createDefaultGlobalCapabilitiesAccessSectionInfo(); |
| |
| accessInput.add.put(AccessSection.GLOBAL_CAPABILITIES, accessSectionInfo); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| assertThrows( |
| AuthException.class, () -> gApi.projects().name(allProjects.get()).access(accessInput)); |
| } |
| |
| @Test |
| public void addGlobalCapabilityAsAdmin() throws Exception { |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = createDefaultGlobalCapabilitiesAccessSectionInfo(); |
| |
| accessInput.add.put(AccessSection.GLOBAL_CAPABILITIES, accessSectionInfo); |
| |
| ProjectAccessInfo updatedAccessSectionInfo = |
| gApi.projects().name(allProjects.get()).access(accessInput); |
| assertThatMap(updatedAccessSectionInfo.local.get(AccessSection.GLOBAL_CAPABILITIES).permissions) |
| .keys() |
| .containsAtLeastElementsIn(accessSectionInfo.permissions.keySet()); |
| } |
| |
| @Test |
| public void addPluginGlobalCapability() throws Exception { |
| try (Registration registration = |
| extensionRegistry |
| .newRegistration() |
| .add( |
| new CapabilityDefinition() { |
| @Override |
| public String getDescription() { |
| return "A Plugin Global Capability"; |
| } |
| }, |
| "fooCapability")) { |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = newAccessSectionInfo(); |
| |
| PermissionInfo foo = newPermissionInfo(); |
| PermissionRuleInfo pri = new PermissionRuleInfo(PermissionRuleInfo.Action.ALLOW, false); |
| foo.rules.put(SystemGroupBackend.REGISTERED_USERS.get(), pri); |
| accessSectionInfo.permissions.put(ExtensionRegistry.PLUGIN_NAME + "-fooCapability", foo); |
| |
| accessInput.add.put(AccessSection.GLOBAL_CAPABILITIES, accessSectionInfo); |
| |
| ProjectAccessInfo updatedAccessSectionInfo = |
| gApi.projects().name(allProjects.get()).access(accessInput); |
| assertThatMap( |
| updatedAccessSectionInfo.local.get(AccessSection.GLOBAL_CAPABILITIES).permissions) |
| .keys() |
| .containsAtLeastElementsIn(accessSectionInfo.permissions.keySet()); |
| } |
| } |
| |
| @Test |
| public void addPermissionAsGlobalCapability() throws Exception { |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = newAccessSectionInfo(); |
| |
| PermissionInfo push = newPermissionInfo(); |
| PermissionRuleInfo pri = new PermissionRuleInfo(PermissionRuleInfo.Action.ALLOW, false); |
| push.rules.put(SystemGroupBackend.REGISTERED_USERS.get(), pri); |
| accessSectionInfo.permissions.put(Permission.PUSH, push); |
| |
| accessInput.add.put(AccessSection.GLOBAL_CAPABILITIES, accessSectionInfo); |
| BadRequestException ex = |
| assertThrows( |
| BadRequestException.class, |
| () -> gApi.projects().name(allProjects.get()).access(accessInput)); |
| assertThat(ex).hasMessageThat().isEqualTo("Unknown global capability: " + Permission.PUSH); |
| } |
| |
| @Test |
| public void addInvalidGlobalCapability() throws Exception { |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = createDefaultGlobalCapabilitiesAccessSectionInfo(); |
| |
| PermissionInfo permissionInfo = newPermissionInfo(); |
| PermissionRuleInfo pri = new PermissionRuleInfo(PermissionRuleInfo.Action.ALLOW, false); |
| permissionInfo.rules.put(SystemGroupBackend.REGISTERED_USERS.get(), pri); |
| accessSectionInfo.permissions.put("Invalid Global Capability", permissionInfo); |
| |
| accessInput.add.put(AccessSection.GLOBAL_CAPABILITIES, accessSectionInfo); |
| BadRequestException ex = |
| assertThrows( |
| BadRequestException.class, |
| () -> gApi.projects().name(allProjects.get()).access(accessInput)); |
| assertThat(ex) |
| .hasMessageThat() |
| .isEqualTo("Unknown global capability: Invalid Global Capability"); |
| } |
| |
| @Test |
| public void addGlobalCapabilityForNonRootProject() throws Exception { |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = createDefaultGlobalCapabilitiesAccessSectionInfo(); |
| |
| accessInput.add.put(AccessSection.GLOBAL_CAPABILITIES, accessSectionInfo); |
| |
| assertThrows(BadRequestException.class, () -> pApi().access(accessInput)); |
| } |
| |
| @Test |
| public void addNonGlobalCapabilityToGlobalCapabilities() throws Exception { |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = newAccessSectionInfo(); |
| |
| PermissionInfo permissionInfo = newPermissionInfo(); |
| permissionInfo.rules.put(adminGroupUuid().get(), null); |
| accessSectionInfo.permissions.put(Permission.PUSH, permissionInfo); |
| |
| accessInput.add.put(AccessSection.GLOBAL_CAPABILITIES, accessSectionInfo); |
| assertThrows( |
| BadRequestException.class, |
| () -> gApi.projects().name(allProjects.get()).access(accessInput)); |
| } |
| |
| @Test |
| public void removeGlobalCapabilityAsUser() throws Exception { |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = createDefaultGlobalCapabilitiesAccessSectionInfo(); |
| |
| accessInput.remove.put(AccessSection.GLOBAL_CAPABILITIES, accessSectionInfo); |
| |
| requestScopeOperations.setApiUser(user.id()); |
| assertThrows( |
| AuthException.class, () -> gApi.projects().name(allProjects.get()).access(accessInput)); |
| } |
| |
| @Test |
| public void removeGlobalCapabilityAsAdmin() throws Exception { |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = newAccessSectionInfo(); |
| |
| PermissionInfo permissionInfo = newPermissionInfo(); |
| permissionInfo.rules.put(adminGroupUuid().get(), null); |
| accessSectionInfo.permissions.put(GlobalCapability.ACCESS_DATABASE, permissionInfo); |
| |
| // Add and validate first as removing existing privileges such as |
| // administrateServer would break upcoming tests |
| accessInput.add.put(AccessSection.GLOBAL_CAPABILITIES, accessSectionInfo); |
| |
| ProjectAccessInfo updatedProjectAccessInfo = |
| gApi.projects().name(allProjects.get()).access(accessInput); |
| assertThatMap(updatedProjectAccessInfo.local.get(AccessSection.GLOBAL_CAPABILITIES).permissions) |
| .keys() |
| .containsAtLeastElementsIn(accessSectionInfo.permissions.keySet()); |
| |
| // Remove |
| accessInput.add.clear(); |
| accessInput.remove.put(AccessSection.GLOBAL_CAPABILITIES, accessSectionInfo); |
| |
| updatedProjectAccessInfo = gApi.projects().name(allProjects.get()).access(accessInput); |
| assertThatMap(updatedProjectAccessInfo.local.get(AccessSection.GLOBAL_CAPABILITIES).permissions) |
| .keys() |
| .containsNoneIn(accessSectionInfo.permissions.keySet()); |
| } |
| |
| @Test |
| public void unknownPermissionRemainsUnchanged() throws Exception { |
| String access = "access"; |
| String unknownPermission = "unknownPermission"; |
| String registeredUsers = "group Registered Users"; |
| String refsFor = "refs/for/*"; |
| // Clone repository to forcefully add permission |
| TestRepository<InMemoryRepository> allProjectsRepo = cloneProject(allProjects, admin); |
| |
| // Fetch permission ref |
| GitUtil.fetch(allProjectsRepo, "refs/meta/config:cfg"); |
| allProjectsRepo.reset("cfg"); |
| |
| // Load current permissions |
| String config = |
| gApi.projects() |
| .name(allProjects.get()) |
| .branch(RefNames.REFS_CONFIG) |
| .file(ProjectConfig.PROJECT_CONFIG) |
| .asString(); |
| |
| // Append and push unknown permission |
| Config cfg = new Config(); |
| cfg.fromText(config); |
| cfg.setString(access, refsFor, unknownPermission, registeredUsers); |
| config = cfg.toText(); |
| PushOneCommit push = |
| pushFactory.create( |
| admin.newIdent(), allProjectsRepo, "Subject", ProjectConfig.PROJECT_CONFIG, config); |
| push.to(RefNames.REFS_CONFIG).assertOkStatus(); |
| |
| // Verify that unknownPermission is present |
| config = |
| gApi.projects() |
| .name(allProjects.get()) |
| .branch(RefNames.REFS_CONFIG) |
| .file(ProjectConfig.PROJECT_CONFIG) |
| .asString(); |
| cfg.fromText(config); |
| assertThat(cfg).stringValue(access, refsFor, unknownPermission).isEqualTo(registeredUsers); |
| |
| // Make permission change through API |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = createDefaultAccessSectionInfo(); |
| accessInput.add.put(refsFor, accessSectionInfo); |
| gApi.projects().name(allProjects.get()).access(accessInput); |
| accessInput.add.clear(); |
| accessInput.remove.put(refsFor, accessSectionInfo); |
| gApi.projects().name(allProjects.get()).access(accessInput); |
| |
| // Verify that unknownPermission is still present |
| config = |
| gApi.projects() |
| .name(allProjects.get()) |
| .branch(RefNames.REFS_CONFIG) |
| .file(ProjectConfig.PROJECT_CONFIG) |
| .asString(); |
| cfg.fromText(config); |
| assertThat(cfg).stringValue(access, refsFor, unknownPermission).isEqualTo(registeredUsers); |
| } |
| |
| @Test |
| public void allUsersCanOnlyInheritFromAllProjects() throws Exception { |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| accessInput.parent = project.get(); |
| BadRequestException thrown = |
| assertThrows( |
| BadRequestException.class, |
| () -> gApi.projects().name(allUsers.get()).access(accessInput)); |
| assertThat(thrown) |
| .hasMessageThat() |
| .contains(allUsers.get() + " must inherit from " + allProjects.get()); |
| } |
| |
| @Test |
| public void syncCreateGroupPermission_addAndRemoveCreateGroupCapability() throws Exception { |
| // Grant CREATE_GROUP to Registered Users |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSection = newAccessSectionInfo(); |
| PermissionInfo createGroup = newPermissionInfo(); |
| PermissionRuleInfo pri = new PermissionRuleInfo(PermissionRuleInfo.Action.ALLOW, false); |
| createGroup.rules.put(SystemGroupBackend.REGISTERED_USERS.get(), pri); |
| accessSection.permissions.put(GlobalCapability.CREATE_GROUP, createGroup); |
| accessInput.add.put(AccessSection.GLOBAL_CAPABILITIES, accessSection); |
| gApi.projects().name(allProjects.get()).access(accessInput); |
| |
| // Assert that the permission was synced from All-Projects (global) to All-Users (ref) |
| Map<String, AccessSectionInfo> local = gApi.projects().name("All-Users").access().local; |
| assertThatMap(local).keys().contains(RefNames.REFS_GROUPS + "*"); |
| Map<String, PermissionInfo> permissions = local.get(RefNames.REFS_GROUPS + "*").permissions; |
| // READ is the default permission and should be preserved by the syncer |
| assertThatMap(permissions).keys().containsExactly(Permission.READ, Permission.CREATE); |
| Map<String, PermissionRuleInfo> rules = permissions.get(Permission.CREATE).rules; |
| assertThatMap(rules).values().containsExactly(pri); |
| |
| // Revoke the permission |
| accessInput.add.clear(); |
| accessInput.remove.put(AccessSection.GLOBAL_CAPABILITIES, accessSection); |
| gApi.projects().name(allProjects.get()).access(accessInput); |
| |
| // Assert that the permission was synced from All-Projects (global) to All-Users (ref) |
| Map<String, AccessSectionInfo> local2 = gApi.projects().name("All-Users").access().local; |
| assertThatMap(local2).keys().contains(RefNames.REFS_GROUPS + "*"); |
| Map<String, PermissionInfo> permissions2 = local2.get(RefNames.REFS_GROUPS + "*").permissions; |
| // READ is the default permission and should be preserved by the syncer |
| assertThatMap(permissions2).keys().containsExactly(Permission.READ); |
| } |
| |
| @Test |
| public void syncCreateGroupPermission_addCreateGroupCapabilityToMultipleGroups() |
| throws Exception { |
| PermissionRuleInfo pri = new PermissionRuleInfo(PermissionRuleInfo.Action.ALLOW, false); |
| |
| // Grant CREATE_GROUP to Registered Users |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSection = newAccessSectionInfo(); |
| PermissionInfo createGroup = newPermissionInfo(); |
| createGroup.rules.put(SystemGroupBackend.REGISTERED_USERS.get(), pri); |
| accessSection.permissions.put(GlobalCapability.CREATE_GROUP, createGroup); |
| accessInput.add.put(AccessSection.GLOBAL_CAPABILITIES, accessSection); |
| gApi.projects().name(allProjects.get()).access(accessInput); |
| |
| // Grant CREATE_GROUP to Administrators |
| accessInput = newProjectAccessInput(); |
| accessSection = newAccessSectionInfo(); |
| createGroup = newPermissionInfo(); |
| createGroup.rules.put(adminGroupUuid().get(), pri); |
| accessSection.permissions.put(GlobalCapability.CREATE_GROUP, createGroup); |
| accessInput.add.put(AccessSection.GLOBAL_CAPABILITIES, accessSection); |
| gApi.projects().name(allProjects.get()).access(accessInput); |
| |
| // Assert that the permissions were synced from All-Projects (global) to All-Users (ref) |
| Map<String, AccessSectionInfo> local = gApi.projects().name("All-Users").access().local; |
| assertThatMap(local).keys().contains(RefNames.REFS_GROUPS + "*"); |
| Map<String, PermissionInfo> permissions = local.get(RefNames.REFS_GROUPS + "*").permissions; |
| // READ is the default permission and should be preserved by the syncer |
| assertThatMap(permissions).keys().containsExactly(Permission.READ, Permission.CREATE); |
| Map<String, PermissionRuleInfo> rules = permissions.get(Permission.CREATE).rules; |
| assertThatMap(rules) |
| .keys() |
| .containsExactly(SystemGroupBackend.REGISTERED_USERS.get(), adminGroupUuid().get()); |
| assertThat(rules.get(SystemGroupBackend.REGISTERED_USERS.get())).isEqualTo(pri); |
| assertThat(rules.get(adminGroupUuid().get())).isEqualTo(pri); |
| } |
| |
| @Test |
| public void addAccessSectionForInvalidRef() throws Exception { |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = createDefaultAccessSectionInfo(); |
| |
| // 'refs/heads/stable_*' is invalid, correct would be '^refs/heads/stable_.*' |
| String invalidRef = Constants.R_HEADS + "stable_*"; |
| accessInput.add.put(invalidRef, accessSectionInfo); |
| |
| BadRequestException thrown = |
| assertThrows(BadRequestException.class, () -> pApi().access(accessInput)); |
| assertThat(thrown).hasMessageThat().contains("Invalid Name: " + invalidRef); |
| } |
| |
| @Test |
| public void createAccessChangeWithAccessSectionForInvalidRef() throws Exception { |
| ProjectAccessInput accessInput = newProjectAccessInput(); |
| AccessSectionInfo accessSectionInfo = createDefaultAccessSectionInfo(); |
| |
| // 'refs/heads/stable_*' is invalid, correct would be '^refs/heads/stable_.*' |
| String invalidRef = Constants.R_HEADS + "stable_*"; |
| accessInput.add.put(invalidRef, accessSectionInfo); |
| |
| BadRequestException thrown = |
| assertThrows(BadRequestException.class, () -> pApi().accessChange(accessInput)); |
| assertThat(thrown).hasMessageThat().contains("Invalid Name: " + invalidRef); |
| } |
| |
| @Test |
| public void grantAllowAndDenyForSameGroup() throws Exception { |
| GroupReference registeredUsers = systemGroupBackend.getGroup(REGISTERED_USERS); |
| String access = "access"; |
| List<String> allowThenDeny = |
| asList(registeredUsers.toConfigValue(), "deny " + registeredUsers.toConfigValue()); |
| // Clone repository to forcefully add permission |
| TestRepository<InMemoryRepository> allProjectsRepo = cloneProject(allProjects, admin); |
| |
| // Fetch permission ref |
| GitUtil.fetch(allProjectsRepo, "refs/meta/config:cfg"); |
| allProjectsRepo.reset("cfg"); |
| |
| // Load current permissions |
| String config = |
| gApi.projects() |
| .name(allProjects.get()) |
| .branch(RefNames.REFS_CONFIG) |
| .file(ProjectConfig.PROJECT_CONFIG) |
| .asString(); |
| |
| // Append and push allowThenDeny permissions |
| Config cfg = new Config(); |
| cfg.fromText(config); |
| cfg.setStringList(access, AccessSection.HEADS, Permission.READ, allowThenDeny); |
| config = cfg.toText(); |
| PushOneCommit push = |
| pushFactory.create( |
| admin.newIdent(), allProjectsRepo, "Subject", ProjectConfig.PROJECT_CONFIG, config); |
| push.to(RefNames.REFS_CONFIG).assertOkStatus(); |
| |
| ProjectAccessInfo pai = gApi.projects().name(allProjects.get()).access(); |
| Map<String, AccessSectionInfo> local = pai.local; |
| AccessSectionInfo heads = local.get(AccessSection.HEADS); |
| Map<String, PermissionInfo> permissions = heads.permissions; |
| PermissionInfo read = permissions.get(Permission.READ); |
| Map<String, PermissionRuleInfo> rules = read.rules; |
| assertEquals( |
| rules.get(registeredUsers.getUUID().get()), |
| new PermissionRuleInfo(PermissionRuleInfo.Action.ALLOW, false)); |
| } |
| |
| @Test |
| public void grantDenyAndAllowForSameGroup() throws Exception { |
| GroupReference registeredUsers = systemGroupBackend.getGroup(REGISTERED_USERS); |
| String access = "access"; |
| List<String> denyThenAllow = |
| asList("deny " + registeredUsers.toConfigValue(), registeredUsers.toConfigValue()); |
| // Clone repository to forcefully add permission |
| TestRepository<InMemoryRepository> allProjectsRepo = cloneProject(allProjects, admin); |
| |
| // Fetch permission ref |
| GitUtil.fetch(allProjectsRepo, "refs/meta/config:cfg"); |
| allProjectsRepo.reset("cfg"); |
| |
| // Load current permissions |
| String config = |
| gApi.projects() |
| .name(allProjects.get()) |
| .branch(RefNames.REFS_CONFIG) |
| .file(ProjectConfig.PROJECT_CONFIG) |
| .asString(); |
| |
| // Append and push denyThenAllow permissions |
| Config cfg = new Config(); |
| cfg.fromText(config); |
| cfg.setStringList(access, AccessSection.HEADS, Permission.READ, denyThenAllow); |
| config = cfg.toText(); |
| PushOneCommit push = |
| pushFactory.create( |
| admin.newIdent(), allProjectsRepo, "Subject", ProjectConfig.PROJECT_CONFIG, config); |
| push.to(RefNames.REFS_CONFIG).assertOkStatus(); |
| |
| ProjectAccessInfo pai = gApi.projects().name(allProjects.get()).access(); |
| Map<String, AccessSectionInfo> local = pai.local; |
| AccessSectionInfo heads = local.get(AccessSection.HEADS); |
| Map<String, PermissionInfo> permissions = heads.permissions; |
| PermissionInfo read = permissions.get(Permission.READ); |
| Map<String, PermissionRuleInfo> rules = read.rules; |
| assertEquals( |
| rules.get(registeredUsers.getUUID().get()), |
| new PermissionRuleInfo(PermissionRuleInfo.Action.DENY, false)); |
| } |
| |
| private ProjectApi pApi() throws Exception { |
| return gApi.projects().name(newProjectName.get()); |
| } |
| |
| private ProjectAccessInput newProjectAccessInput() { |
| ProjectAccessInput p = new ProjectAccessInput(); |
| p.add = new HashMap<>(); |
| p.remove = new HashMap<>(); |
| return p; |
| } |
| |
| private PermissionInfo newPermissionInfo() { |
| PermissionInfo p = new PermissionInfo(null, null); |
| p.rules = new HashMap<>(); |
| return p; |
| } |
| |
| private AccessSectionInfo newAccessSectionInfo() { |
| AccessSectionInfo a = new AccessSectionInfo(); |
| a.permissions = new HashMap<>(); |
| return a; |
| } |
| |
| private AccessSectionInfo createDefaultAccessSectionInfo() { |
| AccessSectionInfo accessSection = newAccessSectionInfo(); |
| |
| PermissionInfo push = newPermissionInfo(); |
| PermissionRuleInfo pri = new PermissionRuleInfo(PermissionRuleInfo.Action.ALLOW, false); |
| push.rules.put(SystemGroupBackend.REGISTERED_USERS.get(), pri); |
| accessSection.permissions.put(Permission.PUSH, push); |
| |
| PermissionInfo codeReview = newPermissionInfo(); |
| codeReview.label = LabelId.CODE_REVIEW; |
| pri = new PermissionRuleInfo(PermissionRuleInfo.Action.DENY, false); |
| codeReview.rules.put(SystemGroupBackend.REGISTERED_USERS.get(), pri); |
| |
| pri = new PermissionRuleInfo(PermissionRuleInfo.Action.ALLOW, false); |
| pri.max = 1; |
| pri.min = -1; |
| codeReview.rules.put(SystemGroupBackend.PROJECT_OWNERS.get(), pri); |
| accessSection.permissions.put(Permission.LABEL + LabelId.CODE_REVIEW, codeReview); |
| |
| return accessSection; |
| } |
| |
| private AccessSectionInfo createDefaultGlobalCapabilitiesAccessSectionInfo() { |
| AccessSectionInfo accessSection = newAccessSectionInfo(); |
| |
| PermissionInfo email = newPermissionInfo(); |
| PermissionRuleInfo pri = new PermissionRuleInfo(PermissionRuleInfo.Action.ALLOW, false); |
| email.rules.put(SystemGroupBackend.REGISTERED_USERS.get(), pri); |
| accessSection.permissions.put(GlobalCapability.EMAIL_REVIEWERS, email); |
| |
| return accessSection; |
| } |
| |
| private AccessSectionInfo createAccessSectionInfoDenyAll() { |
| AccessSectionInfo accessSection = newAccessSectionInfo(); |
| |
| PermissionInfo read = newPermissionInfo(); |
| PermissionRuleInfo pri = new PermissionRuleInfo(PermissionRuleInfo.Action.DENY, false); |
| read.rules.put(SystemGroupBackend.ANONYMOUS_USERS.get(), pri); |
| accessSection.permissions.put(Permission.READ, read); |
| |
| return accessSection; |
| } |
| } |