aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulien Lancelot <julien.lancelot@sonarsource.com>2016-10-25 14:19:25 +0200
committerJulien Lancelot <julien.lancelot@sonarsource.com>2016-10-25 15:34:48 +0200
commit8836dd4591462784a67c9adc77764ca2b876f241 (patch)
tree065a0d9ee8e5a4b237b3382742548afbf09bc04e
parent6dce6c46c7ebfa4408b09d8be40b519671afb1c5 (diff)
downloadsonarqube-8836dd4591462784a67c9adc77764ca2b876f241.tar.gz
sonarqube-8836dd4591462784a67c9adc77764ca2b876f241.zip
SONAR-8160 WS measures/search handles permission check on several projects
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchAction.java53
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchActionTest.java71
-rw-r--r--sonar-db/src/test/java/org/sonar/db/component/ComponentTesting.java28
3 files changed, 93 insertions, 59 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchAction.java
index f29df831428..99fb6524764 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchAction.java
@@ -32,11 +32,12 @@ import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collector;
-import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.util.stream.Collectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
@@ -44,16 +45,20 @@ import org.sonar.db.component.SnapshotDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.measure.MeasureQuery;
import org.sonar.db.metric.MetricDto;
+import org.sonar.server.user.UserSession;
import org.sonarqube.ws.WsMeasures;
import org.sonarqube.ws.WsMeasures.Measure;
import org.sonarqube.ws.WsMeasures.SearchWsResponse;
import org.sonarqube.ws.WsMeasures.SearchWsResponse.Component;
import org.sonarqube.ws.client.measure.SearchRequest;
-import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Comparator.comparing;
import static java.util.Objects.requireNonNull;
import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toMap;
+import static org.sonar.core.util.stream.Collectors.toList;
+import static org.sonar.core.util.stream.Collectors.toSet;
+import static org.sonar.core.util.stream.Collectors.uniqueIndex;
import static org.sonar.server.measure.ws.ComponentDtoToWsComponent.dbToWsComponent;
import static org.sonar.server.measure.ws.MeasureDtoToWsMeasure.dbToWsMeasure;
import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createMetricKeysParameter;
@@ -71,9 +76,11 @@ import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_
public class SearchAction implements MeasuresWsAction {
+ private final UserSession userSession;
private final DbClient dbClient;
- public SearchAction(DbClient dbClient) {
+ public SearchAction(UserSession userSession, DbClient dbClient) {
+ this.userSession = userSession;
this.dbClient = dbClient;
}
@@ -138,7 +145,7 @@ public class SearchAction implements MeasuresWsAction {
private List<SnapshotDto> searchSnapshots() {
requireNonNull(components);
- Set<String> projectUuids = components.stream().map(ComponentDto::projectUuid).collect(Collectors.toSet());
+ Set<String> projectUuids = components.stream().map(ComponentDto::projectUuid).collect(toSet());
return dbClient.snapshotDao().selectLastAnalysesByRootComponentUuids(dbSession, projectUuids);
}
@@ -154,7 +161,7 @@ public class SearchAction implements MeasuresWsAction {
private List<MetricDto> searchMetrics() {
requireNonNull(request);
List<MetricDto> dbMetrics = dbClient.metricDao().selectByKeys(dbSession, request.getMetricKeys());
- List<String> metricKeys = dbMetrics.stream().map(MetricDto::getKey).collect(Collectors.toList());
+ List<String> metricKeys = dbMetrics.stream().map(MetricDto::getKey).collect(toList());
checkRequest(request.getMetricKeys().size() == dbMetrics.size(), "The following metrics are not found: %s",
String.join(", ", difference(request.getMetricKeys(), metricKeys)));
return dbMetrics;
@@ -162,11 +169,19 @@ public class SearchAction implements MeasuresWsAction {
private List<ComponentDto> searchComponents() {
requireNonNull(request);
- List<ComponentDto> componentsByKey = searchByComponentKeys(dbSession, request.getComponentKeys());
- List<String> componentKeys = componentsByKey.stream().map(ComponentDto::key).collect(Collectors.toList());
- checkArgument(componentsByKey.size() == request.getComponentKeys().size(), "The following component keys are not found: %s",
- String.join(", ", difference(request.getComponentKeys(), componentKeys)));
- return componentsByKey;
+ return getAuthorizedComponents(searchByComponentKeys(dbSession, request.getComponentKeys()));
+ }
+
+ private List<ComponentDto> getAuthorizedComponents(List<ComponentDto> componentDtos) {
+ List<String> projectUuids = componentDtos.stream().map(ComponentDto::getRootUuid).collect(Collectors.toList());
+ 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,
+ projects.stream().map(ComponentDto::getId).collect(toList()),
+ userSession.getUserId(), UserRole.USER);
+ return componentDtos.stream()
+ .filter(c -> authorizedProjectIds.contains(projectIdsByUuids.get(c.projectUuid())))
+ .collect(Collectors.toList());
}
private List<ComponentDto> searchByComponentKeys(DbSession dbSession, List<String> componentKeys) {
@@ -178,8 +193,8 @@ public class SearchAction implements MeasuresWsAction {
requireNonNull(metrics);
return dbClient.measureDao().selectByQuery(dbSession, MeasureQuery.builder()
- .setComponentUuids(components.stream().map(ComponentDto::uuid).collect(Collectors.toList()))
- .setMetricIds(metrics.stream().map(MetricDto::getId).collect(Collectors.toList()))
+ .setComponentUuids(components.stream().map(ComponentDto::uuid).collect(toList()))
+ .setMetricIds(metrics.stream().map(MetricDto::getId).collect(toList()))
.build());
}
@@ -189,7 +204,7 @@ public class SearchAction implements MeasuresWsAction {
return expected.stream()
.filter(value -> !actualSet.contains(value))
.sorted(String::compareTo)
- .collect(Collectors.toList());
+ .collect(toList());
}
private SearchWsResponse buildResponse() {
@@ -211,12 +226,12 @@ public class SearchAction implements MeasuresWsAction {
return components.stream()
.map(dbToWsComponent())
.sorted(comparing(Component::getName))
- .collect(Collectors.toList());
+ .collect(toList());
}
private List<Measure> buildWsMeasures() {
- Map<String, String> componentNamesByUuid = components.stream().collect(Collectors.toMap(ComponentDto::uuid, ComponentDto::name));
- Map<Integer, MetricDto> metricsById = metrics.stream().collect(Collectors.toMap(MetricDto::getId, identity()));
+ Map<String, String> componentNamesByUuid = components.stream().collect(toMap(ComponentDto::uuid, ComponentDto::name));
+ Map<Integer, MetricDto> metricsById = metrics.stream().collect(toMap(MetricDto::getId, identity()));
Function<MeasureDto, MetricDto> dbMeasureToDbMetric = dbMeasure -> metricsById.get(dbMeasure.getMetricId());
Function<Measure, String> byMetricKey = Measure::getMetric;
@@ -226,13 +241,13 @@ public class SearchAction implements MeasuresWsAction {
.concat(measures.stream(), buildBestMeasures().stream())
.map(dbMeasure -> dbToWsMeasure(dbMeasure, dbMeasureToDbMetric.apply(dbMeasure)))
.sorted(comparing(byMetricKey).thenComparing(byComponentName))
- .collect(Collectors.toList());
+ .collect(toList());
}
private List<MeasureDto> buildBestMeasures() {
Set<MetricDto> metricsWithBestValue = metrics.stream()
.filter(metric -> metric.isOptimizedBestValue() && metric.getBestValue() != null)
- .collect(Collectors.toSet());
+ .collect(toSet());
Multimap<String, WsMeasures.Period> wsPeriodsByProjectUuid = snapshots.stream().collect(Collector.of(
ImmutableMultimap::<String, WsMeasures.Period>builder,
@@ -256,7 +271,7 @@ public class SearchAction implements MeasuresWsAction {
.flatMap(component -> metricsWithBestValue.stream()
.filter(doesNotHaveAMeasureInDb.apply(component))
.map(buildBestMeasure(component, wsPeriodsByProjectUuid.get(component.projectUuid()))))
- .collect(Collectors.toList());
+ .collect(toList());
}
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchActionTest.java
index 15607c6ce42..082db028f88 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchActionTest.java
@@ -28,6 +28,7 @@ import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javax.annotation.Nullable;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -35,6 +36,7 @@ import org.sonar.api.measures.Metric;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
@@ -42,6 +44,7 @@ import org.sonar.db.component.ComponentDbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.metric.MetricDto;
+import org.sonar.db.user.UserDto;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.ws.TestRequest;
@@ -49,8 +52,10 @@ import org.sonar.server.ws.WsActionTester;
import org.sonarqube.ws.WsMeasures;
import org.sonarqube.ws.WsMeasures.Measure;
import org.sonarqube.ws.WsMeasures.SearchWsResponse;
+import org.sonarqube.ws.WsMeasures.SearchWsResponse.Component;
import static com.google.common.collect.Lists.newArrayList;
+import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
@@ -82,7 +87,15 @@ public class SearchActionTest {
DbClient dbClient = db.getDbClient();
DbSession dbSession = db.getSession();
- WsActionTester ws = new WsActionTester(new SearchAction(dbClient));
+ UserDto user;
+
+ WsActionTester ws = new WsActionTester(new SearchAction(userSession, dbClient));
+
+ @Before
+ public void setUp() throws Exception {
+ user = db.users().insertUser("john");
+ userSession.login(user);
+ }
@Test
public void json_example() {
@@ -101,11 +114,12 @@ public class SearchActionTest {
public void project_without_measures_map_all_fields() {
ComponentDto dbComponent = componentDb.insertComponent(newProjectDto());
insertComplexityMetric();
+ setBrowsePermissionOnUser(dbComponent);
SearchWsResponse result = call(singletonList(dbComponent.key()), singletonList("complexity"));
assertThat(result.getComponentsCount()).isEqualTo(1);
- SearchWsResponse.Component wsComponent = result.getComponents(0);
+ Component wsComponent = result.getComponents(0);
assertThat(wsComponent.getKey()).isEqualTo(dbComponent.key());
assertThat(wsComponent.getName()).isEqualTo(dbComponent.name());
}
@@ -113,6 +127,7 @@ public class SearchActionTest {
@Test
public void search_by_component_key() {
ComponentDto project = componentDb.insertProject();
+ setBrowsePermissionOnUser(project);
insertComplexityMetric();
SearchWsResponse result = call(singletonList(project.key()), singletonList("complexity"));
@@ -125,6 +140,7 @@ public class SearchActionTest {
public void return_measures() throws Exception {
ComponentDto project = newProjectDto();
SnapshotDto projectSnapshot = componentDb.insertProjectAndSnapshot(project);
+ setBrowsePermissionOnUser(project);
MetricDto coverage = insertCoverageMetric();
dbClient.measureDao().insert(dbSession, newMeasureDto(coverage, project, projectSnapshot).setValue(15.5d));
db.commit();
@@ -142,6 +158,7 @@ public class SearchActionTest {
public void return_measures_on_periods() throws Exception {
ComponentDto project = newProjectDto();
SnapshotDto projectSnapshot = componentDb.insertProjectAndSnapshot(project);
+ setBrowsePermissionOnUser(project);
MetricDto coverage = insertCoverageMetric();
dbClient.measureDao().insert(dbSession,
newMeasureDto(coverage, project, projectSnapshot)
@@ -186,6 +203,7 @@ public class SearchActionTest {
newMeasureDto(coverage, file, projectSnapshot).setValue(15.5d),
newMeasureDto(coverage, directoryDto, projectSnapshot).setValue(42.0d));
db.commit();
+ setBrowsePermissionOnUser(projectDto);
SearchWsResponse result = call(newArrayList(directoryDto.key(), file.key()), newArrayList("ncloc", "coverage", "new_violations"));
@@ -201,8 +219,23 @@ public class SearchActionTest {
}
@Test
+ public void only_returns_authorized_components() {
+ insertComplexityMetric();
+ ComponentDto project1 = componentDb.insertProject();
+ ComponentDto file1 = componentDb.insertComponent(newFileDto(project1));
+ setBrowsePermissionOnUser(project1);
+ ComponentDto project2 = componentDb.insertProject();
+ ComponentDto file2 = componentDb.insertComponent(newFileDto(project2));
+
+ SearchWsResponse result = call(asList(file1.key(), file2.key()), singletonList("complexity"));
+
+ assertThat(result.getComponentsList()).extracting(Component::getKey).containsOnly(file1.key());
+ }
+
+ @Test
public void fail_if_no_metric() {
ComponentDto project = componentDb.insertProject();
+ setBrowsePermissionOnUser(project);
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("The 'metricKeys' parameter is missing");
@@ -213,6 +246,7 @@ public class SearchActionTest {
@Test
public void fail_if_empty_metric() {
ComponentDto project = componentDb.insertProject();
+ setBrowsePermissionOnUser(project);
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Metric keys must be provided");
@@ -223,6 +257,7 @@ public class SearchActionTest {
@Test
public void fail_if_unknown_metric() {
ComponentDto project = componentDb.insertProject();
+ setBrowsePermissionOnUser(project);
insertComplexityMetric();
expectedException.expect(BadRequestException.class);
@@ -252,31 +287,6 @@ public class SearchActionTest {
}
@Test
- public void fail_if_unknown_component_key() {
- insertComplexityMetric();
- ComponentDto project = componentDb.insertProject();
-
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("The following component keys are not found: ANOTHER_PROJECT_KEY, YOUR_PROJECT_KEY");
-
- call(newArrayList("YOUR_PROJECT_KEY", project.key(), "ANOTHER_PROJECT_KEY"), singletonList("complexity"));
- }
-
- @Test
- public void fail_if_more_than_100_component_id() {
- List<String> uuids = IntStream.rangeClosed(1, 101)
- .mapToObj(i -> componentDb.insertProject())
- .map(ComponentDto::uuid)
- .collect(Collectors.toList());
- insertComplexityMetric();
-
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("101 components provided, more than maximum authorized (100)");
-
- call(uuids, singletonList("complexity"));
- }
-
- @Test
public void fail_if_more_than_100_component_key() {
List<String> keys = IntStream.rangeClosed(1, 101)
.mapToObj(i -> componentDb.insertProject())
@@ -476,8 +486,13 @@ public class SearchActionTest {
.setVariation(1, 255.0d)
.setVariation(2, 0.0d)
.setVariation(3, 255.0d));
-
db.commit();
+ setBrowsePermissionOnUser(project);
return componentKeys;
}
+
+ private void setBrowsePermissionOnUser(ComponentDto project) {
+ db.users().insertProjectPermissionOnUser(user, UserRole.USER, project);
+ dbSession.commit();
+ }
}
diff --git a/sonar-db/src/test/java/org/sonar/db/component/ComponentTesting.java b/sonar-db/src/test/java/org/sonar/db/component/ComponentTesting.java
index e1b2f364b3b..f26cb90d4b0 100644
--- a/sonar-db/src/test/java/org/sonar/db/component/ComponentTesting.java
+++ b/sonar-db/src/test/java/org/sonar/db/component/ComponentTesting.java
@@ -30,6 +30,10 @@ import static org.sonar.db.component.ComponentDto.UUID_PATH_SEPARATOR;
public class ComponentTesting {
+ public static ComponentDto newFileDto(ComponentDto subProjectOrProject) {
+ return newFileDto(subProjectOrProject, null);
+ }
+
public static ComponentDto newFileDto(ComponentDto subProjectOrProject, @Nullable ComponentDto directory) {
return newFileDto(subProjectOrProject, directory, Uuids.create());
}
@@ -53,21 +57,21 @@ public class ComponentTesting {
public static ComponentDto newDirectory(ComponentDto module, String uuid, String path) {
return newChildComponent(uuid, module, module)
- .setKey(!path.equals("/") ? module.getKey() + ":" + path : module.getKey() + ":/")
- .setName(path)
- .setLongName(path)
- .setPath(path)
- .setScope(Scopes.DIRECTORY)
- .setQualifier(Qualifiers.DIRECTORY);
+ .setKey(!path.equals("/") ? module.getKey() + ":" + path : module.getKey() + ":/")
+ .setName(path)
+ .setLongName(path)
+ .setPath(path)
+ .setScope(Scopes.DIRECTORY)
+ .setQualifier(Qualifiers.DIRECTORY);
}
public static ComponentDto newSubView(ComponentDto viewOrSubView, String uuid, String key) {
return newChildComponent(uuid, viewOrSubView, viewOrSubView)
- .setKey(key)
- .setName(key)
- .setLongName(key)
- .setScope(Scopes.PROJECT)
- .setQualifier(Qualifiers.SUBVIEW);
+ .setKey(key)
+ .setName(key)
+ .setLongName(key)
+ .setScope(Scopes.PROJECT)
+ .setQualifier(Qualifiers.SUBVIEW);
}
public static ComponentDto newModuleDto(String uuid, ComponentDto parentModuleOrProject) {
@@ -120,7 +124,7 @@ public class ComponentTesting {
.setName(name)
.setLongName(name)
.setScope(Scopes.PROJECT)
- // XXX No constant !
+ // XXX No constant !
.setQualifier("DEV")
.setPath(null)
.setLanguage(null)