Skip to content

Commit

Permalink
SONAR-6485 WS permissions/search_project_permissions search for proje…
Browse files Browse the repository at this point in the history
…ct permissions
  • Loading branch information
teryk committed Aug 19, 2015
1 parent 5a41c61 commit 64ac081
Show file tree
Hide file tree
Showing 27 changed files with 4,397 additions and 551 deletions.
Expand Up @@ -45,6 +45,7 @@
import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.exceptions.NotFoundException;


import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Lists.newArrayList;
import static org.sonar.api.utils.Paging.forPageIndex;


@ServerSide @ServerSide
public class PermissionFinder { public class PermissionFinder {
Expand Down Expand Up @@ -141,7 +142,10 @@ private GroupWithPermissionQueryResult toGroupQueryResult(List<GroupWithPermissi
addAnyoneGroup(dtos, query); addAnyoneGroup(dtos, query);
List<GroupWithPermissionDto> filteredDtos = filterMembership(dtos, query); List<GroupWithPermissionDto> filteredDtos = filterMembership(dtos, query);


Paging paging = Paging.create(query.pageSize(), query.pageIndex(), filteredDtos.size()); Paging paging = forPageIndex(query.pageIndex())
.withPageSize(query.pageSize())
.andTotal(filteredDtos.size());

List<GroupWithPermission> pagedGroups = pagedGroups(filteredDtos, paging); List<GroupWithPermission> pagedGroups = pagedGroups(filteredDtos, paging);
return new GroupWithPermissionQueryResult(pagedGroups, filteredDtos.size()); return new GroupWithPermissionQueryResult(pagedGroups, filteredDtos.size());
} }
Expand Down
Expand Up @@ -34,6 +34,8 @@ protected void configureModule() {
UsersAction.class, UsersAction.class,
GroupsAction.class, GroupsAction.class,
SearchGlobalPermissionsAction.class, SearchGlobalPermissionsAction.class,
PermissionWsCommons.class); PermissionWsCommons.class,
SearchProjectPermissionsAction.class,
SearchProjectPermissionsDataLoader.class);
} }
} }
Expand Up @@ -32,8 +32,11 @@
import org.sonar.db.permission.PermissionQuery; import org.sonar.db.permission.PermissionQuery;
import org.sonar.db.user.GroupMembershipQuery; import org.sonar.db.user.GroupMembershipQuery;
import org.sonar.server.user.UserSession; import org.sonar.server.user.UserSession;
import org.sonarqube.ws.Permissions.Permission;
import org.sonarqube.ws.Permissions.SearchGlobalPermissionsResponse; import org.sonarqube.ws.Permissions.SearchGlobalPermissionsResponse;


import static org.sonarqube.ws.Permissions.Permission.newBuilder;

public class SearchGlobalPermissionsAction implements PermissionsWsAction { public class SearchGlobalPermissionsAction implements PermissionsWsAction {
private static final String PROPERTY_PREFIX = "global_permissions."; private static final String PROPERTY_PREFIX = "global_permissions.";
private static final String DESCRIPTION_SUFFIX = ".desc"; private static final String DESCRIPTION_SUFFIX = ".desc";
Expand Down Expand Up @@ -73,20 +76,20 @@ public void handle(Request wsRequest, Response wsResponse) throws Exception {


private SearchGlobalPermissionsResponse.Builder response(DbSession dbSession) { private SearchGlobalPermissionsResponse.Builder response(DbSession dbSession) {
SearchGlobalPermissionsResponse.Builder response = SearchGlobalPermissionsResponse.newBuilder(); SearchGlobalPermissionsResponse.Builder response = SearchGlobalPermissionsResponse.newBuilder();
SearchGlobalPermissionsResponse.Permission.Builder permission = SearchGlobalPermissionsResponse.Permission.newBuilder(); Permission.Builder permission = newBuilder();


for (String permissionKey : GlobalPermissions.ALL) { for (String permissionKey : GlobalPermissions.ALL) {
PermissionQuery permissionQuery = permissionQuery(permissionKey); PermissionQuery permissionQuery = permissionQuery(permissionKey);


response.addGlobalPermissions( response.addPermissions(
permission permission
.clear() .clear()
.setKey(permissionKey) .setKey(permissionKey)
.setName(i18nName(permissionKey)) .setName(i18nName(permissionKey))
.setDescription(i18nDescriptionMessage(permissionKey)) .setDescription(i18nDescriptionMessage(permissionKey))
.setUsersCount(countUsers(dbSession, permissionQuery)) .setUsersCount(countUsers(dbSession, permissionQuery))
.setGroupsCount(countGroups(dbSession, permissionKey)) .setGroupsCount(countGroups(dbSession, permissionKey))
); );
} }


return response; return response;
Expand Down
@@ -0,0 +1,160 @@
/*
* 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 org.sonar.api.i18n.I18n;
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.web.UserRole;
import org.sonar.core.permission.ComponentPermissions;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.Permissions.Permission;
import org.sonarqube.ws.Permissions.SearchProjectPermissionsResponse;
import org.sonarqube.ws.Permissions.SearchProjectPermissionsResponse.Project;

import static org.sonar.server.permission.ws.PermissionWsCommons.PARAM_PROJECT_KEY;
import static org.sonar.server.permission.ws.PermissionWsCommons.PARAM_PROJECT_UUID;
import static org.sonar.server.permission.ws.PermissionWsCommons.createProjectKeyParameter;
import static org.sonar.server.permission.ws.PermissionWsCommons.createProjectUuidParameter;
import static org.sonar.server.ws.WsUtils.checkRequest;
import static org.sonar.server.ws.WsUtils.writeProtobuf;

public class SearchProjectPermissionsAction implements PermissionsWsAction {
private static final String PROPERTY_PREFIX = "projects_role.";
private static final String DESCRIPTION_SUFFIX = ".desc";

private final DbClient dbClient;
private final UserSession userSession;
private final I18n i18n;
private final SearchProjectPermissionsDataLoader dataLoader;

public SearchProjectPermissionsAction(DbClient dbClient, UserSession userSession, I18n i18n, SearchProjectPermissionsDataLoader dataLoader) {
this.dbClient = dbClient;
this.userSession = userSession;
this.i18n = i18n;
this.dataLoader = dataLoader;
}

@Override
public void define(WebService.NewController context) {
WebService.NewAction action = context.createAction("search_project_permissions")
.setDescription("List project permissions. A project can be a technical project, a view or a developer.<br />" +
"Requires 'Administer System' permission or 'Administer' rights on the specified project.")
.setResponseExample(getClass().getResource("search_project_permissions-example.json"))
.setSince("5.2")
.addPagingParams(25)
.addSearchQuery("sonarq", "names", "keys")
.setHandler(this);

createProjectUuidParameter(action);
createProjectKeyParameter(action);
}

@Override
public void handle(Request wsRequest, Response wsResponse) throws Exception {
checkRequestAndPermissions(wsRequest);

DbSession dbSession = dbClient.openSession(false);
try {
SearchProjectPermissionsData data = dataLoader.load(wsRequest);
SearchProjectPermissionsResponse response = buildReponse(data);
writeProtobuf(response, wsRequest, wsResponse);
} finally {
dbClient.closeSession(dbSession);
}
}

private void checkRequestAndPermissions(Request wsRequest) {
String projectUuid = wsRequest.param(PARAM_PROJECT_UUID);
String projectKey = wsRequest.param(PARAM_PROJECT_KEY);
boolean isProjectUuidNonNull = projectUuid != null;
boolean isProjectKeyNonNull = projectKey != null;

if (isProjectUuidNonNull || isProjectKeyNonNull) {
checkRequest(projectUuid != null ^ projectKey != null, "Project id or project key can be provided, not both.");
}
userSession.checkLoggedIn();

if (userSession.hasGlobalPermission(GlobalPermissions.SYSTEM_ADMIN)) {
return;
}

if (isProjectUuidNonNull) {
userSession.checkProjectUuidPermission(UserRole.ADMIN, projectUuid);
return;
}

if (isProjectKeyNonNull) {
userSession.checkProjectPermission(UserRole.ADMIN, projectKey);
return;
}

userSession.checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN);
}

private SearchProjectPermissionsResponse buildReponse(SearchProjectPermissionsData data) {
SearchProjectPermissionsResponse.Builder response = SearchProjectPermissionsResponse.newBuilder();
Permission.Builder permissionResponse = Permission.newBuilder();

Project.Builder rootComponentBuilder = Project.newBuilder();
for (ComponentDto rootComponent : data.rootComponents()) {
rootComponentBuilder
.clear()
.setUuid(rootComponent.uuid())
.setKey(rootComponent.key())
.setName(rootComponent.name());
for (String permission : data.permissions(rootComponent.getId())) {
rootComponentBuilder.addPermissions(
permissionResponse
.clear()
.setKey(permission)
.setUsersCount(data.userCount(rootComponent.getId(), permission))
.setGroupsCount(data.groupCount(rootComponent.getId(), permission)));
}
response.addProjects(rootComponentBuilder);
}

for (String permissionKey : ComponentPermissions.ALL) {
response.addPermissions(
permissionResponse
.clear()
.setKey(permissionKey)
.setName(i18nName(permissionKey))
.setDescription(i18nDescriptionMessage(permissionKey))
);
}

return response.build();
}

private String i18nDescriptionMessage(String permissionKey) {
return i18n.message(userSession.locale(), PROPERTY_PREFIX + permissionKey + DESCRIPTION_SUFFIX, "");
}

private String i18nName(String permissionKey) {
return i18n.message(userSession.locale(), PROPERTY_PREFIX + permissionKey, permissionKey);
}
}
@@ -0,0 +1,116 @@
/*
* 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 com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.common.collect.Ordering;
import com.google.common.collect.Table;
import java.util.List;
import java.util.Set;
import org.sonar.db.component.ComponentDto;

import static com.google.common.base.Objects.firstNonNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.copyOf;
import static com.google.common.collect.ImmutableTable.copyOf;

public class SearchProjectPermissionsData {
private final List<ComponentDto> rootComponents;
private final int total;
private final Table<Long, String, Integer> userCountByProjectIdAndPermission;
private final Table<Long, String, Integer> groupCountByProjectIdAndPermission;

private SearchProjectPermissionsData(Builder builder) {
this.rootComponents = copyOf(builder.projects);
this.total = builder.total;
this.userCountByProjectIdAndPermission = copyOf(builder.userCountByProjectIdAndPermission);
this.groupCountByProjectIdAndPermission = copyOf(builder.groupCountByProjectIdAndPermission);
}

public static Builder newBuilder() {
return new Builder();
}

public List<ComponentDto> rootComponents() {
return rootComponents;
}

public int total() {
return total;
}

public int userCount(long rootComponentId, String permission) {
return firstNonNull(userCountByProjectIdAndPermission.get(rootComponentId, permission), 0);
}

public int groupCount(long rootComponentId, String permission) {
return firstNonNull(groupCountByProjectIdAndPermission.get(rootComponentId, permission), 0);
}

public Set<String> permissions(long rootComponentId) {
return FluentIterable.from(
Iterables.concat(
userCountByProjectIdAndPermission.row(rootComponentId).keySet(),
groupCountByProjectIdAndPermission.row(rootComponentId).keySet()
)
).toSortedSet(Ordering.natural());
}

public static class Builder {
private List<ComponentDto> projects;
private int total;
private Table<Long, String, Integer> userCountByProjectIdAndPermission;
private Table<Long, String, Integer> groupCountByProjectIdAndPermission;

private Builder() {
// prevents instantiation outside main class
}

public SearchProjectPermissionsData build() {
checkState(projects != null);
checkState(userCountByProjectIdAndPermission != null);
checkState(groupCountByProjectIdAndPermission != null);

return new SearchProjectPermissionsData(this);
}

public Builder rootComponents(List<ComponentDto> projects) {
this.projects = projects;
return this;
}

public Builder total(int total) {
this.total = total;
return this;
}

public Builder userCountByProjectIdAndPermission(Table<Long, String, Integer> userCountByProjectIdAndPermission) {
this.userCountByProjectIdAndPermission = userCountByProjectIdAndPermission;
return this;
}

public Builder groupCountByProjectIdAndPermission(Table<Long, String, Integer> groupCountByProjectIdAndPermission) {
this.groupCountByProjectIdAndPermission = groupCountByProjectIdAndPermission;
return this;
}
}
}

0 comments on commit 64ac081

Please sign in to comment.