From 9d712cbccfccb0bc2a0d475d816707e8a057747d Mon Sep 17 00:00:00 2001 From: =?utf8?q?S=C3=A9bastien=20Lesaint?= Date: Mon, 29 May 2017 10:15:23 +0200 Subject: [PATCH] SONAR-9327 enforce in WS permissions are only on projects or views --- .../server/component/ComponentFinder.java | 34 +++++++------ .../permission/ws/PermissionWsSupport.java | 4 +- .../permission/ws/AddGroupActionTest.java | 48 ++++++++++++++++++- .../permission/ws/AddUserActionTest.java | 45 +++++++++++++++++ .../permission/ws/RemoveGroupActionTest.java | 41 +++++++++++++--- .../permission/ws/RemoveUserActionTest.java | 36 ++++++++++++-- 6 files changed, 178 insertions(+), 30 deletions(-) diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java index 1054150c3d6..751581e7257 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java @@ -25,6 +25,7 @@ import javax.annotation.Nullable; import org.sonar.api.resources.Qualifiers; import org.sonar.api.resources.ResourceType; import org.sonar.api.resources.ResourceTypes; +import org.sonar.api.resources.Scopes; import org.sonar.core.util.stream.MoreCollectors; import org.sonar.db.DbClient; import org.sonar.db.DbSession; @@ -67,36 +68,34 @@ public class ComponentFinder { return checkFoundWithOptional(dbClient.componentDao().selectByUuid(dbSession, uuid), "Component id '%s' not found", uuid); } - /** - * A project can be: - * - */ - public ComponentDto getRootComponentOrModuleByUuidOrKey(DbSession dbSession, @Nullable String projectUuid, @Nullable String projectKey, ResourceTypes resourceTypes) { + public ComponentDto getRootComponentByUuidOrKey(DbSession dbSession, @Nullable String projectUuid, @Nullable String projectKey, ResourceTypes resourceTypes) { ComponentDto project; if (projectUuid != null) { project = checkFoundWithOptional(dbClient.componentDao().selectByUuid(dbSession, projectUuid), "Project id '%s' not found", projectUuid); } else { project = checkFoundWithOptional(dbClient.componentDao().selectByKey(dbSession, projectKey), "Project key '%s' not found", projectKey); } - checkIsProjectOrModule(project, resourceTypes); + checkIsProject(project, resourceTypes); return project; } - private static void checkIsProjectOrModule(ComponentDto component, ResourceTypes resourceTypes) { + private static void checkIsProject(ComponentDto component, ResourceTypes resourceTypes) { + Set rootQualifiers = getRootQualifiers(resourceTypes); + + checkRequest(component.scope().equals(Scopes.PROJECT) && rootQualifiers.contains(component.qualifier()), + format( + "Component '%s' (id: %s) must be a project%s.", + component.key(), component.uuid(), + rootQualifiers.contains(Qualifiers.VIEW) ? " or a view" : "")); + } + + private static Set getRootQualifiers(ResourceTypes resourceTypes) { Collection rootTypes = resourceTypes.getRoots(); - Set rootQualifiers = rootTypes + return rootTypes .stream() .map(ResourceType::getQualifier) .collect(MoreCollectors.toSet(rootTypes.size())); - String qualifier = component.qualifier(); - - checkRequest(rootQualifiers.contains(qualifier) || Qualifiers.MODULE.equals(qualifier), - format("Component '%s' (id: %s) must be a project or a module.", component.key(), component.uuid())); } public OrganizationDto getOrganization(DbSession dbSession, ComponentDto component) { @@ -115,8 +114,7 @@ public class ComponentFinder { DEVELOPER_ID_AND_KEY("developerId", "developerKey"), COMPONENT_ID_AND_COMPONENT("componentId", "component"), PROJECT_ID_AND_PROJECT("projectId", "project"), - PROJECT_ID_AND_FROM("projectId", "from") - ; + PROJECT_ID_AND_FROM("projectId", "from"); private final String uuidParamName; private final String keyParamName; diff --git a/server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionWsSupport.java b/server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionWsSupport.java index 12a0659a511..f24ce5df943 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionWsSupport.java +++ b/server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionWsSupport.java @@ -72,13 +72,13 @@ public class PermissionWsSupport { String key = request.param(PermissionsWsParameters.PARAM_PROJECT_KEY); if (uuid != null || key != null) { ProjectWsRef ref = ProjectWsRef.newWsProjectRef(uuid, key); - return Optional.of(componentFinder.getRootComponentOrModuleByUuidOrKey(dbSession, ref.uuid(), ref.key(), resourceTypes)); + return Optional.of(componentFinder.getRootComponentByUuidOrKey(dbSession, ref.uuid(), ref.key(), resourceTypes)); } return Optional.empty(); } public ComponentDto getRootComponentOrModule(DbSession dbSession, ProjectWsRef projectRef) { - return componentFinder.getRootComponentOrModuleByUuidOrKey(dbSession, projectRef.uuid(), projectRef.key(), resourceTypes); + return componentFinder.getRootComponentByUuidOrKey(dbSession, projectRef.uuid(), projectRef.key(), resourceTypes); } public GroupIdOrAnyone findGroup(DbSession dbSession, Request request) { diff --git a/server/sonar-server/src/test/java/org/sonar/server/permission/ws/AddGroupActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/permission/ws/AddGroupActionTest.java index 40c2c352a6f..e37c323a350 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/permission/ws/AddGroupActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/permission/ws/AddGroupActionTest.java @@ -38,7 +38,11 @@ import static org.sonar.api.web.UserRole.ISSUE_ADMIN; import static org.sonar.api.web.UserRole.USER; import static org.sonar.core.permission.GlobalPermissions.PROVISIONING; import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN; +import static org.sonar.db.component.ComponentTesting.newDirectory; +import static org.sonar.db.component.ComponentTesting.newFileDto; +import static org.sonar.db.component.ComponentTesting.newModuleDto; import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; +import static org.sonar.db.component.ComponentTesting.newSubView; import static org.sonar.db.component.ComponentTesting.newView; import static org.sonar.db.permission.OrganizationPermission.ADMINISTER; import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_ID; @@ -161,6 +165,48 @@ public class AddGroupActionTest extends BasePermissionWsTest { .execute(); } + @Test + public void fail_when_component_is_a_module() throws Exception { + ComponentDto module = db.components().insertComponent(newModuleDto(ComponentTesting.newPrivateProjectDto(db.organizations().insert()))); + + failIfComponentIsNotAProjectOrView(module); + } + + @Test + public void fail_when_component_is_a_directory() throws Exception { + ComponentDto file = db.components().insertComponent(newDirectory(ComponentTesting.newPrivateProjectDto(db.organizations().insert()), "A/B")); + + failIfComponentIsNotAProjectOrView(file); + } + + @Test + public void fail_when_component_is_a_file() throws Exception { + ComponentDto file = db.components().insertComponent(newFileDto(ComponentTesting.newPrivateProjectDto(db.organizations().insert()), null, "file-uuid")); + + failIfComponentIsNotAProjectOrView(file); + } + + @Test + public void fail_when_component_is_a_subview() throws Exception { + ComponentDto file = db.components().insertComponent(newSubView(ComponentTesting.newView(db.organizations().insert()))); + + failIfComponentIsNotAProjectOrView(file); + } + + private void failIfComponentIsNotAProjectOrView(ComponentDto file) { + GroupDto group = db.users().insertGroup(db.getDefaultOrganization(), "sonar-administrators"); + loginAsAdmin(db.getDefaultOrganization()); + + expectedException.expect(BadRequestException.class); + expectedException.expectMessage("Component '" + file.key() + "' (id: " + file.uuid() + ") must be a project or a view."); + + newRequest() + .setParam(PARAM_GROUP_NAME, group.getName()) + .setParam(PARAM_PROJECT_ID, file.uuid()) + .setParam(PARAM_PERMISSION, SYSTEM_ADMIN) + .execute(); + } + @Test public void adding_a_project_permission_fails_if_project_is_not_set() throws Exception { GroupDto group = db.users().insertGroup(db.getDefaultOrganization(), "sonar-administrators"); @@ -391,7 +437,7 @@ public class AddGroupActionTest extends BasePermissionWsTest { userSession.logIn().addProjectPermission(UserRole.ADMIN, project); newRequest() - .setParam(PARAM_ORGANIZATION, organization.getKey()) + .setParam(PARAM_ORGANIZATION, organization.getKey()) .setParam(PARAM_GROUP_NAME, group.getName()) .setParam(PARAM_PROJECT_ID, project.uuid()) .setParam(PARAM_PERMISSION, CODEVIEWER) diff --git a/server/sonar-server/src/test/java/org/sonar/server/permission/ws/AddUserActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/permission/ws/AddUserActionTest.java index 9ad22e09e59..0731eec4da0 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/permission/ws/AddUserActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/permission/ws/AddUserActionTest.java @@ -23,6 +23,7 @@ import org.junit.Before; import org.junit.Test; import org.sonar.api.web.UserRole; import org.sonar.db.component.ComponentDto; +import org.sonar.db.component.ComponentTesting; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.user.UserDto; import org.sonar.server.exceptions.BadRequestException; @@ -35,8 +36,11 @@ import static org.sonar.api.web.UserRole.CODEVIEWER; import static org.sonar.api.web.UserRole.ISSUE_ADMIN; import static org.sonar.api.web.UserRole.USER; import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN; +import static org.sonar.db.component.ComponentTesting.newDirectory; import static org.sonar.db.component.ComponentTesting.newFileDto; +import static org.sonar.db.component.ComponentTesting.newModuleDto; import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; +import static org.sonar.db.component.ComponentTesting.newSubView; import static org.sonar.db.component.ComponentTesting.newView; import static org.sonar.db.permission.OrganizationPermission.ADMINISTER; import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_ORGANIZATION; @@ -147,6 +151,47 @@ public class AddUserActionTest extends BasePermissionWsTest { .execute(); } + @Test + public void fail_when_component_is_a_module() throws Exception { + ComponentDto module = db.components().insertComponent(newModuleDto(ComponentTesting.newPrivateProjectDto(db.organizations().insert()))); + + failIfComponentIsNotAProjectOrView(module); + } + + @Test + public void fail_when_component_is_a_directory() throws Exception { + ComponentDto file = db.components().insertComponent(newDirectory(ComponentTesting.newPrivateProjectDto(db.organizations().insert()), "A/B")); + + failIfComponentIsNotAProjectOrView(file); + } + + @Test + public void fail_when_component_is_a_file() throws Exception { + ComponentDto file = db.components().insertComponent(newFileDto(ComponentTesting.newPrivateProjectDto(db.organizations().insert()), null, "file-uuid")); + + failIfComponentIsNotAProjectOrView(file); + } + + @Test + public void fail_when_component_is_a_subview() throws Exception { + ComponentDto file = db.components().insertComponent(newSubView(ComponentTesting.newView(db.organizations().insert()))); + + failIfComponentIsNotAProjectOrView(file); + } + + private void failIfComponentIsNotAProjectOrView(ComponentDto file) { + loginAsAdmin(db.getDefaultOrganization()); + + expectedException.expect(BadRequestException.class); + expectedException.expectMessage("Component '" + file.key() + "' (id: " + file.uuid() + ") must be a project or a view."); + + newRequest() + .setParam(PARAM_USER_LOGIN, user.getLogin()) + .setParam(PARAM_PROJECT_ID, file.uuid()) + .setParam(PARAM_PERMISSION, SYSTEM_ADMIN) + .execute(); + } + @Test public void fail_when_project_permission_without_project() throws Exception { loginAsAdmin(db.getDefaultOrganization()); diff --git a/server/sonar-server/src/test/java/org/sonar/server/permission/ws/RemoveGroupActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/permission/ws/RemoveGroupActionTest.java index 1692fdb8a57..f4d899075f0 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/permission/ws/RemoveGroupActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/permission/ws/RemoveGroupActionTest.java @@ -39,7 +39,10 @@ import static org.sonar.api.web.UserRole.ISSUE_ADMIN; import static org.sonar.api.web.UserRole.USER; import static org.sonar.core.permission.GlobalPermissions.PROVISIONING; import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN; +import static org.sonar.db.component.ComponentTesting.newDirectory; import static org.sonar.db.component.ComponentTesting.newFileDto; +import static org.sonar.db.component.ComponentTesting.newModuleDto; +import static org.sonar.db.component.ComponentTesting.newSubView; import static org.sonar.db.permission.OrganizationPermission.ADMINISTER; import static org.sonar.db.permission.OrganizationPermission.PROVISION_PROJECTS; import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_ID; @@ -182,18 +185,44 @@ public class RemoveGroupActionTest extends BasePermissionWsTest } @Test - public void fail_when_component_is_not_a_project() throws Exception { - db.components().insertComponent(newFileDto(ComponentTesting.newPrivateProjectDto(db.organizations().insert()), null, "file-uuid")); + public void fail_when_component_is_a_module() throws Exception { + ComponentDto module = db.components().insertComponent(newModuleDto(ComponentTesting.newPrivateProjectDto(db.organizations().insert()))); + + failIfComponentIsNotAProjectOrView(module); + } + + @Test + public void fail_when_component_is_a_directory() throws Exception { + ComponentDto file = db.components().insertComponent(newDirectory(ComponentTesting.newPrivateProjectDto(db.organizations().insert()), "A/B")); + + failIfComponentIsNotAProjectOrView(file); + } + + @Test + public void fail_when_component_is_a_file() throws Exception { + ComponentDto file = db.components().insertComponent(newFileDto(ComponentTesting.newPrivateProjectDto(db.organizations().insert()), null, "file-uuid")); + + failIfComponentIsNotAProjectOrView(file); + } + + @Test + public void fail_when_component_is_a_subview() throws Exception { + ComponentDto file = db.components().insertComponent(newSubView(ComponentTesting.newView(db.organizations().insert()))); + + failIfComponentIsNotAProjectOrView(file); + } + + private void failIfComponentIsNotAProjectOrView(ComponentDto file) { loginAsAdmin(db.getDefaultOrganization()); expectedException.expect(BadRequestException.class); + expectedException.expectMessage("Component '" + file.key() + "' (id: " + file.uuid() + ") must be a project or a view."); newRequest() .setParam(PARAM_USER_LOGIN, user.getLogin()) - .setParam(PARAM_PROJECT_ID, "file-uuid") + .setParam(PARAM_PROJECT_ID, file.uuid()) .setParam(PARAM_PERMISSION, SYSTEM_ADMIN) .execute(); } -- 2.39.5