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.ws;
22 import org.junit.Before;
23 import org.junit.Test;
24 import org.sonar.api.resources.Qualifiers;
25 import org.sonar.api.resources.ResourceTypes;
26 import org.sonar.api.web.UserRole;
27 import org.sonar.db.component.ComponentDto;
28 import org.sonar.db.component.ResourceTypesRule;
29 import org.sonar.db.user.UserDto;
30 import org.sonar.server.exceptions.BadRequestException;
31 import org.sonar.server.exceptions.ForbiddenException;
32 import org.sonar.server.exceptions.NotFoundException;
33 import org.sonar.server.exceptions.ServerException;
34 import org.sonar.server.permission.PermissionService;
35 import org.sonar.server.permission.PermissionServiceImpl;
36 import org.sonar.server.ws.TestRequest;
38 import static java.lang.String.format;
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.PROVISIONING;
46 import static org.sonar.core.permission.GlobalPermissions.QUALITY_GATE_ADMIN;
47 import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
48 import static org.sonar.db.component.ComponentTesting.newDirectory;
49 import static org.sonar.db.component.ComponentTesting.newFileDto;
50 import static org.sonar.db.component.ComponentTesting.newSubPortfolio;
51 import static org.sonar.db.permission.GlobalPermission.ADMINISTER;
52 import static org.sonar.db.permission.GlobalPermission.ADMINISTER_QUALITY_GATES;
53 import static org.sonar.db.permission.GlobalPermission.PROVISION_PROJECTS;
54 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PERMISSION;
55 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_ID;
56 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_KEY;
57 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_USER_LOGIN;
59 public class RemoveUserActionTest extends BasePermissionWsTest<RemoveUserAction> {
61 private static final String A_PROJECT_UUID = "project-uuid";
62 private static final String A_PROJECT_KEY = "project-key";
63 private static final String A_LOGIN = "ray.bradbury";
66 private ResourceTypes resourceTypes = new ResourceTypesRule().setRootQualifiers(Qualifiers.PROJECT);
67 private PermissionService permissionService = new PermissionServiceImpl(resourceTypes);
68 private WsParameters wsParameters = new WsParameters(permissionService);
72 user = db.users().insertUser(A_LOGIN);
76 protected RemoveUserAction buildWsAction() {
77 return new RemoveUserAction(db.getDbClient(), userSession, newPermissionUpdater(), newPermissionWsSupport(), wsParameters, permissionService);
81 public void remove_permission_from_user() {
82 db.users().insertPermissionOnUser(user, PROVISION_PROJECTS);
83 db.users().insertPermissionOnUser(user, ADMINISTER_QUALITY_GATES);
87 .setParam(PARAM_USER_LOGIN, user.getLogin())
88 .setParam(PARAM_PERMISSION, QUALITY_GATE_ADMIN)
91 assertThat(db.users().selectPermissionsOfUser(user)).containsOnly(PROVISION_PROJECTS);
95 public void admin_can_not_remove_his_global_admin_right() {
96 db.users().insertPermissionOnUser(user, ADMINISTER);
98 UserDto admin = db.users().insertUser(userSession.getLogin());
99 db.users().insertPermissionOnUser(admin, ADMINISTER);
101 TestRequest request = newRequest()
102 .setParam(PARAM_USER_LOGIN, userSession.getLogin())
103 .setParam(PARAM_PERMISSION, ADMINISTER.getKey());
105 assertThatThrownBy(() -> request.execute())
106 .isInstanceOf(BadRequestException.class)
107 .hasMessage("As an admin, you can't remove your own admin right");
111 public void project_admin_can_not_remove_his_project_admin_right() {
113 UserDto admin = db.users().insertUser(userSession.getLogin());
114 ComponentDto project = db.components().insertPrivateProject();
115 db.users().insertProjectPermissionOnUser(admin, ADMINISTER.getKey(), project);
117 TestRequest request = newRequest()
118 .setParam(PARAM_USER_LOGIN, userSession.getLogin())
119 .setParam(PARAM_PROJECT_ID, project.uuid())
120 .setParam(PARAM_PERMISSION, ADMINISTER.getKey());
122 assertThatThrownBy(() -> request.execute())
123 .isInstanceOf(BadRequestException.class)
124 .hasMessage("As an admin, you can't remove your own admin right");
128 public void fail_to_remove_admin_permission_if_last_admin() {
129 db.users().insertPermissionOnUser(user, ADMINISTER);
132 assertThatThrownBy(() -> {
134 .setParam(PARAM_USER_LOGIN, user.getLogin())
135 .setParam(PARAM_PERMISSION, ADMIN)
138 .isInstanceOf(BadRequestException.class)
139 .hasMessage("Last user with permission 'admin'. Permission cannot be removed.");
143 public void remove_permission_from_project() {
144 ComponentDto project = db.components().insertPrivateProject();
145 db.users().insertProjectPermissionOnUser(user, CODEVIEWER, project);
146 db.users().insertProjectPermissionOnUser(user, ISSUE_ADMIN, project);
150 .setParam(PARAM_USER_LOGIN, user.getLogin())
151 .setParam(PARAM_PROJECT_ID, project.uuid())
152 .setParam(PARAM_PERMISSION, CODEVIEWER)
155 assertThat(db.users().selectProjectPermissionsOfUser(user, project)).containsOnly(ISSUE_ADMIN);
159 public void remove_with_project_key() {
160 ComponentDto project = db.components().insertPrivateProject();
161 db.users().insertProjectPermissionOnUser(user, ISSUE_ADMIN, project);
162 db.users().insertProjectPermissionOnUser(user, CODEVIEWER, project);
166 .setParam(PARAM_USER_LOGIN, user.getLogin())
167 .setParam(PARAM_PROJECT_KEY, project.getKey())
168 .setParam(PARAM_PERMISSION, ISSUE_ADMIN)
171 assertThat(db.users().selectProjectPermissionsOfUser(user, project)).containsOnly(CODEVIEWER);
175 public void remove_with_view_uuid() {
176 ComponentDto view = db.components().insertPrivatePortfolio();
177 db.users().insertProjectPermissionOnUser(user, ISSUE_ADMIN, view);
178 db.users().insertProjectPermissionOnUser(user, ADMIN, view);
182 .setParam(PARAM_USER_LOGIN, user.getLogin())
183 .setParam(PARAM_PROJECT_KEY, view.getKey())
184 .setParam(PARAM_PERMISSION, ISSUE_ADMIN)
187 assertThat(db.users().selectProjectPermissionsOfUser(user, view)).containsOnly(ADMIN);
191 public void fail_when_project_does_not_exist() {
194 assertThatThrownBy(() -> {
196 .setParam(PARAM_USER_LOGIN, user.getLogin())
197 .setParam(PARAM_PROJECT_ID, "unknown-project-uuid")
198 .setParam(PARAM_PERMISSION, ISSUE_ADMIN)
201 .isInstanceOf(NotFoundException.class);
205 public void fail_when_project_permission_without_permission() {
208 assertThatThrownBy(() -> {
210 .setParam(PARAM_USER_LOGIN, user.getLogin())
211 .setParam(PARAM_PERMISSION, ISSUE_ADMIN)
214 .isInstanceOf(BadRequestException.class);
218 public void fail_when_component_is_a_directory() {
219 ComponentDto project = db.components().insertPrivateProject();
220 ComponentDto file = db.components().insertComponent(newDirectory(project, "A/B"));
222 failIfComponentIsNotAProjectOrView(file);
226 public void fail_when_component_is_a_file() {
227 ComponentDto project = db.components().insertPrivateProject();
228 ComponentDto file = db.components().insertComponent(newFileDto(project, null, "file-uuid"));
230 failIfComponentIsNotAProjectOrView(file);
234 public void fail_when_component_is_a_subview() {
235 ComponentDto portfolio = db.components().insertPrivatePortfolio();
236 ComponentDto file = db.components().insertComponent(newSubPortfolio(portfolio));
238 failIfComponentIsNotAProjectOrView(file);
241 private void failIfComponentIsNotAProjectOrView(ComponentDto file) {
244 assertThatThrownBy(() -> {
246 .setParam(PARAM_USER_LOGIN, user.getLogin())
247 .setParam(PARAM_PROJECT_ID, file.uuid())
248 .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
251 .isInstanceOf(BadRequestException.class)
252 .hasMessage("Component '" + file.getKey() + "' (id: " + file.uuid() + ") must be a project or a view.");
256 public void fail_when_get_request() {
259 assertThatThrownBy(() -> {
262 .setParam(PARAM_USER_LOGIN, "george.orwell")
263 .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
266 .isInstanceOf(ServerException.class);
270 public void fail_when_user_login_is_missing() {
273 assertThatThrownBy(() -> {
275 .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
278 .isInstanceOf(IllegalArgumentException.class);
282 public void fail_when_permission_is_missing() {
285 assertThatThrownBy(() -> {
287 .setParam(PARAM_USER_LOGIN, user.getLogin())
290 .isInstanceOf(IllegalArgumentException.class);
294 public void fail_when_project_uuid_and_project_key_are_provided() {
295 ComponentDto project = db.components().insertPrivateProject();
298 assertThatThrownBy(() -> {
300 .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
301 .setParam(PARAM_USER_LOGIN, user.getLogin())
302 .setParam(PARAM_PROJECT_ID, project.uuid())
303 .setParam(PARAM_PROJECT_KEY, project.getKey())
306 .isInstanceOf(BadRequestException.class)
307 .hasMessage("Project id or project key can be provided, not both.");
311 public void removing_global_permission_fails_if_not_system_administrator() {
314 assertThatThrownBy(() -> {
316 .setParam(PARAM_USER_LOGIN, user.getLogin())
317 .setParam(PARAM_PERMISSION, PROVISIONING)
320 .isInstanceOf(ForbiddenException.class);
324 public void removing_project_permission_fails_if_not_administrator_of_project() {
325 ComponentDto project = db.components().insertPrivateProject();
328 assertThatThrownBy(() -> {
330 .setParam(PARAM_USER_LOGIN, user.getLogin())
331 .setParam(PARAM_PERMISSION, ISSUE_ADMIN)
332 .setParam(PARAM_PROJECT_KEY, project.getKey())
335 .isInstanceOf(ForbiddenException.class);
339 * User is project administrator but not system administrator
342 public void removing_project_permission_is_allowed_to_project_administrators() {
343 ComponentDto project = db.components().insertPrivateProject();
344 db.users().insertProjectPermissionOnUser(user, CODEVIEWER, project);
345 db.users().insertProjectPermissionOnUser(user, ISSUE_ADMIN, project);
346 userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
349 .setParam(PARAM_USER_LOGIN, user.getLogin())
350 .setParam(PARAM_PROJECT_ID, project.uuid())
351 .setParam(PARAM_PERMISSION, ISSUE_ADMIN)
354 assertThat(db.users().selectProjectPermissionsOfUser(user, project)).containsOnly(CODEVIEWER);
358 public void fail_when_removing_USER_permission_on_a_public_project() {
359 ComponentDto project = db.components().insertPublicProject();
360 userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
362 assertThatThrownBy(() -> {
364 .setParam(PARAM_USER_LOGIN, user.getLogin())
365 .setParam(PARAM_PROJECT_ID, project.uuid())
366 .setParam(PARAM_PERMISSION, USER)
369 .isInstanceOf(BadRequestException.class)
370 .hasMessage("Permission user can't be removed from a public component");
375 public void fail_when_removing_CODEVIEWER_permission_on_a_public_project() {
376 ComponentDto project = db.components().insertPublicProject();
377 userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
379 assertThatThrownBy(() -> {
381 .setParam(PARAM_USER_LOGIN, user.getLogin())
382 .setParam(PARAM_PROJECT_ID, project.uuid())
383 .setParam(PARAM_PERMISSION, CODEVIEWER)
386 .isInstanceOf(BadRequestException.class)
387 .hasMessage("Permission codeviewer can't be removed from a public component");
391 public void fail_when_using_branch_uuid() {
392 ComponentDto project = db.components().insertPublicProject();
393 userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
394 ComponentDto branch = db.components().insertProjectBranch(project);
396 assertThatThrownBy(() -> {
398 .setParam(PARAM_PROJECT_ID, branch.uuid())
399 .setParam(PARAM_USER_LOGIN, user.getLogin())
400 .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
403 .isInstanceOf(NotFoundException.class)
404 .hasMessage(format("Project id '%s' not found", branch.uuid()));