3 * Copyright (C) 2009-2022 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.server.ws.Change;
27 import org.sonar.api.server.ws.WebService.Action;
28 import org.sonar.api.web.UserRole;
29 import org.sonar.core.util.Uuids;
30 import org.sonar.db.component.ComponentDto;
31 import org.sonar.db.component.ResourceTypesRule;
32 import org.sonar.db.permission.GroupPermissionDto;
33 import org.sonar.db.user.GroupDto;
34 import org.sonar.server.exceptions.BadRequestException;
35 import org.sonar.server.exceptions.ForbiddenException;
36 import org.sonar.server.exceptions.NotFoundException;
37 import org.sonar.server.permission.PermissionService;
38 import org.sonar.server.permission.PermissionServiceImpl;
40 import static java.lang.String.format;
41 import static org.assertj.core.api.Assertions.assertThat;
42 import static org.assertj.core.api.Assertions.assertThatThrownBy;
43 import static org.assertj.core.api.Assertions.tuple;
44 import static org.sonar.api.web.UserRole.ADMIN;
45 import static org.sonar.api.web.UserRole.CODEVIEWER;
46 import static org.sonar.api.web.UserRole.ISSUE_ADMIN;
47 import static org.sonar.api.web.UserRole.USER;
48 import static org.sonar.core.permission.GlobalPermissions.PROVISIONING;
49 import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
50 import static org.sonar.db.component.ComponentTesting.newDirectory;
51 import static org.sonar.db.component.ComponentTesting.newFileDto;
52 import static org.sonar.db.component.ComponentTesting.newModuleDto;
53 import static org.sonar.db.component.ComponentTesting.newSubPortfolio;
54 import static org.sonar.db.permission.GlobalPermission.ADMINISTER;
55 import static org.sonar.db.permission.GlobalPermission.PROVISION_PROJECTS;
56 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_ID;
57 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_NAME;
58 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PERMISSION;
59 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_ID;
60 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_KEY;
62 public class RemoveGroupActionTest extends BasePermissionWsTest<RemoveGroupAction> {
64 private GroupDto aGroup;
65 private ResourceTypes resourceTypes = new ResourceTypesRule().setRootQualifiers(Qualifiers.PROJECT);
66 private PermissionService permissionService = new PermissionServiceImpl(resourceTypes);
67 private WsParameters wsParameters = new WsParameters(permissionService);
71 aGroup = db.users().insertGroup("sonar-administrators");
75 protected RemoveGroupAction buildWsAction() {
76 return new RemoveGroupAction(db.getDbClient(), userSession, newPermissionUpdater(), newPermissionWsSupport(), wsParameters, permissionService);
80 public void verify_definition() {
81 Action wsDef = wsTester.getDef();
83 assertThat(wsDef.isInternal()).isFalse();
84 assertThat(wsDef.since()).isEqualTo("5.2");
85 assertThat(wsDef.isPost()).isTrue();
86 assertThat(wsDef.changelog()).extracting(Change::getVersion, Change::getDescription).containsOnly(
87 tuple("8.4", "Parameter 'groupId' is deprecated. Format changes from integer to string. Use 'groupName' instead."));
91 public void remove_permission_using_group_name() {
92 db.users().insertPermissionOnGroup(aGroup, ADMINISTER);
93 db.users().insertPermissionOnGroup(aGroup, PROVISION_PROJECTS);
97 .setParam(PARAM_GROUP_NAME, aGroup.getName())
98 .setParam(PARAM_PERMISSION, PROVISIONING)
101 assertThat(db.users().selectGroupPermissions(aGroup, null)).containsOnly(ADMINISTER.getKey());
105 public void remove_permission_using_group_id() {
106 db.users().insertPermissionOnGroup(aGroup, ADMINISTER);
107 db.users().insertPermissionOnGroup(aGroup, PROVISION_PROJECTS);
111 .setParam(PARAM_GROUP_ID, aGroup.getUuid())
112 .setParam(PARAM_PERMISSION, PROVISION_PROJECTS.getKey())
115 assertThat(db.users().selectGroupPermissions(aGroup, null)).containsOnly(ADMINISTER.getKey());
119 public void remove_project_permission() {
120 ComponentDto project = db.components().insertPrivateProject();
121 db.users().insertPermissionOnGroup(aGroup, ADMINISTER);
122 db.users().insertProjectPermissionOnGroup(aGroup, ADMIN, project);
123 db.users().insertProjectPermissionOnGroup(aGroup, ISSUE_ADMIN, project);
127 .setParam(PARAM_GROUP_NAME, aGroup.getName())
128 .setParam(PARAM_PROJECT_ID, project.uuid())
129 .setParam(PARAM_PERMISSION, ADMIN)
132 assertThat(db.users().selectGroupPermissions(aGroup, null)).containsOnly(ADMINISTER.getKey());
133 assertThat(db.users().selectGroupPermissions(aGroup, project)).containsOnly(ISSUE_ADMIN);
137 public void remove_with_view_uuid() {
138 ComponentDto view = db.components().insertPrivatePortfolio();
139 db.users().insertPermissionOnGroup(aGroup, ADMINISTER);
140 db.users().insertProjectPermissionOnGroup(aGroup, ADMIN, view);
141 db.users().insertProjectPermissionOnGroup(aGroup, ISSUE_ADMIN, view);
145 .setParam(PARAM_GROUP_NAME, aGroup.getName())
146 .setParam(PARAM_PROJECT_ID, view.uuid())
147 .setParam(PARAM_PERMISSION, ADMIN)
150 assertThat(db.users().selectGroupPermissions(aGroup, null)).containsOnly(ADMINISTER.getKey());
151 assertThat(db.users().selectGroupPermissions(aGroup, view)).containsOnly(ISSUE_ADMIN);
155 public void remove_with_project_key() {
156 ComponentDto project = db.components().insertPrivateProject();
157 db.users().insertPermissionOnGroup(aGroup, ADMINISTER);
158 db.users().insertProjectPermissionOnGroup(aGroup, ADMIN, project);
159 db.users().insertProjectPermissionOnGroup(aGroup, ISSUE_ADMIN, project);
163 .setParam(PARAM_GROUP_NAME, aGroup.getName())
164 .setParam(PARAM_PROJECT_KEY, project.getKey())
165 .setParam(PARAM_PERMISSION, ADMIN)
168 assertThat(db.users().selectGroupPermissions(aGroup, null)).containsOnly(ADMINISTER.getKey());
169 assertThat(db.users().selectGroupPermissions(aGroup, project)).containsOnly(ISSUE_ADMIN);
173 public void fail_to_remove_last_admin_permission() {
174 db.users().insertPermissionOnGroup(aGroup, ADMINISTER);
175 db.users().insertPermissionOnGroup(aGroup, PROVISION_PROJECTS);
178 assertThatThrownBy(() -> {
179 executeRequest(aGroup, SYSTEM_ADMIN);
181 .isInstanceOf(BadRequestException.class)
182 .hasMessage("Last group with permission 'admin'. Permission cannot be removed.");
186 public void fail_when_project_does_not_exist() {
189 assertThatThrownBy(() -> {
191 .setParam(PARAM_GROUP_NAME, aGroup.getName())
192 .setParam(PARAM_PROJECT_ID, "unknown-project-uuid")
193 .setParam(PARAM_PERMISSION, ADMINISTER.getKey())
196 .isInstanceOf(NotFoundException.class)
197 .hasMessage("Project id 'unknown-project-uuid' not found");
201 public void fail_when_project_project_permission_without_project() {
204 assertThatThrownBy(() -> executeRequest(aGroup, ISSUE_ADMIN))
205 .isInstanceOf(BadRequestException.class)
206 .hasMessage("Invalid global permission 'issueadmin'. Valid values are [admin, gateadmin, profileadmin, provisioning, scan]");
210 public void fail_when_component_is_a_module() {
211 ComponentDto project = db.components().insertPrivateProject();
212 ComponentDto module = db.components().insertComponent(newModuleDto(project));
214 failIfComponentIsNotAProjectOrView(module);
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 project = db.components().insertPrivateProject();
236 ComponentDto file = db.components().insertComponent(newSubPortfolio(project));
238 failIfComponentIsNotAProjectOrView(file);
241 private void failIfComponentIsNotAProjectOrView(ComponentDto file) {
244 assertThatThrownBy(() -> {
246 .setParam(PARAM_GROUP_NAME, aGroup.getName())
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_group_name_is_missing() {
259 assertThatThrownBy(() -> {
261 .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
264 .isInstanceOf(BadRequestException.class)
265 .hasMessage("Group name or group id must be provided");
269 public void fail_when_permission_name_and_id_are_missing() {
272 assertThatThrownBy(() -> {
274 .setParam(PARAM_GROUP_NAME, aGroup.getName())
277 .isInstanceOf(IllegalArgumentException.class)
278 .hasMessage("The 'permission' parameter is missing");
282 public void fail_when_group_id_does_not_exist() {
285 assertThatThrownBy(() -> {
287 .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
288 .setParam(PARAM_GROUP_ID, "999999")
291 .isInstanceOf(NotFoundException.class)
292 .hasMessage("No group with id '999999'");
296 public void fail_when_project_uuid_and_project_key_are_provided() {
297 ComponentDto project = db.components().insertPrivateProject();
300 assertThatThrownBy(() -> {
302 .setParam(PARAM_GROUP_NAME, aGroup.getName())
303 .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
304 .setParam(PARAM_PROJECT_ID, project.uuid())
305 .setParam(PARAM_PROJECT_KEY, project.getKey())
308 .isInstanceOf(BadRequestException.class)
309 .hasMessage("Project id or project key can be provided, not both.");
312 private void executeRequest(GroupDto groupDto, String permission) {
314 .setParam(PARAM_GROUP_NAME, groupDto.getName())
315 .setParam(PARAM_PERMISSION, permission)
320 public void removing_global_permission_fails_if_not_system_administrator() {
323 assertThatThrownBy(() -> {
325 .setParam(PARAM_GROUP_NAME, aGroup.getName())
326 .setParam(PARAM_PERMISSION, PROVISIONING)
329 .isInstanceOf(ForbiddenException.class);
333 public void removing_project_permission_fails_if_not_administrator_of_project() {
334 ComponentDto project = db.components().insertPrivateProject();
337 assertThatThrownBy(() -> {
339 .setParam(PARAM_GROUP_NAME, aGroup.getName())
340 .setParam(PARAM_PERMISSION, PROVISIONING)
341 .setParam(PARAM_PROJECT_KEY, project.getKey())
344 .isInstanceOf(ForbiddenException.class);
348 * User is project administrator but not system administrator
351 public void removing_project_permission_is_allowed_to_project_administrators() {
352 ComponentDto project = db.components().insertPrivateProject();
353 db.users().insertProjectPermissionOnGroup(aGroup, CODEVIEWER, project);
354 db.users().insertProjectPermissionOnGroup(aGroup, ISSUE_ADMIN, project);
356 userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
358 .setParam(PARAM_GROUP_NAME, aGroup.getName())
359 .setParam(PARAM_PROJECT_ID, project.uuid())
360 .setParam(PARAM_PERMISSION, ISSUE_ADMIN)
363 assertThat(db.users().selectGroupPermissions(aGroup, project)).containsOnly(CODEVIEWER);
367 public void no_effect_when_removing_any_permission_from_group_AnyOne_on_a_private_project() {
368 ComponentDto project = db.components().insertPrivateProject();
369 permissionService.getAllProjectPermissions()
370 .forEach(perm -> unsafeInsertProjectPermissionOnAnyone(perm, project));
371 userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
373 permissionService.getAllProjectPermissions()
374 .forEach(permission -> {
376 .setParam(PARAM_GROUP_NAME, "anyone")
377 .setParam(PARAM_PROJECT_ID, project.uuid())
378 .setParam(PARAM_PERMISSION, permission)
381 assertThat(db.users().selectAnyonePermissions(project)).contains(permission);
386 public void fail_when_removing_USER_permission_from_group_AnyOne_on_a_public_project() {
387 ComponentDto project = db.components().insertPublicProject();
388 userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
390 assertThatThrownBy(() -> {
392 .setParam(PARAM_GROUP_NAME, "anyone")
393 .setParam(PARAM_PROJECT_ID, project.uuid())
394 .setParam(PARAM_PERMISSION, USER)
397 .isInstanceOf(BadRequestException.class)
398 .hasMessage("Permission user can't be removed from a public component");
402 public void fail_when_removing_CODEVIEWER_permission_from_group_AnyOne_on_a_public_project() {
403 ComponentDto project = db.components().insertPublicProject();
404 userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
406 assertThatThrownBy(() -> {
408 .setParam(PARAM_GROUP_NAME, "anyone")
409 .setParam(PARAM_PROJECT_ID, project.uuid())
410 .setParam(PARAM_PERMISSION, CODEVIEWER)
413 .isInstanceOf(BadRequestException.class)
414 .hasMessage("Permission codeviewer can't be removed from a public component");
418 public void fail_when_removing_USER_permission_from_group_on_a_public_project() {
419 GroupDto group = db.users().insertGroup();
420 ComponentDto project = db.components().insertPublicProject();
421 userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
423 assertThatThrownBy(() -> {
425 .setParam(PARAM_GROUP_NAME, group.getName())
426 .setParam(PARAM_PROJECT_ID, project.uuid())
427 .setParam(PARAM_PERMISSION, USER)
430 .isInstanceOf(BadRequestException.class)
431 .hasMessage("Permission user can't be removed from a public component");
435 public void fail_when_removing_CODEVIEWER_permission_from_group_on_a_public_project() {
436 GroupDto group = db.users().insertGroup();
437 ComponentDto project = db.components().insertPublicProject();
438 userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
440 assertThatThrownBy(() -> {
442 .setParam(PARAM_GROUP_NAME, group.getName())
443 .setParam(PARAM_PROJECT_ID, project.uuid())
444 .setParam(PARAM_PERMISSION, CODEVIEWER)
447 .isInstanceOf(BadRequestException.class)
448 .hasMessage("Permission codeviewer can't be removed from a public component");
452 public void fail_when_using_branch_db_key() {
453 GroupDto group = db.users().insertGroup();
454 ComponentDto project = db.components().insertPublicProject();
455 userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
456 ComponentDto branch = db.components().insertProjectBranch(project);
458 assertThatThrownBy(() -> {
460 .setParam(PARAM_PROJECT_KEY, branch.getKey())
461 .setParam(PARAM_GROUP_NAME, group.getName())
462 .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
465 .isInstanceOf(NotFoundException.class)
466 .hasMessage(format("Project key '%s' not found", branch.getKey()));
470 public void fail_when_using_branch_uuid() {
471 GroupDto group = db.users().insertGroup();
472 ComponentDto project = db.components().insertPublicProject();
473 userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
474 ComponentDto branch = db.components().insertProjectBranch(project);
476 assertThatThrownBy(() -> {
478 .setParam(PARAM_PROJECT_ID, branch.uuid())
479 .setParam(PARAM_GROUP_NAME, group.getName())
480 .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
483 .isInstanceOf(NotFoundException.class)
484 .hasMessage(format("Project id '%s' not found", branch.uuid()));
487 private void unsafeInsertProjectPermissionOnAnyone(String perm, ComponentDto project) {
488 GroupPermissionDto dto = new GroupPermissionDto()
489 .setUuid(Uuids.createFast())
492 .setComponentUuid(project.uuid())
493 .setComponentName(project.name());
494 db.getDbClient().groupPermissionDao().insert(db.getSession(), dto, project, null);