]> source.dussan.org Git - sonarqube.git/blob
5a4faeff39ab5e151c517e420b0c568e1b655d3c
[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;
21
22 import org.apache.commons.lang.StringUtils;
23 import org.junit.Before;
24 import org.junit.Rule;
25 import org.junit.Test;
26 import org.sonar.api.resources.Qualifiers;
27 import org.sonar.api.resources.ResourceTypes;
28 import org.sonar.api.utils.System2;
29 import org.sonar.api.web.UserRole;
30 import org.sonar.core.permission.GlobalPermissions;
31 import org.sonar.core.util.SequenceUuidFactory;
32 import org.sonar.core.util.Uuids;
33 import org.sonar.db.DbTester;
34 import org.sonar.db.component.ComponentDto;
35 import org.sonar.db.component.ResourceTypesRule;
36 import org.sonar.db.permission.GlobalPermission;
37 import org.sonar.db.permission.GroupPermissionDto;
38 import org.sonar.db.user.GroupDto;
39 import org.sonar.db.user.UserDto;
40 import org.sonar.server.exceptions.BadRequestException;
41
42 import static org.assertj.core.api.Assertions.assertThat;
43 import static org.assertj.core.api.Assertions.assertThatThrownBy;
44 import static org.assertj.core.api.Assertions.fail;
45 import static org.sonar.db.permission.GlobalPermission.ADMINISTER;
46 import static org.sonar.db.permission.GlobalPermission.ADMINISTER_QUALITY_GATES;
47 import static org.sonar.db.permission.GlobalPermission.PROVISION_PROJECTS;
48
49 public class GroupPermissionChangerTest {
50
51   @Rule
52   public DbTester db = DbTester.create(System2.INSTANCE);
53
54   private final ResourceTypes resourceTypes = new ResourceTypesRule().setRootQualifiers(Qualifiers.PROJECT);
55   private final PermissionService permissionService = new PermissionServiceImpl(resourceTypes);
56   private final GroupPermissionChanger underTest = new GroupPermissionChanger(db.getDbClient(), new SequenceUuidFactory());
57   private GroupDto group;
58   private ComponentDto privateProject;
59   private ComponentDto publicProject;
60
61   @Before
62   public void setUp() {
63     group = db.users().insertGroup("a-group");
64     privateProject = db.components().insertPrivateProject();
65     publicProject = db.components().insertPublicProject();
66   }
67
68   @Test
69   public void apply_adds_global_permission_to_group() {
70     GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
71
72     apply(new GroupPermissionChange(PermissionChange.Operation.ADD, GlobalPermissions.QUALITY_GATE_ADMIN, null, groupUuid, permissionService));
73
74     assertThat(db.users().selectGroupPermissions(group, null)).containsOnly(GlobalPermissions.QUALITY_GATE_ADMIN);
75   }
76
77   @Test
78   public void apply_adds_global_permission_to_group_AnyOne() {
79     GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.forAnyone();
80
81     apply(new GroupPermissionChange(PermissionChange.Operation.ADD, GlobalPermissions.QUALITY_GATE_ADMIN, null, groupUuid, permissionService));
82
83     assertThat(db.users().selectAnyonePermissions(null)).containsOnly(GlobalPermissions.QUALITY_GATE_ADMIN);
84   }
85
86   @Test
87   public void apply_fails_with_BadRequestException_when_adding_any_permission_to_group_AnyOne_on_private_project() {
88     GroupUuidOrAnyone anyOneGroup = GroupUuidOrAnyone.forAnyone();
89     permissionService.getAllProjectPermissions()
90       .forEach(perm -> {
91         GroupPermissionChange change = new GroupPermissionChange(PermissionChange.Operation.ADD, perm, privateProject, anyOneGroup, permissionService);
92         try {
93           apply(change);
94           fail("a BadRequestException should have been thrown");
95         } catch (BadRequestException e) {
96           assertThat(e).hasMessage("No permission can be granted to Anyone on a private component");
97         }
98       });
99   }
100
101   @Test
102   public void apply_has_no_effect_when_removing_any_permission_to_group_AnyOne_on_private_project() {
103     permissionService.getAllProjectPermissions()
104       .forEach(this::unsafeInsertProjectPermissionOnAnyone);
105
106     GroupUuidOrAnyone anyOneGroup = GroupUuidOrAnyone.forAnyone();
107     permissionService.getAllProjectPermissions()
108       .forEach(perm -> {
109         apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, perm, privateProject, anyOneGroup, permissionService));
110
111         assertThat(db.users().selectAnyonePermissions(privateProject)).contains(perm);
112       });
113   }
114
115   @Test
116   public void apply_adds_permission_USER_to_group_on_private_project() {
117     applyAddsPermissionToGroupOnPrivateProject(UserRole.USER);
118   }
119
120   @Test
121   public void apply_adds_permission_CODEVIEWER_to_group_on_private_project() {
122     applyAddsPermissionToGroupOnPrivateProject(UserRole.CODEVIEWER);
123   }
124
125   @Test
126   public void apply_adds_permission_ADMIN_to_group_on_private_project() {
127     applyAddsPermissionToGroupOnPrivateProject(UserRole.ADMIN);
128   }
129
130   @Test
131   public void apply_adds_permission_ISSUE_ADMIN_to_group_on_private_project() {
132     applyAddsPermissionToGroupOnPrivateProject(UserRole.ISSUE_ADMIN);
133   }
134
135   @Test
136   public void apply_adds_permission_SCAN_EXECUTION_to_group_on_private_project() {
137     applyAddsPermissionToGroupOnPrivateProject(GlobalPermissions.SCAN_EXECUTION);
138   }
139
140   private void applyAddsPermissionToGroupOnPrivateProject(String permission) {
141     GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
142
143     apply(new GroupPermissionChange(PermissionChange.Operation.ADD, permission, privateProject, groupUuid, permissionService));
144
145     assertThat(db.users().selectGroupPermissions(group, null)).isEmpty();
146     assertThat(db.users().selectGroupPermissions(group, privateProject)).containsOnly(permission);
147   }
148
149   @Test
150   public void apply_removes_permission_USER_from_group_on_private_project() {
151     applyRemovesPermissionFromGroupOnPrivateProject(UserRole.USER);
152   }
153
154   @Test
155   public void apply_removes_permission_CODEVIEWER_from_group_on_private_project() {
156     applyRemovesPermissionFromGroupOnPrivateProject(UserRole.CODEVIEWER);
157   }
158
159   @Test
160   public void apply_removes_permission_ADMIN_from_on_private_project() {
161     applyRemovesPermissionFromGroupOnPrivateProject(UserRole.ADMIN);
162   }
163
164   @Test
165   public void apply_removes_permission_ISSUE_ADMIN_from_on_private_project() {
166     applyRemovesPermissionFromGroupOnPrivateProject(UserRole.ISSUE_ADMIN);
167   }
168
169   @Test
170   public void apply_removes_permission_SCAN_EXECUTION_from_on_private_project() {
171     applyRemovesPermissionFromGroupOnPrivateProject(GlobalPermissions.SCAN_EXECUTION);
172   }
173
174   private void applyRemovesPermissionFromGroupOnPrivateProject(String permission) {
175     GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
176     db.users().insertProjectPermissionOnGroup(group, permission, privateProject);
177
178     apply(new GroupPermissionChange(PermissionChange.Operation.ADD, permission, privateProject, groupUuid, permissionService));
179
180     assertThat(db.users().selectGroupPermissions(group, privateProject)).containsOnly(permission);
181   }
182
183   @Test
184   public void apply_has_no_effect_when_adding_USER_permission_to_group_AnyOne_on_a_public_project() {
185     GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.forAnyone();
186
187     apply(new GroupPermissionChange(PermissionChange.Operation.ADD, UserRole.USER, publicProject, groupUuid, permissionService));
188
189     assertThat(db.users().selectAnyonePermissions(publicProject)).isEmpty();
190   }
191
192   @Test
193   public void apply_has_no_effect_when_adding_CODEVIEWER_permission_to_group_AnyOne_on_a_public_project() {
194     GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.forAnyone();
195
196     apply(new GroupPermissionChange(PermissionChange.Operation.ADD, UserRole.CODEVIEWER, publicProject, groupUuid, permissionService));
197
198     assertThat(db.users().selectAnyonePermissions(publicProject)).isEmpty();
199   }
200
201   @Test
202   public void apply_fails_with_BadRequestException_when_adding_permission_ADMIN_to_group_AnyOne_on_a_public_project() {
203     GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.forAnyone();
204
205     assertThatThrownBy(() -> apply(new GroupPermissionChange(PermissionChange.Operation.ADD, UserRole.ADMIN, publicProject, groupUuid, permissionService)))
206       .isInstanceOf(BadRequestException.class)
207       .hasMessage("It is not possible to add the 'admin' permission to group 'Anyone'.");
208   }
209
210   @Test
211   public void apply_adds_permission_ISSUE_ADMIN_to_group_AnyOne_on_a_public_project() {
212     GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.forAnyone();
213
214     apply(new GroupPermissionChange(PermissionChange.Operation.ADD, UserRole.ISSUE_ADMIN, publicProject, groupUuid, permissionService));
215
216     assertThat(db.users().selectAnyonePermissions(publicProject)).containsOnly(UserRole.ISSUE_ADMIN);
217   }
218
219   @Test
220   public void apply_adds_permission_SCAN_EXECUTION_to_group_AnyOne_on_a_public_project() {
221     GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.forAnyone();
222
223     apply(new GroupPermissionChange(PermissionChange.Operation.ADD, GlobalPermissions.SCAN_EXECUTION, publicProject, groupUuid, permissionService));
224
225     assertThat(db.users().selectAnyonePermissions(publicProject)).containsOnly(GlobalPermissions.SCAN_EXECUTION);
226   }
227
228   @Test
229   public void apply_fails_with_BadRequestException_when_removing_USER_permission_from_group_AnyOne_on_a_public_project() {
230     GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.forAnyone();
231
232     assertThatThrownBy(() -> apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, UserRole.USER, publicProject, groupUuid, permissionService)))
233       .isInstanceOf(BadRequestException.class)
234       .hasMessage("Permission user can't be removed from a public component");
235   }
236
237   @Test
238   public void apply_fails_with_BadRequestException_when_removing_CODEVIEWER_permission_from_group_AnyOne_on_a_public_project() {
239     GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.forAnyone();
240
241     assertThatThrownBy(() -> apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, UserRole.CODEVIEWER, publicProject, groupUuid, permissionService)))
242       .isInstanceOf(BadRequestException.class)
243       .hasMessage("Permission codeviewer can't be removed from a public component");
244   }
245
246   @Test
247   public void apply_removes_ADMIN_permission_from_group_AnyOne_on_a_public_project() {
248     applyRemovesPermissionFromGroupAnyOneOnAPublicProject(UserRole.ADMIN);
249   }
250
251   @Test
252   public void apply_removes_ISSUE_ADMIN_permission_from_group_AnyOne_on_a_public_project() {
253     applyRemovesPermissionFromGroupAnyOneOnAPublicProject(UserRole.ISSUE_ADMIN);
254   }
255
256   @Test
257   public void apply_removes_SCAN_EXECUTION_permission_from_group_AnyOne_on_a_public_project() {
258     applyRemovesPermissionFromGroupAnyOneOnAPublicProject(GlobalPermissions.SCAN_EXECUTION);
259   }
260
261   private void applyRemovesPermissionFromGroupAnyOneOnAPublicProject(String permission) {
262     GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.forAnyone();
263     db.users().insertProjectPermissionOnAnyone(permission, publicProject);
264
265     apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, permission, publicProject, groupUuid, permissionService));
266
267     assertThat(db.users().selectAnyonePermissions(publicProject)).isEmpty();
268   }
269
270   @Test
271   public void apply_fails_with_BadRequestException_when_removing_USER_permission_from_a_group_on_a_public_project() {
272     GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
273
274     assertThatThrownBy(() -> apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, UserRole.USER, publicProject, groupUuid, permissionService)))
275       .isInstanceOf(BadRequestException.class)
276       .hasMessage("Permission user can't be removed from a public component");
277   }
278
279   @Test
280   public void apply_fails_with_BadRequestException_when_removing_CODEVIEWER_permission_from_a_group_on_a_public_project() {
281     GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
282
283     assertThatThrownBy(() -> apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, UserRole.CODEVIEWER, publicProject, groupUuid, permissionService)))
284       .isInstanceOf(BadRequestException.class)
285       .hasMessage("Permission codeviewer can't be removed from a public component");
286   }
287
288   @Test
289   public void add_permission_to_anyone() {
290     GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.forAnyone();
291
292     apply(new GroupPermissionChange(PermissionChange.Operation.ADD, GlobalPermissions.QUALITY_GATE_ADMIN, null, groupUuid, permissionService));
293
294     assertThat(db.users().selectGroupPermissions(group, null)).isEmpty();
295     assertThat(db.users().selectAnyonePermissions(null)).containsOnly(GlobalPermissions.QUALITY_GATE_ADMIN);
296   }
297
298   @Test
299   public void do_nothing_when_adding_permission_that_already_exists() {
300     GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
301     db.users().insertPermissionOnGroup(group, ADMINISTER_QUALITY_GATES);
302
303     apply(new GroupPermissionChange(PermissionChange.Operation.ADD, ADMINISTER_QUALITY_GATES.getKey(), null, groupUuid, permissionService));
304
305     assertThat(db.users().selectGroupPermissions(group, null)).containsOnly(ADMINISTER_QUALITY_GATES.getKey());
306   }
307
308   @Test
309   public void fail_to_add_global_permission_but_SCAN_and_ADMIN_on_private_project() {
310     GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
311
312     permissionService.getGlobalPermissions().stream()
313       .map(GlobalPermission::getKey)
314       .filter(perm -> !UserRole.ADMIN.equals(perm) && !GlobalPermissions.SCAN_EXECUTION.equals(perm))
315       .forEach(perm -> {
316         try {
317           new GroupPermissionChange(PermissionChange.Operation.ADD, perm, privateProject, groupUuid, permissionService);
318           fail("a BadRequestException should have been thrown for permission " + perm);
319         } catch (BadRequestException e) {
320           assertThat(e).hasMessage("Invalid project permission '" + perm +
321             "'. Valid values are [" + StringUtils.join(permissionService.getAllProjectPermissions(), ", ") + "]");
322         }
323       });
324   }
325
326   @Test
327   public void fail_to_add_global_permission_but_SCAN_and_ADMIN_on_public_project() {
328     GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
329
330     permissionService.getGlobalPermissions().stream()
331       .map(GlobalPermission::getKey)
332       .filter(perm -> !UserRole.ADMIN.equals(perm) && !GlobalPermissions.SCAN_EXECUTION.equals(perm))
333       .forEach(perm -> {
334         try {
335           new GroupPermissionChange(PermissionChange.Operation.ADD, perm, publicProject, groupUuid, permissionService);
336           fail("a BadRequestException should have been thrown for permission " + perm);
337         } catch (BadRequestException e) {
338           assertThat(e).hasMessage("Invalid project permission '" + perm +
339             "'. Valid values are [" + StringUtils.join(permissionService.getAllProjectPermissions(), ", ") + "]");
340         }
341       });
342   }
343
344   @Test
345   public void fail_to_add_project_permission_but_SCAN_and_ADMIN_on_global_group() {
346     GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
347
348     permissionService.getAllProjectPermissions()
349       .stream()
350       .filter(perm -> !GlobalPermissions.SCAN_EXECUTION.equals(perm) && !GlobalPermission.ADMINISTER.getKey().equals(perm))
351       .forEach(permission -> {
352         try {
353           new GroupPermissionChange(PermissionChange.Operation.ADD, permission, null, groupUuid, permissionService);
354           fail("a BadRequestException should have been thrown for permission " + permission);
355         } catch (BadRequestException e) {
356           assertThat(e).hasMessage("Invalid global permission '" + permission + "'. Valid values are [admin, gateadmin, profileadmin, provisioning, scan]");
357         }
358       });
359   }
360
361   @Test
362   public void remove_permission_from_group() {
363     GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
364     db.users().insertPermissionOnGroup(group, ADMINISTER_QUALITY_GATES);
365     db.users().insertPermissionOnGroup(group, PROVISION_PROJECTS);
366
367     apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, ADMINISTER_QUALITY_GATES.getKey(), null, groupUuid, permissionService));
368
369     assertThat(db.users().selectGroupPermissions(group, null)).containsOnly(PROVISION_PROJECTS.getKey());
370   }
371
372   @Test
373   public void remove_project_permission_from_group() {
374     GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
375     db.users().insertPermissionOnGroup(group, ADMINISTER_QUALITY_GATES);
376     db.users().insertProjectPermissionOnGroup(group, UserRole.ISSUE_ADMIN, privateProject);
377     db.users().insertProjectPermissionOnGroup(group, UserRole.CODEVIEWER, privateProject);
378
379     apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, UserRole.ISSUE_ADMIN, privateProject, groupUuid, permissionService));
380
381     assertThat(db.users().selectGroupPermissions(group, null)).containsOnly(ADMINISTER_QUALITY_GATES.getKey());
382     assertThat(db.users().selectGroupPermissions(group, privateProject)).containsOnly(UserRole.CODEVIEWER);
383   }
384
385   @Test
386   public void do_not_fail_if_removing_a_permission_that_does_not_exist() {
387     GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
388
389     apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, UserRole.ISSUE_ADMIN, privateProject, groupUuid, permissionService));
390
391     assertThat(db.users().selectGroupPermissions(group, null)).isEmpty();
392     assertThat(db.users().selectGroupPermissions(group, privateProject)).isEmpty();
393   }
394
395   @Test
396   public void fail_to_remove_admin_permission_if_no_more_admins() {
397     GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
398     db.users().insertPermissionOnGroup(group, ADMINISTER);
399
400     assertThatThrownBy(() -> underTest.apply(db.getSession(), new GroupPermissionChange(PermissionChange.Operation.REMOVE, ADMINISTER.getKey(), null, groupUuid, permissionService)))
401       .isInstanceOf(BadRequestException.class)
402       .hasMessage("Last group with permission 'admin'. Permission cannot be removed.");
403   }
404
405   @Test
406   public void remove_admin_group_if_still_other_admins() {
407     GroupUuidOrAnyone groupUuid = GroupUuidOrAnyone.from(group);
408     db.users().insertPermissionOnGroup(group, ADMINISTER);
409     UserDto admin = db.users().insertUser();
410     db.users().insertPermissionOnUser(admin, ADMINISTER);
411
412     apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, ADMINISTER.getKey(), null, groupUuid, permissionService));
413
414     assertThat(db.users().selectGroupPermissions(group, null)).isEmpty();
415   }
416
417   private void apply(GroupPermissionChange change) {
418     underTest.apply(db.getSession(), change);
419     db.commit();
420   }
421
422   private void unsafeInsertProjectPermissionOnAnyone(String perm) {
423     GroupPermissionDto dto = new GroupPermissionDto()
424       .setUuid(Uuids.createFast())
425       .setGroupUuid(null)
426       .setRole(perm)
427       .setComponentUuid(privateProject.uuid())
428       .setComponentName(privateProject.name());
429     db.getDbClient().groupPermissionDao().insert(db.getSession(), dto, privateProject, null);
430     db.commit();
431   }
432 }