@@ -48,6 +48,7 @@ public class PermissionQuery { | |||
private final String permission; | |||
// filter on project, else filter org permissions | |||
private final String componentUuid; | |||
private final Long componentId; | |||
private final String template; | |||
// filter on login, email or name of users or groups | |||
@@ -67,6 +68,7 @@ public class PermissionQuery { | |||
this.permission = builder.permission; | |||
this.withAtLeastOnePermission = builder.withAtLeastOnePermission; | |||
this.componentUuid = builder.componentUuid; | |||
this.componentId = builder.componentId; | |||
this.template = builder.template; | |||
this.searchQuery = builder.searchQuery; | |||
this.searchQueryToSql = builder.searchQuery == null ? null : buildLikeValue(builder.searchQuery, WildcardPosition.BEFORE_AND_AFTER); | |||
@@ -99,6 +101,11 @@ public class PermissionQuery { | |||
return componentUuid; | |||
} | |||
@CheckForNull | |||
public Long getComponentId() { | |||
return componentId; | |||
} | |||
@CheckForNull | |||
public String getSearchQuery() { | |||
return searchQuery; | |||
@@ -130,6 +137,7 @@ public class PermissionQuery { | |||
private String permission; | |||
private String organizationUuid; | |||
private String componentUuid; | |||
private Long componentId; | |||
private String template; | |||
private String searchQuery; | |||
private boolean withAtLeastOnePermission; | |||
@@ -157,6 +165,11 @@ public class PermissionQuery { | |||
return this; | |||
} | |||
public Builder setComponentId(Long componentId) { | |||
this.componentId = componentId; | |||
return this; | |||
} | |||
public Builder setOrganizationUuid(String organizationUuid) { | |||
this.organizationUuid = organizationUuid; | |||
return this; |
@@ -51,7 +51,16 @@ public class UserPermissionDao implements Dao { | |||
} | |||
public List<Integer> selectUserIdsByQuery(DbSession dbSession, PermissionQuery query) { | |||
return mapper(dbSession).selectUserIdsByQuery(query) | |||
return paginate(mapper(dbSession).selectUserIdsByQuery(query), query); | |||
} | |||
public List<Integer> selectUserIdsByQueryAndScope(DbSession dbSession, PermissionQuery query) { | |||
return paginate(mapper(dbSession).selectUserIdsByQueryAndScope(query), query); | |||
} | |||
private static List<Integer> paginate(List<Integer> results, PermissionQuery query) { | |||
return results | |||
.stream() | |||
// Pagination is done in Java because it's too complex to use SQL pagination in Oracle and MsSQL with the distinct | |||
.skip(query.getPageOffset()) |
@@ -30,6 +30,11 @@ public interface UserPermissionMapper { | |||
List<Integer> selectUserIdsByQuery(@Param("query") PermissionQuery query); | |||
/** | |||
* Fetch user ids based on permission query and only in a specific scope (global permissions only, organization permissions only or project permissions only) | |||
*/ | |||
List<Integer> selectUserIdsByQueryAndScope(@Param("query") PermissionQuery query); | |||
/** | |||
* Count the number of distinct users returned by {@link #selectUserIdsByQuery(PermissionQuery)} | |||
* {@link PermissionQuery#getPageOffset()} and {@link PermissionQuery#getPageSize()} are ignored. |
@@ -27,6 +27,28 @@ | |||
order by case when (count(ur.role) > 0) then 1 else 2 end asc, lower(u.name) asc | |||
</select> | |||
<select id="selectUserIdsByQueryAndScope" parameterType="map" resultType="int"> | |||
select | |||
u.id, lower(u.name) | |||
from users u | |||
left join user_roles ur on ur.user_id = u.id and ur.organization_uuid=#{query.organizationUuid,jdbcType=VARCHAR} | |||
<choose> | |||
<when test="query.componentId == null"> | |||
and ur.resource_id is null | |||
</when> | |||
<otherwise> | |||
and ur.resource_id = #{query.componentId,jdbcType=BIGINT} | |||
</otherwise> | |||
</choose> | |||
left join projects p on ur.resource_id = p.id | |||
inner join organization_members om on u.id=om.user_id and om.organization_uuid=#{query.organizationUuid,jdbcType=VARCHAR} | |||
<where> | |||
<include refid="sqlQueryFilters" /> | |||
</where> | |||
group by u.id, lower(u.name) | |||
order by case when (count(distinct ur.role) > 0) then 1 else 2 end asc, lower(u.name) asc | |||
</select> | |||
<select id="countUsersByQuery" parameterType="map" resultType="int"> | |||
select count(distinct(u.id)) | |||
<include refid="sqlQueryJoins" /> |
@@ -34,12 +34,14 @@ public class PermissionQueryTest { | |||
public void create_query() { | |||
PermissionQuery quey = PermissionQuery.builder() | |||
.setComponentUuid("COMPONENT_UUID") | |||
.setComponentId(1234L) | |||
.setOrganizationUuid("ORGANIZATION_UUID") | |||
.setPermission("user") | |||
.setSearchQuery("sonar") | |||
.build(); | |||
assertThat(quey.getComponentUuid()).isEqualTo("COMPONENT_UUID"); | |||
assertThat(quey.getComponentId()).isEqualTo(1234L); | |||
assertThat(quey.getOrganizationUuid()).isEqualTo("ORGANIZATION_UUID"); | |||
assertThat(quey.getPermission()).isEqualTo("user"); | |||
assertThat(quey.getSearchQuery()).isEqualTo("sonar"); |
@@ -260,6 +260,48 @@ public class UserPermissionDaoTest { | |||
assertThat(underTest.selectUserIdsByQuery(dbSession, query)).isEmpty(); | |||
} | |||
@Test | |||
public void selectUserIdsByQueryAndScope_with_organization_scope() { | |||
OrganizationDto org1 = db.organizations().insert(); | |||
OrganizationDto org2 = db.organizations().insert(); | |||
UserDto user1 = insertUser(u -> u.setLogin("login1").setName("Marius").setEmail("email1@email.com"), org1, org2); | |||
UserDto user2 = insertUser(u -> u.setLogin("login2").setName("Marie").setEmail("email2@email.com"), org1, org2); | |||
ComponentDto project1 = db.components().insertPrivateProject(org1); | |||
ComponentDto project2 = db.components().insertPrivateProject(org2); | |||
addProjectPermission(org1, USER, user1, project1); | |||
addGlobalPermission(org1, PROVISIONING, user1); | |||
addProjectPermission(org2, ISSUE_ADMIN, user2, project2); | |||
PermissionQuery query = PermissionQuery.builder().setOrganizationUuid(org1.getUuid()).build(); | |||
List<Integer> result = underTest.selectUserIdsByQueryAndScope(dbSession, query); | |||
// users with any kind of global permissions are first on the list and then sorted by name | |||
assertThat(result).containsExactly(user1.getId(), user2.getId()); | |||
} | |||
@Test | |||
public void selectUserIdsByQueryAndScope_with_project_scope() { | |||
OrganizationDto org1 = db.organizations().insert(); | |||
OrganizationDto org2 = db.organizations().insert(); | |||
UserDto user1 = insertUser(u -> u.setLogin("login1").setName("Marius").setEmail("email1@email.com"), org1, org2); | |||
UserDto user2 = insertUser(u -> u.setLogin("login2").setName("Marie").setEmail("email2@email.com"), org1, org2); | |||
ComponentDto project1 = db.components().insertPrivateProject(org1); | |||
ComponentDto project2 = db.components().insertPrivateProject(org2); | |||
addProjectPermission(org1, USER, user1, project1); | |||
addGlobalPermission(org1, PROVISIONING, user1); | |||
addProjectPermission(org2, ISSUE_ADMIN, user2, project2); | |||
PermissionQuery query = PermissionQuery.builder() | |||
.setOrganizationUuid(org1.getUuid()) | |||
.setComponentUuid(project1.uuid()) | |||
.setComponentId(project1.getId()) | |||
.build(); | |||
List<Integer> result = underTest.selectUserIdsByQueryAndScope(dbSession, query); | |||
// users with any this projects permissions are first on the list and then sorted by name | |||
assertThat(result).containsExactly(user1.getId(), user2.getId()); | |||
} | |||
@Test | |||
public void selectUserIdsByQuery_is_paginated() { | |||
OrganizationDto organization = db.organizations().insert(); |
@@ -125,6 +125,7 @@ public class GroupsAction implements PermissionsWsAction { | |||
.setSearchQuery(textQuery); | |||
if (project.isPresent()) { | |||
permissionQuery.setComponentUuid(project.get().getUuid()); | |||
permissionQuery.setComponentId(project.get().getId()); | |||
} | |||
return permissionQuery.build(); |
@@ -131,7 +131,10 @@ public class UsersAction implements PermissionsWsAction { | |||
.setPageIndex(request.mandatoryParamAsInt(Param.PAGE)) | |||
.setPageSize(request.mandatoryParamAsInt(Param.PAGE_SIZE)) | |||
.setSearchQuery(textQuery); | |||
project.ifPresent(projectId -> permissionQuery.setComponentUuid(projectId.getUuid())); | |||
project.ifPresent(projectId -> { | |||
permissionQuery.setComponentUuid(projectId.getUuid()); | |||
permissionQuery.setComponentId(projectId.getId()); | |||
}); | |||
if (permission != null) { | |||
if (project.isPresent()) { | |||
requestValidator.validateProjectPermission(permission); | |||
@@ -167,7 +170,7 @@ public class UsersAction implements PermissionsWsAction { | |||
} | |||
private List<UserDto> findUsers(DbSession dbSession, PermissionQuery query) { | |||
List<Integer> orderedIds = dbClient.userPermissionDao().selectUserIdsByQuery(dbSession, query); | |||
List<Integer> orderedIds = dbClient.userPermissionDao().selectUserIdsByQueryAndScope(dbSession, query); | |||
return Ordering.explicit(orderedIds).onResultOf(UserDto::getId).immutableSortedCopy(dbClient.userDao().selectByIds(dbSession, orderedIds)); | |||
} | |||
@@ -176,11 +179,10 @@ public class UsersAction implements PermissionsWsAction { | |||
return emptyList(); | |||
} | |||
List<Integer> userIds = users.stream().map(UserDto::getId).collect(Collectors.toList()); | |||
PermissionQuery query = PermissionQuery.builder() | |||
PermissionQuery.Builder queryBuilder = PermissionQuery.builder() | |||
.setOrganizationUuid(org.getUuid()) | |||
.setComponentUuid(project.map(ProjectId::getUuid).orElse(null)) | |||
.withAtLeastOnePermission() | |||
.build(); | |||
return dbClient.userPermissionDao().selectUserPermissionsByQuery(dbSession, query, userIds); | |||
.withAtLeastOnePermission(); | |||
project.ifPresent(p -> queryBuilder.setComponentUuid(p.getUuid())); | |||
return dbClient.userPermissionDao().selectUserPermissionsByQuery(dbSession, queryBuilder.build(), userIds); | |||
} | |||
} |
@@ -2,7 +2,7 @@ | |||
"paging": { | |||
"pageIndex": 1, | |||
"pageSize": 20, | |||
"total": 2 | |||
"total": 3 | |||
}, | |||
"users": [ | |||
{ | |||
@@ -18,6 +18,13 @@ | |||
"email": "george.orwell@1984.net", | |||
"avatar": "583af86a274c1027ef078cada831babf", | |||
"permissions": ["scan"] | |||
}, | |||
{ | |||
"login": "adam.west", | |||
"name": "Adam West", | |||
"email": "adamwest@adamwest.com", | |||
"avatar": "9b55aba24cc5ee533294334bd20abb34", | |||
"permissions": [] | |||
} | |||
] | |||
} |
@@ -76,12 +76,14 @@ public class UsersActionTest extends BasePermissionWsTest<UsersAction> { | |||
public void search_for_users_with_response_example() { | |||
UserDto user1 = db.users().insertUser(newUserDto().setLogin("admin").setName("Administrator").setEmail("admin@admin.com")); | |||
db.organizations().addMember(db.getDefaultOrganization(), user1); | |||
UserDto user2 = db.users().insertUser(newUserDto().setLogin("george.orwell").setName("George Orwell").setEmail("george.orwell@1984.net")); | |||
UserDto user2 = db.users().insertUser(newUserDto().setLogin("adam.west").setName("Adam West").setEmail("adamwest@adamwest.com")); | |||
db.organizations().addMember(db.getDefaultOrganization(), user2); | |||
UserDto user3 = db.users().insertUser(newUserDto().setLogin("george.orwell").setName("George Orwell").setEmail("george.orwell@1984.net")); | |||
db.organizations().addMember(db.getDefaultOrganization(), user3); | |||
db.users().insertPermissionOnUser(user1, ADMINISTER_QUALITY_PROFILES); | |||
db.users().insertPermissionOnUser(user1, ADMINISTER); | |||
db.users().insertPermissionOnUser(user1, ADMINISTER_QUALITY_GATES); | |||
db.users().insertPermissionOnUser(user2, SCAN); | |||
db.users().insertPermissionOnUser(user3, SCAN); | |||
loginAsAdmin(db.getDefaultOrganization()); | |||
String result = newRequest().execute().getInput(); |