Browse Source

SONAR-6853 WS permissions/template_users list users of one template and permission

tags/5.2-RC1
Teryk Bellahsene 8 years ago
parent
commit
b69446d9a9

+ 2
- 1
server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionRequestValidator.java View File

@@ -45,9 +45,10 @@ public class PermissionRequestValidator {
// static methods only
}

public static void validateProjectPermission(String permission) {
public static String validateProjectPermission(String permission) {
checkRequest(ProjectPermissions.ALL.contains(permission),
format("The '%s' parameter for project permissions must be one of %s. '%s' was passed.", PARAM_PERMISSION, ProjectPermissions.ALL_ON_ONE_LINE, permission));
return permission;
}

public static void validateGlobalPermission(String permission) {

+ 1
- 0
server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionsWsModule.java View File

@@ -57,6 +57,7 @@ public class PermissionsWsModule extends Module {
ApplyTemplateAction.class,
SetDefaultTemplateAction.class,
SearchTemplatesAction.class,
TemplateUsersAction.class,
// utility classes
PermissionChangeBuilder.class,
SearchProjectPermissionsDataLoader.class,

+ 147
- 0
server/sonar-server/src/main/java/org/sonar/server/permission/ws/TemplateUsersAction.java View File

@@ -0,0 +1,147 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube 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.
*
* SonarQube 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.server.permission.ws;

import java.util.List;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.server.ws.WebService.Param;
import org.sonar.api.server.ws.WebService.SelectionMode;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.permission.PermissionQuery;
import org.sonar.db.permission.PermissionTemplateDto;
import org.sonar.db.permission.UserWithPermissionDto;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.WsPermissions.WsTemplateUsersResponse;
import org.sonarqube.ws.WsPermissions.WsTemplateUsersResponse.User;

import static java.lang.String.format;
import static org.sonar.server.permission.PermissionPrivilegeChecker.checkGlobalAdminUser;
import static org.sonar.server.permission.ws.PermissionQueryParser.fromSelectionModeToMembership;
import static org.sonar.server.permission.ws.PermissionRequestValidator.validateProjectPermission;
import static org.sonar.server.permission.ws.WsPermissionParameters.PARAM_PERMISSION;
import static org.sonar.server.permission.ws.WsPermissionParameters.createProjectPermissionParameter;
import static org.sonar.server.permission.ws.WsPermissionParameters.createTemplateParameters;
import static org.sonar.server.ws.WsUtils.writeProtobuf;

public class TemplateUsersAction implements PermissionsWsAction {

private final DbClient dbClient;
private final UserSession userSession;
private final PermissionDependenciesFinder dependenciesFinder;

public TemplateUsersAction(DbClient dbClient, UserSession userSession, PermissionDependenciesFinder dependenciesFinder) {
this.dbClient = dbClient;
this.userSession = userSession;
this.dependenciesFinder = dependenciesFinder;
}

@Override
public void define(WebService.NewController context) {
WebService.NewAction action = context
.createAction("template_users")
.setSince("5.2")
.setDescription(
format("Lists the users that have been granted the specified permission as individual users rather than through group affiliation on the chosen template. <br />" +
"If the query parameter '%s' is specified, the '%s' parameter is forced to '%s'.<br />" +
"It requires administration permissions to access.<br />",
Param.TEXT_QUERY, Param.SELECTED, SelectionMode.ALL.value()))
.addPagingParams(100)
.addSearchQuery("stas", "names")
.addSelectionModeParam()
.setInternal(true)
.setResponseExample(getClass().getResource("template-users-example.json"))
.setHandler(this);

createProjectPermissionParameter(action);
createTemplateParameters(action);
}

@Override
public void handle(Request wsRequest, Response wsResponse) throws Exception {
checkGlobalAdminUser(userSession);
DbSession dbSession = dbClient.openSession(false);
try {
WsTemplateRef templateRef = WsTemplateRef.fromRequest(wsRequest);
PermissionTemplateDto template = dependenciesFinder.getTemplate(dbSession, templateRef);

PermissionQuery query = buildQuery(wsRequest, template);
WsTemplateUsersResponse templateUsersResponse = buildResponse(dbSession, query, template);
writeProtobuf(templateUsersResponse, wsRequest, wsResponse);
} finally {
dbClient.closeSession(dbSession);
}
}

private static PermissionQuery buildQuery(Request wsRequest, PermissionTemplateDto template) {
String permission = validateProjectPermission(wsRequest.mandatoryParam(PARAM_PERMISSION));

return PermissionQuery.builder()
.template(template.getUuid())
.permission(permission)
.membership(fromSelectionModeToMembership(wsRequest.mandatoryParam(Param.SELECTED)))
.pageIndex(wsRequest.mandatoryParamAsInt(Param.PAGE))
.pageSize(wsRequest.mandatoryParamAsInt(Param.PAGE_SIZE))
.search(wsRequest.param(Param.TEXT_QUERY))
.build();
}

private WsTemplateUsersResponse buildResponse(DbSession dbSession, PermissionQuery query, PermissionTemplateDto template) {
List<UserWithPermissionDto> usersWithPermission = dbClient.permissionTemplateDao().selectUsers(dbSession, query, template.getId(), offset(query), query.pageSize());
int total = dbClient.permissionTemplateDao().countUsers(dbSession, query, template.getId());
WsTemplateUsersResponse.Builder responseBuilder = WsTemplateUsersResponse.newBuilder();
for (UserWithPermissionDto userWithPermission : usersWithPermission) {
responseBuilder.addUsers(userDtoToUserResponse(userWithPermission));
}

responseBuilder.getPagingBuilder()
.setPageIndex(query.pageIndex())
.setPageSize(query.pageSize())
.setTotal(total)
.build();

return responseBuilder.build();
}

private static int offset(PermissionQuery query) {
int pageSize = query.pageSize();
int pageIndex = query.pageIndex();
return (pageIndex - 1) * pageSize;
}

private static User userDtoToUserResponse(UserWithPermissionDto userWithPermission) {
User.Builder userBuilder = User.newBuilder();
userBuilder.setLogin(userWithPermission.getLogin());
String email = userWithPermission.getEmail();
if (email != null) {
userBuilder.setEmail(email);
}
String name = userWithPermission.getName();
if (name != null) {
userBuilder.setName(name);
}
userBuilder.setSelected(userWithPermission.getPermission() != null);

return userBuilder.build();
}
}

+ 22
- 0
server/sonar-server/src/main/resources/org/sonar/server/permission/ws/template_users-example.json View File

@@ -0,0 +1,22 @@
{
"paging": {
"pageIndex": 1,
"pageSize": 100,
"total": 2
},
"users": [
{
"login": "admin",
"name": "Administrator",
"email": "admin@admin.com",
"selected": true
},
{
"login": "george.orwell",
"name": "George Orwell",
"email": "george.orwell@1984.net",
"selected": true
}
]
}


+ 1
- 1
server/sonar-server/src/test/java/org/sonar/server/permission/ws/PermissionsWsModuleTest.java View File

@@ -30,6 +30,6 @@ public class PermissionsWsModuleTest {
public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer();
new PermissionsWsModule().configure(container);
assertThat(container.size()).isEqualTo(26);
assertThat(container.size()).isEqualTo(27);
}
}

+ 26
- 10
server/sonar-server/src/test/java/org/sonar/server/permission/ws/PermissionsWsTest.java View File

@@ -24,33 +24,36 @@ import org.junit.Before;
import org.junit.Test;
import org.sonar.api.server.ws.RailsHandler;
import org.sonar.api.server.ws.WebService;
import org.sonar.db.DbClient;
import org.sonar.server.user.UserSession;
import org.sonar.server.ws.WsTester;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;

public class PermissionsWsTest {

WsTester tester;
WsTester ws;

@Before
public void setUp() {
tester = new WsTester(new PermissionsWs());
ws = new WsTester(new PermissionsWs(
new TemplateUsersAction(mock(DbClient.class), mock(UserSession.class), mock(PermissionDependenciesFinder.class))
));
}

@Test
public void define_controller() {
WebService.Controller controller = tester.controller("api/permissions");
WebService.Controller controller = controller();
assertThat(controller).isNotNull();
assertThat(controller.description()).isNotEmpty();
assertThat(controller.since()).isEqualTo("3.7");
assertThat(controller.actions()).hasSize(2);
assertThat(controller.actions()).hasSize(3);
}

@Test
public void define_add_action() {
WebService.Controller controller = tester.controller("api/permissions");

WebService.Action action = controller.action("add");
WebService.Action action = controller().action("add");
assertThat(action).isNotNull();
assertThat(action.handler()).isInstanceOf(RailsHandler.INSTANCE.getClass());
assertThat(action.params()).hasSize(5);
@@ -62,11 +65,24 @@ public class PermissionsWsTest {

@Test
public void define_remove_action() {
WebService.Controller controller = tester.controller("api/permissions");

WebService.Action action = controller.action("remove");
WebService.Action action = controller().action("remove");
assertThat(action).isNotNull();
assertThat(action.handler()).isInstanceOf(RailsHandler.INSTANCE.getClass());
assertThat(action.params()).hasSize(5);
}

@Test
public void define_template_users() {
WebService.Action action = controller().action("template_users");

assertThat(action).isNotNull();
assertThat(action.isPost()).isFalse();
assertThat(action.isInternal()).isTrue();
assertThat(action.since()).isEqualTo("5.2");
assertThat(action.param(WsPermissionParameters.PARAM_PERMISSION).isRequired()).isTrue();
}

private WebService.Controller controller() {
return ws.controller("api/permissions");
}
}

+ 242
- 0
server/sonar-server/src/test/java/org/sonar/server/permission/ws/TemplateUsersActionTest.java View File

@@ -0,0 +1,242 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube 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.
*
* SonarQube 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.server.permission.ws;

import java.io.IOException;
import java.io.InputStream;
import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.rules.ExpectedException;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.System2;
import org.sonar.api.web.UserRole;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.permission.PermissionTemplateDto;
import org.sonar.db.permission.PermissionTemplateUserDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.ws.TestRequest;
import org.sonar.server.ws.WsActionTester;
import org.sonar.test.DbTests;
import org.sonarqube.ws.WsPermissions.WsTemplateUsersResponse;

import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.api.web.UserRole.ADMIN;
import static org.sonar.db.permission.PermissionTemplateTesting.newPermissionTemplateDto;
import static org.sonar.db.permission.PermissionTemplateTesting.newPermissionTemplateUserDto;
import static org.sonar.server.plugins.MimeTypes.PROTOBUF;
import static org.sonar.test.JsonAssert.assertJson;
import static org.sonarqube.ws.WsPermissions.WsTemplateUsersResponse.parseFrom;

@Category(DbTests.class)
public class TemplateUsersActionTest {

@Rule
public ExpectedException expectedException = ExpectedException.none();
@Rule
public UserSessionRule userSession = UserSessionRule.standalone();
@Rule
public DbTester db = DbTester.create(System2.INSTANCE);
DbClient dbClient = db.getDbClient();
DbSession dbSession = db.getSession();
WsActionTester ws;

TemplateUsersAction underTest;

PermissionTemplateDto template1;
PermissionTemplateDto template2;

@Before
public void setUp() {
PermissionDependenciesFinder dependenciesFinder = new PermissionDependenciesFinder(dbClient, new ComponentFinder(dbClient));
underTest = new TemplateUsersAction(dbClient, userSession, dependenciesFinder);
ws = new WsActionTester(underTest);

userSession.login("login").setGlobalPermissions(ADMIN);

template1 = dbClient.permissionTemplateDao().insert(dbSession, newPermissionTemplateDto().setUuid("template-uuid-1"));
template2 = dbClient.permissionTemplateDao().insert(dbSession, newPermissionTemplateDto().setUuid("template-uuid-2"));

UserDto user1 = insertUser(new UserDto().setLogin("login-1").setName("name-1").setEmail("email-1"));
UserDto user2 = insertUser(new UserDto().setLogin("login-2").setName("name-2").setEmail("email-2"));
UserDto user3 = insertUser(new UserDto().setLogin("login-3").setName("name-3").setEmail("email-3"));

addUserToTemplate(newPermissionTemplateUser(UserRole.USER, template1.getId(), user1.getId()));
addUserToTemplate(newPermissionTemplateUser(UserRole.USER, template1.getId(), user2.getId()));
addUserToTemplate(newPermissionTemplateUser(UserRole.ISSUE_ADMIN, template1.getId(), user1.getId()));
addUserToTemplate(newPermissionTemplateUser(UserRole.ISSUE_ADMIN, template1.getId(), user3.getId()));
addUserToTemplate(newPermissionTemplateUser(UserRole.USER, template2.getId(), user1.getId()));
addUserToTemplate(newPermissionTemplateUser(UserRole.USER, template2.getId(), user2.getId()));
addUserToTemplate(newPermissionTemplateUser(UserRole.USER, template2.getId(), user3.getId()));
addUserToTemplate(newPermissionTemplateUser(UserRole.ISSUE_ADMIN, template2.getId(), user1.getId()));

commit();
}

@Test
public void search_for_users_with_response_example() {
UserDto user1 = insertUser(new UserDto().setLogin("admin").setName("Administrator").setEmail("admin@admin.com"));
UserDto user2 = insertUser(new UserDto().setLogin("george.orwell").setName("George Orwell").setEmail("george.orwell@1984.net"));
addUserToTemplate(newPermissionTemplateUser(UserRole.CODEVIEWER, template1.getId(), user1.getId()));
addUserToTemplate(newPermissionTemplateUser(UserRole.CODEVIEWER, template1.getId(), user2.getId()));
commit();

String result = newRequest(UserRole.CODEVIEWER, template1.getUuid()).execute().getInput();

assertJson(result).isSimilarTo(getClass().getResource("template_users-example.json"));
}

@Test
public void search_for_users_by_template_name() throws IOException {
InputStream responseStream = newRequest(UserRole.USER, null)
.setParam(WsPermissionParameters.PARAM_TEMPLATE_NAME, template1.getName())
.setMediaType(PROTOBUF)
.execute().getInputStream();

WsTemplateUsersResponse response = parseFrom(responseStream);

assertThat(response.getUsersList()).extracting("login").containsExactly("login-1", "login-2");
}

@Test
public void search_using_text_query() throws IOException {
InputStream responseStream = newRequest(UserRole.USER, null)
.setParam(WsPermissionParameters.PARAM_TEMPLATE_NAME, template1.getName())
.setParam(WebService.Param.TEXT_QUERY, "ame-1")
.setMediaType(PROTOBUF)
.execute().getInputStream();

WsTemplateUsersResponse response = parseFrom(responseStream);

assertThat(response.getUsersList()).extracting("login").containsOnly("login-1");
}

@Test
public void search_using_selected() throws IOException {
InputStream responseStream = newRequest(UserRole.USER, null)
.setParam(WsPermissionParameters.PARAM_TEMPLATE_NAME, template1.getName())
.setParam(WebService.Param.SELECTED, "all")
.setMediaType(PROTOBUF)
.execute().getInputStream();

WsTemplateUsersResponse response = parseFrom(responseStream);

assertThat(response.getUsersList()).extracting("login").containsExactly("login-1", "login-2", "login-3");
assertThat(response.getUsers(2).getSelected()).isFalse();
}

@Test
public void search_with_pagination() throws IOException {
InputStream responseStream = newRequest(UserRole.USER, null)
.setParam(WsPermissionParameters.PARAM_TEMPLATE_NAME, template1.getName())
.setParam(WebService.Param.SELECTED, "all")
.setParam(WebService.Param.PAGE, "2")
.setParam(WebService.Param.PAGE_SIZE, "1")
.setMediaType(PROTOBUF)
.execute().getInputStream();

WsTemplateUsersResponse response = parseFrom(responseStream);

assertThat(response.getUsersList()).extracting("login").containsOnly("login-2");
}

@Test
public void fail_if_not_a_project_permission() throws IOException {
expectedException.expect(BadRequestException.class);

newRequest(GlobalPermissions.PREVIEW_EXECUTION, template1.getUuid())
.execute();
}

@Test
public void fail_if_template_does_not_exist() {
expectedException.expect(NotFoundException.class);

newRequest(UserRole.USER, "unknown-template-uuid")
.execute();
}

@Test
public void fail_if_template_uuid_and_name_provided() {
expectedException.expect(BadRequestException.class);

newRequest(UserRole.USER, template1.getUuid())
.setParam(WsPermissionParameters.PARAM_TEMPLATE_NAME, template1.getName())
.execute();
}

@Test
public void fail_if_not_logged_in() {
expectedException.expect(UnauthorizedException.class);
userSession.anonymous();

newRequest(UserRole.USER, template1.getUuid()).execute();
}

@Test
public void fail_if_insufficient_privileges() {
expectedException.expect(ForbiddenException.class);
userSession.login("login");

newRequest(UserRole.USER, template1.getUuid()).execute();
}

private UserDto insertUser(UserDto userDto) {
UserDto user = dbClient.userDao().insert(dbSession, userDto.setActive(true));
return user;
}

private void addUserToTemplate(PermissionTemplateUserDto userRoleDto) {
dbClient.permissionTemplateDao().insertUserPermission(dbSession, userRoleDto);
}

private void commit() {
dbSession.commit();
}

private static PermissionTemplateUserDto newPermissionTemplateUser(String permission, long permissionTemplateId, long userId) {
return newPermissionTemplateUserDto()
.setPermission(permission)
.setTemplateId(permissionTemplateId)
.setUserId(userId);
}

private TestRequest newRequest(String permission, @Nullable String templateUuid) {
TestRequest request = ws.newRequest();
request.setParam(WsPermissionParameters.PARAM_PERMISSION, permission);
if (templateUuid != null) {
request.setParam(WsPermissionParameters.PARAM_TEMPLATE_ID, templateUuid);
}

return request;
}
}

+ 4
- 0
sonar-db/src/main/java/org/sonar/db/permission/PermissionTemplateDao.java View File

@@ -307,6 +307,10 @@ public class PermissionTemplateDao implements Dao {
session.commit();
}

public void insertUserPermission(DbSession session, PermissionTemplateUserDto permissionTemplateUserDto) {
mapper(session).insertUserPermission(permissionTemplateUserDto);
}

/**
* @deprecated since 5.2 {@link #deleteUserPermission(DbSession, Long, Long, String)}
*/

+ 1
- 1
sonar-db/src/main/resources/org/sonar/db/permission/PermissionTemplateMapper.xml View File

@@ -67,7 +67,7 @@
</delete>

<select id="selectUsers" parameterType="map" resultType="UserWithPermission">
SELECT u.login as login, u.name as name, ptu.permission_reference as permission
SELECT u.login as login, u.name as name, u.email, ptu.permission_reference as permission
<include refid="usersSelection"/>
ORDER BY u.name
</select>

+ 9
- 0
sonar-db/src/test/java/org/sonar/db/permission/PermissionTemplateTesting.java View File

@@ -21,6 +21,8 @@
package org.sonar.db.permission;

import java.util.Date;
import org.apache.commons.lang.math.RandomUtils;
import org.sonar.core.permission.ProjectPermissions;
import org.sonar.core.util.Uuids;

import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
@@ -35,4 +37,11 @@ public class PermissionTemplateTesting {
.setCreatedAt(new Date())
.setUpdatedAt(new Date());
}

public static PermissionTemplateUserDto newPermissionTemplateUserDto() {
return new PermissionTemplateUserDto()
.setPermission(ProjectPermissions.ALL.get(RandomUtils.nextInt(ProjectPermissions.ALL.size())))
.setCreatedAt(new Date())
.setUpdatedAt(new Date());
}
}

+ 1806
- 42
sonar-ws/src/main/gen-java/org/sonarqube/ws/WsPermissions.java
File diff suppressed because it is too large
View File


+ 14
- 2
sonar-ws/src/main/protobuf/ws-permissions.proto View File

@@ -23,9 +23,7 @@ package sonarqube.ws.permissions;
import "ws-commons.proto";

option java_package = "org.sonarqube.ws";

option java_outer_classname = "WsPermissions";

option optimize_for = SPEED;

// WS api/permissions/users for internal use only
@@ -56,6 +54,20 @@ message WsGroupsResponse {
repeated Group groups = 2;
}

// WS api/permissions/template_users for internal use only
message WsTemplateUsersResponse {

message User {
optional string login = 1;
optional string name = 2;
optional string email = 3;
optional bool selected = 4;
}

optional sonarqube.ws.commons.Paging paging = 1;
repeated User users = 2;
}

message Permission {
optional string key = 1;
optional string name = 2;

Loading…
Cancel
Save