]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9327 enforce in WS permissions are only on projects or views 2108/head
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Mon, 29 May 2017 08:15:23 +0000 (10:15 +0200)
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Wed, 31 May 2017 12:38:39 +0000 (14:38 +0200)
server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java
server/sonar-server/src/main/java/org/sonar/server/permission/ws/PermissionWsSupport.java
server/sonar-server/src/test/java/org/sonar/server/permission/ws/AddGroupActionTest.java
server/sonar-server/src/test/java/org/sonar/server/permission/ws/AddUserActionTest.java
server/sonar-server/src/test/java/org/sonar/server/permission/ws/RemoveGroupActionTest.java
server/sonar-server/src/test/java/org/sonar/server/permission/ws/RemoveUserActionTest.java

index 1054150c3d632c4d8fa6f1f5e3c398e6810099d2..751581e72578397585c706a6e7c6aeb2974f8ced 100644 (file)
@@ -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:
-   * <ul>
-   * <li>a project – ex: SonarQube</li>
-   * <li>a view – ex: Language Team</li>
-   * <li>a developer – ex: Simon Brandhof</li>
-   * </ul>
-   */
-  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<String> 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<String> getRootQualifiers(ResourceTypes resourceTypes) {
     Collection<ResourceType> rootTypes = resourceTypes.getRoots();
-    Set<String> 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;
index 12a0659a51129abfbe772c0fbe3414339f1d4727..f24ce5df943f7de0bf618f0a5f8bfd0c3bfb5f92 100644 (file)
@@ -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) {
index 40c2c352a6f5a93e8128060544fd27cd9d9e0a2b..e37c323a3509e5965323db5d0fe7e0f2b79dcb49 100644 (file)
@@ -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<AddGroupAction> {
       .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<AddGroupAction> {
     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)
index 9ad22e09e59b9960fbf23444cb485b696798eebb..0731eec4da0c7f775e48f5238704e49bac9d3081 100644 (file)
@@ -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<AddUserAction> {
       .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());
index 1692fdb8a57dc3b7a88cac2716213a1bce63af01..f4d899075f0c0a0bbdd72db87829cccc610d859d 100644 (file)
@@ -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<RemoveGroupActio
   }
 
   @Test
-  public void fail_when_component_is_not_a_project() throws Exception {
+  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 'KEY_file-uuid' (id: file-uuid) must be a project or a module.");
+    expectedException.expectMessage("Component '" + file.key() + "' (id: " + file.uuid() + ") must be a project or a view.");
 
     newRequest()
-      .setParam(PARAM_GROUP_NAME, aGroup.getName())
-      .setParam(PARAM_PROJECT_ID, file.uuid())
-      .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
-      .execute();
+        .setParam(PARAM_GROUP_NAME, aGroup.getName())
+        .setParam(PARAM_PROJECT_ID, file.uuid())
+        .setParam(PARAM_PERMISSION, SYSTEM_ADMIN)
+        .execute();
   }
 
   @Test
index 730f6bcdf9c17e19fa46e0b6ff9a407e9e2136ef..2e1b2a177a44d222f7ece43e919fb11f3497f634 100644 (file)
@@ -39,8 +39,11 @@ import static org.sonar.api.web.UserRole.USER;
 import static org.sonar.core.permission.GlobalPermissions.PROVISIONING;
 import static org.sonar.core.permission.GlobalPermissions.QUALITY_GATE_ADMIN;
 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.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_GATES;
@@ -170,15 +173,42 @@ public class RemoveUserActionTest extends BasePermissionWsTest<RemoveUserAction>
   }
 
   @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();
   }