From 0cf3bfc1a7cbfe72af51861e074b77214972ae24 Mon Sep 17 00:00:00 2001 From: Teryk Bellahsene Date: Fri, 26 Jun 2015 14:46:12 +0200 Subject: [PATCH] SONAR-6612 ws custom_measures/metrics requires 'Administer System' permission or 'Administer' permission on the project --- .../measure/custom/ws/MetricsAction.java | 68 ++++++++++++++----- .../measure/custom/ws/MetricsActionTest.java | 37 ++++++++-- 2 files changed, 82 insertions(+), 23 deletions(-) diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/MetricsAction.java b/server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/MetricsAction.java index 791dc5e1a04..858020602da 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/MetricsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/MetricsAction.java @@ -26,11 +26,16 @@ 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.utils.text.JsonWriter; +import org.sonar.api.web.UserRole; +import org.sonar.core.component.ComponentDto; import org.sonar.core.metric.db.MetricDto; +import org.sonar.core.permission.GlobalPermissions; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; import org.sonar.server.db.DbClient; +import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.metric.ws.MetricJsonWriter; +import org.sonar.server.user.UserSession; import static com.google.common.base.Preconditions.checkArgument; @@ -40,9 +45,11 @@ public class MetricsAction implements CustomMeasuresWsAction { public static final String PARAM_PROJECT_KEY = "projectKey"; private final DbClient dbClient; + private final UserSession userSession; - public MetricsAction(DbClient dbClient) { + public MetricsAction(DbClient dbClient, UserSession userSession) { this.dbClient = dbClient; + this.userSession = userSession; } @Override @@ -53,7 +60,8 @@ public class MetricsAction implements CustomMeasuresWsAction { .setHandler(this) .setResponseExample(Resources.getResource(getClass(), "example-metrics.json")) .setDescription("List all custom metrics for which no custom measure already exists on a given project.
" + - "The project id or project key must be provided."); + "The project id or project key must be provided.
" + + "Requires 'Administer System' permission or 'Administer' permission on the project."); action.createParam(PARAM_PROJECT_ID) .setDescription("Project id") @@ -67,32 +75,56 @@ public class MetricsAction implements CustomMeasuresWsAction { @Override public void handle(Request request, Response response) throws Exception { DbSession dbSession = dbClient.openSession(false); + try { - List metrics = searchMetrics(dbSession, request); + ComponentDto project = searchProject(dbSession, request); + checkPermissions(project); + List metrics = searchMetrics(dbSession, project); - JsonWriter json = response.newJsonWriter(); - json.beginObject(); - MetricJsonWriter.write(json, metrics, MetricJsonWriter.ALL_FIELDS); - json.endObject(); - json.close(); + writeResponse(response.newJsonWriter(), metrics); } finally { MyBatis.closeQuietly(dbSession); } } - private List searchMetrics(DbSession dbSession, Request request) { - String projectUuidParam = request.param(PARAM_PROJECT_ID); - String projectKeyParam = request.param(PARAM_PROJECT_KEY); - String projectUuid; + private void writeResponse(JsonWriter json, List metrics) { + json.beginObject(); + MetricJsonWriter.write(json, metrics, MetricJsonWriter.ALL_FIELDS); + json.endObject(); + json.close(); + } + + private List searchMetrics(DbSession dbSession, ComponentDto project) { + return dbClient.metricDao().selectAvailableCustomMetricsByComponentUuid(dbSession, project.uuid()); + } + + private ComponentDto searchProject(DbSession dbSession, Request request) { + String projectUuid = request.param(PARAM_PROJECT_ID); + String projectKey = request.param(PARAM_PROJECT_KEY); + checkArgument(projectUuid != null ^ projectKey != null, "The project key or the project id must be provided, not both."); + + if (projectUuid != null) { + ComponentDto project = dbClient.componentDao().selectNullableByUuid(dbSession, projectUuid); + if (project == null) { + throw new NotFoundException(String.format("Project id '%s' not found", projectUuid)); + } - checkArgument(projectUuidParam != null ^ projectKeyParam != null, "The project uuid or the project key must be provided, not both."); + return project; + } + + ComponentDto project = dbClient.componentDao().selectNullableByKey(dbSession, projectKey); + if (project == null) { + throw new NotFoundException(String.format("Project key '%s' not found", projectKey)); + } + + return project; + } - if (projectUuidParam == null) { - projectUuid = dbClient.componentDao().selectByKey(dbSession, projectKeyParam).uuid(); - } else { - projectUuid = projectUuidParam; + private void checkPermissions(ComponentDto component) { + if (userSession.hasGlobalPermission(GlobalPermissions.SYSTEM_ADMIN)) { + return; } - return dbClient.metricDao().selectAvailableCustomMetricsByComponentUuid(dbSession, projectUuid); + userSession.checkLoggedIn().checkProjectUuidPermission(UserRole.ADMIN, component.projectUuid()); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/custom/ws/MetricsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/custom/ws/MetricsActionTest.java index 5dfafb50df0..e48f2d76142 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/measure/custom/ws/MetricsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/measure/custom/ws/MetricsActionTest.java @@ -30,17 +30,21 @@ import org.junit.experimental.categories.Category; import org.junit.rules.ExpectedException; import org.sonar.api.config.Settings; import org.sonar.api.measures.Metric; +import org.sonar.api.web.UserRole; import org.sonar.core.component.ComponentDto; import org.sonar.core.measure.custom.db.CustomMeasureDto; import org.sonar.core.metric.db.MetricDto; +import org.sonar.core.permission.GlobalPermissions; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.DbTester; import org.sonar.server.component.ComponentTesting; import org.sonar.server.component.db.ComponentDao; import org.sonar.server.db.DbClient; import org.sonar.server.es.EsTester; +import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.measure.custom.persistence.CustomMeasureDao; import org.sonar.server.metric.persistence.MetricDao; +import org.sonar.server.tester.UserSessionRule; import org.sonar.server.user.index.UserDoc; import org.sonar.server.user.index.UserIndexDefinition; import org.sonar.server.ws.WsTester; @@ -59,6 +63,8 @@ public class MetricsActionTest { @Rule public ExpectedException expectedException = ExpectedException.none(); + @Rule + public UserSessionRule userSession = UserSessionRule.standalone(); @ClassRule public static EsTester es = new EsTester().addDefinitions(new UserIndexDefinition(new Settings())); @ClassRule @@ -66,6 +72,7 @@ public class MetricsActionTest { DbClient dbClient; DbSession dbSession; WsTester ws; + ComponentDto defaultProject; @BeforeClass public static void setUpClass() throws Exception { @@ -81,7 +88,9 @@ public class MetricsActionTest { dbClient = new DbClient(db.database(), db.myBatis(), new MetricDao(), new ComponentDao(), new CustomMeasureDao()); dbSession = dbClient.openSession(false); db.truncateTables(); - ws = new WsTester(new CustomMeasuresWs(new MetricsAction(dbClient))); + ws = new WsTester(new CustomMeasuresWs(new MetricsAction(dbClient, userSession))); + userSession.login("login").setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN); + defaultProject = insertDefaultProject(); } @After @@ -118,11 +127,10 @@ public class MetricsActionTest { public void list_metrics_where_no_existing_custom_measure() throws Exception { MetricDto metric = insertCustomMetric("metric-key-1"); insertCustomMetric("metric-key-2"); - ComponentDto project = insertDefaultProject(); insertProject("project-uuid-2", "project-key-2"); CustomMeasureDto customMeasure = newCustomMeasureDto() - .setComponentUuid(project.uuid()) + .setComponentUuid(defaultProject.uuid()) .setMetricId(metric.getId()); dbClient.customMeasureDao().insert(dbSession, customMeasure); dbSession.commit(); @@ -137,11 +145,10 @@ public class MetricsActionTest { public void list_metrics_based_on_project_key() throws Exception { MetricDto metric = insertCustomMetric("metric-key-1"); insertCustomMetric("metric-key-2"); - ComponentDto project = insertDefaultProject(); insertProject("project-uuid-2", "project-key-2"); CustomMeasureDto customMeasure = newCustomMeasureDto() - .setComponentUuid(project.uuid()) + .setComponentUuid(defaultProject.uuid()) .setMetricId(metric.getId()); dbClient.customMeasureDao().insert(dbSession, customMeasure); dbSession.commit(); @@ -154,6 +161,16 @@ public class MetricsActionTest { .doesNotContain("metric-key-1"); } + @Test + public void list_metrics_as_a_project_admin() throws Exception { + insertCustomMetric("metric-key-1"); + userSession.login("login").addProjectUuidPermissions(UserRole.ADMIN, defaultProject.uuid()); + + String response = newRequest().outputAsString(); + + assertThat(response).contains("metric-key-1"); + } + @Test public void response_with_correct_formatting() throws Exception { dbClient.metricDao().insert(dbSession, newCustomMetric("custom-key-1") @@ -196,6 +213,16 @@ public class MetricsActionTest { ws.newGetRequest(ENDPOINT, ACTION).execute(); } + @Test + public void fail_if_insufficient_privilege() throws Exception { + expectedException.expect(ForbiddenException.class); + userSession.login("login"); + + insertCustomMetric("metric-key-1"); + + newRequest(); + } + private WsTester.Result newRequest() throws Exception { return ws.newGetRequest(ENDPOINT, ACTION) .setParam(MetricsAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID) -- 2.39.5