3 * Copyright (C) 2009-2021 SonarSource SA
4 * mailto:info AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.server.permission;
22 import org.apache.commons.lang.StringUtils;
23 import org.junit.Before;
24 import org.junit.Rule;
25 import org.junit.Test;
26 import org.junit.rules.ExpectedException;
27 import org.sonar.api.resources.Qualifiers;
28 import org.sonar.api.resources.ResourceTypes;
29 import org.sonar.api.utils.System2;
30 import org.sonar.api.web.UserRole;
31 import org.sonar.core.permission.GlobalPermissions;
32 import org.sonar.core.util.SequenceUuidFactory;
33 import org.sonar.core.util.Uuids;
34 import org.sonar.db.DbTester;
35 import org.sonar.db.component.ComponentDto;
36 import org.sonar.db.component.ResourceTypesRule;
37 import org.sonar.db.permission.GlobalPermission;
38 import org.sonar.db.permission.GroupPermissionDto;
39 import org.sonar.db.user.GroupDto;
40 import org.sonar.db.user.UserDto;
41 import org.sonar.server.exceptions.BadRequestException;
43 import static org.assertj.core.api.Assertions.assertThat;
44 import static org.assertj.core.api.Assertions.fail;
45 import static org.sonar.db.permission.GlobalPermission.ADMINISTER;
46 import static org.sonar.db.permission.GlobalPermission.ADMINISTER_QUALITY_GATES;
47 import static org.sonar.db.permission.GlobalPermission.PROVISION_PROJECTS;
49 public class GroupPermissionChangerTest {
52 public DbTester db = DbTester.create(System2.INSTANCE);
54 public ExpectedException expectedException = ExpectedException.none();
56 private final ResourceTypes resourceTypes = new ResourceTypesRule().setRootQualifiers(Qualifiers.PROJECT);
57 private final PermissionService permissionService = new PermissionServiceImpl(resourceTypes);
58 private final GroupPermissionChanger underTest = new GroupPermissionChanger(db.getDbClient(), new SequenceUuidFactory());
59 private GroupDto group;
60 private ComponentDto privateProject;
61 private ComponentDto publicProject;
65 group = db.users().insertGroup("a-group");
66 privateProject = db.components().insertPrivateProject();
67 publicProject = db.components().insertPublicProject();
71 public void apply_adds_global_permission_to_group() {
72 GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
74 apply(new GroupPermissionChange(PermissionChange.Operation.ADD, GlobalPermissions.QUALITY_GATE_ADMIN, null, groupUuid, permissionService));
76 assertThat(db.users().selectGroupPermissions(group, null)).containsOnly(GlobalPermissions.QUALITY_GATE_ADMIN);
80 public void apply_adds_global_permission_to_group_AnyOne() {
81 GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.forAnyone();
83 apply(new GroupPermissionChange(PermissionChange.Operation.ADD, GlobalPermissions.QUALITY_GATE_ADMIN, null, groupUuid, permissionService));
85 assertThat(db.users().selectAnyonePermissions(null)).containsOnly(GlobalPermissions.QUALITY_GATE_ADMIN);
89 public void apply_fails_with_BadRequestException_when_adding_any_permission_to_group_AnyOne_on_private_project() {
90 GroupUuidOrAnyone anyOneGroup = GroupUuidOrAnyone.forAnyone();
91 permissionService.getAllProjectPermissions()
93 GroupPermissionChange change = new GroupPermissionChange(PermissionChange.Operation.ADD, perm, privateProject, anyOneGroup, permissionService);
96 fail("a BadRequestException should have been thrown");
97 } catch (BadRequestException e) {
98 assertThat(e).hasMessage("No permission can be granted to Anyone on a private component");
104 public void apply_has_no_effect_when_removing_any_permission_to_group_AnyOne_on_private_project() {
105 permissionService.getAllProjectPermissions()
106 .forEach(this::unsafeInsertProjectPermissionOnAnyone);
108 GroupUuidOrAnyone anyOneGroup = GroupUuidOrAnyone.forAnyone();
109 permissionService.getAllProjectPermissions()
111 apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, perm, privateProject, anyOneGroup, permissionService));
113 assertThat(db.users().selectAnyonePermissions(privateProject)).contains(perm);
118 public void apply_adds_permission_USER_to_group_on_private_project() {
119 applyAddsPermissionToGroupOnPrivateProject(UserRole.USER);
123 public void apply_adds_permission_CODEVIEWER_to_group_on_private_project() {
124 applyAddsPermissionToGroupOnPrivateProject(UserRole.CODEVIEWER);
128 public void apply_adds_permission_ADMIN_to_group_on_private_project() {
129 applyAddsPermissionToGroupOnPrivateProject(UserRole.ADMIN);
133 public void apply_adds_permission_ISSUE_ADMIN_to_group_on_private_project() {
134 applyAddsPermissionToGroupOnPrivateProject(UserRole.ISSUE_ADMIN);
138 public void apply_adds_permission_SCAN_EXECUTION_to_group_on_private_project() {
139 applyAddsPermissionToGroupOnPrivateProject(GlobalPermissions.SCAN_EXECUTION);
142 private void applyAddsPermissionToGroupOnPrivateProject(String permission) {
143 GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
145 apply(new GroupPermissionChange(PermissionChange.Operation.ADD, permission, privateProject, groupUuid, permissionService));
147 assertThat(db.users().selectGroupPermissions(group, null)).isEmpty();
148 assertThat(db.users().selectGroupPermissions(group, privateProject)).containsOnly(permission);
152 public void apply_removes_permission_USER_from_group_on_private_project() {
153 applyRemovesPermissionFromGroupOnPrivateProject(UserRole.USER);
157 public void apply_removes_permission_CODEVIEWER_from_group_on_private_project() {
158 applyRemovesPermissionFromGroupOnPrivateProject(UserRole.CODEVIEWER);
162 public void apply_removes_permission_ADMIN_from_on_private_project() {
163 applyRemovesPermissionFromGroupOnPrivateProject(UserRole.ADMIN);
167 public void apply_removes_permission_ISSUE_ADMIN_from_on_private_project() {
168 applyRemovesPermissionFromGroupOnPrivateProject(UserRole.ISSUE_ADMIN);
172 public void apply_removes_permission_SCAN_EXECUTION_from_on_private_project() {
173 applyRemovesPermissionFromGroupOnPrivateProject(GlobalPermissions.SCAN_EXECUTION);
176 private void applyRemovesPermissionFromGroupOnPrivateProject(String permission) {
177 GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
178 db.users().insertProjectPermissionOnGroup(group, permission, privateProject);
180 apply(new GroupPermissionChange(PermissionChange.Operation.ADD, permission, privateProject, groupUuid, permissionService));
182 assertThat(db.users().selectGroupPermissions(group, privateProject)).containsOnly(permission);
186 public void apply_has_no_effect_when_adding_USER_permission_to_group_AnyOne_on_a_public_project() {
187 GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.forAnyone();
189 apply(new GroupPermissionChange(PermissionChange.Operation.ADD, UserRole.USER, publicProject, groupUuid, permissionService));
191 assertThat(db.users().selectAnyonePermissions(publicProject)).isEmpty();
195 public void apply_has_no_effect_when_adding_CODEVIEWER_permission_to_group_AnyOne_on_a_public_project() {
196 GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.forAnyone();
198 apply(new GroupPermissionChange(PermissionChange.Operation.ADD, UserRole.CODEVIEWER, publicProject, groupUuid, permissionService));
200 assertThat(db.users().selectAnyonePermissions(publicProject)).isEmpty();
204 public void apply_fails_with_BadRequestException_when_adding_permission_ADMIN_to_group_AnyOne_on_a_public_project() {
205 GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.forAnyone();
207 expectedException.expect(BadRequestException.class);
208 expectedException.expectMessage("It is not possible to add the 'admin' permission to group 'Anyone'");
210 apply(new GroupPermissionChange(PermissionChange.Operation.ADD, UserRole.ADMIN, publicProject, groupUuid, permissionService));
214 public void apply_adds_permission_ISSUE_ADMIN_to_group_AnyOne_on_a_public_project() {
215 GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.forAnyone();
217 apply(new GroupPermissionChange(PermissionChange.Operation.ADD, UserRole.ISSUE_ADMIN, publicProject, groupUuid, permissionService));
219 assertThat(db.users().selectAnyonePermissions(publicProject)).containsOnly(UserRole.ISSUE_ADMIN);
223 public void apply_adds_permission_SCAN_EXECUTION_to_group_AnyOne_on_a_public_project() {
224 GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.forAnyone();
226 apply(new GroupPermissionChange(PermissionChange.Operation.ADD, GlobalPermissions.SCAN_EXECUTION, publicProject, groupUuid, permissionService));
228 assertThat(db.users().selectAnyonePermissions(publicProject)).containsOnly(GlobalPermissions.SCAN_EXECUTION);
232 public void apply_fails_with_BadRequestException_when_removing_USER_permission_from_group_AnyOne_on_a_public_project() {
233 GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.forAnyone();
235 expectedException.expect(BadRequestException.class);
236 expectedException.expectMessage("Permission user can't be removed from a public component");
238 apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, UserRole.USER, publicProject, groupUuid, permissionService));
242 public void apply_fails_with_BadRequestException_when_removing_CODEVIEWER_permission_from_group_AnyOne_on_a_public_project() {
243 GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.forAnyone();
245 expectedException.expect(BadRequestException.class);
246 expectedException.expectMessage("Permission codeviewer can't be removed from a public component");
248 apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, UserRole.CODEVIEWER, publicProject, groupUuid, permissionService));
252 public void apply_removes_ADMIN_permission_from_group_AnyOne_on_a_public_project() {
253 applyRemovesPermissionFromGroupAnyOneOnAPublicProject(UserRole.ADMIN);
257 public void apply_removes_ISSUE_ADMIN_permission_from_group_AnyOne_on_a_public_project() {
258 applyRemovesPermissionFromGroupAnyOneOnAPublicProject(UserRole.ISSUE_ADMIN);
262 public void apply_removes_SCAN_EXECUTION_permission_from_group_AnyOne_on_a_public_project() {
263 applyRemovesPermissionFromGroupAnyOneOnAPublicProject(GlobalPermissions.SCAN_EXECUTION);
266 private void applyRemovesPermissionFromGroupAnyOneOnAPublicProject(String permission) {
267 GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.forAnyone();
268 db.users().insertProjectPermissionOnAnyone(permission, publicProject);
270 apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, permission, publicProject, groupUuid, permissionService));
272 assertThat(db.users().selectAnyonePermissions(publicProject)).isEmpty();
276 public void apply_fails_with_BadRequestException_when_removing_USER_permission_from_a_group_on_a_public_project() {
277 GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
279 expectedException.expect(BadRequestException.class);
280 expectedException.expectMessage("Permission user can't be removed from a public component");
282 apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, UserRole.USER, publicProject, groupUuid, permissionService));
286 public void apply_fails_with_BadRequestException_when_removing_CODEVIEWER_permission_from_a_group_on_a_public_project() {
287 GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
289 expectedException.expect(BadRequestException.class);
290 expectedException.expectMessage("Permission codeviewer can't be removed from a public component");
292 apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, UserRole.CODEVIEWER, publicProject, groupUuid, permissionService));
296 public void add_permission_to_anyone() {
297 GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.forAnyone();
299 apply(new GroupPermissionChange(PermissionChange.Operation.ADD, GlobalPermissions.QUALITY_GATE_ADMIN, null, groupUuid, permissionService));
301 assertThat(db.users().selectGroupPermissions(group, null)).isEmpty();
302 assertThat(db.users().selectAnyonePermissions(null)).containsOnly(GlobalPermissions.QUALITY_GATE_ADMIN);
306 public void do_nothing_when_adding_permission_that_already_exists() {
307 GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
308 db.users().insertPermissionOnGroup(group, ADMINISTER_QUALITY_GATES);
310 apply(new GroupPermissionChange(PermissionChange.Operation.ADD, ADMINISTER_QUALITY_GATES.getKey(), null, groupUuid, permissionService));
312 assertThat(db.users().selectGroupPermissions(group, null)).containsOnly(ADMINISTER_QUALITY_GATES.getKey());
316 public void fail_to_add_global_permission_but_SCAN_and_ADMIN_on_private_project() {
317 GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
319 permissionService.getGlobalPermissions().stream()
320 .map(GlobalPermission::getKey)
321 .filter(perm -> !UserRole.ADMIN.equals(perm) && !GlobalPermissions.SCAN_EXECUTION.equals(perm))
324 new GroupPermissionChange(PermissionChange.Operation.ADD, perm, privateProject, groupUuid, permissionService);
325 fail("a BadRequestException should have been thrown for permission " + perm);
326 } catch (BadRequestException e) {
327 assertThat(e).hasMessage("Invalid project permission '" + perm +
328 "'. Valid values are [" + StringUtils.join(permissionService.getAllProjectPermissions(), ", ") + "]");
334 public void fail_to_add_global_permission_but_SCAN_and_ADMIN_on_public_project() {
335 GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
337 permissionService.getGlobalPermissions().stream()
338 .map(GlobalPermission::getKey)
339 .filter(perm -> !UserRole.ADMIN.equals(perm) && !GlobalPermissions.SCAN_EXECUTION.equals(perm))
342 new GroupPermissionChange(PermissionChange.Operation.ADD, perm, publicProject, groupUuid, permissionService);
343 fail("a BadRequestException should have been thrown for permission " + perm);
344 } catch (BadRequestException e) {
345 assertThat(e).hasMessage("Invalid project permission '" + perm +
346 "'. Valid values are [" + StringUtils.join(permissionService.getAllProjectPermissions(), ", ") + "]");
352 public void fail_to_add_project_permission_but_SCAN_and_ADMIN_on_global_group() {
353 GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
355 permissionService.getAllProjectPermissions()
357 .filter(perm -> !GlobalPermissions.SCAN_EXECUTION.equals(perm) && !GlobalPermission.ADMINISTER.getKey().equals(perm))
358 .forEach(permission -> {
360 new GroupPermissionChange(PermissionChange.Operation.ADD, permission, null, groupUuid, permissionService);
361 fail("a BadRequestException should have been thrown for permission " + permission);
362 } catch (BadRequestException e) {
363 assertThat(e).hasMessage("Invalid global permission '" + permission + "'. Valid values are [admin, gateadmin, profileadmin, provisioning, scan]");
369 public void remove_permission_from_group() {
370 GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
371 db.users().insertPermissionOnGroup(group, ADMINISTER_QUALITY_GATES);
372 db.users().insertPermissionOnGroup(group, PROVISION_PROJECTS);
374 apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, ADMINISTER_QUALITY_GATES.getKey(), null, groupUuid, permissionService));
376 assertThat(db.users().selectGroupPermissions(group, null)).containsOnly(PROVISION_PROJECTS.getKey());
380 public void remove_project_permission_from_group() {
381 GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
382 db.users().insertPermissionOnGroup(group, ADMINISTER_QUALITY_GATES);
383 db.users().insertProjectPermissionOnGroup(group, UserRole.ISSUE_ADMIN, privateProject);
384 db.users().insertProjectPermissionOnGroup(group, UserRole.CODEVIEWER, privateProject);
386 apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, UserRole.ISSUE_ADMIN, privateProject, groupUuid, permissionService));
388 assertThat(db.users().selectGroupPermissions(group, null)).containsOnly(ADMINISTER_QUALITY_GATES.getKey());
389 assertThat(db.users().selectGroupPermissions(group, privateProject)).containsOnly(UserRole.CODEVIEWER);
393 public void do_not_fail_if_removing_a_permission_that_does_not_exist() {
394 GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
396 apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, UserRole.ISSUE_ADMIN, privateProject, groupUuid, permissionService));
398 assertThat(db.users().selectGroupPermissions(group, null)).isEmpty();
399 assertThat(db.users().selectGroupPermissions(group, privateProject)).isEmpty();
403 public void fail_to_remove_admin_permission_if_no_more_admins() {
404 GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
405 db.users().insertPermissionOnGroup(group, ADMINISTER);
407 expectedException.expect(BadRequestException.class);
408 expectedException.expectMessage("Last group with permission 'admin'. Permission cannot be removed.");
410 underTest.apply(db.getSession(), new GroupPermissionChange(PermissionChange.Operation.REMOVE, ADMINISTER.getKey(), null, groupUuid, permissionService));
414 public void remove_admin_group_if_still_other_admins() {
415 GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
416 db.users().insertPermissionOnGroup(group, ADMINISTER);
417 UserDto admin = db.users().insertUser();
418 db.users().insertPermissionOnUser(admin, ADMINISTER);
420 apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, ADMINISTER.getKey(), null, groupUuid, permissionService));
422 assertThat(db.users().selectGroupPermissions(group, null)).isEmpty();
425 private void apply(GroupPermissionChange change) {
426 underTest.apply(db.getSession(), change);
430 private void unsafeInsertProjectPermissionOnAnyone(String perm) {
431 GroupPermissionDto dto = new GroupPermissionDto()
432 .setUuid(Uuids.createFast())
435 .setComponentUuid(privateProject.uuid())
436 .setComponentName(privateProject.name());
437 db.getDbClient().groupPermissionDao().insert(db.getSession(), dto, privateProject);