Browse Source

SONAR-8253 do not delete the last administrators of organization

tags/6.2-RC1
Simon Brandhof 7 years ago
parent
commit
175b5abd3a

+ 52
- 39
server/sonar-server/src/main/java/org/sonar/server/permission/GroupPermissionChanger.java View File

@@ -20,12 +20,14 @@
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));
}
}
}


+ 8
- 16
server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/DeleteAction.java View 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) {

+ 22
- 7
server/sonar-server/src/test/java/org/sonar/server/permission/GroupPermissionChangerTest.java View 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();

+ 2
- 2
server/sonar-server/src/test/java/org/sonar/server/permission/ws/RemoveGroupActionTest.java View 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())

+ 88
- 50
server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/DeleteActionTest.java View 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() {

+ 9
- 0
sonar-db/src/main/java/org/sonar/db/permission/AuthorizationDao.java View 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,

+ 2
- 0
sonar-db/src/main/java/org/sonar/db/permission/AuthorizationMapper.java View 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);

+ 9
- 10
sonar-db/src/main/java/org/sonar/db/permission/GroupPermissionDao.java View 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);
}

/**

+ 4
- 4
sonar-db/src/main/java/org/sonar/db/permission/GroupPermissionMapper.java View 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);
}

+ 4
- 0
sonar-db/src/main/java/org/sonar/db/user/RoleDao.java View 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);
}

+ 23
- 0
sonar-db/src/main/resources/org/sonar/db/permission/AuthorizationMapper.xml View File

@@ -71,6 +71,29 @@
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

+ 24
- 18
sonar-db/src/main/resources/org/sonar/db/permission/GroupPermissionMapper.xml View File

@@ -124,30 +124,36 @@
</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">

+ 53
- 0
sonar-db/src/test/java/org/sonar/db/permission/AuthorizationDaoTest.java View 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");

+ 35
- 24
sonar-db/src/test/java/org/sonar/db/permission/GroupPermissionDaoTest.java View 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

+ 15
- 9
sonar-db/src/test/java/org/sonar/db/user/UserDbTester.java View 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

Loading…
Cancel
Save