]> source.dussan.org Git - sonarqube.git/blob
8ffaf36bcfd66dd47e7b9130d1e04ec4ba24b666
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2022 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.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;
61
62 public class RemoveGroupActionTest extends BasePermissionWsTest<RemoveGroupAction> {
63
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);
68
69   @Before
70   public void setUp() {
71     aGroup = db.users().insertGroup("sonar-administrators");
72   }
73
74   @Override
75   protected RemoveGroupAction buildWsAction() {
76     return new RemoveGroupAction(db.getDbClient(), userSession, newPermissionUpdater(), newPermissionWsSupport(), wsParameters, permissionService);
77   }
78
79   @Test
80   public void verify_definition() {
81     Action wsDef = wsTester.getDef();
82
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."));
88   }
89
90   @Test
91   public void remove_permission_using_group_name() {
92     db.users().insertPermissionOnGroup(aGroup, ADMINISTER);
93     db.users().insertPermissionOnGroup(aGroup, PROVISION_PROJECTS);
94     loginAsAdmin();
95
96     newRequest()
97       .setParam(PARAM_GROUP_NAME, aGroup.getName())
98       .setParam(PARAM_PERMISSION, PROVISIONING)
99       .execute();
100
101     assertThat(db.users().selectGroupPermissions(aGroup, null)).containsOnly(ADMINISTER.getKey());
102   }
103
104   @Test
105   public void remove_permission_using_group_id() {
106     db.users().insertPermissionOnGroup(aGroup, ADMINISTER);
107     db.users().insertPermissionOnGroup(aGroup, PROVISION_PROJECTS);
108     loginAsAdmin();
109
110     newRequest()
111       .setParam(PARAM_GROUP_ID, aGroup.getUuid())
112       .setParam(PARAM_PERMISSION, PROVISION_PROJECTS.getKey())
113       .execute();
114
115     assertThat(db.users().selectGroupPermissions(aGroup, null)).containsOnly(ADMINISTER.getKey());
116   }
117
118   @Test
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);
124     loginAsAdmin();
125
126     newRequest()
127       .setParam(PARAM_GROUP_NAME, aGroup.getName())
128       .setParam(PARAM_PROJECT_ID, project.uuid())
129       .setParam(PARAM_PERMISSION, ADMIN)
130       .execute();
131
132     assertThat(db.users().selectGroupPermissions(aGroup, null)).containsOnly(ADMINISTER.getKey());
133     assertThat(db.users().selectGroupPermissions(aGroup, project)).containsOnly(ISSUE_ADMIN);
134   }
135
136   @Test
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);
142     loginAsAdmin();
143
144     newRequest()
145       .setParam(PARAM_GROUP_NAME, aGroup.getName())
146       .setParam(PARAM_PROJECT_ID, view.uuid())
147       .setParam(PARAM_PERMISSION, ADMIN)
148       .execute();
149
150     assertThat(db.users().selectGroupPermissions(aGroup, null)).containsOnly(ADMINISTER.getKey());
151     assertThat(db.users().selectGroupPermissions(aGroup, view)).containsOnly(ISSUE_ADMIN);
152   }
153
154   @Test
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);
160     loginAsAdmin();
161
162     newRequest()
163       .setParam(PARAM_GROUP_NAME, aGroup.getName())
164       .setParam(PARAM_PROJECT_KEY, project.getKey())
165       .setParam(PARAM_PERMISSION, ADMIN)
166       .execute();
167
168     assertThat(db.users().selectGroupPermissions(aGroup, null)).containsOnly(ADMINISTER.getKey());
169     assertThat(db.users().selectGroupPermissions(aGroup, project)).containsOnly(ISSUE_ADMIN);
170   }
171
172   @Test
173   public void fail_to_remove_last_admin_permission() {
174     db.users().insertPermissionOnGroup(aGroup, ADMINISTER);
175     db.users().insertPermissionOnGroup(aGroup, PROVISION_PROJECTS);
176     loginAsAdmin();
177
178     assertThatThrownBy(() ->  {
179       executeRequest(aGroup, SYSTEM_ADMIN);
180     })
181       .isInstanceOf(BadRequestException.class)
182       .hasMessage("Last group with permission 'admin'. Permission cannot be removed.");
183   }
184
185   @Test
186   public void fail_when_project_does_not_exist() {
187     loginAsAdmin();
188
189     assertThatThrownBy(() ->  {
190       newRequest()
191         .setParam(PARAM_GROUP_NAME, aGroup.getName())
192         .setParam(PARAM_PROJECT_ID, "unknown-project-uuid")
193         .setParam(PARAM_PERMISSION, ADMINISTER.getKey())
194         .execute();
195     })
196       .isInstanceOf(NotFoundException.class)
197       .hasMessage("Project id 'unknown-project-uuid' not found");
198   }
199
200   @Test
201   public void fail_when_project_project_permission_without_project() {
202     loginAsAdmin();
203
204     assertThatThrownBy(() -> executeRequest(aGroup, ISSUE_ADMIN))
205       .isInstanceOf(BadRequestException.class)
206       .hasMessage("Invalid global permission 'issueadmin'. Valid values are [admin, gateadmin, profileadmin, provisioning, scan]");
207   }
208
209   @Test
210   public void fail_when_component_is_a_module() {
211     ComponentDto project = db.components().insertPrivateProject();
212     ComponentDto module = db.components().insertComponent(newModuleDto(project));
213
214     failIfComponentIsNotAProjectOrView(module);
215   }
216
217   @Test
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"));
221
222     failIfComponentIsNotAProjectOrView(file);
223   }
224
225   @Test
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"));
229
230     failIfComponentIsNotAProjectOrView(file);
231   }
232
233   @Test
234   public void fail_when_component_is_a_subview() {
235     ComponentDto project = db.components().insertPrivateProject();
236     ComponentDto file = db.components().insertComponent(newSubPortfolio(project));
237
238     failIfComponentIsNotAProjectOrView(file);
239   }
240
241   private void failIfComponentIsNotAProjectOrView(ComponentDto file) {
242     loginAsAdmin();
243
244     assertThatThrownBy(() -> {
245       newRequest()
246         .setParam(PARAM_GROUP_NAME, aGroup.getName())
247         .setParam(PARAM_PROJECT_ID, file.uuid())
248         .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
249         .execute();
250     })
251       .isInstanceOf(BadRequestException.class)
252       .hasMessage("Component '" + file.getKey() + "' (id: " + file.uuid() + ") must be a project or a view.");
253   }
254
255   @Test
256   public void fail_when_group_name_is_missing() {
257     loginAsAdmin();
258
259     assertThatThrownBy(() -> {
260       newRequest()
261         .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
262         .execute();
263     })
264       .isInstanceOf(BadRequestException.class)
265       .hasMessage("Group name or group id must be provided");
266   }
267
268   @Test
269   public void fail_when_permission_name_and_id_are_missing() {
270     loginAsAdmin();
271
272     assertThatThrownBy(() -> {
273       newRequest()
274         .setParam(PARAM_GROUP_NAME, aGroup.getName())
275         .execute();
276     })
277       .isInstanceOf(IllegalArgumentException.class)
278       .hasMessage("The 'permission' parameter is missing");
279   }
280
281   @Test
282   public void fail_when_group_id_does_not_exist() {
283     loginAsAdmin();
284
285     assertThatThrownBy(() -> {
286       newRequest()
287         .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
288         .setParam(PARAM_GROUP_ID, "999999")
289         .execute();
290     })
291       .isInstanceOf(NotFoundException.class)
292       .hasMessage("No group with id '999999'");
293   }
294
295   @Test
296   public void fail_when_project_uuid_and_project_key_are_provided() {
297     ComponentDto project = db.components().insertPrivateProject();
298     loginAsAdmin();
299
300     assertThatThrownBy(() -> {
301       newRequest()
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())
306         .execute();
307     })
308       .isInstanceOf(BadRequestException.class)
309       .hasMessage("Project id or project key can be provided, not both.");
310   }
311
312   private void executeRequest(GroupDto groupDto, String permission) {
313     newRequest()
314       .setParam(PARAM_GROUP_NAME, groupDto.getName())
315       .setParam(PARAM_PERMISSION, permission)
316       .execute();
317   }
318
319   @Test
320   public void removing_global_permission_fails_if_not_system_administrator() {
321     userSession.logIn();
322
323     assertThatThrownBy(() -> {
324       newRequest()
325         .setParam(PARAM_GROUP_NAME, aGroup.getName())
326         .setParam(PARAM_PERMISSION, PROVISIONING)
327         .execute();
328     })
329       .isInstanceOf(ForbiddenException.class);
330   }
331
332   @Test
333   public void removing_project_permission_fails_if_not_administrator_of_project() {
334     ComponentDto project = db.components().insertPrivateProject();
335     userSession.logIn();
336
337     assertThatThrownBy(() -> {
338       newRequest()
339         .setParam(PARAM_GROUP_NAME, aGroup.getName())
340         .setParam(PARAM_PERMISSION, PROVISIONING)
341         .setParam(PARAM_PROJECT_KEY, project.getKey())
342         .execute();
343     })
344       .isInstanceOf(ForbiddenException.class);
345   }
346
347   /**
348    * User is project administrator but not system administrator
349    */
350   @Test
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);
355
356     userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
357     newRequest()
358       .setParam(PARAM_GROUP_NAME, aGroup.getName())
359       .setParam(PARAM_PROJECT_ID, project.uuid())
360       .setParam(PARAM_PERMISSION, ISSUE_ADMIN)
361       .execute();
362
363     assertThat(db.users().selectGroupPermissions(aGroup, project)).containsOnly(CODEVIEWER);
364   }
365
366   @Test
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);
372
373     permissionService.getAllProjectPermissions()
374       .forEach(permission -> {
375         newRequest()
376           .setParam(PARAM_GROUP_NAME, "anyone")
377           .setParam(PARAM_PROJECT_ID, project.uuid())
378           .setParam(PARAM_PERMISSION, permission)
379           .execute();
380
381         assertThat(db.users().selectAnyonePermissions(project)).contains(permission);
382       });
383   }
384
385   @Test
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);
389
390     assertThatThrownBy(() -> {
391       newRequest()
392         .setParam(PARAM_GROUP_NAME, "anyone")
393         .setParam(PARAM_PROJECT_ID, project.uuid())
394         .setParam(PARAM_PERMISSION, USER)
395         .execute();
396     })
397       .isInstanceOf(BadRequestException.class)
398       .hasMessage("Permission user can't be removed from a public component");
399   }
400
401   @Test
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);
405
406     assertThatThrownBy(() -> {
407       newRequest()
408         .setParam(PARAM_GROUP_NAME, "anyone")
409         .setParam(PARAM_PROJECT_ID, project.uuid())
410         .setParam(PARAM_PERMISSION, CODEVIEWER)
411         .execute();
412     })
413       .isInstanceOf(BadRequestException.class)
414       .hasMessage("Permission codeviewer can't be removed from a public component");
415   }
416
417   @Test
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);
422
423     assertThatThrownBy(() -> {
424       newRequest()
425         .setParam(PARAM_GROUP_NAME, group.getName())
426         .setParam(PARAM_PROJECT_ID, project.uuid())
427         .setParam(PARAM_PERMISSION, USER)
428         .execute();
429     })
430       .isInstanceOf(BadRequestException.class)
431       .hasMessage("Permission user can't be removed from a public component");
432   }
433
434   @Test
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);
439
440     assertThatThrownBy(() -> {
441       newRequest()
442         .setParam(PARAM_GROUP_NAME, group.getName())
443         .setParam(PARAM_PROJECT_ID, project.uuid())
444         .setParam(PARAM_PERMISSION, CODEVIEWER)
445         .execute();
446     })
447       .isInstanceOf(BadRequestException.class)
448       .hasMessage("Permission codeviewer can't be removed from a public component");
449   }
450
451   @Test
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);
457
458     assertThatThrownBy(() -> {
459       newRequest()
460         .setParam(PARAM_PROJECT_KEY, branch.getKey())
461         .setParam(PARAM_GROUP_NAME, group.getName())
462         .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
463         .execute();
464     })
465       .isInstanceOf(NotFoundException.class)
466       .hasMessage(format("Project key '%s' not found", branch.getKey()));
467   }
468
469   @Test
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);
475
476     assertThatThrownBy(() -> {
477       newRequest()
478         .setParam(PARAM_PROJECT_ID, branch.uuid())
479         .setParam(PARAM_GROUP_NAME, group.getName())
480         .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
481         .execute();
482     })
483       .isInstanceOf(NotFoundException.class)
484       .hasMessage(format("Project id '%s' not found", branch.uuid()));
485   }
486
487   private void unsafeInsertProjectPermissionOnAnyone(String perm, ComponentDto project) {
488     GroupPermissionDto dto = new GroupPermissionDto()
489       .setUuid(Uuids.createFast())
490       .setGroupUuid(null)
491       .setRole(perm)
492       .setComponentUuid(project.uuid())
493       .setComponentName(project.name());
494     db.getDbClient().groupPermissionDao().insert(db.getSession(), dto, project, null);
495     db.commit();
496   }
497 }