3 * Copyright (C) 2009-2023 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.sonar.api.resources.Qualifiers;
27 import org.sonar.api.resources.ResourceTypes;
28 import org.sonar.api.utils.System2;
29 import org.sonar.core.util.SequenceUuidFactory;
30 import org.sonar.db.DbTester;
31 import org.sonar.db.component.ComponentDto;
32 import org.sonar.db.component.ResourceTypesRule;
33 import org.sonar.db.permission.GlobalPermission;
34 import org.sonar.db.user.GroupDto;
35 import org.sonar.db.user.UserDto;
36 import org.sonar.db.user.UserIdDto;
37 import org.sonar.server.exceptions.BadRequestException;
39 import static org.assertj.core.api.Assertions.assertThat;
40 import static org.assertj.core.api.Assertions.assertThatThrownBy;
41 import static org.sonar.api.web.UserRole.ADMIN;
42 import static org.sonar.api.web.UserRole.CODEVIEWER;
43 import static org.sonar.api.web.UserRole.ISSUE_ADMIN;
44 import static org.sonar.api.web.UserRole.USER;
45 import static org.sonar.core.permission.GlobalPermissions.QUALITY_GATE_ADMIN;
46 import static org.sonar.core.permission.GlobalPermissions.SCAN_EXECUTION;
47 import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
48 import static org.sonar.db.permission.GlobalPermission.ADMINISTER;
49 import static org.sonar.db.permission.GlobalPermission.ADMINISTER_QUALITY_GATES;
50 import static org.sonar.db.permission.GlobalPermission.SCAN;
51 import static org.sonar.server.permission.PermissionChange.Operation.ADD;
52 import static org.sonar.server.permission.PermissionChange.Operation.REMOVE;
54 public class UserPermissionChangerTest {
56 public DbTester db = DbTester.create(System2.INSTANCE);
59 private final ResourceTypes resourceTypes = new ResourceTypesRule().setRootQualifiers(Qualifiers.PROJECT);
60 private final PermissionService permissionService = new PermissionServiceImpl(resourceTypes);
61 private final UserPermissionChanger underTest = new UserPermissionChanger(db.getDbClient(), new SequenceUuidFactory());
62 private UserDto user1;
63 private UserDto user2;
64 private ComponentDto privateProject;
65 private ComponentDto publicProject;
69 user1 = db.users().insertUser();
70 user2 = db.users().insertUser();
71 privateProject = db.components().insertPrivateProject();
72 publicProject = db.components().insertPublicProject();
76 public void apply_adds_any_global_permission_to_user() {
77 permissionService.getGlobalPermissions()
79 UserPermissionChange change = new UserPermissionChange(ADD, perm.getKey(), null, UserIdDto.from(user1), permissionService);
83 assertThat(db.users().selectPermissionsOfUser(user1)).contains(perm);
88 public void apply_removes_any_global_permission_to_user() {
89 // give ADMIN perm to user2 so that user1 is not the only one with this permission and it can be removed from user1
90 db.users().insertPermissionOnUser(user2, GlobalPermission.ADMINISTER);
91 permissionService.getGlobalPermissions()
92 .forEach(perm -> db.users().insertPermissionOnUser(user1, perm));
93 assertThat(db.users().selectPermissionsOfUser(user1))
94 .containsOnly(permissionService.getGlobalPermissions().toArray(new GlobalPermission[0]));
96 permissionService.getGlobalPermissions()
98 UserPermissionChange change = new UserPermissionChange(REMOVE, perm.getKey(), null, UserIdDto.from(user1), permissionService);
102 assertThat(db.users().selectPermissionsOfUser(user1)).doesNotContain(perm);
107 public void apply_has_no_effect_when_adding_permission_USER_on_a_public_project() {
108 UserPermissionChange change = new UserPermissionChange(ADD, USER, publicProject, UserIdDto.from(user1), permissionService);
112 assertThat(db.users().selectProjectPermissionsOfUser(user1, publicProject)).doesNotContain(USER);
116 public void apply_has_no_effect_when_adding_permission_CODEVIEWER_on_a_public_project() {
117 UserPermissionChange change = new UserPermissionChange(ADD, CODEVIEWER, publicProject, UserIdDto.from(user1), permissionService);
121 assertThat(db.users().selectProjectPermissionsOfUser(user1, publicProject)).doesNotContain(CODEVIEWER);
125 public void apply_adds_permission_ADMIN_on_a_public_project() {
126 applyAddsPermissionOnAPublicProject(ADMIN);
130 public void apply_adds_permission_ISSUE_ADMIN_on_a_public_project() {
131 applyAddsPermissionOnAPublicProject(ISSUE_ADMIN);
135 public void apply_adds_permission_SCAN_EXECUTION_on_a_public_project() {
136 applyAddsPermissionOnAPublicProject(SCAN_EXECUTION);
139 private void applyAddsPermissionOnAPublicProject(String permission) {
140 UserPermissionChange change = new UserPermissionChange(ADD, permission, publicProject, UserIdDto.from(user1), permissionService);
144 assertThat(db.users().selectProjectPermissionsOfUser(user1, publicProject)).containsOnly(permission);
148 public void apply_fails_with_BadRequestException_when_removing_permission_USER_from_a_public_project() {
149 UserPermissionChange change = new UserPermissionChange(REMOVE, USER, publicProject, UserIdDto.from(user1), permissionService);
151 assertThatThrownBy(() -> apply(change))
152 .isInstanceOf(BadRequestException.class)
153 .hasMessage("Permission user can't be removed from a public component");
157 public void apply_fails_with_BadRequestException_when_removing_permission_CODEVIEWER_from_a_public_project() {
158 UserPermissionChange change = new UserPermissionChange(REMOVE, CODEVIEWER, publicProject, UserIdDto.from(user1), permissionService);
160 assertThatThrownBy(() -> apply(change))
161 .isInstanceOf(BadRequestException.class)
162 .hasMessage("Permission codeviewer can't be removed from a public component");
166 public void apply_removes_permission_ADMIN_from_a_public_project() {
167 applyRemovesPermissionFromPublicProject(ADMIN);
171 public void apply_removes_permission_ISSUE_ADMIN_from_a_public_project() {
172 applyRemovesPermissionFromPublicProject(ISSUE_ADMIN);
176 public void apply_removes_permission_SCAN_EXECUTION_from_a_public_project() {
177 applyRemovesPermissionFromPublicProject(SCAN_EXECUTION);
180 private void applyRemovesPermissionFromPublicProject(String permission) {
181 db.users().insertProjectPermissionOnUser(user1, permission, publicProject);
182 UserPermissionChange change = new UserPermissionChange(REMOVE, permission, publicProject, UserIdDto.from(user1), permissionService);
186 assertThat(db.users().selectProjectPermissionsOfUser(user1, publicProject)).isEmpty();
190 public void apply_adds_any_permission_to_a_private_project() {
191 permissionService.getAllProjectPermissions()
192 .forEach(permission -> {
193 UserPermissionChange change = new UserPermissionChange(ADD, permission, privateProject, UserIdDto.from(user1), permissionService);
197 assertThat(db.users().selectProjectPermissionsOfUser(user1, privateProject)).contains(permission);
202 public void apply_removes_any_permission_from_a_private_project() {
203 permissionService.getAllProjectPermissions()
204 .forEach(permission -> db.users().insertProjectPermissionOnUser(user1, permission, privateProject));
206 permissionService.getAllProjectPermissions()
207 .forEach(permission -> {
208 UserPermissionChange change = new UserPermissionChange(REMOVE, permission, privateProject, UserIdDto.from(user1), permissionService);
212 assertThat(db.users().selectProjectPermissionsOfUser(user1, privateProject)).doesNotContain(permission);
217 public void add_global_permission_to_user() {
218 UserPermissionChange change = new UserPermissionChange(ADD, SCAN_EXECUTION, null, UserIdDto.from(user1), permissionService);
222 assertThat(db.users().selectPermissionsOfUser(user1)).containsOnly(SCAN);
223 assertThat(db.users().selectProjectPermissionsOfUser(user1, privateProject)).isEmpty();
224 assertThat(db.users().selectPermissionsOfUser(user2)).isEmpty();
225 assertThat(db.users().selectProjectPermissionsOfUser(user2, privateProject)).isEmpty();
229 public void add_project_permission_to_user() {
230 UserPermissionChange change = new UserPermissionChange(ADD, ISSUE_ADMIN, privateProject, UserIdDto.from(user1), permissionService);
233 assertThat(db.users().selectPermissionsOfUser(user1)).isEmpty();
234 assertThat(db.users().selectProjectPermissionsOfUser(user1, privateProject)).contains(ISSUE_ADMIN);
235 assertThat(db.users().selectPermissionsOfUser(user2)).isEmpty();
236 assertThat(db.users().selectProjectPermissionsOfUser(user2, privateProject)).isEmpty();
240 public void do_nothing_when_adding_global_permission_that_already_exists() {
241 db.users().insertPermissionOnUser(user1, ADMINISTER_QUALITY_GATES);
243 UserPermissionChange change = new UserPermissionChange(ADD, QUALITY_GATE_ADMIN, null, UserIdDto.from(user1), permissionService);
246 assertThat(db.users().selectPermissionsOfUser(user1)).containsOnly(ADMINISTER_QUALITY_GATES);
250 public void fail_to_add_global_permission_on_project() {
251 assertThatThrownBy(() -> {
252 UserPermissionChange change = new UserPermissionChange(ADD, QUALITY_GATE_ADMIN, privateProject, UserIdDto.from(user1), permissionService);
255 .isInstanceOf(BadRequestException.class)
256 .hasMessage("Invalid project permission 'gateadmin'. Valid values are [" + StringUtils.join(permissionService.getAllProjectPermissions(), ", ") + "]");
260 public void fail_to_add_project_permission() {
261 assertThatThrownBy(() -> {
262 UserPermissionChange change = new UserPermissionChange(ADD, ISSUE_ADMIN, null, UserIdDto.from(user1), permissionService);
265 .isInstanceOf(BadRequestException.class)
266 .hasMessage("Invalid global permission 'issueadmin'. Valid values are [admin, gateadmin, profileadmin, provisioning, scan]");
270 public void remove_global_permission_from_user() {
271 db.users().insertPermissionOnUser(user1, QUALITY_GATE_ADMIN);
272 db.users().insertPermissionOnUser(user1, SCAN_EXECUTION);
273 db.users().insertPermissionOnUser(user2, QUALITY_GATE_ADMIN);
274 db.users().insertProjectPermissionOnUser(user1, ISSUE_ADMIN, privateProject);
276 UserPermissionChange change = new UserPermissionChange(REMOVE, QUALITY_GATE_ADMIN, null, UserIdDto.from(user1), permissionService);
279 assertThat(db.users().selectPermissionsOfUser(user1)).containsOnly(SCAN);
280 assertThat(db.users().selectPermissionsOfUser(user2)).containsOnly(ADMINISTER_QUALITY_GATES);
281 assertThat(db.users().selectProjectPermissionsOfUser(user1, privateProject)).containsOnly(ISSUE_ADMIN);
285 public void remove_project_permission_from_user() {
286 ComponentDto project2 = db.components().insertPrivateProject();
287 db.users().insertPermissionOnUser(user1, ADMINISTER_QUALITY_GATES);
288 db.users().insertProjectPermissionOnUser(user1, ISSUE_ADMIN, privateProject);
289 db.users().insertProjectPermissionOnUser(user1, USER, privateProject);
290 db.users().insertProjectPermissionOnUser(user2, ISSUE_ADMIN, privateProject);
291 db.users().insertProjectPermissionOnUser(user1, ISSUE_ADMIN, project2);
293 UserPermissionChange change = new UserPermissionChange(REMOVE, ISSUE_ADMIN, privateProject, UserIdDto.from(user1), permissionService);
296 assertThat(db.users().selectProjectPermissionsOfUser(user1, privateProject)).containsOnly(USER);
297 assertThat(db.users().selectProjectPermissionsOfUser(user2, privateProject)).containsOnly(ISSUE_ADMIN);
298 assertThat(db.users().selectProjectPermissionsOfUser(user1, project2)).containsOnly(ISSUE_ADMIN);
302 public void do_not_fail_if_removing_a_global_permission_that_does_not_exist() {
303 UserPermissionChange change = new UserPermissionChange(REMOVE, QUALITY_GATE_ADMIN, null, UserIdDto.from(user1), permissionService);
306 assertThat(db.users().selectPermissionsOfUser(user1)).isEmpty();
310 public void do_not_fail_if_removing_a_project_permission_that_does_not_exist() {
311 UserPermissionChange change = new UserPermissionChange(REMOVE, ISSUE_ADMIN, privateProject, UserIdDto.from(user1), permissionService);
314 assertThat(db.users().selectProjectPermissionsOfUser(user1, privateProject)).isEmpty();
318 public void fail_to_remove_admin_global_permission_if_no_more_admins() {
319 db.users().insertPermissionOnUser(user1, SYSTEM_ADMIN);
321 assertThatThrownBy(() -> {
322 UserPermissionChange change = new UserPermissionChange(REMOVE, SYSTEM_ADMIN, null, UserIdDto.from(user1), permissionService);
323 underTest.apply(db.getSession(), change);
325 .isInstanceOf(BadRequestException.class)
326 .hasMessage("Last user with permission 'admin'. Permission cannot be removed.");
330 public void remove_admin_user_if_still_other_admins() {
331 db.users().insertPermissionOnUser(user1, ADMINISTER);
332 GroupDto admins = db.users().insertGroup("admins");
333 db.users().insertMember(admins, user2);
334 db.users().insertPermissionOnGroup(admins, ADMINISTER);
336 UserPermissionChange change = new UserPermissionChange(REMOVE, ADMINISTER.getKey(), null, UserIdDto.from(user1), permissionService);
337 underTest.apply(db.getSession(), change);
339 assertThat(db.users().selectPermissionsOfUser(user1)).isEmpty();
342 private void apply(UserPermissionChange change) {
343 underTest.apply(db.getSession(), change);