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;
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;
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;
@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
}
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);
}
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) {
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;
@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
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() {