]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8253 do not delete the last administrators of organization
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Sun, 16 Oct 2016 15:41:11 +0000 (17:41 +0200)
committerSimon Brandhof <simon.brandhof@sonarsource.com>
Sun, 16 Oct 2016 17:10:49 +0000 (19:10 +0200)
15 files changed:
server/sonar-server/src/main/java/org/sonar/server/permission/GroupPermissionChanger.java
server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/DeleteAction.java
server/sonar-server/src/test/java/org/sonar/server/permission/GroupPermissionChangerTest.java
server/sonar-server/src/test/java/org/sonar/server/permission/ws/RemoveGroupActionTest.java
server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/DeleteActionTest.java
sonar-db/src/main/java/org/sonar/db/permission/AuthorizationDao.java
sonar-db/src/main/java/org/sonar/db/permission/AuthorizationMapper.java
sonar-db/src/main/java/org/sonar/db/permission/GroupPermissionDao.java
sonar-db/src/main/java/org/sonar/db/permission/GroupPermissionMapper.java
sonar-db/src/main/java/org/sonar/db/user/RoleDao.java
sonar-db/src/main/resources/org/sonar/db/permission/AuthorizationMapper.xml
sonar-db/src/main/resources/org/sonar/db/permission/GroupPermissionMapper.xml
sonar-db/src/test/java/org/sonar/db/permission/AuthorizationDaoTest.java
sonar-db/src/test/java/org/sonar/db/permission/GroupPermissionDaoTest.java
sonar-db/src/test/java/org/sonar/db/user/UserDbTester.java

index 5139541f3aaace2aeb710ec42382998946ede119..37a58cbed9ac4901530529cb78ab5912e8504157 100644 (file)
 package org.sonar.server.permission;
 
 import java.util.List;
-import org.sonar.core.permission.GlobalPermissions;
+import java.util.Optional;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.permission.GroupPermissionDto;
 import org.sonar.server.exceptions.BadRequestException;
 
+import static java.lang.String.format;
+import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
 import static org.sonar.server.permission.ws.PermissionRequestValidator.validateNotAnyoneAndAdminPermission;
 
 public class GroupPermissionChanger {
@@ -37,57 +39,68 @@ public class GroupPermissionChanger {
   }
 
   public boolean apply(DbSession dbSession, GroupPermissionChange change) {
-    if (shouldSkip(dbSession, change)) {
-      return false;
-    }
-
     switch (change.getOperation()) {
       case ADD:
-        validateNotAnyoneAndAdminPermission(change.getPermission(), change.getGroupIdOrAnyone());
-        GroupPermissionDto addedDto = new GroupPermissionDto()
-          .setRole(change.getPermission())
-          .setOrganizationUuid(change.getOrganizationUuid())
-          .setGroupId(change.getGroupIdOrAnyone().getId())
-          .setResourceId(change.getNullableProjectId());
-        dbClient.groupPermissionDao().insert(dbSession, addedDto);
-        break;
+        return addPermission(dbSession, change);
       case REMOVE:
-        checkAdminUsersExistOutsideTheRemovedGroup(dbSession, change);
-        dbClient.groupPermissionDao().delete(dbSession,
-          change.getPermission(),
-          change.getOrganizationUuid(),
-          change.getGroupIdOrAnyone().getId(),
-          change.getNullableProjectId());
-        break;
+        return removePermission(dbSession, change);
       default:
         throw new UnsupportedOperationException("Unsupported permission change: " + change.getOperation());
     }
+  }
+
+  private boolean addPermission(DbSession dbSession, GroupPermissionChange change) {
+    if (loadExistingPermissions(dbSession, change).contains(change.getPermission())) {
+      return false;
+    }
+
+    validateNotAnyoneAndAdminPermission(change.getPermission(), change.getGroupIdOrAnyone());
+    GroupPermissionDto addedDto = new GroupPermissionDto()
+      .setRole(change.getPermission())
+      .setOrganizationUuid(change.getOrganizationUuid())
+      .setGroupId(change.getGroupIdOrAnyone().getId())
+      .setResourceId(change.getNullableProjectId());
+    dbClient.groupPermissionDao().insert(dbSession, addedDto);
     return true;
   }
 
-  private boolean shouldSkip(DbSession dbSession, GroupPermissionChange change) {
-    List<String> existingPermissions;
-    if (change.getGroupIdOrAnyone().isAnyone()) {
-      existingPermissions = dbClient.groupPermissionDao().selectAnyonePermissions(dbSession, change.getNullableProjectId());
-    } else {
-      existingPermissions = dbClient.groupPermissionDao().selectGroupPermissions(dbSession, change.getGroupIdOrAnyone().getId(), change.getNullableProjectId());
+  private boolean removePermission(DbSession dbSession, GroupPermissionChange change) {
+    if (!loadExistingPermissions(dbSession, change).contains(change.getPermission())) {
+      return false;
     }
-    switch (change.getOperation()) {
-      case ADD:
-        return existingPermissions.contains(change.getPermission());
-      case REMOVE:
-        return !existingPermissions.contains(change.getPermission());
-      default:
-        throw new UnsupportedOperationException("Unsupported operation: " + change.getOperation());
+    checkIfRemainingGlobalAdministrators(dbSession, change);
+    dbClient.groupPermissionDao().delete(dbSession,
+      change.getPermission(),
+      change.getOrganizationUuid(),
+      change.getGroupIdOrAnyone().getId(),
+      change.getNullableProjectId());
+    return true;
+  }
+
+  private List<String> loadExistingPermissions(DbSession dbSession, GroupPermissionChange change) {
+    Optional<ProjectId> projectId = change.getProjectId();
+    if (projectId.isPresent()) {
+      return dbClient.groupPermissionDao().selectProjectPermissionsOfGroup(dbSession,
+        change.getOrganizationUuid(),
+        change.getGroupIdOrAnyone().getId(),
+        projectId.get().getId());
     }
+    return dbClient.groupPermissionDao().selectGlobalPermissionsOfGroup(dbSession,
+      change.getOrganizationUuid(),
+      change.getGroupIdOrAnyone().getId());
   }
 
-  private void checkAdminUsersExistOutsideTheRemovedGroup(DbSession dbSession, GroupPermissionChange change) {
-    if (GlobalPermissions.SYSTEM_ADMIN.equals(change.getPermission()) &&
-      !change.getProjectId().isPresent() &&
-      // TODO support organizations
-      dbClient.roleDao().countUserPermissions(dbSession, change.getPermission(), change.getGroupIdOrAnyone().getId()) <= 0) {
-      throw new BadRequestException(String.format("Last group with '%s' permission. Permission cannot be removed.", GlobalPermissions.SYSTEM_ADMIN));
+  private void checkIfRemainingGlobalAdministrators(DbSession dbSession, GroupPermissionChange change) {
+    if (SYSTEM_ADMIN.equals(change.getPermission()) &&
+      !change.getGroupIdOrAnyone().isAnyone() &&
+      !change.getProjectId().isPresent()) {
+      // removing global admin permission from group
+      int remaining = dbClient.authorizationDao().countRemainingUserIdsWithGlobalPermissionIfExcludeGroup(dbSession,
+        change.getOrganizationUuid(), SYSTEM_ADMIN, change.getGroupIdOrAnyone().getId());
+
+      if (remaining == 0) {
+        throw new BadRequestException(format("Last group with permission '%s'. Permission cannot be removed.", SYSTEM_ADMIN));
+      }
     }
   }
 
index 48c390a11d2fd5f647c4f14869dd342aa03f1017..d94126147c08ee90d8a911e4b42c051f3214da16 100644 (file)
@@ -26,16 +26,15 @@ import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.api.server.ws.WebService.NewController;
-import org.sonar.core.permission.GlobalPermissions;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
-import org.sonar.db.permission.PermissionQuery;
 import org.sonar.db.user.GroupDto;
 import org.sonar.server.organization.DefaultOrganizationProvider;
 import org.sonar.server.user.UserSession;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static java.lang.String.format;
+import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
 import static org.sonar.server.usergroups.ws.GroupWsSupport.PARAM_GROUP_ID;
 import static org.sonar.server.usergroups.ws.GroupWsSupport.PARAM_GROUP_NAME;
 import static org.sonar.server.usergroups.ws.GroupWsSupport.defineGroupWsParameters;
@@ -73,13 +72,12 @@ public class DeleteAction implements UserGroupsWsAction {
 
   @Override
   public void handle(Request request, Response response) throws Exception {
-    userSession.checkLoggedIn().checkPermission(GlobalPermissions.SYSTEM_ADMIN);
-
     try (DbSession dbSession = dbClient.openSession(false)) {
       GroupId groupId = support.findGroup(dbSession, request);
+      userSession.checkOrganizationPermission(groupId.getOrganizationUuid(), SYSTEM_ADMIN);
 
       checkNotTryingToDeleteDefaultGroup(dbSession, groupId);
-      checkNotTryingToDeleteLastSystemAdminGroup(dbSession, groupId);
+      checkNotTryingToDeleteLastAdminGroup(dbSession, groupId);
       removeGroupMembers(dbSession, groupId);
       removeGroupPermissions(dbSession, groupId);
       removeFromPermissionTemplates(dbSession, groupId);
@@ -105,17 +103,11 @@ public class DeleteAction implements UserGroupsWsAction {
     }
   }
 
-  private void checkNotTryingToDeleteLastSystemAdminGroup(DbSession dbSession, GroupId group) {
-    boolean hasAdminPermission = dbClient.groupPermissionDao()
-      .selectGroupPermissions(dbSession, group.getId(), null)
-      .contains(GlobalPermissions.SYSTEM_ADMIN);
-    // TODO support organizations
-    boolean isOneRemainingAdminGroup = dbClient.groupPermissionDao().countGroups(dbSession, GlobalPermissions.SYSTEM_ADMIN, null) == 1;
-    boolean hasNoStandaloneAdminUser = dbClient.userPermissionDao().countUsers(dbSession,
-      PermissionQuery.builder().setPermission(GlobalPermissions.SYSTEM_ADMIN).withAtLeastOnePermission().build()) == 0;
-    boolean isLastAdminGroup = hasAdminPermission && isOneRemainingAdminGroup && hasNoStandaloneAdminUser;
-
-    checkArgument(!isLastAdminGroup, "The last system admin group cannot be deleted");
+  private void checkNotTryingToDeleteLastAdminGroup(DbSession dbSession, GroupId group) {
+    int remaining = dbClient.authorizationDao().countRemainingUserIdsWithGlobalPermissionIfExcludeGroup(dbSession,
+      group.getOrganizationUuid(), SYSTEM_ADMIN, group.getId());
+
+    checkArgument(remaining > 0, "The last system admin group cannot be deleted");
   }
 
   private void removeGroupMembers(DbSession dbSession, GroupId groupId) {
index af8d9deb5d730e2364d7b1787fc19ecfe70c55fc..8588fe97601c111801d6bbc956dac4333e805bc5 100644 (file)
@@ -33,6 +33,7 @@ import org.sonar.db.component.ComponentTesting;
 import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.organization.OrganizationTesting;
 import org.sonar.db.user.GroupDto;
+import org.sonar.db.user.UserDto;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.usergroups.ws.GroupIdOrAnyone;
 
@@ -79,22 +80,24 @@ public class GroupPermissionChangerTest {
 
   @Test
   public void add_permission_to_anyone() {
-    GroupIdOrAnyone groupId = new GroupIdOrAnyone(db.getDefaultOrganization().getUuid(), null);
+    OrganizationDto defaultOrganization = db.getDefaultOrganization();
+    GroupIdOrAnyone groupId = new GroupIdOrAnyone(defaultOrganization.getUuid(), null);
 
     apply(new GroupPermissionChange(PermissionChange.Operation.ADD, GlobalPermissions.QUALITY_GATE_ADMIN, null, groupId));
 
     assertThat(db.users().selectGroupPermissions(group, null)).isEmpty();
-    assertThat(db.users().selectAnyonePermissions(null)).containsOnly(GlobalPermissions.QUALITY_GATE_ADMIN);
+    assertThat(db.users().selectAnyonePermissions(defaultOrganization, null)).containsOnly(GlobalPermissions.QUALITY_GATE_ADMIN);
   }
 
   @Test
   public void add_project_permission_to_anyone() {
-    GroupIdOrAnyone groupId = new GroupIdOrAnyone(db.getDefaultOrganization().getUuid(), null);
+    OrganizationDto defaultOrganization = db.getDefaultOrganization();
+    GroupIdOrAnyone groupId = new GroupIdOrAnyone(defaultOrganization.getUuid(), null);
 
     apply(new GroupPermissionChange(PermissionChange.Operation.ADD, UserRole.ISSUE_ADMIN, new ProjectId(project), groupId));
 
-    assertThat(db.users().selectAnyonePermissions(null)).isEmpty();
-    assertThat(db.users().selectAnyonePermissions(project)).containsOnly(UserRole.ISSUE_ADMIN);
+    assertThat(db.users().selectAnyonePermissions(defaultOrganization, null)).isEmpty();
+    assertThat(db.users().selectAnyonePermissions(defaultOrganization, project)).containsOnly(UserRole.ISSUE_ADMIN);
   }
 
   @Test
@@ -162,16 +165,28 @@ public class GroupPermissionChangerTest {
   }
 
   @Test
-  public void fail_to_remove_sysadmin_permission_if_no_more_sysadmins() {
+  public void fail_to_remove_admin_permission_if_no_more_admins() {
     GroupIdOrAnyone groupId = new GroupIdOrAnyone(group);
     db.users().insertPermissionOnGroup(group, GlobalPermissions.SYSTEM_ADMIN);
 
     expectedException.expect(BadRequestException.class);
-    expectedException.expectMessage("Last group with 'admin' permission. Permission cannot be removed.");
+    expectedException.expectMessage("Last group with permission 'admin'. Permission cannot be removed.");
 
     underTest.apply(db.getSession(), new GroupPermissionChange(PermissionChange.Operation.REMOVE, GlobalPermissions.SYSTEM_ADMIN, null, groupId));
   }
 
+  @Test
+  public void remove_admin_group_if_still_other_admins() {
+    GroupIdOrAnyone groupId = new GroupIdOrAnyone(group);
+    db.users().insertPermissionOnGroup(group, GlobalPermissions.SYSTEM_ADMIN);
+    UserDto admin = db.users().insertUser();
+    db.users().insertPermissionOnUser(admin, GlobalPermissions.SYSTEM_ADMIN);
+
+    apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, GlobalPermissions.SYSTEM_ADMIN, null, groupId));
+
+    assertThat(db.users().selectGroupPermissions(group, null)).isEmpty();
+  }
+
   private void apply(GroupPermissionChange change) {
     underTest.apply(db.getSession(), change);
     db.commit();
index a0d30f01d856dd4e0c38d39b03d3c846882770d0..46a2e2aff5cd143cbd507a55d5e97c77a3f8b574 100644 (file)
@@ -145,13 +145,13 @@ public class RemoveGroupActionTest extends BasePermissionWsTest<RemoveGroupActio
   }
 
   @Test
-  public void fail_to_remove_last_sysadmin_permission() throws Exception {
+  public void fail_to_remove_last_admin_permission() throws Exception {
     db.users().insertPermissionOnGroup(aGroup, SYSTEM_ADMIN);
     db.users().insertPermissionOnGroup(aGroup, PROVISIONING);
     loginAsAdmin();
 
     expectedException.expect(BadRequestException.class);
-    expectedException.expectMessage("Last group with 'admin' permission. Permission cannot be removed.");
+    expectedException.expectMessage("Last group with permission 'admin'. Permission cannot be removed.");
 
     newRequest()
       .setParam(PARAM_GROUP_NAME, aGroup.getName())
index 2cdd2e07a25dee5f73dc7dba216c73b4596c55f8..63cbb458d271a7b289cc59c4b19462a730253eb3 100644 (file)
@@ -28,12 +28,15 @@ import org.sonar.api.config.MapSettings;
 import org.sonar.api.config.Settings;
 import org.sonar.api.utils.System2;
 import org.sonar.api.web.UserRole;
+import org.sonar.core.permission.GlobalPermissions;
 import org.sonar.db.DbTester;
 import org.sonar.db.component.ComponentDbTester;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ComponentTesting;
 import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.organization.OrganizationTesting;
+import org.sonar.db.permission.template.PermissionTemplateDto;
+import org.sonar.db.permission.template.PermissionTemplateTesting;
 import org.sonar.db.user.GroupDto;
 import org.sonar.db.user.UserDto;
 import org.sonar.server.exceptions.NotFoundException;
@@ -44,6 +47,7 @@ import org.sonar.server.ws.WsTester;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
 import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto;
+import static org.sonar.server.usergroups.ws.GroupWsSupport.PARAM_GROUP_ID;
 import static org.sonar.server.usergroups.ws.GroupWsSupport.PARAM_GROUP_NAME;
 import static org.sonar.server.usergroups.ws.GroupWsSupport.PARAM_ORGANIZATION_KEY;
 
@@ -63,7 +67,7 @@ public class DeleteActionTest {
 
   @Before
   public void setUp() {
-    defaultGroup = db.users().insertGroup(defaultOrganizationProvider.getDto(), CoreProperties.CORE_DEFAULT_GROUP_DEFAULT_VALUE);
+    defaultGroup = db.users().insertGroup(db.getDefaultOrganization(), CoreProperties.CORE_DEFAULT_GROUP_DEFAULT_VALUE);
     Settings settings = new MapSettings().setProperty(CoreProperties.CORE_DEFAULT_GROUP, CoreProperties.CORE_DEFAULT_GROUP_DEFAULT_VALUE);
 
     ws = new WsTester(new UserGroupsWs(
@@ -76,9 +80,10 @@ public class DeleteActionTest {
 
   @Test
   public void delete_by_id() throws Exception {
-    GroupDto group = db.users().insertGroup(defaultOrganizationProvider.getDto(), "to-delete");
+    addAdmin(db.getDefaultOrganization());
+    GroupDto group = db.users().insertGroup();
+    loginAsAdminOnDefaultOrganization();
 
-    loginAsAdmin();
     newRequest()
       .setParam("id", group.getId().toString())
       .execute()
@@ -89,9 +94,10 @@ public class DeleteActionTest {
 
   @Test
   public void delete_by_name_on_default_organization() throws Exception {
-    GroupDto group = db.users().insertGroup(defaultOrganizationProvider.getDto(), "to-delete");
+    addAdminToDefaultOrganization();
+    GroupDto group = db.users().insertGroup();
+    loginAsAdminOnDefaultOrganization();
 
-    loginAsAdmin();
     newRequest()
       .setParam(PARAM_GROUP_NAME, group.getName())
       .execute()
@@ -103,9 +109,10 @@ public class DeleteActionTest {
   @Test
   public void delete_by_name_and_organization() throws Exception {
     OrganizationDto org = OrganizationTesting.insert(db, newOrganizationDto());
+    addAdmin(org);
     GroupDto group = db.users().insertGroup(org, "to-delete");
+    loginAsAdmin(org);
 
-    loginAsAdmin();
     newRequest()
       .setParam(PARAM_ORGANIZATION_KEY, org.getKey())
       .setParam(PARAM_GROUP_NAME, group.getName())
@@ -117,27 +124,28 @@ public class DeleteActionTest {
 
   @Test
   public void delete_by_name_fails_if_organization_is_not_correct() throws Exception {
-    OrganizationDto org = newOrganizationDto().setUuid("org1");
+    OrganizationDto org = newOrganizationDto();
     OrganizationTesting.insert(db, org);
 
-    loginAsAdmin();
+    loginAsAdmin(org);
 
     expectedException.expect(NotFoundException.class);
-    expectedException.expectMessage("No organization with key 'org1'");
+    expectedException.expectMessage("No organization with key 'missing'");
 
     newRequest()
-      .setParam(PARAM_ORGANIZATION_KEY, "org1")
+      .setParam(PARAM_ORGANIZATION_KEY, "missing")
       .setParam(PARAM_GROUP_NAME, "a-group")
       .execute();
   }
 
   @Test
   public void delete_members() throws Exception {
-    GroupDto group = db.users().insertGroup(defaultOrganizationProvider.getDto(), "to-be-deleted");
-    UserDto user = db.users().insertUser("a-user");
+    addAdminToDefaultOrganization();
+    GroupDto group = db.users().insertGroup();
+    UserDto user = db.users().insertUser();
     db.users().insertMember(group, user);
+    loginAsAdminOnDefaultOrganization();
 
-    loginAsAdmin();
     newRequest()
       .setParam("id", group.getId().toString())
       .execute()
@@ -148,11 +156,12 @@ public class DeleteActionTest {
 
   @Test
   public void delete_permissions() throws Exception {
-    GroupDto group = db.users().insertGroup(defaultOrganizationProvider.getDto(), "to-be-deleted");
+    addAdminToDefaultOrganization();
+    GroupDto group = db.users().insertGroup();
     ComponentDto project = componentTester.insertComponent(ComponentTesting.newProjectDto());
     db.users().insertProjectPermissionOnGroup(group, UserRole.ADMIN, project);
+    loginAsAdminOnDefaultOrganization();
 
-    loginAsAdmin();
     newRequest()
       .setParam("id", group.getId().toString())
       .execute()
@@ -162,11 +171,15 @@ public class DeleteActionTest {
   }
 
   @Test
-  public void delete_permission_templates() throws Exception {
-    GroupDto group = db.users().insertGroup(defaultOrganizationProvider.getDto(), "to-be-deleted");
-    // TODO
+  public void delete_group_from_permission_templates() throws Exception {
+    addAdminToDefaultOrganization();
+    GroupDto group = db.users().insertGroup();
+    PermissionTemplateDto template = db.getDbClient().permissionTemplateDao().insert(db.getSession(), PermissionTemplateTesting.newPermissionTemplateDto());
+    db.getDbClient().permissionTemplateDao().insertGroupPermission(db.getSession(), template.getId(), group.getId(), "perm");
+    db.commit();
+    loginAsAdminOnDefaultOrganization();
+    assertThat(db.countRowsOfTable("perm_templates_groups")).isEqualTo(1);
 
-    loginAsAdmin();
     newRequest()
       .setParam("id", group.getId().toString())
       .execute().assertNoContent();
@@ -174,17 +187,27 @@ public class DeleteActionTest {
     assertThat(db.countRowsOfTable("perm_templates_groups")).isEqualTo(0);
   }
 
-  @Test(expected = NotFoundException.class)
+  @Test
   public void fail_if_id_does_not_exist() throws Exception {
-    loginAsAdmin();
+    addAdminToDefaultOrganization();
+    loginAsAdminOnDefaultOrganization();
+    long groupId = defaultGroup.getId() + 123;
+
+    expectedException.expect(NotFoundException.class);
+    expectedException.expectMessage("No group with id '" + groupId + "'");
+
     newRequest()
-      .setParam("id", String.valueOf(defaultGroup.getId() + 123))
+      .setParam("id", String.valueOf(groupId))
       .execute();
   }
 
-  @Test(expected = IllegalArgumentException.class)
+  @Test
   public void cannot_delete_default_group_of_default_organization() throws Exception {
-    loginAsAdmin();
+    loginAsAdminOnDefaultOrganization();
+
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Default group 'sonar-users' cannot be deleted");
+
     newRequest()
       .setParam("id", defaultGroup.getId().toString())
       .execute();
@@ -193,9 +216,10 @@ public class DeleteActionTest {
   @Test
   public void delete_group_of_an_organization_even_if_name_is_default_group_of_default_organization() throws Exception {
     OrganizationDto org = OrganizationTesting.insert(db, newOrganizationDto());
+    addAdmin(org);
     GroupDto group = db.users().insertGroup(org, defaultGroup.getName());
+    loginAsAdmin(org);
 
-    loginAsAdmin();
     newRequest()
       .setParam("id", group.getId().toString())
       .execute();
@@ -206,10 +230,9 @@ public class DeleteActionTest {
 
   @Test
   public void cannot_delete_last_system_admin_group() throws Exception {
-    GroupDto group = db.users().insertGroup(defaultOrganizationProvider.getDto(), "system-admins");
+    GroupDto group = db.users().insertGroup();
     db.users().insertPermissionOnGroup(group, SYSTEM_ADMIN);
-
-    loginAsAdmin();
+    loginAsAdminOnDefaultOrganization();
 
     expectedException.expect(IllegalArgumentException.class);
     expectedException.expectMessage("The last system admin group cannot be deleted");
@@ -220,37 +243,52 @@ public class DeleteActionTest {
   }
 
   @Test
-  public void delete_system_admin_group_if_not_last() throws Exception {
-    OrganizationDto defaultOrg = defaultOrganizationProvider.getDto();
-    GroupDto funkyAdmins = db.users().insertGroup(defaultOrg, "funky-admins");
-    db.users().insertPermissionOnGroup(funkyAdmins, SYSTEM_ADMIN);
-    GroupDto boringAdmins = db.users().insertGroup(defaultOrg, "boring-admins");
-    db.users().insertPermissionOnGroup(boringAdmins, SYSTEM_ADMIN);
-
-    loginAsAdmin();
+  public void delete_admin_group_fails_if_no_admin_users_left() throws Exception {
+    // admin users are part of the group to be deleted
+    OrganizationDto org = OrganizationTesting.insert(db, newOrganizationDto());
+    GroupDto adminGroup = db.users().insertGroup(org, "admins");
+    db.users().insertPermissionOnGroup(adminGroup, SYSTEM_ADMIN);
+    UserDto bigBoss = db.users().insertUser();
+    db.users().insertMember(adminGroup, bigBoss);
+    loginAsAdmin(org);
 
-    newRequest()
-      .setParam(PARAM_GROUP_NAME, boringAdmins.getName())
-      .execute();
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("The last system admin group cannot be deleted");
 
-    assertThat(db.getDbClient().groupPermissionDao().countGroups(db.getSession(), SYSTEM_ADMIN, null)).isEqualTo(1);
+    newRequest().setParam(PARAM_GROUP_ID, adminGroup.getId().toString()).execute();
   }
 
   @Test
-  public void delete_last_system_admin_group_if_admin_user_left() throws Exception {
-    GroupDto lastGroup = db.users().insertGroup(defaultOrganizationProvider.getDto(), "last-group");
-    db.users().insertPermissionOnGroup(lastGroup, SYSTEM_ADMIN);
-    UserDto bigBoss = db.users().insertUser("big.boss");
-    db.users().insertPermissionOnUser(bigBoss, SYSTEM_ADMIN);
+  public void delete_admin_group_succeeds_if_other_groups_have_administrators() throws Exception {
+    OrganizationDto org = OrganizationTesting.insert(db, newOrganizationDto());
+    GroupDto adminGroup1 = db.users().insertGroup(org, "admins");
+    db.users().insertPermissionOnGroup(adminGroup1, SYSTEM_ADMIN);
+    GroupDto adminGroup2 = db.users().insertGroup(org, "admins");
+    db.users().insertPermissionOnGroup(adminGroup2, SYSTEM_ADMIN);
+    UserDto bigBoss = db.users().insertUser();
+    db.users().insertMember(adminGroup2, bigBoss);
+    loginAsAdmin(org);
+
+    newRequest().setParam(PARAM_GROUP_ID, adminGroup1.getId().toString()).execute();
 
-    loginAsAdmin();
-    newRequest().setParam(PARAM_GROUP_NAME, lastGroup.getName()).execute();
+    assertThat(db.users().selectGroupPermissions(adminGroup2, null)).hasSize(1);
+  }
+
+  private void addAdminToDefaultOrganization() {
+    addAdmin(db.getDefaultOrganization());
+  }
+
+  private void addAdmin(OrganizationDto org) {
+    UserDto admin = db.users().insertUser();
+    db.users().insertPermissionOnUser(org, admin, SYSTEM_ADMIN);
+  }
 
-    assertThat(db.users().selectGroupById(lastGroup.getId())).isNull();
+  private void loginAsAdminOnDefaultOrganization() {
+    loginAsAdmin(db.getDefaultOrganization());
   }
 
-  private void loginAsAdmin() {
-    userSession.login("admin").setGlobalPermissions(SYSTEM_ADMIN);
+  private void loginAsAdmin(OrganizationDto org) {
+    userSession.login().addOrganizationPermission(org.getUuid(), GlobalPermissions.SYSTEM_ADMIN);
   }
 
   private WsTester.TestRequest newRequest() {
index c8a9c39e71ad9eefb949f9aa47abbffb518e69a8..dc6677e63d12183be14769957d7c92108ee36388 100644 (file)
@@ -77,6 +77,15 @@ public class AuthorizationDao implements Dao {
     return mapper(dbSession).selectRootComponentPermissionsOfAnonymous(rootComponentId);
   }
 
+  /**
+   * The number of users who will still have the permission when the group {@code excludedGroupId}
+   * is deleted.
+   */
+  public int countRemainingUserIdsWithGlobalPermissionIfExcludeGroup(DbSession dbSession, String organizationUuid,
+    String permission, long excludedGroupId) {
+    return mapper(dbSession).countRemainingUserIdsWithGlobalPermissionIfExcludeGroup(organizationUuid, permission, excludedGroupId);
+  }
+
   public Collection<Long> keepAuthorizedProjectIds(DbSession dbSession, Collection<Long> componentIds, @Nullable Integer userId, String role) {
     return executeLargeInputs(
       componentIds,
index cfbcf015e9adbdd7ff8b9152e3c118dfadfee738..dc422f10476da9246fec94cebf4f82d89ddde216 100644 (file)
@@ -37,6 +37,8 @@ public interface AuthorizationMapper {
 
   Set<String> selectRootComponentPermissionsOfAnonymous(@Param("rootComponentId") long rootComponentId);
 
+  int countRemainingUserIdsWithGlobalPermissionIfExcludeGroup(@Param("organizationUuid") String organizationUuid, @Param("permission") String permission, @Param("excludedGroupId") long excludedGroupId);
+
   List<Long> keepAuthorizedProjectIdsForAnonymous(@Param("role") String role, @Param("componentIds") Collection<Long> componentIds);
 
   List<Long> keepAuthorizedProjectIdsForUser(@Param("userId") long userId, @Param("role") String role, @Param("componentIds") Collection<Long> componentIds);
index 53e579fa6c11bc2633de7062aeeb2f6103304dd6..18bff43b55e9136ab80c839752ee371a5799ce40 100644 (file)
@@ -92,21 +92,20 @@ public class GroupPermissionDao implements Dao {
   }
 
   /**
-   * @return the permissions granted to the requested group, optionally on the requested project. An
-   * empty list is returned if the group or project do not exist.
+   * Selects the global permissions granted to group. An empty list is returned if the
+   * group does not exist.
    */
-  public List<String> selectGroupPermissions(DbSession session, long groupId, @Nullable Long projectId) {
-    return session.getMapper(GroupPermissionMapper.class).selectGroupPermissions(groupId, projectId);
+  public List<String> selectGlobalPermissionsOfGroup(DbSession session, String organizationUuid, @Nullable Long groupId) {
+    return mapper(session).selectGlobalPermissionsOfGroup(organizationUuid, groupId);
   }
 
+
   /**
-   * @return the permissions granted to Anyone virtual group, optionally on the requested project. An
-   * empty list is returned if the project does not exist.
-   * @deprecated not compatible with organizations if {@code projectId} is null. Should have an organization parameter.
+   * Selects the permissions granted to group and project. An empty list is returned if the
+   * group or project do not exist.
    */
-  @Deprecated
-  public List<String> selectAnyonePermissions(DbSession session, @Nullable Long projectId) {
-    return session.getMapper(GroupPermissionMapper.class).selectAnyonePermissions(projectId);
+  public List<String> selectProjectPermissionsOfGroup(DbSession session, String organizationUuid, @Nullable Long groupId, long projectId) {
+    return mapper(session).selectProjectPermissionsOfGroup(organizationUuid, groupId, projectId);
   }
 
   /**
index 6c3978bc10eeb033cbaef88b652d232495a75722..28c9af27a761c466323b86673450d4d6f2e6decf 100644 (file)
@@ -54,10 +54,6 @@ public interface GroupPermissionMapper {
 
   void groupsCountByProjectIdAndPermission(Map<String, Object> parameters, ResultHandler resultHandler);
 
-  List<String> selectGroupPermissions(@Param("groupId") long groupId, @Nullable @Param("projectId") Long projectId);
-
-  List<String> selectAnyonePermissions(@Nullable @Param("projectId") Long projectId);
-
   void insert(GroupPermissionDto dto);
 
   void deleteByRootComponentId(@Param("rootComponentId") long componentId);
@@ -66,4 +62,8 @@ public interface GroupPermissionMapper {
     @Nullable @Param("groupId") Long groupId, @Nullable @Param("rootComponentId") Long rootComponentId);
 
   int countRowsByRootComponentId(@Param("rootComponentId") long rootComponentId);
+
+  List<String> selectGlobalPermissionsOfGroup(@Param("organizationUuid") String organizationUuid, @Nullable @Param("groupId") Long groupId);
+
+  List<String> selectProjectPermissionsOfGroup(@Param("organizationUuid") String organizationUuid, @Nullable @Param("groupId") Long groupId, @Param("projectId") long projectId);
 }
index a09bba9ec3390f842031642081a829be5bf71fc4..cfd6a2ea69c143fea9c499f06a14c3c7dcab3391 100644 (file)
@@ -49,6 +49,10 @@ public class RoleDao implements Dao {
     mapper(session).deleteGroupRolesByGroupId(groupId);
   }
 
+  /**
+   * @deprecated does not support organizations
+   */
+  @Deprecated
   public int countUserPermissions(DbSession session, String permission, @Nullable Long allGroupsExceptThisGroupId) {
     return mapper(session).countUsersWithPermission(permission, allGroupsExceptThisGroupId);
   }
index d819a2b2294216de2e017a66831c639910eda095..63043b8bf02a7e9bc198179d306fe067c414f7aa 100644 (file)
     gr.group_id is null
   </select>
 
+  <select id="countRemainingUserIdsWithGlobalPermissionIfExcludeGroup" parameterType="map" resultType="int">
+    select count(1) from
+    (
+      select gu.user_id
+      from groups_users gu
+      inner join group_roles gr on gr.group_id = gu.group_id
+      where
+      gr.organization_uuid = #{organizationUuid,jdbcType=VARCHAR} and
+      gr.role = #{permission,jdbcType=VARCHAR} and
+      gr.resource_id is null and
+      gr.group_id is not null and
+      gr.group_id != #{excludedGroupId,jdbcType=BIGINT}
+
+      union
+
+      select ur.user_id
+      from user_roles ur
+      where
+      ur.resource_id is null and
+      ur.role = #{permission,jdbcType=VARCHAR}
+    ) remaining
+  </select>
+
   <select id="keepAuthorizedProjectIdsForUser" parameterType="map" resultType="long">
     SELECT gr.resource_id
     FROM group_roles gr
index e9c9bbb780e4a30c807bb8eea0d4774bf9907550..683b2990d5d69cc784d1683344d3200150c04b4d 100644 (file)
       </if>
   </select>
 
-  <select id="selectGroupPermissions" parameterType="map" resultType="String">
+  <select id="selectGlobalPermissionsOfGroup" parameterType="map" resultType="String">
     select gr.role
     from group_roles gr
-    where gr.group_id = #{groupId}
-    and
-    <if test="projectId == null">
-      gr.resource_id is null
-    </if>
-    <if test="projectId != null">
-      gr.resource_id = #{projectId}
-    </if>
+    where
+    gr.organization_uuid = #{organizationUuid,jdbcType=VARCHAR} and
+    gr.resource_id is null and
+    <choose>
+      <when test="groupId != null">
+        gr.group_id = #{groupId,jdbcType=BIGINT}
+      </when>
+      <otherwise>
+        gr.group_id is null
+      </otherwise>
+    </choose>
   </select>
 
-  <select id="selectAnyonePermissions" parameterType="map" resultType="String">
+  <select id="selectProjectPermissionsOfGroup" parameterType="map" resultType="String">
     select gr.role
     from group_roles gr
-    where gr.group_id is null
-    and
-    <if test="projectId == null">
-      gr.resource_id is null
-    </if>
-    <if test="projectId != null">
-      gr.resource_id = #{projectId}
-    </if>
+    where
+    gr.organization_uuid = #{organizationUuid,jdbcType=VARCHAR} and
+    gr.resource_id = #{projectId,jdbcType=BIGINT} and
+    <choose>
+      <when test="groupId != null">
+        gr.group_id = #{groupId,jdbcType=BIGINT}
+      </when>
+      <otherwise>
+        gr.group_id is null
+      </otherwise>
+    </choose>
   </select>
 
   <select id="countRowsByRootComponentId" parameterType="long" resultType="int">
index 68201780ed97824450e873154999efbf48f099ec..726b2959cfec1c8a51ab1e9120cbdc649db1decd 100644 (file)
@@ -174,6 +174,59 @@ public class AuthorizationDaoTest {
       USER, "admin")).isEmpty();
   }
 
+  @Test
+  public void countRemainingUserIdsWithGlobalPermissionIfExcludeGroup() {
+    // users with global permission "perm1" :
+    // - "u1" and "u2" through group "g1"
+    // - "u1" and "u3" through group "g2"
+    // - "u4"
+
+    UserDto user1 = db.users().insertUser();
+    UserDto user2 = db.users().insertUser();
+    UserDto user3 = db.users().insertUser();
+    UserDto user4 = db.users().insertUser();
+    UserDto user5 = db.users().insertUser();
+
+    OrganizationDto org = OrganizationTesting.insert(db, newOrganizationDto());
+    GroupDto group1 = db.users().insertGroup(org, "g1");
+    db.users().insertPermissionOnGroup(group1, "perm1");
+    db.users().insertPermissionOnGroup(group1, "perm2");
+    db.users().insertMember(group1, user1);
+    db.users().insertMember(group1, user2);
+
+    GroupDto group2 = db.users().insertGroup(org, "g2");
+    db.users().insertPermissionOnGroup(group2, "perm1");
+    db.users().insertPermissionOnGroup(group2, "perm2");
+    db.users().insertMember(group2, user1);
+    db.users().insertMember(group2, user3);
+
+    // group3 has the permission "perm1" but has no users
+    GroupDto group3 = db.users().insertGroup(org, "g2");
+    db.users().insertPermissionOnGroup(group3, "perm1");
+
+    db.users().insertPermissionOnUser(user4, "perm1");
+    db.users().insertPermissionOnUser(user4, "perm2");
+
+    db.users().insertPermissionOnAnyone(org, "perm1");
+
+    // excluding group "g1" -> remain u1, u3 and u4
+    assertThat(underTest.countRemainingUserIdsWithGlobalPermissionIfExcludeGroup(db.getSession(),
+      org.getUuid(), "perm1", group1.getId())).isEqualTo(3);
+
+    // excluding group "g2" -> remain u1, u2 and u4
+    assertThat(underTest.countRemainingUserIdsWithGlobalPermissionIfExcludeGroup(db.getSession(),
+      org.getUuid(), "perm1", group2.getId())).isEqualTo(3);
+
+    // excluding group "g3" -> remain u1, u2, u3 and u4
+    assertThat(underTest.countRemainingUserIdsWithGlobalPermissionIfExcludeGroup(db.getSession(),
+      org.getUuid(), "perm1", group3.getId())).isEqualTo(4);
+
+    // nobody has the permission
+    assertThat(underTest.countRemainingUserIdsWithGlobalPermissionIfExcludeGroup(db.getSession(),
+      org.getUuid(), "missingPermission", group1.getId())).isEqualTo(0);
+
+  }
+
   @Test
   public void keep_authorized_project_ids_for_user() {
     db.prepareDbUnit(getClass(), "keep_authorized_project_ids_for_user.xml");
index 453e05bce37000242711a03f0f944f5e679d3492..9c1c34c31bc0e4bfdaf8a4a931ce1f573c9746b5 100644 (file)
@@ -30,6 +30,8 @@ import org.sonar.api.web.UserRole;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
 import org.sonar.db.component.ComponentDto;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.organization.OrganizationTesting;
 import org.sonar.db.user.GroupDto;
 
 import static java.util.Arrays.asList;
@@ -43,6 +45,7 @@ import static org.sonar.core.permission.GlobalPermissions.PROVISIONING;
 import static org.sonar.core.permission.GlobalPermissions.SCAN_EXECUTION;
 import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
 import static org.sonar.db.component.ComponentTesting.newProjectDto;
+import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto;
 import static org.sonar.db.user.GroupTesting.newGroupDto;
 
 public class GroupPermissionDaoTest {
@@ -277,43 +280,51 @@ public class GroupPermissionDaoTest {
   }
 
   @Test
-  public void selectGroupPermissions() {
-    GroupDto group1 = db.users().insertGroup(newGroupDto());
-    GroupDto group2 = db.users().insertGroup(newGroupDto());
+  public void selectGlobalPermissionsOfGroup() {
+    OrganizationDto org1 = OrganizationTesting.insert(db, newOrganizationDto());
+    OrganizationDto org2 = OrganizationTesting.insert(db, newOrganizationDto());
+    GroupDto group1 = db.users().insertGroup(org1, "group1");
+    GroupDto group2 = db.users().insertGroup(org2, "group2");
     ComponentDto project = db.components().insertProject();
 
-    db.users().insertPermissionOnAnyone("perm1");
+    db.users().insertPermissionOnAnyone(org1, "perm1");
     db.users().insertPermissionOnGroup(group1, "perm2");
     db.users().insertPermissionOnGroup(group1, "perm3");
     db.users().insertPermissionOnGroup(group2, "perm4");
     db.users().insertProjectPermissionOnGroup(group1, "perm5", project);
-    db.users().insertProjectPermissionOnAnyone("perm6", project);
+    db.users().insertProjectPermissionOnAnyone(org1, "perm6", project);
 
-    // select global permissions on group
-    assertThat(underTest.selectGroupPermissions(dbSession, group1.getId(), null)).containsOnly("perm2", "perm3");
-    assertThat(underTest.selectGroupPermissions(dbSession, UNKNOWN_GROUP_ID, null)).isEmpty();
+    assertThat(underTest.selectGlobalPermissionsOfGroup(dbSession, org1.getUuid(), group1.getId())).containsOnly("perm2", "perm3");
+    assertThat(underTest.selectGlobalPermissionsOfGroup(dbSession, org2.getUuid(), group2.getId())).containsOnly("perm4");
+    assertThat(underTest.selectGlobalPermissionsOfGroup(dbSession, org1.getUuid(), null)).containsOnly("perm1");
 
-    // select project permissions on group
-    assertThat(underTest.selectGroupPermissions(dbSession, group1.getId(), project.getId())).containsOnly("perm5");
-    assertThat(underTest.selectGroupPermissions(dbSession, group1.getId(), UNKNOWN_PROJECT_ID)).isEmpty();
+    // group1 is not in org2
+    assertThat(underTest.selectGlobalPermissionsOfGroup(dbSession, org2.getUuid(), group1.getId())).isEmpty();
+    assertThat(underTest.selectGlobalPermissionsOfGroup(dbSession, org2.getUuid(), null)).isEmpty();
   }
 
   @Test
-  public void selectAnyonePermissions() {
-    GroupDto group1 = db.users().insertGroup(newGroupDto());
-    ComponentDto project = db.components().insertProject();
+  public void selectProjectPermissionsOfGroup() {
+    OrganizationDto org1 = OrganizationTesting.insert(db, newOrganizationDto());
+    GroupDto group1 = db.users().insertGroup(org1, "group1");
+    ComponentDto project1 = db.components().insertProject();
+    ComponentDto project2 = db.components().insertProject();
 
-    db.users().insertPermissionOnAnyone("perm1");
+    db.users().insertPermissionOnAnyone(org1, "perm1");
     db.users().insertPermissionOnGroup(group1, "perm2");
-    db.users().insertProjectPermissionOnGroup(group1, "perm3", project);
-    db.users().insertProjectPermissionOnAnyone("perm4", project);
-
-    // select global permissions on group
-    assertThat(underTest.selectAnyonePermissions(dbSession, null)).containsOnly("perm1");
-
-    // select project permissions on group
-    assertThat(underTest.selectAnyonePermissions(dbSession, project.getId())).containsOnly("perm4");
-    assertThat(underTest.selectAnyonePermissions(dbSession, UNKNOWN_PROJECT_ID)).isEmpty();
+    db.users().insertProjectPermissionOnGroup(group1, "perm3", project1);
+    db.users().insertProjectPermissionOnGroup(group1, "perm4", project1);
+    db.users().insertProjectPermissionOnGroup(group1, "perm5", project2);
+    db.users().insertProjectPermissionOnAnyone(org1, "perm6", project1);
+
+    assertThat(underTest.selectProjectPermissionsOfGroup(dbSession, org1.getUuid(), group1.getId(), project1.getId()))
+      .containsOnly("perm3", "perm4");
+    assertThat(underTest.selectProjectPermissionsOfGroup(dbSession, org1.getUuid(), group1.getId(), project2.getId()))
+      .containsOnly("perm5");
+    assertThat(underTest.selectProjectPermissionsOfGroup(dbSession, org1.getUuid(), null, project1.getId()))
+      .containsOnly("perm6");
+    assertThat(underTest.selectProjectPermissionsOfGroup(dbSession, org1.getUuid(), null, project2.getId()))
+      .isEmpty();
   }
 
   @Test
index a2a75166c453b2637b4181bc0ce8871f29ff926b..444beb61ee86b4b9b2ef0acac5580f17130153e1 100644 (file)
@@ -167,15 +167,21 @@ public class UserDbTester {
   }
 
   public List<String> selectGroupPermissions(GroupDto group, @Nullable ComponentDto project) {
-    return db.getDbClient().groupPermissionDao().selectGroupPermissions(db.getSession(), group.getId(), project == null ? null : project.getId());
-  }
-
-  /**
-   * @deprecated does not support organizations
-   */
-  @Deprecated
-  public List<String> selectAnyonePermissions(@Nullable ComponentDto project) {
-    return db.getDbClient().groupPermissionDao().selectAnyonePermissions(db.getSession(), project == null ? null : project.getId());
+    if (project == null) {
+      return db.getDbClient().groupPermissionDao().selectGlobalPermissionsOfGroup(db.getSession(),
+        group.getOrganizationUuid(), group.getId());
+    }
+    return db.getDbClient().groupPermissionDao().selectProjectPermissionsOfGroup(db.getSession(),
+      group.getOrganizationUuid(), group.getId(), project.getId());
+  }
+
+  public List<String> selectAnyonePermissions(OrganizationDto org, @Nullable ComponentDto project) {
+    if (project == null) {
+      return db.getDbClient().groupPermissionDao().selectGlobalPermissionsOfGroup(db.getSession(),
+        org.getUuid(), null);
+    }
+    return db.getDbClient().groupPermissionDao().selectProjectPermissionsOfGroup(db.getSession(),
+      org.getUuid(), null, project.getId());
   }
 
   // USER PERMISSIONS