]> source.dussan.org Git - sonarqube.git/blob
3212c34a75aa618d37e91eb2c5b49a17a43d3585
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2023 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
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.
10  *
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.
15  *
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.
19  */
20 package org.sonar.server.permission.ws;
21
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;
39
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;
59
60 public class RemoveGroupActionTest extends BasePermissionWsTest<RemoveGroupAction> {
61
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);
66
67   @Before
68   public void setUp() {
69     aGroup = db.users().insertGroup("sonar-administrators");
70   }
71
72   @Override
73   protected RemoveGroupAction buildWsAction() {
74     return new RemoveGroupAction(db.getDbClient(), userSession, newPermissionUpdater(), newPermissionWsSupport(), wsParameters, permissionService);
75   }
76
77   @Test
78   public void verify_definition() {
79     Action wsDef = wsTester.getDef();
80
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."));
87   }
88
89   @Test
90   public void remove_permission_using_group_name() {
91     db.users().insertPermissionOnGroup(aGroup, ADMINISTER);
92     db.users().insertPermissionOnGroup(aGroup, PROVISION_PROJECTS);
93     loginAsAdmin();
94
95     newRequest()
96       .setParam(PARAM_GROUP_NAME, aGroup.getName())
97       .setParam(PARAM_PERMISSION, PROVISIONING)
98       .execute();
99
100     assertThat(db.users().selectGroupPermissions(aGroup, null)).containsOnly(ADMINISTER.getKey());
101   }
102
103   @Test
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);
109     loginAsAdmin();
110
111     newRequest()
112       .setParam(PARAM_GROUP_NAME, aGroup.getName())
113       .setParam(PARAM_PROJECT_ID, project.uuid())
114       .setParam(PARAM_PERMISSION, ADMIN)
115       .execute();
116
117     assertThat(db.users().selectGroupPermissions(aGroup, null)).containsOnly(ADMINISTER.getKey());
118     assertThat(db.users().selectGroupPermissions(aGroup, project)).containsOnly(ISSUE_ADMIN);
119   }
120
121   @Test
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);
127     loginAsAdmin();
128
129     newRequest()
130       .setParam(PARAM_GROUP_NAME, aGroup.getName())
131       .setParam(PARAM_PROJECT_ID, view.uuid())
132       .setParam(PARAM_PERMISSION, ADMIN)
133       .execute();
134
135     assertThat(db.users().selectGroupPermissions(aGroup, null)).containsOnly(ADMINISTER.getKey());
136     assertThat(db.users().selectGroupPermissions(aGroup, view)).containsOnly(ISSUE_ADMIN);
137   }
138
139   @Test
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);
145     loginAsAdmin();
146
147     newRequest()
148       .setParam(PARAM_GROUP_NAME, aGroup.getName())
149       .setParam(PARAM_PROJECT_KEY, project.getKey())
150       .setParam(PARAM_PERMISSION, ADMIN)
151       .execute();
152
153     assertThat(db.users().selectGroupPermissions(aGroup, null)).containsOnly(ADMINISTER.getKey());
154     assertThat(db.users().selectGroupPermissions(aGroup, project)).containsOnly(ISSUE_ADMIN);
155   }
156
157   @Test
158   public void fail_to_remove_last_admin_permission() {
159     db.users().insertPermissionOnGroup(aGroup, ADMINISTER);
160     db.users().insertPermissionOnGroup(aGroup, PROVISION_PROJECTS);
161     loginAsAdmin();
162
163     assertThatThrownBy(() -> {
164       executeRequest(aGroup, SYSTEM_ADMIN);
165     })
166       .isInstanceOf(BadRequestException.class)
167       .hasMessage("Last group with permission 'admin'. Permission cannot be removed.");
168   }
169
170   @Test
171   public void fail_when_project_does_not_exist() {
172     loginAsAdmin();
173
174     assertThatThrownBy(() -> {
175       newRequest()
176         .setParam(PARAM_GROUP_NAME, aGroup.getName())
177         .setParam(PARAM_PROJECT_ID, "unknown-project-uuid")
178         .setParam(PARAM_PERMISSION, ADMINISTER.getKey())
179         .execute();
180     })
181       .isInstanceOf(NotFoundException.class)
182       .hasMessage("Project id 'unknown-project-uuid' not found");
183   }
184
185   @Test
186   public void fail_when_project_project_permission_without_project() {
187     loginAsAdmin();
188
189     assertThatThrownBy(() -> executeRequest(aGroup, ISSUE_ADMIN))
190       .isInstanceOf(BadRequestException.class)
191       .hasMessage("Invalid global permission 'issueadmin'. Valid values are [admin, gateadmin, profileadmin, provisioning, scan]");
192   }
193
194   @Test
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"));
198
199     failIfComponentIsNotAProjectOrView(file);
200   }
201
202   @Test
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"));
206
207     failIfComponentIsNotAProjectOrView(file);
208   }
209
210   @Test
211   public void fail_when_component_is_a_subview() {
212     ComponentDto project = db.components().insertPrivateProject();
213     ComponentDto file = db.components().insertComponent(newSubPortfolio(project));
214
215     failIfComponentIsNotAProjectOrView(file);
216   }
217
218   private void failIfComponentIsNotAProjectOrView(ComponentDto file) {
219     loginAsAdmin();
220
221     assertThatThrownBy(() -> {
222       newRequest()
223         .setParam(PARAM_GROUP_NAME, aGroup.getName())
224         .setParam(PARAM_PROJECT_ID, file.uuid())
225         .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
226         .execute();
227     })
228       .isInstanceOf(BadRequestException.class)
229       .hasMessage("Component '" + file.getKey() + "' (id: " + file.uuid() + ") must be a project or a view.");
230   }
231
232   @Test
233   public void fail_when_group_name_is_missing() {
234     loginAsAdmin();
235
236     assertThatThrownBy(() -> {
237       newRequest()
238         .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
239         .execute();
240     })
241       .isInstanceOf(IllegalArgumentException.class)
242       .hasMessage("The 'groupName' parameter is missing");
243   }
244
245   @Test
246   public void fail_when_permission_name_and_id_are_missing() {
247     loginAsAdmin();
248
249     assertThatThrownBy(() -> {
250       newRequest()
251         .setParam(PARAM_GROUP_NAME, aGroup.getName())
252         .execute();
253     })
254       .isInstanceOf(IllegalArgumentException.class)
255       .hasMessage("The 'permission' parameter is missing");
256   }
257
258   @Test
259   public void fail_when_project_uuid_and_project_key_are_provided() {
260     ComponentDto project = db.components().insertPrivateProject();
261     loginAsAdmin();
262
263     assertThatThrownBy(() -> {
264       newRequest()
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())
269         .execute();
270     })
271       .isInstanceOf(BadRequestException.class)
272       .hasMessage("Project id or project key can be provided, not both.");
273   }
274
275   private void executeRequest(GroupDto groupDto, String permission) {
276     newRequest()
277       .setParam(PARAM_GROUP_NAME, groupDto.getName())
278       .setParam(PARAM_PERMISSION, permission)
279       .execute();
280   }
281
282   @Test
283   public void removing_global_permission_fails_if_not_system_administrator() {
284     userSession.logIn();
285
286     assertThatThrownBy(() -> {
287       newRequest()
288         .setParam(PARAM_GROUP_NAME, aGroup.getName())
289         .setParam(PARAM_PERMISSION, PROVISIONING)
290         .execute();
291     })
292       .isInstanceOf(ForbiddenException.class);
293   }
294
295   @Test
296   public void removing_project_permission_fails_if_not_administrator_of_project() {
297     ComponentDto project = db.components().insertPrivateProject();
298     userSession.logIn();
299
300     assertThatThrownBy(() -> {
301       newRequest()
302         .setParam(PARAM_GROUP_NAME, aGroup.getName())
303         .setParam(PARAM_PERMISSION, PROVISIONING)
304         .setParam(PARAM_PROJECT_KEY, project.getKey())
305         .execute();
306     })
307       .isInstanceOf(ForbiddenException.class);
308   }
309
310   /**
311    * User is project administrator but not system administrator
312    */
313   @Test
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);
318
319     userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
320     newRequest()
321       .setParam(PARAM_GROUP_NAME, aGroup.getName())
322       .setParam(PARAM_PROJECT_ID, project.uuid())
323       .setParam(PARAM_PERMISSION, ISSUE_ADMIN)
324       .execute();
325
326     assertThat(db.users().selectGroupPermissions(aGroup, project)).containsOnly(CODEVIEWER);
327   }
328
329   @Test
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);
335
336     permissionService.getAllProjectPermissions()
337       .forEach(permission -> {
338         newRequest()
339           .setParam(PARAM_GROUP_NAME, "anyone")
340           .setParam(PARAM_PROJECT_ID, project.uuid())
341           .setParam(PARAM_PERMISSION, permission)
342           .execute();
343
344         assertThat(db.users().selectAnyonePermissions(project)).contains(permission);
345       });
346   }
347
348   @Test
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);
352
353     assertThatThrownBy(() -> {
354       newRequest()
355         .setParam(PARAM_GROUP_NAME, "anyone")
356         .setParam(PARAM_PROJECT_ID, project.uuid())
357         .setParam(PARAM_PERMISSION, USER)
358         .execute();
359     })
360       .isInstanceOf(BadRequestException.class)
361       .hasMessage("Permission user can't be removed from a public component");
362   }
363
364   @Test
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);
368
369     assertThatThrownBy(() -> {
370       newRequest()
371         .setParam(PARAM_GROUP_NAME, "anyone")
372         .setParam(PARAM_PROJECT_ID, project.uuid())
373         .setParam(PARAM_PERMISSION, CODEVIEWER)
374         .execute();
375     })
376       .isInstanceOf(BadRequestException.class)
377       .hasMessage("Permission codeviewer can't be removed from a public component");
378   }
379
380   @Test
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);
385
386     assertThatThrownBy(() -> {
387       newRequest()
388         .setParam(PARAM_GROUP_NAME, group.getName())
389         .setParam(PARAM_PROJECT_ID, project.uuid())
390         .setParam(PARAM_PERMISSION, USER)
391         .execute();
392     })
393       .isInstanceOf(BadRequestException.class)
394       .hasMessage("Permission user can't be removed from a public component");
395   }
396
397   @Test
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);
402
403     assertThatThrownBy(() -> {
404       newRequest()
405         .setParam(PARAM_GROUP_NAME, group.getName())
406         .setParam(PARAM_PROJECT_ID, project.uuid())
407         .setParam(PARAM_PERMISSION, CODEVIEWER)
408         .execute();
409     })
410       .isInstanceOf(BadRequestException.class)
411       .hasMessage("Permission codeviewer can't be removed from a public component");
412   }
413
414   @Test
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);
420
421     assertThatThrownBy(() -> {
422       newRequest()
423         .setParam(PARAM_PROJECT_ID, branch.uuid())
424         .setParam(PARAM_GROUP_NAME, group.getName())
425         .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
426         .execute();
427     })
428       .isInstanceOf(NotFoundException.class)
429       .hasMessage(format("Project id '%s' not found", branch.uuid()));
430   }
431
432   private void unsafeInsertProjectPermissionOnAnyone(String perm, ComponentDto project) {
433     GroupPermissionDto dto = new GroupPermissionDto()
434       .setUuid(Uuids.createFast())
435       .setGroupUuid(null)
436       .setRole(perm)
437       .setComponentUuid(project.uuid())
438       .setComponentName(project.name());
439     db.getDbClient().groupPermissionDao().insert(db.getSession(), dto, project, null);
440     db.commit();
441   }
442 }