diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2017-01-18 18:21:53 +0100 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2017-01-24 18:36:48 +0100 |
commit | c74399adb30498873e91b3b009632689ad71572b (patch) | |
tree | 134865b3f3636a858250d1d204c2d54edc2b0aaf /server | |
parent | 14017cf25e11153f42f7ef8411909b61a9c358e5 (diff) | |
download | sonarqube-c74399adb30498873e91b3b009632689ad71572b.tar.gz sonarqube-c74399adb30498873e91b3b009632689ad71572b.zip |
SONAR-8609 Set api/components/search as public
Diffstat (limited to 'server')
3 files changed, 148 insertions, 119 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchAction.java index af31e97b2ce..3538aaca55d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchAction.java @@ -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) { diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java index 7c065ea01ec..742215fef1a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java @@ -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"); - } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchActionTest.java index f84759ba51a..24ee4f44ea0 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchActionTest.java @@ -20,41 +20,53 @@ 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() { |