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.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.newSubPortfolio;
53 import static org.sonar.db.permission.GlobalPermission.ADMINISTER;
54 import static org.sonar.db.permission.GlobalPermission.PROVISION_PROJECTS;
55 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_NAME;
56 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PERMISSION;
57 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_ID;
58 import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_KEY;
60 public class RemoveGroupActionTest extends BasePermissionWsTest<RemoveGroupAction> {
62 private GroupDto aGroup;
63 private ResourceTypes resourceTypes = new ResourceTypesRule().setRootQualifiers(Qualifiers.PROJECT);
64 private PermissionService permissionService = new PermissionServiceImpl(resourceTypes);
65 private WsParameters wsParameters = new WsParameters(permissionService);
69 aGroup = db.users().insertGroup("sonar-administrators");
73 protected RemoveGroupAction buildWsAction() {
74 return new RemoveGroupAction(db.getDbClient(), userSession, newPermissionUpdater(), newPermissionWsSupport(), wsParameters, permissionService);
78 public void verify_definition() {
79 Action wsDef = wsTester.getDef();
81 assertThat(wsDef.isInternal()).isFalse();
82 assertThat(wsDef.since()).isEqualTo("5.2");
83 assertThat(wsDef.isPost()).isTrue();
84 assertThat(wsDef.changelog()).extracting(Change::getVersion, Change::getDescription).containsOnly(
85 tuple("10.0", "Parameter 'groupId' is removed. Use 'groupName' instead."),
86 tuple("8.4", "Parameter 'groupId' is deprecated. Format changes from integer to string. Use 'groupName' instead."));
90 public void remove_permission_using_group_name() {
91 db.users().insertPermissionOnGroup(aGroup, ADMINISTER);
92 db.users().insertPermissionOnGroup(aGroup, PROVISION_PROJECTS);
96 .setParam(PARAM_GROUP_NAME, aGroup.getName())
97 .setParam(PARAM_PERMISSION, PROVISIONING)
100 assertThat(db.users().selectGroupPermissions(aGroup, null)).containsOnly(ADMINISTER.getKey());
104 public void remove_project_permission() {
105 ComponentDto project = db.components().insertPrivateProject();
106 db.users().insertPermissionOnGroup(aGroup, ADMINISTER);
107 db.users().insertProjectPermissionOnGroup(aGroup, ADMIN, project);
108 db.users().insertProjectPermissionOnGroup(aGroup, ISSUE_ADMIN, project);
112 .setParam(PARAM_GROUP_NAME, aGroup.getName())
113 .setParam(PARAM_PROJECT_ID, project.uuid())
114 .setParam(PARAM_PERMISSION, ADMIN)
117 assertThat(db.users().selectGroupPermissions(aGroup, null)).containsOnly(ADMINISTER.getKey());
118 assertThat(db.users().selectGroupPermissions(aGroup, project)).containsOnly(ISSUE_ADMIN);
122 public void remove_with_view_uuid() {
123 ComponentDto view = db.components().insertPrivatePortfolio();
124 db.users().insertPermissionOnGroup(aGroup, ADMINISTER);
125 db.users().insertProjectPermissionOnGroup(aGroup, ADMIN, view);
126 db.users().insertProjectPermissionOnGroup(aGroup, ISSUE_ADMIN, view);
130 .setParam(PARAM_GROUP_NAME, aGroup.getName())
131 .setParam(PARAM_PROJECT_ID, view.uuid())
132 .setParam(PARAM_PERMISSION, ADMIN)
135 assertThat(db.users().selectGroupPermissions(aGroup, null)).containsOnly(ADMINISTER.getKey());
136 assertThat(db.users().selectGroupPermissions(aGroup, view)).containsOnly(ISSUE_ADMIN);
140 public void remove_with_project_key() {
141 ComponentDto project = db.components().insertPrivateProject();
142 db.users().insertPermissionOnGroup(aGroup, ADMINISTER);
143 db.users().insertProjectPermissionOnGroup(aGroup, ADMIN, project);
144 db.users().insertProjectPermissionOnGroup(aGroup, ISSUE_ADMIN, project);
148 .setParam(PARAM_GROUP_NAME, aGroup.getName())
149 .setParam(PARAM_PROJECT_KEY, project.getKey())
150 .setParam(PARAM_PERMISSION, ADMIN)
153 assertThat(db.users().selectGroupPermissions(aGroup, null)).containsOnly(ADMINISTER.getKey());
154 assertThat(db.users().selectGroupPermissions(aGroup, project)).containsOnly(ISSUE_ADMIN);
158 public void fail_to_remove_last_admin_permission() {
159 db.users().insertPermissionOnGroup(aGroup, ADMINISTER);
160 db.users().insertPermissionOnGroup(aGroup, PROVISION_PROJECTS);
163 assertThatThrownBy(() -> {
164 executeRequest(aGroup, SYSTEM_ADMIN);
166 .isInstanceOf(BadRequestException.class)
167 .hasMessage("Last group with permission 'admin'. Permission cannot be removed.");
171 public void fail_when_project_does_not_exist() {
174 assertThatThrownBy(() -> {
176 .setParam(PARAM_GROUP_NAME, aGroup.getName())
177 .setParam(PARAM_PROJECT_ID, "unknown-project-uuid")
178 .setParam(PARAM_PERMISSION, ADMINISTER.getKey())
181 .isInstanceOf(NotFoundException.class)
182 .hasMessage("Project id 'unknown-project-uuid' not found");
186 public void fail_when_project_project_permission_without_project() {
189 assertThatThrownBy(() -> executeRequest(aGroup, ISSUE_ADMIN))
190 .isInstanceOf(BadRequestException.class)
191 .hasMessage("Invalid global permission 'issueadmin'. Valid values are [admin, gateadmin, profileadmin, provisioning, scan]");
195 public void fail_when_component_is_a_directory() {
196 ComponentDto project = db.components().insertPrivateProject();
197 ComponentDto file = db.components().insertComponent(newDirectory(project, "A/B"));
199 failIfComponentIsNotAProjectOrView(file);
203 public void fail_when_component_is_a_file() {
204 ComponentDto project = db.components().insertPrivateProject();
205 ComponentDto file = db.components().insertComponent(newFileDto(project, null, "file-uuid"));
207 failIfComponentIsNotAProjectOrView(file);
211 public void fail_when_component_is_a_subview() {
212 ComponentDto project = db.components().insertPrivateProject();
213 ComponentDto file = db.components().insertComponent(newSubPortfolio(project));
215 failIfComponentIsNotAProjectOrView(file);
218 private void failIfComponentIsNotAProjectOrView(ComponentDto file) {
221 assertThatThrownBy(() -> {
223 .setParam(PARAM_GROUP_NAME, aGroup.getName())
224 .setParam(PARAM_PROJECT_ID, file.uuid())
225 .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
228 .isInstanceOf(BadRequestException.class)
229 .hasMessage("Component '" + file.getKey() + "' (id: " + file.uuid() + ") must be a project or a view.");
233 public void fail_when_group_name_is_missing() {
236 assertThatThrownBy(() -> {
238 .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
241 .isInstanceOf(IllegalArgumentException.class)
242 .hasMessage("The 'groupName' parameter is missing");
246 public void fail_when_permission_name_and_id_are_missing() {
249 assertThatThrownBy(() -> {
251 .setParam(PARAM_GROUP_NAME, aGroup.getName())
254 .isInstanceOf(IllegalArgumentException.class)
255 .hasMessage("The 'permission' parameter is missing");
259 public void fail_when_project_uuid_and_project_key_are_provided() {
260 ComponentDto project = db.components().insertPrivateProject();
263 assertThatThrownBy(() -> {
265 .setParam(PARAM_GROUP_NAME, aGroup.getName())
266 .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
267 .setParam(PARAM_PROJECT_ID, project.uuid())
268 .setParam(PARAM_PROJECT_KEY, project.getKey())
271 .isInstanceOf(BadRequestException.class)
272 .hasMessage("Project id or project key can be provided, not both.");
275 private void executeRequest(GroupDto groupDto, String permission) {
277 .setParam(PARAM_GROUP_NAME, groupDto.getName())
278 .setParam(PARAM_PERMISSION, permission)
283 public void removing_global_permission_fails_if_not_system_administrator() {
286 assertThatThrownBy(() -> {
288 .setParam(PARAM_GROUP_NAME, aGroup.getName())
289 .setParam(PARAM_PERMISSION, PROVISIONING)
292 .isInstanceOf(ForbiddenException.class);
296 public void removing_project_permission_fails_if_not_administrator_of_project() {
297 ComponentDto project = db.components().insertPrivateProject();
300 assertThatThrownBy(() -> {
302 .setParam(PARAM_GROUP_NAME, aGroup.getName())
303 .setParam(PARAM_PERMISSION, PROVISIONING)
304 .setParam(PARAM_PROJECT_KEY, project.getKey())
307 .isInstanceOf(ForbiddenException.class);
311 * User is project administrator but not system administrator
314 public void removing_project_permission_is_allowed_to_project_administrators() {
315 ComponentDto project = db.components().insertPrivateProject();
316 db.users().insertProjectPermissionOnGroup(aGroup, CODEVIEWER, project);
317 db.users().insertProjectPermissionOnGroup(aGroup, ISSUE_ADMIN, project);
319 userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
321 .setParam(PARAM_GROUP_NAME, aGroup.getName())
322 .setParam(PARAM_PROJECT_ID, project.uuid())
323 .setParam(PARAM_PERMISSION, ISSUE_ADMIN)
326 assertThat(db.users().selectGroupPermissions(aGroup, project)).containsOnly(CODEVIEWER);
330 public void no_effect_when_removing_any_permission_from_group_AnyOne_on_a_private_project() {
331 ComponentDto project = db.components().insertPrivateProject();
332 permissionService.getAllProjectPermissions()
333 .forEach(perm -> unsafeInsertProjectPermissionOnAnyone(perm, project));
334 userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
336 permissionService.getAllProjectPermissions()
337 .forEach(permission -> {
339 .setParam(PARAM_GROUP_NAME, "anyone")
340 .setParam(PARAM_PROJECT_ID, project.uuid())
341 .setParam(PARAM_PERMISSION, permission)
344 assertThat(db.users().selectAnyonePermissions(project)).contains(permission);
349 public void fail_when_removing_USER_permission_from_group_AnyOne_on_a_public_project() {
350 ComponentDto project = db.components().insertPublicProject();
351 userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
353 assertThatThrownBy(() -> {
355 .setParam(PARAM_GROUP_NAME, "anyone")
356 .setParam(PARAM_PROJECT_ID, project.uuid())
357 .setParam(PARAM_PERMISSION, USER)
360 .isInstanceOf(BadRequestException.class)
361 .hasMessage("Permission user can't be removed from a public component");
365 public void fail_when_removing_CODEVIEWER_permission_from_group_AnyOne_on_a_public_project() {
366 ComponentDto project = db.components().insertPublicProject();
367 userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
369 assertThatThrownBy(() -> {
371 .setParam(PARAM_GROUP_NAME, "anyone")
372 .setParam(PARAM_PROJECT_ID, project.uuid())
373 .setParam(PARAM_PERMISSION, CODEVIEWER)
376 .isInstanceOf(BadRequestException.class)
377 .hasMessage("Permission codeviewer can't be removed from a public component");
381 public void fail_when_removing_USER_permission_from_group_on_a_public_project() {
382 GroupDto group = db.users().insertGroup();
383 ComponentDto project = db.components().insertPublicProject();
384 userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
386 assertThatThrownBy(() -> {
388 .setParam(PARAM_GROUP_NAME, group.getName())
389 .setParam(PARAM_PROJECT_ID, project.uuid())
390 .setParam(PARAM_PERMISSION, USER)
393 .isInstanceOf(BadRequestException.class)
394 .hasMessage("Permission user can't be removed from a public component");
398 public void fail_when_removing_CODEVIEWER_permission_from_group_on_a_public_project() {
399 GroupDto group = db.users().insertGroup();
400 ComponentDto project = db.components().insertPublicProject();
401 userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
403 assertThatThrownBy(() -> {
405 .setParam(PARAM_GROUP_NAME, group.getName())
406 .setParam(PARAM_PROJECT_ID, project.uuid())
407 .setParam(PARAM_PERMISSION, CODEVIEWER)
410 .isInstanceOf(BadRequestException.class)
411 .hasMessage("Permission codeviewer can't be removed from a public component");
415 public void fail_when_using_branch_uuid() {
416 GroupDto group = db.users().insertGroup();
417 ComponentDto project = db.components().insertPublicProject();
418 userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
419 ComponentDto branch = db.components().insertProjectBranch(project);
421 assertThatThrownBy(() -> {
423 .setParam(PARAM_PROJECT_ID, branch.uuid())
424 .setParam(PARAM_GROUP_NAME, group.getName())
425 .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
428 .isInstanceOf(NotFoundException.class)
429 .hasMessage(format("Project id '%s' not found", branch.uuid()));
432 private void unsafeInsertProjectPermissionOnAnyone(String perm, ComponentDto project) {
433 GroupPermissionDto dto = new GroupPermissionDto()
434 .setUuid(Uuids.createFast())
437 .setComponentUuid(project.uuid())
438 .setComponentName(project.name());
439 db.getDbClient().groupPermissionDao().insert(db.getSession(), dto, project, null);