Browse Source

SONAR-8192 set/unset root with admin permission change though WS

this applies only to the admin permission of the default organization
tags/6.2-RC1
Sébastien Lesaint 7 years ago
parent
commit
c1e059083d
23 changed files with 1227 additions and 68 deletions
  1. 12
    1
      server/sonar-server/src/main/java/org/sonar/server/permission/GroupPermissionChanger.java
  2. 11
    4
      server/sonar-server/src/main/java/org/sonar/server/permission/UserPermissionChanger.java
  3. 5
    1
      server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/AddUserAction.java
  4. 5
    1
      server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/RemoveUserAction.java
  5. 2
    2
      server/sonar-server/src/test/java/org/sonar/server/permission/GroupPermissionChangerTest.java
  6. 74
    6
      server/sonar-server/src/test/java/org/sonar/server/permission/ws/AddGroupActionTest.java
  7. 89
    0
      server/sonar-server/src/test/java/org/sonar/server/permission/ws/AddUserActionTest.java
  8. 2
    2
      server/sonar-server/src/test/java/org/sonar/server/permission/ws/BasePermissionWsTest.java
  9. 71
    8
      server/sonar-server/src/test/java/org/sonar/server/permission/ws/RemoveGroupActionTest.java
  10. 68
    19
      server/sonar-server/src/test/java/org/sonar/server/permission/ws/RemoveUserActionTest.java
  11. 76
    8
      server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/AddUserActionTest.java
  12. 102
    1
      server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/RemoveUserActionTest.java
  13. 11
    0
      sonar-db/src/main/java/org/sonar/db/user/GroupDao.java
  14. 8
    0
      sonar-db/src/main/java/org/sonar/db/user/GroupMapper.java
  15. 25
    14
      sonar-db/src/main/java/org/sonar/db/user/UserDao.java
  16. 5
    0
      sonar-db/src/main/java/org/sonar/db/user/UserMapper.java
  17. 86
    0
      sonar-db/src/main/resources/org/sonar/db/user/GroupMapper.xml
  18. 80
    0
      sonar-db/src/main/resources/org/sonar/db/user/UserMapper.xml
  19. 7
    0
      sonar-db/src/test/java/org/sonar/db/DbTester.java
  20. 196
    0
      sonar-db/src/test/java/org/sonar/db/user/GroupDaoTest.java
  21. 56
    0
      sonar-db/src/test/java/org/sonar/db/user/RootFlagAssertions.java
  22. 122
    1
      sonar-db/src/test/java/org/sonar/db/user/UserDaoTest.java
  23. 114
    0
      sonar-db/src/test/java/org/sonar/db/user/UserDbTester.java

+ 12
- 1
server/sonar-server/src/main/java/org/sonar/server/permission/GroupPermissionChanger.java View File

@@ -25,6 +25,7 @@ import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.permission.GroupPermissionDto;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.organization.DefaultOrganizationProvider;

import static java.lang.String.format;
import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
@@ -33,9 +34,11 @@ import static org.sonar.server.permission.ws.PermissionRequestValidator.validate
public class GroupPermissionChanger {

private final DbClient dbClient;
private final DefaultOrganizationProvider defaultOrganizationProvider;

public GroupPermissionChanger(DbClient dbClient) {
public GroupPermissionChanger(DbClient dbClient, DefaultOrganizationProvider defaultOrganizationProvider) {
this.dbClient = dbClient;
this.defaultOrganizationProvider = defaultOrganizationProvider;
}

public boolean apply(DbSession dbSession, GroupPermissionChange change) {
@@ -61,6 +64,7 @@ public class GroupPermissionChanger {
.setGroupId(change.getGroupIdOrAnyone().getId())
.setResourceId(change.getNullableProjectId());
dbClient.groupPermissionDao().insert(dbSession, addedDto);
updateRootFlag(dbSession, change);
return true;
}

@@ -74,9 +78,16 @@ public class GroupPermissionChanger {
change.getOrganizationUuid(),
change.getGroupIdOrAnyone().getId(),
change.getNullableProjectId());
updateRootFlag(dbSession, change);
return true;
}

private void updateRootFlag(DbSession dbSession, GroupPermissionChange change) {
if (SYSTEM_ADMIN.equals(change.getPermission()) && !change.getGroupIdOrAnyone().isAnyone() && !change.getProjectId().isPresent()) {
dbClient.groupDao().updateRootFlagOfUsersInGroupFromPermissions(dbSession, change.getGroupIdOrAnyone().getId(), defaultOrganizationProvider.get().getUuid());
}
}

private List<String> loadExistingPermissions(DbSession dbSession, GroupPermissionChange change) {
Optional<ProjectId> projectId = change.getProjectId();
if (projectId.isPresent()) {

+ 11
- 4
server/sonar-server/src/main/java/org/sonar/server/permission/UserPermissionChanger.java View File

@@ -21,19 +21,23 @@ package org.sonar.server.permission;

import java.util.Optional;
import java.util.Set;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.permission.UserPermissionDto;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.organization.DefaultOrganizationProvider;
import org.sonar.server.permission.PermissionChange.Operation;

import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;

public class UserPermissionChanger {

private final DbClient dbClient;
private final DefaultOrganizationProvider defaultOrganizationProvider;

public UserPermissionChanger(DbClient dbClient) {
public UserPermissionChanger(DbClient dbClient, DefaultOrganizationProvider defaultOrganizationProvider) {
this.dbClient = dbClient;
this.defaultOrganizationProvider = defaultOrganizationProvider;
}

public boolean apply(DbSession dbSession, UserPermissionChange change) {
@@ -58,6 +62,9 @@ public class UserPermissionChanger {
default:
throw new UnsupportedOperationException("Unsupported permission change: " + change.getOperation());
}
if (SYSTEM_ADMIN.equals(change.getPermission()) && !change.getProjectId().isPresent()) {
dbClient.userDao().updateRootFlagFromPermissions(dbSession, change.getUserId().getId(), defaultOrganizationProvider.get().getUuid());
}
return true;
}

@@ -68,10 +75,10 @@ public class UserPermissionChanger {
}

private void checkOtherAdminUsersExist(DbSession session, PermissionChange change) {
if (GlobalPermissions.SYSTEM_ADMIN.equals(change.getPermission()) &&
if (SYSTEM_ADMIN.equals(change.getPermission()) &&
!change.getProjectId().isPresent() &&
dbClient.roleDao().countUserPermissions(session, change.getPermission(), null) <= 1) {
throw new BadRequestException(String.format("Last user with '%s' permission. Permission cannot be removed.", GlobalPermissions.SYSTEM_ADMIN));
throw new BadRequestException(String.format("Last user with '%s' permission. Permission cannot be removed.", SYSTEM_ADMIN));
}
}
}

+ 5
- 1
server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/AddUserAction.java View File

@@ -28,6 +28,7 @@ import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.user.UserDto;
import org.sonar.db.user.UserGroupDto;
import org.sonar.server.organization.DefaultOrganizationProvider;
import org.sonar.server.user.UserSession;

import static java.lang.String.format;
@@ -43,11 +44,13 @@ public class AddUserAction implements UserGroupsWsAction {
private final DbClient dbClient;
private final UserSession userSession;
private final GroupWsSupport support;
private final DefaultOrganizationProvider defaultOrganizationProvider;

public AddUserAction(DbClient dbClient, UserSession userSession, GroupWsSupport support) {
public AddUserAction(DbClient dbClient, UserSession userSession, GroupWsSupport support, DefaultOrganizationProvider defaultOrganizationProvider) {
this.dbClient = dbClient;
this.userSession = userSession;
this.support = support;
this.defaultOrganizationProvider = defaultOrganizationProvider;
}

@Override
@@ -77,6 +80,7 @@ public class AddUserAction implements UserGroupsWsAction {
if (!isMemberOf(dbSession, user, groupId)) {
UserGroupDto membershipDto = new UserGroupDto().setGroupId(groupId.getId()).setUserId(user.getId());
dbClient.userGroupDao().insert(dbSession, membershipDto);
dbClient.userDao().updateRootFlagFromPermissions(dbSession, user.getId(), defaultOrganizationProvider.get().getUuid());
dbSession.commit();
}


+ 5
- 1
server/sonar-server/src/main/java/org/sonar/server/usergroups/ws/RemoveUserAction.java View File

@@ -27,6 +27,7 @@ import org.sonar.core.permission.GlobalPermissions;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.user.UserDto;
import org.sonar.server.organization.DefaultOrganizationProvider;
import org.sonar.server.user.UserSession;

import static java.lang.String.format;
@@ -42,11 +43,13 @@ public class RemoveUserAction implements UserGroupsWsAction {
private final DbClient dbClient;
private final UserSession userSession;
private final GroupWsSupport support;
private final DefaultOrganizationProvider defaultOrganizationProvider;

public RemoveUserAction(DbClient dbClient, UserSession userSession, GroupWsSupport support) {
public RemoveUserAction(DbClient dbClient, UserSession userSession, GroupWsSupport support, DefaultOrganizationProvider defaultOrganizationProvider) {
this.dbClient = dbClient;
this.userSession = userSession;
this.support = support;
this.defaultOrganizationProvider = defaultOrganizationProvider;
}

@Override
@@ -73,6 +76,7 @@ public class RemoveUserAction implements UserGroupsWsAction {
UserDto user = getUser(dbSession, login);

dbClient.userGroupDao().delete(dbSession, group.getId(), user.getId());
dbClient.userDao().updateRootFlagFromPermissions(dbSession, user.getId(), defaultOrganizationProvider.get().getUuid());
dbSession.commit();

response.noContent();

+ 2
- 2
server/sonar-server/src/test/java/org/sonar/server/permission/GroupPermissionChangerTest.java View File

@@ -34,6 +34,7 @@ import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.user.GroupDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.organization.TestDefaultOrganizationProvider;
import org.sonar.server.usergroups.ws.GroupIdOrAnyone;

import static org.assertj.core.api.Assertions.assertThat;
@@ -42,11 +43,10 @@ public class GroupPermissionChangerTest {

@Rule
public DbTester db = DbTester.create(System2.INSTANCE);

@Rule
public ExpectedException expectedException = ExpectedException.none();

private GroupPermissionChanger underTest = new GroupPermissionChanger(db.getDbClient());
private GroupPermissionChanger underTest = new GroupPermissionChanger(db.getDbClient(), TestDefaultOrganizationProvider.from(db));
private OrganizationDto org;
private GroupDto group;
private ComponentDto project;

+ 74
- 6
server/sonar-server/src/test/java/org/sonar/server/permission/ws/AddGroupActionTest.java View File

@@ -25,6 +25,7 @@ import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.user.GroupDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
@@ -165,10 +166,7 @@ public class AddGroupActionTest extends BasePermissionWsTest<AddGroupAction> {

expectedException.expect(BadRequestException.class);

newRequest()
.setParam(PARAM_GROUP_NAME, group.getName())
.setParam(PARAM_PERMISSION, UserRole.ISSUE_ADMIN)
.execute();
executeRequest(group, UserRole.ISSUE_ADMIN);
}

@Test
@@ -231,8 +229,8 @@ public class AddGroupActionTest extends BasePermissionWsTest<AddGroupAction> {
expectedException.expect(IllegalArgumentException.class);

newRequest()
.setParam(PARAM_GROUP_NAME, group.getName())
.execute();
.setParam(PARAM_GROUP_NAME, group.getName())
.execute();
}

@Test
@@ -314,6 +312,76 @@ public class AddGroupActionTest extends BasePermissionWsTest<AddGroupAction> {
assertThat(db.users().selectGroupPermissions(group, project)).containsOnly(ISSUE_ADMIN);
}

@Test
public void set_root_flag_to_true_on_all_users_in_group_when_admin_permission_to_group_of_default_organization_without_org_param() throws Exception {
GroupDto group = db.users().insertGroup(db.getDefaultOrganization());
UserDto rootByUserPermissionUser = db.users().insertRootByUserPermission();
UserDto rootByGroupPermissionUser = db.users().insertRootByGroupPermission();
UserDto notRootUser = db.users().insertUser();
UserDto notInGroupUser = db.users().insertUser();
db.users().insertMembers(group, rootByUserPermissionUser, rootByGroupPermissionUser, notRootUser);
loginAsAdminOnDefaultOrganization();

executeRequest(group, SYSTEM_ADMIN);

db.rootFlag().verify(rootByUserPermissionUser, true);
db.rootFlag().verify(rootByGroupPermissionUser, true);
db.rootFlag().verify(notRootUser, true);
db.rootFlag().verifyUnchanged(notInGroupUser);
}

@Test
public void set_root_flag_to_true_on_all_users_in_group_when_admin_permission_to_group_of_default_organization_with_org_param() throws Exception {
GroupDto group = db.users().insertGroup(db.getDefaultOrganization());
UserDto rootByUserPermissionUser = db.users().insertRootByUserPermission();
UserDto rootByGroupPermissionUser = db.users().insertRootByGroupPermission();
UserDto notRootUser = db.users().insertUser();
UserDto notInGroupUser = db.users().insertUser();
db.users().insertMembers(group, rootByUserPermissionUser, rootByGroupPermissionUser, notRootUser);
loginAsAdminOnDefaultOrganization();

executeRequest(group, db.getDefaultOrganization(), SYSTEM_ADMIN);

db.rootFlag().verify(rootByUserPermissionUser, true);
db.rootFlag().verify(rootByGroupPermissionUser, true);
db.rootFlag().verify(notRootUser, true);
db.rootFlag().verifyUnchanged(notInGroupUser);
}

@Test
public void does_not_set_root_flag_to_true_on_all_users_in_group_when_admin_permission_to_group_of_default_organization() throws Exception {
OrganizationDto otherOrganization = db.organizations().insert();
GroupDto group = db.users().insertGroup(otherOrganization);
UserDto rootByUserPermissionUser = db.users().insertRootByUserPermission();
UserDto rootByGroupPermissionUser = db.users().insertRootByGroupPermission();
UserDto notRootUser = db.users().insertUser();
UserDto notInGroupUser = db.users().insertUser();
db.users().insertMembers(group, rootByUserPermissionUser, rootByGroupPermissionUser, notRootUser);
loginAsAdmin(otherOrganization);

executeRequest(group, otherOrganization, SYSTEM_ADMIN);

db.rootFlag().verify(rootByUserPermissionUser, true);
db.rootFlag().verify(rootByGroupPermissionUser, true);
db.rootFlag().verify(notRootUser, false);
db.rootFlag().verifyUnchanged(notInGroupUser);
}

private void executeRequest(GroupDto groupDto, OrganizationDto organizationDto, String permission) throws Exception {
newRequest()
.setParam(PARAM_GROUP_NAME, groupDto.getName())
.setParam(PARAM_PERMISSION, permission)
.setParam(PARAM_ORGANIZATION_KEY, organizationDto.getKey())
.execute();
}

private void executeRequest(GroupDto groupDto, String permission) throws Exception {
newRequest()
.setParam(PARAM_GROUP_NAME, groupDto.getName())
.setParam(PARAM_PERMISSION, permission)
.execute();
}

private WsTester.TestRequest newRequest() {
return wsTester.newPostRequest(CONTROLLER, ACTION);
}

+ 89
- 0
server/sonar-server/src/test/java/org/sonar/server/permission/ws/AddUserActionTest.java View File

@@ -19,15 +19,18 @@
*/
package org.sonar.server.permission.ws;

import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Test;
import org.sonar.api.web.UserRole;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.exceptions.ServerException;
import org.sonar.server.ws.WsTester;

import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.api.web.UserRole.ISSUE_ADMIN;
@@ -37,6 +40,7 @@ import static org.sonar.db.component.ComponentTesting.newProjectDto;
import static org.sonar.db.component.ComponentTesting.newView;
import static org.sonar.server.permission.ws.AddUserAction.ACTION;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.CONTROLLER;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_ORGANIZATION_KEY;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PERMISSION;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_ID;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_KEY;
@@ -244,4 +248,89 @@ public class AddUserActionTest extends BasePermissionWsTest<AddUserAction> {

assertThat(db.users().selectUserPermissions(user, project)).containsOnly(ISSUE_ADMIN);
}

@Test
public void sets_root_flag_to_true_when_adding_user_admin_permission_without_org_parameter() throws Exception {
UserDto rootByUserPermissionUser = db.users().insertRootByUserPermission();
UserDto rootByGroupPermissionUser = db.users().insertRootByGroupPermission();
UserDto notRootUser = db.users().insertUser();
loginAsAdminOnDefaultOrganization();

executeRequest(notRootUser, SYSTEM_ADMIN);
db.rootFlag().verify(notRootUser, true);
db.rootFlag().verifyUnchanged(rootByUserPermissionUser);
db.rootFlag().verifyUnchanged(rootByGroupPermissionUser);

executeRequest(rootByUserPermissionUser, SYSTEM_ADMIN);
db.rootFlag().verify(notRootUser, true);
db.rootFlag().verifyUnchanged(rootByUserPermissionUser); // because already has specified user permission
db.rootFlag().verifyUnchanged(rootByGroupPermissionUser);

executeRequest(rootByGroupPermissionUser, SYSTEM_ADMIN);
db.rootFlag().verify(notRootUser, true);
db.rootFlag().verifyUnchanged(rootByUserPermissionUser);
db.rootFlag().verify(rootByGroupPermissionUser, true);
}

@Test
public void sets_root_flag_to_true_when_adding_user_admin_permission_with_default_organization_uuid() throws Exception {
UserDto rootByUserPermissionUser = db.users().insertRootByUserPermission();
UserDto rootByGroupPermissionUser = db.users().insertRootByGroupPermission();
UserDto notRootUser = db.users().insertUser();
loginAsAdminOnDefaultOrganization();

executeRequest(notRootUser, SYSTEM_ADMIN, db.getDefaultOrganization());
db.rootFlag().verify(notRootUser, true);
db.rootFlag().verifyUnchanged(rootByUserPermissionUser);
db.rootFlag().verifyUnchanged(rootByGroupPermissionUser);

executeRequest(rootByUserPermissionUser, SYSTEM_ADMIN, db.getDefaultOrganization());
db.rootFlag().verify(notRootUser, true);
db.rootFlag().verifyUnchanged(rootByUserPermissionUser); // because already has specified user permission
db.rootFlag().verifyUnchanged(rootByGroupPermissionUser);

executeRequest(rootByGroupPermissionUser, SYSTEM_ADMIN, db.getDefaultOrganization());
db.rootFlag().verify(notRootUser, true);
db.rootFlag().verifyUnchanged(rootByUserPermissionUser);
db.rootFlag().verify(rootByGroupPermissionUser, true);
}

@Test
public void does_not_set_root_flag_when_adding_user_admin_permission_with_other_organization_uuid() throws Exception {
OrganizationDto otherOrganization = db.organizations().insert();
UserDto rootByUserPermissionUser = db.users().insertRootByUserPermission();
UserDto rootByGroupPermissionUser = db.users().insertRootByGroupPermission();
UserDto notRootUser = db.users().insertUser();
loginAsAdmin(otherOrganization);

executeRequest(notRootUser, SYSTEM_ADMIN, otherOrganization);
db.rootFlag().verify(notRootUser, false);
db.rootFlag().verifyUnchanged(rootByUserPermissionUser);
db.rootFlag().verifyUnchanged(rootByGroupPermissionUser);

executeRequest(rootByUserPermissionUser, SYSTEM_ADMIN, otherOrganization);
db.rootFlag().verify(notRootUser, false);
db.rootFlag().verifyUnchanged(rootByUserPermissionUser); // because already has specified permission
db.rootFlag().verifyUnchanged(rootByGroupPermissionUser);

executeRequest(rootByGroupPermissionUser, SYSTEM_ADMIN, otherOrganization);
db.rootFlag().verify(notRootUser, false);
db.rootFlag().verifyUnchanged(rootByUserPermissionUser);
db.rootFlag().verify(rootByGroupPermissionUser, true);
}

private void executeRequest(UserDto userDto, String permission) throws Exception {
executeRequest(userDto, permission, null);
}

private void executeRequest(UserDto userDto, String permission, @Nullable OrganizationDto organizationDto) throws Exception {
WsTester.TestRequest request = wsTester.newPostRequest(CONTROLLER, ACTION)
.setParam(PARAM_USER_LOGIN, userDto.getLogin())
.setParam(PARAM_PERMISSION, permission);
if (organizationDto != null) {
request.setParam(PARAM_ORGANIZATION_KEY, organizationDto.getKey());
}
request.execute();
}

}

+ 2
- 2
server/sonar-server/src/test/java/org/sonar/server/permission/ws/BasePermissionWsTest.java View File

@@ -78,8 +78,8 @@ public abstract class BasePermissionWsTest<A extends PermissionsWsAction> {
protected PermissionUpdater newPermissionUpdater() {
return new PermissionUpdater(db.getDbClient(),
mock(IssueAuthorizationIndexer.class),
new UserPermissionChanger(db.getDbClient()),
new GroupPermissionChanger(db.getDbClient()));
new UserPermissionChanger(db.getDbClient(), defaultOrganizationProvider),
new GroupPermissionChanger(db.getDbClient(), defaultOrganizationProvider));
}

protected PermissionTemplateDto insertTemplate() {

+ 71
- 8
server/sonar-server/src/test/java/org/sonar/server/permission/ws/RemoveGroupActionTest.java View File

@@ -23,7 +23,9 @@ import org.junit.Before;
import org.junit.Test;
import org.sonar.api.web.UserRole;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.user.GroupDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
@@ -43,6 +45,7 @@ import static org.sonar.server.permission.ws.RemoveGroupAction.ACTION;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.CONTROLLER;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_ID;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_NAME;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_ORGANIZATION_KEY;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PERMISSION;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_ID;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_KEY;
@@ -152,10 +155,7 @@ public class RemoveGroupActionTest extends BasePermissionWsTest<RemoveGroupActio
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Last group with permission 'admin'. Permission cannot be removed.");

newRequest()
.setParam(PARAM_GROUP_NAME, aGroup.getName())
.setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
.execute();
executeRequest(aGroup, SYSTEM_ADMIN);
}

@Test
@@ -179,10 +179,7 @@ public class RemoveGroupActionTest extends BasePermissionWsTest<RemoveGroupActio
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Invalid global permission 'issueadmin'. Valid values are [admin, profileadmin, gateadmin, scan, provisioning]");

newRequest()
.setParam(PARAM_GROUP_NAME, aGroup.getName())
.setParam(PARAM_PERMISSION, ISSUE_ADMIN)
.execute();
executeRequest(aGroup, ISSUE_ADMIN);
}

@Test
@@ -266,6 +263,72 @@ public class RemoveGroupActionTest extends BasePermissionWsTest<RemoveGroupActio
.execute();
}

@Test
public void sets_root_flag_to_false_on_all_users_in_group_when_removing_admin_permission_from_group_of_default_organization_without_org_param() throws Exception {
UserDto lastAdminUser = db.users().insertRootByUserPermission();
GroupDto adminGroup = db.users().insertAdminGroup();
UserDto user1 = db.users().insertRootByGroupPermission("user1", adminGroup);
UserDto user2 = db.users().insertRootByGroupPermission("user2", adminGroup);
loginAsAdminOnDefaultOrganization();

executeRequest(adminGroup, SYSTEM_ADMIN);

db.rootFlag().verify(user1, false);
db.rootFlag().verify(user2, false);
db.rootFlag().verifyUnchanged(lastAdminUser);
}

@Test
public void sets_root_flag_to_false_on_all_users_in_group_when_removing_admin_permission_from_group_of_default_organization_with_org_param() throws Exception {
UserDto lastAdminUser = db.users().insertRootByUserPermission();
GroupDto adminGroup = db.users().insertAdminGroup();
UserDto user1 = db.users().insertRootByGroupPermission("user1", adminGroup);
UserDto user2 = db.users().insertRootByGroupPermission("user2", adminGroup);
loginAsAdminOnDefaultOrganization();

executeRequest(adminGroup, db.getDefaultOrganization(), SYSTEM_ADMIN);

db.rootFlag().verify(user1, false);
db.rootFlag().verify(user2, false);
db.rootFlag().verifyUnchanged(lastAdminUser);
}

@Test
public void does_not_set_root_flag_to_false_on_all_users_in_group_when_removing_admin_permission_from_group_of_other_organization() throws Exception {
OrganizationDto otherOrganization = db.organizations().insert();
UserDto lastAdmin = db.users().insertUser();
db.users().insertPermissionOnUser(otherOrganization, lastAdmin, SYSTEM_ADMIN);
GroupDto adminGroup = db.users().insertAdminGroup(otherOrganization);
UserDto rootByUserPermissionUser = db.users().insertRootByUserPermission();
UserDto rootByGroupPermissionUser = db.users().insertRootByGroupPermission();
UserDto inAdminGroupUser = db.users().insertUser();
UserDto notInGroupUser = db.users().insertUser();
db.users().insertMembers(adminGroup, rootByUserPermissionUser, rootByGroupPermissionUser, inAdminGroupUser);
loginAsAdmin(otherOrganization);

executeRequest(adminGroup, otherOrganization, SYSTEM_ADMIN);

db.rootFlag().verify(rootByUserPermissionUser, true);
db.rootFlag().verify(rootByGroupPermissionUser, true);
db.rootFlag().verify(inAdminGroupUser, false);
db.rootFlag().verifyUnchanged(notInGroupUser);
}

private void executeRequest(GroupDto groupDto, String permission) throws Exception {
newRequest()
.setParam(PARAM_GROUP_NAME, groupDto.getName())
.setParam(PARAM_PERMISSION, permission)
.execute();
}

private void executeRequest(GroupDto groupDto, OrganizationDto organizationDto, String permission) throws Exception {
newRequest()
.setParam(PARAM_GROUP_NAME, groupDto.getName())
.setParam(PARAM_PERMISSION, permission)
.setParam(PARAM_ORGANIZATION_KEY, organizationDto.getKey())
.execute();
}

@Test
public void removing_global_permission_fails_if_not_administrator_of_organization() throws Exception {
userSession.login();

+ 68
- 19
server/sonar-server/src/test/java/org/sonar/server/permission/ws/RemoveUserActionTest.java View File

@@ -42,6 +42,7 @@ import static org.sonar.db.component.ComponentTesting.newProjectDto;
import static org.sonar.db.component.ComponentTesting.newView;
import static org.sonar.server.permission.ws.RemoveUserAction.ACTION;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.CONTROLLER;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_ORGANIZATION_KEY;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PERMISSION;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_ID;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_KEY;
@@ -69,7 +70,7 @@ public class RemoveUserActionTest extends BasePermissionWsTest<RemoveUserAction>
public void remove_permission_from_user() throws Exception {
db.users().insertPermissionOnUser(user, PROVISIONING);
db.users().insertPermissionOnUser(user, QUALITY_GATE_ADMIN);
loginAsAdmin();
loginAsAdminOnDefaultOrganization();

wsTester.newPostRequest(CONTROLLER, ACTION)
.setParam(PARAM_USER_LOGIN, user.getLogin())
@@ -83,7 +84,7 @@ public class RemoveUserActionTest extends BasePermissionWsTest<RemoveUserAction>
public void fail_to_remove_admin_permission_if_last_admin() throws Exception {
db.users().insertPermissionOnUser(user, CODEVIEWER);
db.users().insertPermissionOnUser(user, ADMIN);
loginAsAdmin();
loginAsAdminOnDefaultOrganization();

expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Last user with 'admin' permission. Permission cannot be removed.");
@@ -99,7 +100,7 @@ public class RemoveUserActionTest extends BasePermissionWsTest<RemoveUserAction>
ComponentDto project = db.components().insertComponent(newProjectDto(A_PROJECT_UUID).setKey(A_PROJECT_KEY));
db.users().insertProjectPermissionOnUser(user, CODEVIEWER, project);
db.users().insertProjectPermissionOnUser(user, ISSUE_ADMIN, project);
loginAsAdmin();
loginAsAdminOnDefaultOrganization();

wsTester.newPostRequest(CONTROLLER, ACTION)
.setParam(PARAM_USER_LOGIN, user.getLogin())
@@ -115,7 +116,7 @@ public class RemoveUserActionTest extends BasePermissionWsTest<RemoveUserAction>
ComponentDto project = db.components().insertComponent(newProjectDto(A_PROJECT_UUID).setKey(A_PROJECT_KEY));
db.users().insertProjectPermissionOnUser(user, ISSUE_ADMIN, project);
db.users().insertProjectPermissionOnUser(user, CODEVIEWER, project);
loginAsAdmin();
loginAsAdminOnDefaultOrganization();

wsTester.newPostRequest(CONTROLLER, ACTION)
.setParam(PARAM_USER_LOGIN, user.getLogin())
@@ -131,7 +132,7 @@ public class RemoveUserActionTest extends BasePermissionWsTest<RemoveUserAction>
ComponentDto view = db.components().insertComponent(newView("view-uuid").setKey("view-key"));
db.users().insertProjectPermissionOnUser(user, ISSUE_ADMIN, view);
db.users().insertProjectPermissionOnUser(user, CODEVIEWER, view);
loginAsAdmin();
loginAsAdminOnDefaultOrganization();

wsTester.newPostRequest(CONTROLLER, ACTION)
.setParam(PARAM_USER_LOGIN, user.getLogin())
@@ -144,7 +145,7 @@ public class RemoveUserActionTest extends BasePermissionWsTest<RemoveUserAction>

@Test
public void fail_when_project_does_not_exist() throws Exception {
loginAsAdmin();
loginAsAdminOnDefaultOrganization();

expectedException.expect(NotFoundException.class);

@@ -157,7 +158,7 @@ public class RemoveUserActionTest extends BasePermissionWsTest<RemoveUserAction>

@Test
public void fail_when_project_permission_without_permission() throws Exception {
loginAsAdmin();
loginAsAdminOnDefaultOrganization();

expectedException.expect(BadRequestException.class);

@@ -170,7 +171,7 @@ public class RemoveUserActionTest extends BasePermissionWsTest<RemoveUserAction>
@Test
public void fail_when_component_is_not_a_project() throws Exception {
db.components().insertComponent(newFileDto(newProjectDto(), null, "file-uuid"));
loginAsAdmin();
loginAsAdminOnDefaultOrganization();

expectedException.expect(BadRequestException.class);

@@ -183,7 +184,7 @@ public class RemoveUserActionTest extends BasePermissionWsTest<RemoveUserAction>

@Test
public void fail_when_get_request() throws Exception {
loginAsAdmin();
loginAsAdminOnDefaultOrganization();

expectedException.expect(ServerException.class);

@@ -195,7 +196,7 @@ public class RemoveUserActionTest extends BasePermissionWsTest<RemoveUserAction>

@Test
public void fail_when_user_login_is_missing() throws Exception {
loginAsAdmin();
loginAsAdminOnDefaultOrganization();

expectedException.expect(IllegalArgumentException.class);

@@ -206,7 +207,7 @@ public class RemoveUserActionTest extends BasePermissionWsTest<RemoveUserAction>

@Test
public void fail_when_permission_is_missing() throws Exception {
loginAsAdmin();
loginAsAdminOnDefaultOrganization();

expectedException.expect(IllegalArgumentException.class);

@@ -218,7 +219,7 @@ public class RemoveUserActionTest extends BasePermissionWsTest<RemoveUserAction>
@Test
public void fail_when_project_uuid_and_project_key_are_provided() throws Exception {
ComponentDto project = db.components().insertComponent(newProjectDto(A_PROJECT_UUID).setKey(A_PROJECT_KEY));
loginAsAdmin();
loginAsAdminOnDefaultOrganization();

expectedException.expect(BadRequestException.class);
expectedException.expectMessage("Project id or project key can be provided, not both.");
@@ -231,6 +232,61 @@ public class RemoveUserActionTest extends BasePermissionWsTest<RemoveUserAction>
.execute();
}

@Test
public void sets_root_flag_to_false_when_removing_user_admin_permission_of_default_organization_without_org_parameter() throws Exception {
UserDto lastAdminUser = db.users().insertRootByUserPermission();
UserDto adminUser = db.users().insertRootByUserPermission();
loginAsAdminOnDefaultOrganization();

executeRequest(adminUser, SYSTEM_ADMIN);

db.rootFlag().verify(adminUser, false);
}

@Test
public void sets_root_flag_to_false_when_removing_user_admin_permission_of_default_organization_with_org_parameter() throws Exception {
UserDto lastAdminUser = db.users().insertRootByUserPermission();
UserDto adminUser = db.users().insertRootByUserPermission();
loginAsAdminOnDefaultOrganization();

executeRequest(adminUser, db.getDefaultOrganization(), SYSTEM_ADMIN);

db.rootFlag().verify(adminUser, false);
}

@Test
public void does_not_set_root_flag_to_false_when_removing_user_admin_permission_of_other_organization() throws Exception {
UserDto rootUser = db.users().insertRootByUserPermission();
UserDto notRootUser = db.users().insertUser();
OrganizationDto otherOrganization = db.organizations().insert();
db.users().insertPermissionOnUser(otherOrganization, rootUser, SYSTEM_ADMIN);
db.users().insertPermissionOnUser(otherOrganization, notRootUser, SYSTEM_ADMIN);
loginAsAdmin(otherOrganization);

executeRequest(rootUser, otherOrganization, SYSTEM_ADMIN);
db.rootFlag().verify(rootUser, true);
db.rootFlag().verifyUnchanged(notRootUser);

executeRequest(notRootUser, otherOrganization, SYSTEM_ADMIN);
db.rootFlag().verify(rootUser, true);
db.rootFlag().verify(notRootUser, false);
}

private void executeRequest(UserDto userDto, OrganizationDto organizationDto, String permission) throws Exception {
wsTester.newPostRequest(CONTROLLER, ACTION)
.setParam(PARAM_USER_LOGIN, userDto.getLogin())
.setParam(PARAM_PERMISSION, permission)
.setParam(PARAM_ORGANIZATION_KEY, organizationDto.getKey())
.execute();
}

private void executeRequest(UserDto userDto, String permission) throws Exception {
wsTester.newPostRequest(CONTROLLER, ACTION)
.setParam(PARAM_USER_LOGIN, userDto.getLogin())
.setParam(PARAM_PERMISSION, permission)
.execute();
}

@Test
public void removing_global_permission_fails_if_not_administrator_of_organization() throws Exception {
userSession.login();
@@ -276,11 +332,4 @@ public class RemoveUserActionTest extends BasePermissionWsTest<RemoveUserAction>
assertThat(db.users().selectUserPermissions(user, project)).containsOnly(CODEVIEWER);
}

private void loginAsAdmin() {
loginAsOrganizationAdmin(db.getDefaultOrganization());
}

private void loginAsOrganizationAdmin(OrganizationDto org) {
userSession.login().addOrganizationPermission(org.getUuid(), SYSTEM_ADMIN);
}
}

+ 76
- 8
server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/AddUserActionTest.java View File

@@ -19,16 +19,18 @@
*/
package org.sonar.server.usergroups.ws;

import java.util.List;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.utils.System2;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.db.DbTester;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.user.GroupDto;
import org.sonar.db.user.UserDto;
import org.sonar.db.user.UserMembershipDto;
import org.sonar.db.user.UserMembershipQuery;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.exceptions.UnauthorizedException;
@@ -37,6 +39,7 @@ import org.sonar.server.tester.UserSessionRule;
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.server.usergroups.ws.GroupWsSupport.PARAM_GROUP_NAME;
import static org.sonar.server.usergroups.ws.GroupWsSupport.PARAM_LOGIN;
import static org.sonar.server.usergroups.ws.GroupWsSupport.PARAM_ORGANIZATION_KEY;
@@ -55,7 +58,7 @@ public class AddUserActionTest {

@Before
public void setUp() {
ws = new WsTester(new UserGroupsWs(new AddUserAction(db.getDbClient(), userSession, newGroupWsSupport())));
ws = new WsTester(new UserGroupsWs(new AddUserAction(db.getDbClient(), userSession, newGroupWsSupport(), defaultOrganizationProvider)));
}

@Test
@@ -146,8 +149,8 @@ public class AddUserActionTest {
UserDto user1 = db.users().insertUser();
UserDto user2 = db.users().insertUser();
db.users().insertMember(users, user1);

loginAsAdminOnDefaultOrganization();

newRequest()
.setParam("id", users.getId().toString())
.setParam("login", user2.getLogin())
@@ -161,10 +164,10 @@ public class AddUserActionTest {
@Test
public void fail_if_group_does_not_exist() throws Exception {
UserDto user = db.users().insertUser();
loginAsAdminOnDefaultOrganization();

expectedException.expect(NotFoundException.class);

loginAsAdminOnDefaultOrganization();
newRequest()
.setParam("id", "42")
.setParam("login", user.getLogin())
@@ -174,10 +177,10 @@ public class AddUserActionTest {
@Test
public void fail_if_user_does_not_exist() throws Exception {
GroupDto group = db.users().insertGroup(db.getDefaultOrganization(), "admins");
loginAsAdminOnDefaultOrganization();

expectedException.expect(NotFoundException.class);

loginAsAdminOnDefaultOrganization();
newRequest()
.setParam("id", group.getId().toString())
.setParam("login", "my-admin")
@@ -191,9 +194,54 @@ public class AddUserActionTest {

expectedException.expect(UnauthorizedException.class);

executeRequest(group, user);
}

@Test
public void set_root_flag_to_true_when_adding_user_to_group_of_default_organization_with_admin_permission() throws Exception {
GroupDto group = db.users().insertAdminGroup();
UserDto falselyRootUser = db.users().makeRoot(db.users().insertUser("falselyRootUser"));
UserDto notRootUser = db.users().insertUser("notRootUser");
loginAsAdminOnDefaultOrganization();

executeRequest(group, falselyRootUser);
verifyUserInGroup(falselyRootUser, group);
db.rootFlag().verify(falselyRootUser, true);
verifyUserNotInGroup(notRootUser, group);
db.rootFlag().verifyUnchanged(notRootUser);

executeRequest(group, notRootUser);
verifyUserInGroup(falselyRootUser, group);
db.rootFlag().verify(falselyRootUser, true);
verifyUserInGroup(notRootUser, group);
db.rootFlag().verify(notRootUser, true);
}

@Test
public void does_not_set_root_flag_to_true_when_adding_user_to_group_of_other_organization_with_admin_permission() throws Exception {
OrganizationDto otherOrganization = db.organizations().insert();
GroupDto group = db.users().insertAdminGroup(otherOrganization);
UserDto falselyRootUser = db.users().makeRoot(db.users().insertUser("falselyRootUser"));
UserDto notRootUser = db.users().insertUser("notRootUser");
loginAsAdmin(otherOrganization);

executeRequest(group, falselyRootUser);
verifyUserInGroup(falselyRootUser, group);
db.rootFlag().verify(falselyRootUser, false);
verifyUserNotInGroup(notRootUser, group);
db.rootFlag().verifyUnchanged(notRootUser);

executeRequest(group, notRootUser);
verifyUserInGroup(falselyRootUser, group);
db.rootFlag().verify(falselyRootUser, false);
verifyUserInGroup(notRootUser, group);
db.rootFlag().verify(notRootUser, false);
}

private void executeRequest(GroupDto groupDto, UserDto userDto) throws Exception {
newRequest()
.setParam("id", group.getId().toString())
.setParam("login", user.getLogin())
.setParam("id", groupDto.getId().toString())
.setParam("login", userDto.getLogin())
.execute();
}

@@ -223,11 +271,31 @@ public class AddUserActionTest {
}

private void loginAsAdmin(OrganizationDto org) {
userSession.login().addOrganizationPermission(org.getUuid(), GlobalPermissions.SYSTEM_ADMIN);
userSession.login().addOrganizationPermission(org.getUuid(), SYSTEM_ADMIN);
}

private GroupWsSupport newGroupWsSupport() {
return new GroupWsSupport(db.getDbClient(), defaultOrganizationProvider);
}

private void verifyUserInGroup(UserDto userDto, GroupDto groupDto) {
assertThat(isUserInGroup(userDto, groupDto))
.as("user '%s' is a member of group '%s' of organization '%s'", userDto.getLogin(), groupDto.getName(), groupDto.getOrganizationUuid())
.isTrue();
}

private void verifyUserNotInGroup(UserDto userDto, GroupDto groupDto) {
assertThat(isUserInGroup(userDto, groupDto))
.as("user '%s' is not a member of group '%s' of organization '%s'", userDto.getLogin(), groupDto.getName(), groupDto.getOrganizationUuid())
.isFalse();
}

private boolean isUserInGroup(UserDto userDto, GroupDto groupDto) {
List<UserMembershipDto> members = db.getDbClient().groupMembershipDao()
.selectMembers(db.getSession(), UserMembershipQuery.builder().groupId(groupDto.getId()).membership(UserMembershipQuery.IN).build(), 0, Integer.MAX_VALUE);
return members
.stream()
.anyMatch(dto -> dto.getLogin().equals(userDto.getLogin()));
}

}

+ 102
- 1
server/sonar-server/src/test/java/org/sonar/server/usergroups/ws/RemoveUserActionTest.java View File

@@ -19,6 +19,7 @@
*/
package org.sonar.server.usergroups.ws;

import java.util.List;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -29,6 +30,8 @@ import org.sonar.db.DbTester;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.user.GroupDto;
import org.sonar.db.user.UserDto;
import org.sonar.db.user.UserMembershipDto;
import org.sonar.db.user.UserMembershipQuery;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.organization.TestDefaultOrganizationProvider;
import org.sonar.server.tester.UserSessionRule;
@@ -54,7 +57,7 @@ public class RemoveUserActionTest {
@Before
public void setUp() {
GroupWsSupport groupSupport = new GroupWsSupport(db.getDbClient(), defaultOrganizationProvider);
ws = new WsTester(new UserGroupsWs(new RemoveUserAction(db.getDbClient(), userSession, groupSupport)));
ws = new WsTester(new UserGroupsWs(new RemoveUserAction(db.getDbClient(), userSession, groupSupport, defaultOrganizationProvider)));
}

@Test
@@ -167,6 +170,72 @@ public class RemoveUserActionTest {
.execute();
}

@Test
public void sets_root_flag_to_false_when_removing_user_from_group_of_default_organization_with_admin_permission() throws Exception {
GroupDto adminGroup = db.users().insertAdminGroup();
UserDto user1 = db.users().insertRootByGroupPermission(adminGroup);
UserDto user2 = db.users().insertRootByGroupPermission(adminGroup);
loginAsAdmin();

executeRequest(adminGroup, user1);
verifyUserNotInGroup(user1, adminGroup);
verifyRootFlagUpdated(user1, false);
verifyUserInGroup(user2, adminGroup);
verifyUnchanged(user2);

executeRequest(adminGroup, user2);
verifyUserNotInGroup(user1, adminGroup);
verifyRootFlag(user1, false);
verifyUserNotInGroup(user2, adminGroup);
verifyRootFlagUpdated(user2, false);
}

@Test
public void does_not_set_root_flag_to_false_when_removing_user_from_group_of_default_organization_and_user_is_admin_of_default_organization_another_way()
throws Exception {
GroupDto adminGroup1 = db.users().insertAdminGroup();
UserDto adminUserByUserPermission = db.users().insertRootByUserPermission("adminUserByUserPermission");
UserDto adminUserByTwoGroups = db.users().insertRootByGroupPermission("adminUserByTwoGroups", adminGroup1);
UserDto adminUserBySingleGroup = db.users().insertUser("adminUserBySingleGroup");
GroupDto adminGroup2 = db.users().insertAdminGroup();
db.users().insertMembers(adminGroup2, adminUserByUserPermission, adminUserByTwoGroups, adminUserBySingleGroup);
loginAsAdmin();

executeRequest(adminGroup2, adminUserByUserPermission);
verifyUserNotInGroup(adminUserByUserPermission, adminGroup2);
verifyRootFlagUpdated(adminUserByUserPermission, true);
verifyUserInGroup(adminUserByTwoGroups, adminGroup2);
verifyUserInGroup(adminUserByTwoGroups, adminGroup1);
verifyUnchanged(adminUserByTwoGroups);
verifyUserInGroup(adminUserBySingleGroup, adminGroup2);
verifyUnchanged(adminUserBySingleGroup);

executeRequest(adminGroup2, adminUserByTwoGroups);
verifyUserNotInGroup(adminUserByUserPermission, adminGroup2);
verifyRootFlag(adminUserByUserPermission, true);
verifyUserNotInGroup(adminUserByTwoGroups, adminGroup2);
verifyUserInGroup(adminUserByTwoGroups, adminGroup1);
verifyRootFlagUpdated(adminUserByTwoGroups, true);
verifyUserInGroup(adminUserBySingleGroup, adminGroup2);
verifyUnchanged(adminUserBySingleGroup);

executeRequest(adminGroup2, adminUserBySingleGroup);
verifyUserNotInGroup(adminUserByUserPermission, adminGroup2);
verifyRootFlag(adminUserByUserPermission, true);
verifyUserNotInGroup(adminUserByTwoGroups, adminGroup2);
verifyUserInGroup(adminUserByTwoGroups, adminGroup1);
verifyRootFlagUpdated(adminUserByTwoGroups, true);
verifyUserNotInGroup(adminUserBySingleGroup, adminGroup2);
verifyRootFlagUpdated(adminUserBySingleGroup, false);
}

private void executeRequest(GroupDto group, UserDto user) throws Exception {
newRequest()
.setParam("id", group.getId().toString())
.setParam("login", user.getLogin())
.execute();
}

private WsTester.TestRequest newRequest() {
return ws.newPostRequest("api/user_groups", "remove_user");
}
@@ -175,4 +244,36 @@ public class RemoveUserActionTest {
userSession.login("admin").setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
}

private void verifyUnchanged(UserDto user) {
db.rootFlag().verifyUnchanged(user);
}

private void verifyRootFlagUpdated(UserDto userDto, boolean root) {
db.rootFlag().verify(userDto, root);
}

private void verifyRootFlag(UserDto userDto, boolean root) {
db.rootFlag().verify(userDto, root);
}

private void verifyUserInGroup(UserDto userDto, GroupDto groupDto) {
assertThat(isUserInGroup(userDto, groupDto))
.as("user '%s' is a member of group '%s' of organization '%s'", userDto.getLogin(), groupDto.getName(), groupDto.getOrganizationUuid())
.isTrue();
}

private void verifyUserNotInGroup(UserDto userDto, GroupDto groupDto) {
assertThat(isUserInGroup(userDto, groupDto))
.as("user '%s' is not a member of group '%s' of organization '%s'", userDto.getLogin(), groupDto.getName(), groupDto.getOrganizationUuid())
.isFalse();
}

private boolean isUserInGroup(UserDto userDto, GroupDto groupDto) {
List<UserMembershipDto> members = db.getDbClient().groupMembershipDao()
.selectMembers(db.getSession(), UserMembershipQuery.builder().groupId(groupDto.getId()).membership(UserMembershipQuery.IN).build(), 0, Integer.MAX_VALUE);
return members
.stream()
.anyMatch(dto -> dto.getLogin().equals(userDto.getLogin()));
}

}

+ 11
- 0
sonar-db/src/main/java/org/sonar/db/user/GroupDao.java View File

@@ -120,6 +120,17 @@ public class GroupDao implements Dao {
return mapper(dbSession).selectByOrganizationUuid(organizationUuid);
}

/**
* Ensures all users of the specified group have its root flag set or unset depending on whether each of them have the
* 'admin' permission in the default organization or not.
*/
public void updateRootFlagOfUsersInGroupFromPermissions(DbSession dbSession, long groupId, String defaultOrganizationUuid) {
long now = system.now();
GroupMapper mapper = mapper(dbSession);
mapper.updateRootUsersOfGroup(groupId, defaultOrganizationUuid, now);
mapper.updateNonRootUsersOfGroup(groupId, defaultOrganizationUuid, now);
}

private static GroupMapper mapper(DbSession session) {
return session.getMapper(GroupMapper.class);
}

+ 8
- 0
sonar-db/src/main/java/org/sonar/db/user/GroupMapper.java View File

@@ -51,4 +51,12 @@ public interface GroupMapper {
GroupDto selectByName(@Param("organizationUuid") String organizationUuid, @Param("name") String name);

List<GroupDto> selectByOrganizationUuid(@Param("organizationUuid") String organizationUuid);

int updateRootUsersOfGroup(@Param("groupId") long groupId,
@Param("defaultOrganizationUuid") String defaultOrganizationUuid,
@Param("now") long now);

int updateNonRootUsersOfGroup(@Param("groupId") long groupId,
@Param("defaultOrganizationUuid") String defaultOrganizationUuid,
@Param("now") long now);
}

+ 25
- 14
sonar-db/src/main/java/org/sonar/db/user/UserDao.java View File

@@ -57,7 +57,7 @@ public class UserDao implements Dao {
}

public UserDto selectUserById(DbSession session, long userId) {
return getMapper(session).selectUser(userId);
return mapper(session).selectUser(userId);
}

/**
@@ -67,7 +67,7 @@ public class UserDao implements Dao {
* Used by the Governance plugin
*/
public List<UserDto> selectByIds(DbSession session, Collection<Long> ids) {
return executeLargeInputs(ids, getMapper(session)::selectByIds);
return executeLargeInputs(ids, mapper(session)::selectByIds);
}

/**
@@ -84,7 +84,7 @@ public class UserDao implements Dao {

@CheckForNull
public UserDto selectActiveUserByLogin(DbSession session, String login) {
UserMapper mapper = getMapper(session);
UserMapper mapper = mapper(session);
return mapper.selectUserByLogin(login);
}

@@ -93,7 +93,7 @@ public class UserDao implements Dao {
* if list of logins is empty, without any db round trips.
*/
public List<UserDto> selectByLogins(DbSession session, Collection<String> logins) {
return executeLargeInputs(logins, getMapper(session)::selectByLogins);
return executeLargeInputs(logins, mapper(session)::selectByLogins);
}

/**
@@ -124,25 +124,25 @@ public class UserDao implements Dao {
}

public List<UserDto> selectUsers(DbSession dbSession, UserQuery query) {
return getMapper(dbSession).selectUsers(query);
return mapper(dbSession).selectUsers(query);
}

public long countRootUsersButLogin(DbSession dbSession, String login) {
return getMapper(dbSession).countRootUsersButLogin(login);
return mapper(dbSession).countRootUsersButLogin(login);
}

public UserDto insert(DbSession session, UserDto dto) {
getMapper(session).insert(dto);
mapper(session).insert(dto);
return dto;
}

public UserDto update(DbSession session, UserDto dto) {
getMapper(session).update(dto);
mapper(session).update(dto);
return dto;
}

public void setRoot(DbSession session, String login, boolean root) {
getMapper(session).setRoot(login, root, system2.now());
mapper(session).setRoot(login, root, system2.now());
}

/**
@@ -150,7 +150,7 @@ public class UserDao implements Dao {
* @return false if the user does not exist, true if the existing user has been deactivated
*/
public boolean deactivateUserByLogin(DbSession dbSession, String login) {
UserMapper mapper = getMapper(dbSession);
UserMapper mapper = mapper(dbSession);
UserDto dto = mapper.selectUserByLogin(login);
if (dto == null) {
return false;
@@ -173,7 +173,7 @@ public class UserDao implements Dao {

@CheckForNull
public UserDto selectByLogin(DbSession session, String login) {
return getMapper(session).selectByLogin(login);
return mapper(session).selectByLogin(login);
}

public UserDto selectOrFailByLogin(DbSession session, String login) {
@@ -188,7 +188,7 @@ public class UserDao implements Dao {
String like = new StringBuilder().append("%")
.append(UserDto.SCM_ACCOUNTS_SEPARATOR).append(scmAccountOrLoginOrEmail)
.append(UserDto.SCM_ACCOUNTS_SEPARATOR).append("%").toString();
return getMapper(session).selectNullableByScmAccountOrLoginOrEmail(scmAccountOrLoginOrEmail, like);
return mapper(session).selectNullableByScmAccountOrLoginOrEmail(scmAccountOrLoginOrEmail, like);
}

/**
@@ -197,10 +197,21 @@ public class UserDao implements Dao {
* Please note that email is case insensitive, result for searching 'mail@email.com' or 'Mail@Email.com' will be the same
*/
public boolean doesEmailExist(DbSession dbSession, String email) {
return getMapper(dbSession).countByEmail(email.toLowerCase(Locale.ENGLISH)) > 0;
return mapper(dbSession).countByEmail(email.toLowerCase(Locale.ENGLISH)) > 0;
}

private static UserMapper getMapper(DbSession session) {
/**
* Ensures the specified user has its root flag set or unset depending on whether the user has the 'admin' permission
* in the default organization or not.
*/
public void updateRootFlagFromPermissions(DbSession dbSession, long userId, String defaultOrganizationUuid) {
long now = system2.now();
UserMapper mapper = mapper(dbSession);
mapper.updateRootUser(userId, defaultOrganizationUuid, now);
mapper.updateNonRootUser(userId, defaultOrganizationUuid, now);
}

protected UserMapper mapper(DbSession session) {
return session.getMapper(UserMapper.class);
}


+ 5
- 0
sonar-db/src/main/java/org/sonar/db/user/UserMapper.java View File

@@ -85,4 +85,9 @@ public interface UserMapper {
void deletePropertiesMatchingLogin(@Param("propertyKeys") List<String> propertyKeys, @Param("login") String login);

void deactivateUser(@Param("id") long userId, @Param("now") long now);

int updateRootUser(@Param("userId") long userId, @Param("defaultOrganizationUuid") String defaultOrganizationUuid, @Param("now") long now);

int updateNonRootUser(@Param("userId") long userId, @Param("defaultOrganizationUuid") String defaultOrganizationUuid, @Param("now") long now);

}

+ 86
- 0
sonar-db/src/main/resources/org/sonar/db/user/GroupMapper.xml View File

@@ -118,6 +118,92 @@
order by upper(g.name)
</select>

<update id="updateRootUsersOfGroup">
update users u set
is_root = ${_true},
updated_at = #{now,jdbcType=BIGINT}
where
u.id in (<include refid="userIdsForGroupId"/>)
and exists (
<include refid="userPermissionAdminInDefaultOrganizationForUser"/>
union
<include refid="groupPermissionAdminInDefaultOrganizationForUser"/>
)
</update>

<update id="updateRootUsersOfGroup" databaseId="mssql">
update u set
is_root = ${_true},
updated_at = #{now,jdbcType=BIGINT}
from users u
where
u.id in (<include refid="userIdsForGroupId"/>)
and exists (
<include refid="userPermissionAdminInDefaultOrganizationForUser"/>
union
<include refid="groupPermissionAdminInDefaultOrganizationForUser"/>
)
</update>

<update id="updateNonRootUsersOfGroup">
update users u set
is_root = ${_false},
updated_at = #{now,jdbcType=BIGINT}
where
u.id in (<include refid="userIdsForGroupId"/>)
and not exists (
<include refid="userPermissionAdminInDefaultOrganizationForUser"/>
union
<include refid="groupPermissionAdminInDefaultOrganizationForUser"/>
)
</update>

<update id="updateNonRootUsersOfGroup" databaseId="mssql">
update u set
is_root = ${_false},
updated_at = #{now,jdbcType=BIGINT}
from users u
where
u.id in (<include refid="userIdsForGroupId"/>)
and not exists (
<include refid="userPermissionAdminInDefaultOrganizationForUser"/>
union
<include refid="groupPermissionAdminInDefaultOrganizationForUser"/>
)
</update>

<sql id="userIdsForGroupId">
select
gu.user_id
from
groups_users gu
where
gu.group_id = #{groupId,jdbcType=BIGINT}
</sql>

<sql id="userPermissionAdminInDefaultOrganizationForUser">
select
1
from
user_roles ur
where
ur.user_id = u.id
and ur.role = 'admin'
and ur.resource_id is null
and ur.organization_uuid = #{defaultOrganizationUuid,jdbcType=VARCHAR}
</sql>

<sql id="groupPermissionAdminInDefaultOrganizationForUser">
select
1
from
groups_users gu,
group_roles gr
where
gu.user_id = u.id
and gu.group_id = gr.group_id
and gr.role = 'admin'
and gr.resource_id is null
and gr.organization_uuid = #{defaultOrganizationUuid,jdbcType=VARCHAR}
</sql>
</mapper>

+ 80
- 0
sonar-db/src/main/resources/org/sonar/db/user/UserMapper.xml View File

@@ -234,4 +234,84 @@
login = #{login}
</insert>

<update id="updateRootUser">
update users u set
is_root = ${_true},
updated_at = #{now,jdbcType=BIGINT}
where
u.id = #{userId,jdbcType=BIGINT}
and exists (
<include refid="userPermissionAdminInDefaultOrganizationForUser"/>
union
<include refid="groupPermissionAdminInDefaultOrganizationForUser"/>
)
</update>

<update id="updateRootUser" databaseId="mssql">
update u set
is_root = ${_true},
updated_at = #{now,jdbcType=BIGINT}
from users u
where
u.id = #{userId,jdbcType=BIGINT}
and exists (
<include refid="userPermissionAdminInDefaultOrganizationForUser"/>
union
<include refid="groupPermissionAdminInDefaultOrganizationForUser"/>
)
</update>

<update id="updateNonRootUser">
update users u set
is_root = ${_false},
updated_at = #{now,jdbcType=BIGINT}
where
u.id = #{userId,jdbcType=BIGINT}
and not exists (
<include refid="userPermissionAdminInDefaultOrganizationForUser"/>
union
<include refid="groupPermissionAdminInDefaultOrganizationForUser"/>
)
</update>

<update id="updateNonRootUser" databaseId="mssql">
update u set
is_root = ${_false},
updated_at = #{now,jdbcType=BIGINT}
from users u
where
u.id = #{userId,jdbcType=BIGINT}
and not exists (
<include refid="userPermissionAdminInDefaultOrganizationForUser"/>
union
<include refid="groupPermissionAdminInDefaultOrganizationForUser"/>
)
</update>

<sql id="userPermissionAdminInDefaultOrganizationForUser">
select
1
from
user_roles ur
where
ur.user_id = u.id
and ur.role = 'admin'
and ur.resource_id is null
and ur.organization_uuid = #{defaultOrganizationUuid,jdbcType=VARCHAR}
</sql>

<sql id="groupPermissionAdminInDefaultOrganizationForUser">
select
1
from
groups_users gu,
group_roles gr
where
gu.user_id = u.id
and gu.group_id = gr.group_id
and gr.role = 'admin'
and gr.resource_id is null
and gr.organization_uuid = #{defaultOrganizationUuid,jdbcType=VARCHAR}
</sql>

</mapper>

+ 7
- 0
sonar-db/src/test/java/org/sonar/db/DbTester.java View File

@@ -68,6 +68,7 @@ import org.sonar.db.component.ComponentDbTester;
import org.sonar.db.organization.OrganizationDbTester;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.organization.OrganizationTesting;
import org.sonar.db.user.RootFlagAssertions;
import org.sonar.db.user.UserDbTester;

import static com.google.common.base.Preconditions.checkArgument;
@@ -97,6 +98,7 @@ public class DbTester extends ExternalResource {
private final UserDbTester userTester;
private final ComponentDbTester componentTester;
private final OrganizationDbTester organizationTester;
private final RootFlagAssertions rootFlagAssertions;

private DbTester(System2 system2, @Nullable String schemaPath) {
this.system2 = system2;
@@ -105,6 +107,7 @@ public class DbTester extends ExternalResource {
this.userTester = new UserDbTester(this);
this.componentTester = new ComponentDbTester(this);
this.organizationTester = new OrganizationDbTester(this);
this.rootFlagAssertions = new RootFlagAssertions(this);
}

public static DbTester create(System2 system2) {
@@ -172,6 +175,10 @@ public class DbTester extends ExternalResource {
return organizationTester;
}

public RootFlagAssertions rootFlag() {
return rootFlagAssertions;
}

@Override
protected void after() {
if (session != null) {

+ 196
- 0
sonar-db/src/test/java/org/sonar/db/user/GroupDaoTest.java View File

@@ -32,10 +32,12 @@ import org.sonar.db.DbTester;
import org.sonar.db.organization.OrganizationDto;

import static java.util.Arrays.asList;
import static java.util.Arrays.stream;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
import static org.sonar.db.user.GroupTesting.newGroupDto;

public class GroupDaoTest {
@@ -45,11 +47,14 @@ public class GroupDaoTest {
.setKey("an-org")
.setName("An Org")
.setUuid("abcde");
private static final long DATE_1 = 8_776_543L;
private static final long DATE_2 = 4_776_898L;

private System2 system2 = mock(System2.class);

@Rule
public DbTester db = DbTester.create(system2);

private final DbSession dbSession = db.getSession();
private GroupDao underTest = new GroupDao(system2);

@@ -201,4 +206,195 @@ public class GroupDaoTest {
assertThat(db.countRowsOfTable(dbSession, "groups")).isEqualTo(0);
}

@Test
public void updateRootFlagOfUsersInGroupFromPermissions_sets_root_flag_to_false_if_users_have_no_permission_at_all() {
UserDto[] usersInGroup1 = {
db.users().makeRoot(db.users().insertUser()),
db.users().insertUser()
};
UserDto[] usersInGroup2 = {
db.users().makeRoot(db.users().insertUser()),
db.users().insertUser()
};
UserDto[] usersNotInGroup = {
db.users().makeRoot(db.users().insertUser()),
db.users().insertUser()
};
GroupDto group1 = db.users().insertGroup();
stream(usersInGroup1).forEach(user -> db.users().insertMember(group1, user));
GroupDto group2 = db.users().insertGroup();
stream(usersInGroup2).forEach(user -> db.users().insertMember(group2, user));

call_updateRootFlagFromPermissions(group1, DATE_1);
stream(usersInGroup1).forEach(user -> db.rootFlag().verify(user, false, DATE_1));
stream(usersInGroup2).forEach(db.rootFlag()::verifyUnchanged);
stream(usersNotInGroup).forEach(db.rootFlag()::verifyUnchanged);

call_updateRootFlagFromPermissions(group2, DATE_2);
stream(usersInGroup2).forEach(user -> db.rootFlag().verify(user, false, DATE_2));
stream(usersInGroup1).forEach(user -> db.rootFlag().verify(user, false, DATE_1));
stream(usersNotInGroup).forEach(db.rootFlag()::verifyUnchanged);
}

@Test
public void updateRootFlagOfUsersInGroupFromPermissions_sets_root_flag_to_true_if_users_has_admin_user_permission_on_default_organization() {
UserDto[] usersWithAdminInGroup1 = {
db.users().makeRoot(db.users().insertUser()),
db.users().insertUser(),
};
UserDto[] usersWithoutAdminInGroup1 = {
db.users().makeRoot(db.users().insertUser()),
db.users().insertUser(),
};
UserDto[] usersWithAdminInGroup2 = {
db.users().makeRoot(db.users().insertUser()),
db.users().insertUser()
};
UserDto[] usersWithoutAdminInGroup2 = {
db.users().makeRoot(db.users().insertUser()),
db.users().insertUser()
};
UserDto[] usersNotInGroup = {
db.users().makeRoot(db.users().insertUser()),
db.users().insertUser()
};
GroupDto group1 = db.users().insertGroup();
stream(usersWithAdminInGroup1).forEach(user -> db.users().insertMember(group1, user));
stream(usersWithoutAdminInGroup1).forEach(user -> db.users().insertMember(group1, user));
stream(usersWithAdminInGroup1).forEach(user -> db.users().insertPermissionOnUser(db.getDefaultOrganization(), user, SYSTEM_ADMIN));
GroupDto group2 = db.users().insertGroup();
stream(usersWithAdminInGroup2).forEach(user -> db.users().insertMember(group2, user));
stream(usersWithoutAdminInGroup2).forEach(user -> db.users().insertMember(group2, user));
stream(usersWithAdminInGroup2).forEach(user -> db.users().insertPermissionOnUser(db.getDefaultOrganization(), user, SYSTEM_ADMIN));

call_updateRootFlagFromPermissions(group1, DATE_1);
stream(usersWithAdminInGroup1).forEach(user -> db.rootFlag().verify(user, true, DATE_1));
stream(usersWithoutAdminInGroup1).forEach(user -> db.rootFlag().verify(user, false, DATE_1));
stream(usersWithAdminInGroup2).forEach(db.rootFlag()::verifyUnchanged);
stream(usersWithoutAdminInGroup2).forEach(db.rootFlag()::verifyUnchanged);
stream(usersNotInGroup).forEach(db.rootFlag()::verifyUnchanged);

call_updateRootFlagFromPermissions(group2, DATE_2);
stream(usersWithAdminInGroup1).forEach(user -> db.rootFlag().verify(user, true, DATE_1));
stream(usersWithoutAdminInGroup1).forEach(user -> db.rootFlag().verify(user, false, DATE_1));
stream(usersWithAdminInGroup2).forEach(user -> db.rootFlag().verify(user, true, DATE_2));
stream(usersWithoutAdminInGroup2).forEach(user -> db.rootFlag().verify(user, false, DATE_2));
stream(usersNotInGroup).forEach(db.rootFlag()::verifyUnchanged);
}

@Test
public void updateRootFlagOfUsersInGroupFromPermissions_ignores_permissions_on_anyone_on_default_organization() {
UserDto[] usersWithAdminInGroup1 = {
db.users().makeRoot(db.users().insertUser()),
db.users().insertUser(),
};
UserDto[] usersWithoutAdminInGroup1 = {
db.users().makeRoot(db.users().insertUser()),
db.users().insertUser(),
};
UserDto[] usersWithAdminInGroup2 = {
db.users().makeRoot(db.users().insertUser()),
db.users().insertUser()
};
UserDto[] usersWithoutAdminInGroup2 = {
db.users().makeRoot(db.users().insertUser()),
db.users().insertUser()
};
UserDto[] usersNotInGroup = {
db.users().makeRoot(db.users().insertUser()),
db.users().insertUser()
};
GroupDto group1 = db.users().insertGroup();
stream(usersWithAdminInGroup1).forEach(user -> db.users().insertMember(group1, user));
stream(usersWithoutAdminInGroup1).forEach(user -> db.users().insertMember(group1, user));
GroupDto group2 = db.users().insertGroup();
stream(usersWithAdminInGroup2).forEach(user -> db.users().insertMember(group2, user));
stream(usersWithoutAdminInGroup2).forEach(user -> db.users().insertMember(group2, user));
db.users().insertPermissionOnAnyone(db.getDefaultOrganization(), SYSTEM_ADMIN);

call_updateRootFlagFromPermissions(group1, DATE_1);
stream(usersWithAdminInGroup1).forEach(user -> db.rootFlag().verify(user, false, DATE_1));
stream(usersWithoutAdminInGroup1).forEach(user -> db.rootFlag().verify(user, false, DATE_1));
stream(usersWithAdminInGroup2).forEach(db.rootFlag()::verifyUnchanged);
stream(usersWithoutAdminInGroup2).forEach(db.rootFlag()::verifyUnchanged);
stream(usersNotInGroup).forEach(db.rootFlag()::verifyUnchanged);

call_updateRootFlagFromPermissions(group2, DATE_2);
stream(usersWithAdminInGroup1).forEach(user -> db.rootFlag().verify(user, false, DATE_1));
stream(usersWithoutAdminInGroup1).forEach(user -> db.rootFlag().verify(user, false, DATE_1));
stream(usersWithAdminInGroup2).forEach(user -> db.rootFlag().verify(user, false, DATE_2));
stream(usersWithoutAdminInGroup2).forEach(user -> db.rootFlag().verify(user, false, DATE_2));
stream(usersNotInGroup).forEach(db.rootFlag()::verifyUnchanged);
}

@Test
public void updateRootFlagOfUsersInGroupFromPermissions_ignores_permissions_on_anyone_on_other_organization() {
UserDto[] usersInGroup = {
db.users().makeRoot(db.users().insertUser()),
db.users().insertUser(),
};
UserDto[] usersInOtherGroup = {
db.users().makeRoot(db.users().insertUser()),
db.users().insertUser()
};
UserDto[] usersNotInGroup = {
db.users().makeRoot(db.users().insertUser()),
db.users().insertUser()
};
GroupDto group = db.users().insertGroup();
stream(usersInGroup).forEach(user -> db.users().insertMember(group, user));
OrganizationDto otherOrganization = db.organizations().insert();
GroupDto otherGroup = db.users().insertGroup(otherOrganization);
stream(usersInOtherGroup).forEach(user -> db.users().insertMember(otherGroup, user));
db.users().insertPermissionOnAnyone(otherOrganization, SYSTEM_ADMIN);

call_updateRootFlagFromPermissions(group, DATE_1);
stream(usersInGroup).forEach(user -> db.rootFlag().verify(user, false, DATE_1));
stream(usersInOtherGroup).forEach(db.rootFlag()::verifyUnchanged);
stream(usersNotInGroup).forEach(db.rootFlag()::verifyUnchanged);

call_updateRootFlagFromPermissions(otherGroup, DATE_2);
stream(usersInGroup).forEach(user -> db.rootFlag().verify(user, false, DATE_1));
stream(usersInOtherGroup).forEach(user -> db.rootFlag().verify(user, false, DATE_2));
stream(usersNotInGroup).forEach(db.rootFlag()::verifyUnchanged);
}

@Test
public void updateRootFlagOfUsersInGroupFromPermissions_set_root_flag_to_false_on_users_of_group_of_non_default_organization() {
UserDto[] nonAdminUsers = {
db.users().makeRoot(db.users().insertUser()),
db.users().insertUser()
};
UserDto[] adminPerUserPermissionUsers = {
db.users().makeRoot(db.users().insertUser()),
db.users().insertUser() // incorrectly not root
};
UserDto[] adminPerGroupPermissionUsers = {
db.users().makeRoot(db.users().insertUser()),
db.users().insertUser() // incorrectly not root
};
OrganizationDto otherOrganization = db.organizations().insert();
GroupDto nonAdminGroup = db.users().insertGroup(otherOrganization);
db.users().insertMembers(nonAdminGroup, nonAdminUsers);
db.users().insertMembers(nonAdminGroup, adminPerUserPermissionUsers);
stream(adminPerUserPermissionUsers).forEach(user -> db.users().insertPermissionOnUser(otherOrganization, user, SYSTEM_ADMIN));
GroupDto adminGroup = db.users().insertAdminGroup(otherOrganization);
db.users().insertMembers(adminGroup, adminPerGroupPermissionUsers);

call_updateRootFlagFromPermissions(nonAdminGroup, DATE_2);
stream(nonAdminUsers).forEach(user -> db.rootFlag().verify(user, false, DATE_2));
stream(adminPerUserPermissionUsers).forEach(user -> db.rootFlag().verify(user, false, DATE_2));
stream(adminPerGroupPermissionUsers).forEach(db.rootFlag()::verifyUnchanged);

call_updateRootFlagFromPermissions(adminGroup, DATE_1);
stream(nonAdminUsers).forEach(user -> db.rootFlag().verify(user, false, DATE_2));
stream(adminPerUserPermissionUsers).forEach(user -> db.rootFlag().verify(user, false, DATE_2));
stream(adminPerGroupPermissionUsers).forEach(user -> db.rootFlag().verify(user, false, DATE_1));
}

private void call_updateRootFlagFromPermissions(GroupDto groupDto, long now) {
when(system2.now()).thenReturn(now);
underTest.updateRootFlagOfUsersInGroupFromPermissions(db.getSession(), groupDto.getId(), db.getDefaultOrganization().getUuid());
db.commit();
}
}

+ 56
- 0
sonar-db/src/test/java/org/sonar/db/user/RootFlagAssertions.java View File

@@ -0,0 +1,56 @@
/*
* SonarQube
* Copyright (C) 2009-2016 SonarSource SA
* mailto:contact AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.db.user;

import org.sonar.db.DbTester;

import static org.assertj.core.api.Assertions.assertThat;

public class RootFlagAssertions {
private final DbTester db;

public RootFlagAssertions(DbTester db) {
this.db = db;
}

public void verifyUnchanged(UserDto user) {
verify(user, user.isRoot(), user.getUpdatedAt());
}

public void verify(UserDto userDto, boolean root, long updatedAt) {
UserDto dto = db.getDbClient().userDao().selectByLogin(db.getSession(), userDto.getLogin());
assertThat(dto.isRoot())
.as("Root flag of user '%s' is same as when created", userDto.getLogin())
.isEqualTo(root);
assertThat(dto.getUpdatedAt())
.as("UpdatedAt of user '%s' has not changed since created")
.isEqualTo(updatedAt);
}

public void verify(UserDto userDto, boolean root) {
UserDto dto = db.getDbClient().userDao().selectByLogin(db.getSession(), userDto.getLogin());
assertThat(dto.isRoot())
.as("Root flag of user '%s' is '%s'", userDto.getLogin(), root)
.isEqualTo(root);
assertThat(dto.getUpdatedAt())
.as("UpdatedAt of user '%s' has changed since insertion", userDto.getLogin())
.isNotEqualTo(userDto);
}
}

+ 122
- 1
sonar-db/src/test/java/org/sonar/db/user/UserDaoTest.java View File

@@ -39,6 +39,7 @@ import org.sonar.db.issue.IssueFilterDto;
import org.sonar.db.issue.IssueFilterFavouriteDto;
import org.sonar.db.measure.MeasureFilterDto;
import org.sonar.db.measure.MeasureFilterFavouriteDto;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.permission.UserPermissionDto;
import org.sonar.db.property.PropertyDto;
import org.sonar.db.property.PropertyQuery;
@@ -49,13 +50,16 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
import static org.sonar.db.user.GroupMembershipQuery.IN;
import static org.sonar.db.user.GroupMembershipQuery.builder;
import static org.sonar.db.user.GroupTesting.newGroupDto;
import static org.sonar.db.user.UserTesting.newUserDto;

public class UserDaoTest {
private static final long NOW = 1500000000000L;
private static final long NOW = 1_500_000_000_000L;
private static final long DATE_1 = 1_222_001L;
private static final long DATE_2 = 4_333_555L;

private System2 system2 = mock(System2.class);

@@ -659,6 +663,116 @@ public class UserDaoTest {
assertThat(underTest.selectByLogin(session, inactiveRootUser.getLogin()).isRoot()).isTrue();
}

@Test
public void updateRootFlagFromPermissions_sets_root_flag_to_false_if_user_has_no_permission_at_all() {
UserDto rootUser = db.users().makeRoot(db.users().insertUser());
UserDto notRootUser = db.users().insertUser();

call_updateRootFlagFromPermissions(rootUser, DATE_1);
db.rootFlag().verify(rootUser, false, DATE_1);
db.rootFlag().verify(notRootUser, false, notRootUser.getUpdatedAt());

call_updateRootFlagFromPermissions(notRootUser, DATE_2);
db.rootFlag().verify(rootUser, false, DATE_1);
db.rootFlag().verify(notRootUser, false, DATE_2);
}

@Test
public void updateRootFlagFromPermissions_sets_root_flag_to_true_if_user_has_admin_user_permission_on_default_organization() {
UserDto rootUser = db.users().insertRootByUserPermission();
UserDto incorrectlyNotRootUser = db.users().insertUser();
db.users().insertPermissionOnUser(db.getDefaultOrganization(), incorrectlyNotRootUser, SYSTEM_ADMIN);

call_updateRootFlagFromPermissions(rootUser, DATE_1);
db.rootFlag().verify(rootUser, true, DATE_1);
db.rootFlag().verify(incorrectlyNotRootUser, false, incorrectlyNotRootUser.getUpdatedAt());

call_updateRootFlagFromPermissions(incorrectlyNotRootUser, DATE_2);
db.rootFlag().verify(rootUser, true, DATE_1);
db.rootFlag().verify(incorrectlyNotRootUser, true, DATE_2);
}

@Test
public void updateRootFlagFromPermissions_ignores_permissions_on_anyone_on_default_organization() {
UserDto rootUser = db.users().makeRoot(db.users().insertUser());
UserDto incorrectlyNotRootUser = db.users().insertUser();
db.users().insertPermissionOnAnyone(db.getDefaultOrganization(), SYSTEM_ADMIN);

call_updateRootFlagFromPermissions(rootUser, DATE_1);
db.rootFlag().verify(rootUser, false, DATE_1);
db.rootFlag().verify(incorrectlyNotRootUser, false, incorrectlyNotRootUser.getUpdatedAt());

call_updateRootFlagFromPermissions(incorrectlyNotRootUser, DATE_2);
db.rootFlag().verify(rootUser, false, DATE_1);
db.rootFlag().verify(incorrectlyNotRootUser, false, DATE_2);
}

@Test
public void updateRootFlagFromPermissions_ignores_permissions_on_anyone_on_other_organization() {
UserDto falselyRootUser = db.users().makeRoot(db.users().insertUser());
UserDto notRootUser = db.users().insertUser();
OrganizationDto otherOrganization = db.organizations().insert();
db.users().insertPermissionOnAnyone(otherOrganization, SYSTEM_ADMIN);

call_updateRootFlagFromPermissions(falselyRootUser, DATE_2);
db.rootFlag().verify(falselyRootUser, false, DATE_2);
db.rootFlag().verify(notRootUser, false, notRootUser.getUpdatedAt());

call_updateRootFlagFromPermissions(notRootUser, DATE_1);
db.rootFlag().verify(falselyRootUser, false, DATE_2);
db.rootFlag().verify(notRootUser, false, DATE_1);
}

@Test
public void updateRootFlagFromPermissions_sets_root_flag_to_false_if_user_has_admin_user_permission_on_other_organization() {
UserDto falselyRootUser = db.users().makeRoot(db.users().insertUser());
UserDto notRootUser = db.users().insertUser();
OrganizationDto otherOrganization = db.organizations().insert();
db.users().insertPermissionOnUser(otherOrganization, falselyRootUser, SYSTEM_ADMIN);
db.users().insertPermissionOnUser(otherOrganization, notRootUser, SYSTEM_ADMIN);

call_updateRootFlagFromPermissions(falselyRootUser, DATE_1);
db.rootFlag().verify(falselyRootUser, false, DATE_1);
db.rootFlag().verify(notRootUser, false, notRootUser.getUpdatedAt());

call_updateRootFlagFromPermissions(notRootUser, DATE_2);
db.rootFlag().verify(falselyRootUser, false, DATE_1);
db.rootFlag().verify(notRootUser, false, DATE_2);
}

@Test
public void updateRootFlagFromPermissions_sets_root_flag_to_true_if_user_has_admin_group_permission_on_default_organization() {
UserDto rootUser = db.users().makeRoot(db.users().insertUser());
UserDto incorrectlyNotRootUser = db.users().insertUser();
GroupDto groupDto = db.users().insertAdminGroup(db.getDefaultOrganization());
db.users().insertMembers(groupDto, rootUser, incorrectlyNotRootUser);

call_updateRootFlagFromPermissions(rootUser, DATE_1);
db.rootFlag().verify(rootUser, true, DATE_1);
db.rootFlag().verify(incorrectlyNotRootUser, false, incorrectlyNotRootUser.getUpdatedAt());

call_updateRootFlagFromPermissions(incorrectlyNotRootUser, DATE_2);
db.rootFlag().verify(rootUser, true, DATE_1);
db.rootFlag().verify(incorrectlyNotRootUser, true, DATE_2);
}

@Test
public void updateRootFlagFromPermissions_sets_root_flag_to_false_if_user_has_admin_group_permission_on_other_organization() {
UserDto falselyRootUser = db.users().makeRoot(db.users().insertUser());
UserDto notRootUser = db.users().insertUser();
GroupDto otherOrganizationGroupDto = db.users().insertGroup(db.organizations().insert());
db.users().insertPermissionOnGroup(otherOrganizationGroupDto, SYSTEM_ADMIN);
db.users().insertMembers(otherOrganizationGroupDto, falselyRootUser, notRootUser);

call_updateRootFlagFromPermissions(falselyRootUser, DATE_2);
db.rootFlag().verify(falselyRootUser, false, DATE_2);
db.rootFlag().verify(notRootUser, false, notRootUser.getUpdatedAt());

call_updateRootFlagFromPermissions(notRootUser, DATE_1);
db.rootFlag().verify(falselyRootUser, false, DATE_2);
db.rootFlag().verify(notRootUser, false, DATE_1);
}

private void commit(Runnable runnable) {
runnable.run();
session.commit();
@@ -737,4 +851,11 @@ public class UserDaoTest {
dbClient.userGroupDao().insert(session, dto);
return dto;
}

private void call_updateRootFlagFromPermissions(UserDto userDto, long now) {
when(system2.now()).thenReturn(now);
underTest.updateRootFlagFromPermissions(db.getSession(), userDto.getId(), db.getDefaultOrganization().getUuid());
db.commit();
}

}

+ 114
- 0
sonar-db/src/test/java/org/sonar/db/user/UserDbTester.java View File

@@ -19,11 +19,13 @@
*/
package org.sonar.db.user;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
@@ -31,6 +33,9 @@ import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.permission.GroupPermissionDto;
import org.sonar.db.permission.UserPermissionDto;

import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
import static org.sonar.db.user.GroupTesting.newGroupDto;
import static org.sonar.db.user.UserTesting.newUserDto;

@@ -60,6 +65,76 @@ public class UserDbTester {
return updatedUser;
}

public UserDto makeRoot(UserDto userDto) {
dbClient.userDao().setRoot(db.getSession(), userDto.getLogin(), true);
db.commit();
return dbClient.userDao().selectByLogin(db.getSession(), userDto.getLogin());
}

public UserDto makeNotRoot(UserDto userDto) {
dbClient.userDao().setRoot(db.getSession(), userDto.getLogin(), false);
db.commit();
return dbClient.userDao().selectByLogin(db.getSession(), userDto.getLogin());
}

public UserDto insertRootByUserPermission(String login) {
return insertRootByUserPermissionImpl(requireNonNull(login));
}

public UserDto insertRootByUserPermission() {
return insertRootByUserPermissionImpl(null);
}

private UserDto insertRootByUserPermissionImpl(@Nullable String login) {
UserDto rootByUserPermissionUser = makeRoot(login == null ? insertUser() : insertUser(login));
insertPermissionOnUser(db.getDefaultOrganization(), rootByUserPermissionUser, SYSTEM_ADMIN);
return rootByUserPermissionUser;
}

public UserDto insertRootByGroupPermission(String login) {
return insertRootByGroupPermissionImpl(requireNonNull(login), null);
}

/**
* @see #insertAdminGroup()
*/
public UserDto insertRootByGroupPermission(String login, GroupDto adminGroupDto) {
return insertRootByGroupPermissionImpl(requireNonNull(login), adminGroupDto);
}


/**
* @see #insertAdminGroup()
*/
public UserDto insertRootByGroupPermission(GroupDto adminGroupDto) {
return insertRootByGroupPermissionImpl(null, adminGroupDto);
}

public UserDto insertRootByGroupPermission() {
return insertRootByGroupPermissionImpl(null, null);
}

public UserDto insertRootByGroupPermissionImpl(@Nullable String login, @Nullable GroupDto groupDto) {
UserDto rootByGroupPermissionUser = db.users().makeRoot(login == null ? insertUser() : insertUser(login));
GroupDto adminGroup = createOrCheckAdminGroup(groupDto);
insertMember(adminGroup, rootByGroupPermissionUser);
return rootByGroupPermissionUser;
}

private GroupDto createOrCheckAdminGroup(@Nullable GroupDto groupDto) {
if (groupDto == null) {
GroupDto adminGroup = insertGroup(db.getDefaultOrganization());
insertPermissionOnGroup(adminGroup, SYSTEM_ADMIN);
return adminGroup;
}
checkArgument(
groupDto.getOrganizationUuid().equals(db.getDefaultOrganization().getUuid()),
"Group '%s' must belong to the default organization", groupDto.getName());
List<String> groupPermissions = db.getDbClient().groupPermissionDao().selectGlobalPermissionsOfGroup(db.getSession(), groupDto.getOrganizationUuid(), groupDto.getId());
checkArgument(groupPermissions.contains(SYSTEM_ADMIN), "Group '%s' must have permission '%s'", groupDto.getId(), SYSTEM_ADMIN);
return groupDto;
}

public Optional<UserDto> selectUserByLogin(String login) {
return Optional.ofNullable(dbClient.userDao().selectByLogin(db.getSession(), login));
}
@@ -79,6 +154,32 @@ public class UserDbTester {
return insertGroup(group);
}

/**
* Creates a group in the default organization with {@link GlobalPermissions#SYSTEM_ADMIN} permission.
*/
public GroupDto insertAdminGroup() {
GroupDto groupDto = insertGroup();
insertPermissionOnGroup(groupDto, SYSTEM_ADMIN);
return groupDto;
}

/**
* Creates a group in the specified organization with {@link GlobalPermissions#SYSTEM_ADMIN} permission.
*/
public GroupDto insertAdminGroup(OrganizationDto organizationDto) {
GroupDto groupDto = insertGroup(organizationDto);
insertPermissionOnGroup(groupDto, SYSTEM_ADMIN);
return groupDto;
}

/**
* Create group in specified organization
*/
public GroupDto insertGroup(OrganizationDto organizationDto) {
GroupDto group = newGroupDto().setOrganizationUuid(organizationDto.getUuid());
return insertGroup(group);
}

public GroupDto insertGroup(GroupDto dto) {
db.getDbClient().groupDao().insert(db.getSession(), dto);
db.commit();
@@ -107,6 +208,14 @@ public class UserDbTester {
return dto;
}

public void insertMembers(GroupDto group, UserDto... users) {
Arrays.stream(users).forEach(user -> {
UserGroupDto dto = new UserGroupDto().setGroupId(group.getId()).setUserId(user.getId());
db.getDbClient().userGroupDao().insert(db.getSession(), dto);
});
db.commit();
}

public List<Long> selectGroupIdsOfUser(UserDto user) {
return db.getDbClient().groupMembershipDao().selectGroupIdsByUserId(db.getSession(), user.getId());
}
@@ -140,6 +249,11 @@ public class UserDbTester {
return dto;
}

public void deletePermissionFromGroup(GroupDto group, String permission) {
db.getDbClient().groupPermissionDao().delete(db.getSession(), permission, group.getOrganizationUuid(), group.getId(), null);
db.commit();
}

public GroupPermissionDto insertProjectPermissionOnAnyone(String permission, ComponentDto project) {
return insertProjectPermissionOnAnyone(db.getDefaultOrganization(), permission, project);
}

Loading…
Cancel
Save