]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8609 Set api/components/search as public
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 18 Jan 2017 17:21:53 +0000 (18:21 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Tue, 24 Jan 2017 17:36:48 +0000 (18:36 +0100)
server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchAction.java
server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java
server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchActionTest.java

index af31e97b2ce56495aac6da9d09d6cbc91125267a..3538aaca55d51662cb5a888f32850a076d208bf1 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.server.component.ws;
 
 import com.google.common.base.Function;
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -32,7 +33,6 @@ 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.utils.Paging;
-import org.sonar.core.permission.GlobalPermissions;
 import org.sonar.core.util.stream.Collectors;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
@@ -40,13 +40,16 @@ import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ComponentQuery;
 import org.sonar.db.organization.OrganizationDto;
 import org.sonar.server.user.UserSession;
-import org.sonar.server.util.LanguageParamUtils;
 import org.sonarqube.ws.WsComponents;
 import org.sonarqube.ws.WsComponents.SearchWsResponse;
 import org.sonarqube.ws.client.component.SearchWsRequest;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.sonar.api.web.UserRole.USER;
 import static org.sonar.core.util.Protobuf.setNullable;
+import static org.sonar.core.util.stream.Collectors.uniqueIndex;
+import static org.sonar.server.util.LanguageParamUtils.getExampleValue;
+import static org.sonar.server.util.LanguageParamUtils.getLanguageKeys;
 import static org.sonar.server.ws.WsParameterBuilder.createQualifiersParameter;
 import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext;
 import static org.sonar.server.ws.WsUtils.writeProtobuf;
@@ -72,23 +75,19 @@ public class SearchAction implements ComponentsWsAction {
   @Override
   public void define(WebService.NewController context) {
     WebService.NewAction action = context.createAction(ACTION_SEARCH)
-      .setSince("5.2")
-      .setInternal(true)
+      .setSince("6.3")
       .setDescription("Search for components")
       .addPagingParams(100)
       .addSearchQuery("sona", "component names", "component keys")
       .setResponseExample(getClass().getResource("search-components-example.json"))
       .setHandler(this);
-
-    createQualifiersParameter(action, newQualifierParameterContext(i18n, resourceTypes))
-      .setRequired(true);
-
     action
       .createParam(PARAM_LANGUAGE)
       .setDescription("Language key. If provided, only components for the given language are returned.")
-      .setExampleValue(LanguageParamUtils.getExampleValue(languages))
-      .setPossibleValues(LanguageParamUtils.getLanguageKeys(languages))
-      .setSince("5.4");
+      .setExampleValue(getExampleValue(languages))
+      .setPossibleValues(getLanguageKeys(languages));
+    createQualifiersParameter(action, newQualifierParameterContext(i18n, resourceTypes))
+      .setRequired(true);
   }
 
   @Override
@@ -98,8 +97,6 @@ public class SearchAction implements ComponentsWsAction {
   }
 
   private SearchWsResponse doHandle(SearchWsRequest request) {
-    userSession.checkLoggedIn().checkPermission(GlobalPermissions.SYSTEM_ADMIN);
-
     try (DbSession dbSession = dbClient.openSession(false)) {
       ComponentQuery query = buildQuery(request);
       Paging paging = buildPaging(dbSession, request, query);
@@ -125,11 +122,18 @@ public class SearchAction implements ComponentsWsAction {
   }
 
   private List<ComponentDto> searchComponents(DbSession dbSession, ComponentQuery query, Paging paging) {
-    return dbClient.componentDao().selectByQuery(
-      dbSession,
-      query,
-      paging.offset(),
-      paging.pageSize());
+    List<ComponentDto> componentDtos = dbClient.componentDao().selectByQuery(dbSession, query, paging.offset(), paging.pageSize());
+    return filterAuthorizedComponents(dbSession, componentDtos);
+  }
+
+  private List<ComponentDto> filterAuthorizedComponents(DbSession dbSession, List<ComponentDto> componentDtos) {
+    Set<String> projectUuids = componentDtos.stream().map(ComponentDto::projectUuid).collect(Collectors.toSet());
+    List<ComponentDto> projects = dbClient.componentDao().selectByUuids(dbSession, projectUuids);
+    Map<String, Long> projectIdsByUuids = projects.stream().collect(uniqueIndex(ComponentDto::uuid, ComponentDto::getId));
+    Collection<Long> authorizedProjectIds = dbClient.authorizationDao().keepAuthorizedProjectIds(dbSession, projectIdsByUuids.values(), userSession.getUserId(), USER);
+    return componentDtos.stream()
+      .filter(component -> authorizedProjectIds.contains(projectIdsByUuids.get(component.projectUuid())))
+      .collect(Collectors.toList());
   }
 
   private static SearchWsResponse buildResponse(List<ComponentDto> components, Map<String, OrganizationDto> organizationsByUuid, Paging paging) {
index 7c065ea01ecb511e7426d072a5e71fcbe5503741..742215fef1a3d8e51256d9f9439ca630054cae3b 100644 (file)
@@ -97,16 +97,4 @@ public class ComponentsWsTest {
     assertThat(action.params()).hasSize(4);
   }
 
-  @Test
-  public void define_search_action() {
-    WebService.Action action = controller.action("search");
-
-    assertThat(action).isNotNull();
-    assertThat(action.param("qualifiers").isRequired()).isTrue();
-    assertThat(action.responseExampleAsString()).isNotEmpty();
-    assertThat(action.description()).isNotEmpty();
-    assertThat(action.isInternal()).isTrue();
-    assertThat(action.isPost()).isFalse();
-    assertThat(action.since()).isEqualTo("5.2");
-  }
 }
index f84759ba51a7faf8023c0769484a7a930a8370ca..24ee4f44ea095f1094cb47673c853c6e78eadeed 100644 (file)
 package org.sonar.server.component.ws;
 
 import com.google.common.base.Joiner;
+import com.google.common.base.Throwables;
 import java.io.IOException;
-import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.sonar.api.resources.Language;
 import org.sonar.api.resources.Languages;
-import org.sonar.api.resources.Qualifiers;
-import org.sonar.api.server.ws.WebService.Param;
+import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.System2;
-import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.api.web.UserRole;
 import org.sonar.db.DbTester;
-import org.sonar.db.component.ComponentDbTester;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ResourceTypesRule;
 import org.sonar.db.organization.OrganizationDto;
-import org.sonar.server.exceptions.ForbiddenException;
-import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.db.user.UserDto;
 import org.sonar.server.i18n.I18nRule;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.ws.TestRequest;
 import org.sonar.server.ws.WsActionTester;
 import org.sonarqube.ws.MediaTypes;
 import org.sonarqube.ws.WsComponents.SearchWsResponse;
+import org.sonarqube.ws.client.component.SearchWsRequest;
 
+import static java.util.Collections.singletonList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
+import static org.sonar.api.resources.Qualifiers.DIRECTORY;
+import static org.sonar.api.resources.Qualifiers.FILE;
+import static org.sonar.api.resources.Qualifiers.MODULE;
+import static org.sonar.api.resources.Qualifiers.PROJECT;
+import static org.sonar.api.server.ws.WebService.Param.PAGE;
+import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE;
+import static org.sonar.api.server.ws.WebService.Param.TEXT_QUERY;
+import static org.sonar.core.util.Protobuf.setNullable;
 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.newProjectDto;
 import static org.sonar.db.component.ComponentTesting.newView;
 import static org.sonar.test.JsonAssert.assertJson;
+import static org.sonarqube.ws.MediaTypes.PROTOBUF;
+import static org.sonarqube.ws.WsComponents.Component;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_LANGUAGE;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_QUALIFIERS;
 
@@ -66,105 +78,87 @@ public class SearchActionTest {
   @Rule
   public DbTester db = DbTester.create(System2.INSTANCE);
 
-  private ComponentDbTester componentDb = new ComponentDbTester(db);
   private I18nRule i18n = new I18nRule();
 
-  private WsActionTester ws;
   private ResourceTypesRule resourceTypes = new ResourceTypesRule();
-  private Languages languages;
+  private Languages languages = mock(Languages.class);
+  private UserDto user;
+
+  private WsActionTester ws;
 
   @Before
   public void setUp() {
-    userSession.login().setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
-    resourceTypes.setAllQualifiers(Qualifiers.PROJECT, Qualifiers.MODULE, Qualifiers.DIRECTORY, Qualifiers.FILE);
-    languages = mock(Languages.class);
+    resourceTypes.setAllQualifiers(PROJECT, MODULE, DIRECTORY, FILE);
     when(languages.all()).thenReturn(javaLanguage());
-
     ws = new WsActionTester(new SearchAction(db.getDbClient(), resourceTypes, i18n, userSession, languages));
+
+    user = db.users().insertUser("john");
+    userSession.login(user);
   }
 
   @Test
-  public void search_json_example() {
-    OrganizationDto organizationDto = db.organizations().insertForKey("my-org-1");
-    componentDb.insertComponent(newView(organizationDto));
-    ComponentDto project = componentDb.insertComponent(
-      newProjectDto(organizationDto, "project-uuid")
-        .setName("Project Name")
-        .setKey("project-key"));
-    ComponentDto module = componentDb.insertComponent(
-      newModuleDto("module-uuid", project)
-        .setName("Module Name")
-        .setKey("module-key"));
-    ComponentDto directory = newDirectory(module, "path/to/directoy")
-      .setUuid("directory-uuid")
-      .setKey("directory-key")
-      .setName("Directory Name");
-    componentDb.insertComponent(directory);
-    componentDb.insertComponent(
-      newFileDto(module, directory, "file-uuid")
-        .setKey("file-key")
-        .setLanguage("java")
-        .setName("File Name"));
-    db.commit();
+  public void search_by_key_query() throws IOException {
+    insertProjectsAuthorizedForUser(
+      newProjectDto(db.getDefaultOrganization()).setKey("project-_%-key"),
+      newProjectDto(db.getDefaultOrganization()).setKey("project-key-without-escaped-characters"));
 
-    String response = newRequest(Qualifiers.PROJECT, Qualifiers.MODULE, Qualifiers.DIRECTORY, Qualifiers.FILE)
-      .setMediaType(MediaTypes.JSON)
-      .execute()
-      .getInput();
+    SearchWsResponse response = call(new SearchWsRequest().setQuery("project-_%-key").setQualifiers(singletonList(PROJECT)));
 
-    assertJson(response).isSimilarTo(getClass().getResource("search-components-example.json"));
+    assertThat(response.getComponentsList()).extracting(Component::getKey).containsOnly("project-_%-key");
   }
 
   @Test
-  public void search_with_pagination() throws IOException {
-    OrganizationDto organizationDto = db.organizations().insert();
-    for (int i = 1; i <= 9; i++) {
-      componentDb.insertComponent(
-        newProjectDto(organizationDto, "project-uuid-" + i)
-          .setName("Project Name " + i));
-    }
-    db.commit();
+  public void search_for_files() throws IOException {
+    ComponentDto project = newProjectDto(db.getDefaultOrganization());
+    ComponentDto file1 = newFileDto(project).setKey("file1");
+    ComponentDto file2 = newFileDto(project).setKey("file2");
+    db.components().insertComponents(project, file1, file2);
+    setBrowsePermissionOnUser(project);
 
-    InputStream responseStream = newRequest(Qualifiers.PROJECT)
-      .setParam(Param.PAGE, "2")
-      .setParam(Param.PAGE_SIZE, "3")
-      .execute()
-      .getInputStream();
-    SearchWsResponse response = SearchWsResponse.parseFrom(responseStream);
+    SearchWsResponse response = call(new SearchWsRequest().setQuery(file1.key()).setQualifiers(singletonList(FILE)));
 
-    assertThat(response.getComponentsCount()).isEqualTo(3);
-    assertThat(response.getComponentsList()).extracting("id").containsExactly("project-uuid-4", "project-uuid-5", "project-uuid-6");
+    assertThat(response.getComponentsList()).extracting(Component::getKey).containsOnly(file1.getKey());
   }
 
   @Test
-  public void search_with_key_query() throws IOException {
-    componentDb.insertComponent(newProjectDto(db.getDefaultOrganization()).setKey("project-_%-key"));
-    componentDb.insertComponent(newProjectDto(db.getDefaultOrganization()).setKey("project-key-without-escaped-characters"));
-    db.commit();
+  public void search_with_pagination() throws IOException {
+    OrganizationDto organizationDto = db.organizations().insert();
+    List<ComponentDto> componentDtoList = new ArrayList<>();
+    for (int i = 1; i <= 9; i++) {
+      componentDtoList.add(newProjectDto(organizationDto, "project-uuid-" + i).setKey("project-key-" + i).setName("Project Name " + i));
+    }
+    insertProjectsAuthorizedForUser(componentDtoList.toArray(new ComponentDto[] {}));
 
-    InputStream responseStream = newRequest(Qualifiers.PROJECT)
-      .setParam(Param.TEXT_QUERY, "project-_%-key")
-      .execute().getInputStream();
-    SearchWsResponse response = SearchWsResponse.parseFrom(responseStream);
+    SearchWsResponse response = call(new SearchWsRequest().setPage(2).setPageSize(3).setQualifiers(singletonList(PROJECT)));
 
-    assertThat(response.getComponentsCount()).isEqualTo(1);
-    assertThat(response.getComponentsList()).extracting("key").containsExactly("project-_%-key");
+    assertThat(response.getComponentsList()).extracting(Component::getKey).containsExactly("project-key-4", "project-key-5", "project-key-6");
   }
 
   @Test
   public void search_with_language() throws IOException {
     OrganizationDto organizationDto = db.organizations().insert();
-    componentDb.insertComponent(newProjectDto(organizationDto).setKey("java-project").setLanguage("java"));
-    componentDb.insertComponent(newProjectDto(organizationDto).setKey("cpp-project").setLanguage("cpp"));
-    db.commit();
+    insertProjectsAuthorizedForUser(
+      newProjectDto(organizationDto).setKey("java-project").setLanguage("java"),
+      newProjectDto(organizationDto).setKey("cpp-project").setLanguage("cpp"));
 
-    InputStream responseStream = newRequest(Qualifiers.PROJECT)
-      .setParam(PARAM_LANGUAGE, "java")
-      .execute().getInputStream();
-    SearchWsResponse response = SearchWsResponse.parseFrom(responseStream);
+    SearchWsResponse response = call(new SearchWsRequest().setLanguage("java").setQualifiers(singletonList(PROJECT)));
+
+    assertThat(response.getComponentsList()).extracting(Component::getKey).containsOnly("java-project");
+  }
 
-    assertThat(response.getComponentsCount()).isEqualTo(1);
-    assertThat(response.getComponentsList().get(0).getKey()).isEqualTo("java-project");
+  @Test
+  public void return_only_components_from_projects_on_which_user_has_browse_permission() throws IOException {
+    ComponentDto project1 = newProjectDto(db.getDefaultOrganization());
+    ComponentDto file1 = newFileDto(project1).setKey("file1");
+    ComponentDto file2 = newFileDto(project1).setKey("file2");
+    ComponentDto project2 = newProjectDto(db.getDefaultOrganization());
+    ComponentDto file3 = newFileDto(project2).setKey("file3");
+    db.components().insertComponents(project1, file1, file2, project2, file3);
+    setBrowsePermissionOnUser(project1);
+
+    SearchWsResponse response = call(new SearchWsRequest().setQualifiers(singletonList(FILE)));
+
+    assertThat(response.getComponentsList()).extracting(Component::getKey).containsOnly(file1.getKey(), file2.getKey());
   }
 
   @Test
@@ -172,29 +166,72 @@ public class SearchActionTest {
     expectedException.expect(IllegalArgumentException.class);
     expectedException.expectMessage("Value of parameter 'qualifiers' (Unknown-Qualifier) must be one of: [BRC, DIR, FIL, TRK]");
 
-    newRequest("Unknown-Qualifier").execute();
+    call(new SearchWsRequest().setQualifiers(singletonList("Unknown-Qualifier")));
   }
 
   @Test
-  public void fail_if_not_logged_in() {
-    expectedException.expect(UnauthorizedException.class);
-    userSession.anonymous();
+  public void fail_when_no_qualifier_provided() {
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("The 'qualifiers' parameter is missing");
+
+    call(new SearchWsRequest());
+  }
 
-    newRequest(Qualifiers.PROJECT).execute();
+  @Test
+  public void test_json_example() {
+    OrganizationDto organizationDto = db.organizations().insertForKey("my-org-1");
+    db.components().insertComponent(newView(organizationDto));
+    ComponentDto project = newProjectDto(organizationDto, "project-uuid").setName("Project Name").setKey("project-key");
+    ComponentDto module = newModuleDto("module-uuid", project).setName("Module Name").setKey("module-key");
+    ComponentDto directory = newDirectory(module, "path/to/directoy").setUuid("directory-uuid").setKey("directory-key").setName("Directory Name");
+    db.components().insertComponents(project, module, directory,
+      newFileDto(module, directory, "file-uuid").setKey("file-key").setLanguage("java").setName("File Name"));
+    setBrowsePermissionOnUser(project);
+
+    String response = ws.newRequest()
+      .setMediaType(MediaTypes.JSON)
+      .setParam(PARAM_QUALIFIERS, Joiner.on(",").join(PROJECT, MODULE, DIRECTORY, FILE))
+      .execute().getInput();
+    assertJson(response).isSimilarTo(ws.getDef().responseExampleAsString());
   }
 
   @Test
-  public void fail_if_insufficient_privileges() {
-    expectedException.expect(ForbiddenException.class);
-    userSession.login().setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
+  public void test_definition() {
+    WebService.Action action = ws.getDef();
+
+    assertThat(action).isNotNull();
+    assertThat(action.param("qualifiers").isRequired()).isTrue();
+    assertThat(action.responseExampleAsString()).isNotEmpty();
+    assertThat(action.description()).isNotEmpty();
+    assertThat(action.isInternal()).isFalse();
+    assertThat(action.isPost()).isFalse();
+    assertThat(action.since()).isEqualTo("6.3");
+  }
 
-    newRequest(Qualifiers.PROJECT).execute();
+  private void insertProjectsAuthorizedForUser(ComponentDto... projects) {
+    db.components().insertComponents(projects);
+    setBrowsePermissionOnUser(projects);
+    db.commit();
+  }
+
+  private void setBrowsePermissionOnUser(ComponentDto... projects) {
+    Arrays.stream(projects).forEach(project -> db.users().insertProjectPermissionOnUser(user, UserRole.USER, project));
+    db.getSession().commit();
   }
 
-  private TestRequest newRequest(String... qualifiers) {
-    return ws.newRequest()
-      .setMediaType(MediaTypes.PROTOBUF)
-      .setParam(PARAM_QUALIFIERS, Joiner.on(",").join(qualifiers));
+  private SearchWsResponse call(SearchWsRequest wsRequest) {
+    TestRequest request = ws.newRequest()
+      .setMediaType(PROTOBUF);
+    setNullable(wsRequest.getLanguage(), p -> request.setParam(PARAM_LANGUAGE, p));
+    setNullable(wsRequest.getQualifiers(), p -> request.setParam(PARAM_QUALIFIERS, Joiner.on(",").join(p)));
+    setNullable(wsRequest.getQuery(), p -> request.setParam(TEXT_QUERY, p));
+    setNullable(wsRequest.getPage(), page -> request.setParam(PAGE, String.valueOf(page)));
+    setNullable(wsRequest.getPageSize(), pageSize -> request.setParam(PAGE_SIZE, String.valueOf(pageSize)));
+    try {
+      return SearchWsResponse.parseFrom(request.execute().getInputStream());
+    } catch (IOException e) {
+      throw Throwables.propagate(e);
+    }
   }
 
   private static Language[] javaLanguage() {