From 568ac968c81d2e5b29fbb427c40637fdde207f6c Mon Sep 17 00:00:00 2001 From: Teryk Bellahsene Date: Fri, 26 Jun 2015 16:46:16 +0200 Subject: [PATCH] SONAR-6615 ws custom_measures/search requires 'Administer System' permission or 'Administer' permission on the project --- .../measure/custom/ws/CreateAction.java | 13 ++------ .../custom/ws/CustomMeasureValidator.java | 12 +++++++ .../measure/custom/ws/MetricsAction.java | 13 ++------ .../measure/custom/ws/SearchAction.java | 10 ++++-- .../measure/custom/ws/UpdateAction.java | 15 ++------- .../measure/custom/ws/SearchActionTest.java | 33 ++++++++++++++++++- 6 files changed, 59 insertions(+), 37 deletions(-) diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/CreateAction.java b/server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/CreateAction.java index 129bf9d24ab1..9ea34b0146c9 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/CreateAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/CreateAction.java @@ -27,11 +27,9 @@ import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.System2; import org.sonar.api.utils.text.JsonWriter; -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.MyBatis; import org.sonar.server.db.DbClient; @@ -42,6 +40,7 @@ import org.sonar.server.user.index.UserIndex; import static com.google.common.base.Preconditions.checkArgument; +import static org.sonar.server.measure.custom.ws.CustomMeasureValidator.checkPermissions; import static org.sonar.server.measure.custom.ws.CustomMeasureValueDescription.measureValueDescription; public class CreateAction implements CustomMeasuresWsAction { @@ -116,7 +115,7 @@ public void handle(Request request, Response response) throws Exception { try { ComponentDto component = searchProject(dbSession, request); MetricDto metric = searchMetric(dbSession, request); - checkPermissions(component); + checkPermissions(userSession, component); checkIsProjectOrModule(component); checkMeasureDoesNotExistAlready(dbSession, component, metric); UserDoc user = userIndex.getByLogin(userSession.getLogin()); @@ -145,14 +144,6 @@ private static void checkIsProjectOrModule(ComponentDto component) { } } - private void checkPermissions(ComponentDto component) { - if (userSession.hasGlobalPermission(GlobalPermissions.SYSTEM_ADMIN)) { - return; - } - - userSession.checkLoggedIn().checkProjectUuidPermission(UserRole.ADMIN, component.projectUuid()); - } - private void checkMeasureDoesNotExistAlready(DbSession dbSession, ComponentDto component, MetricDto metric) { int nbMeasuresOnSameMetricAndMeasure = dbClient.customMeasureDao().countByComponentIdAndMetricId(dbSession, component.uuid(), metric.getId()); if (nbMeasuresOnSameMetricAndMeasure > 0) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/CustomMeasureValidator.java b/server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/CustomMeasureValidator.java index f698c7d21bc5..68cb3e5c76cd 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/CustomMeasureValidator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/CustomMeasureValidator.java @@ -23,8 +23,12 @@ import org.sonar.api.PropertyType; import org.sonar.api.measures.Metric; import org.sonar.api.server.ServerSide; +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.server.user.UserSession; import org.sonar.server.util.TypeValidations; @ServerSide @@ -95,4 +99,12 @@ private void checkAndSetBooleanMeasureValue(CustomMeasureDto measure, String val typeValidations.validate(valueAsString, PropertyType.BOOLEAN.name(), null); measure.setValue(Boolean.parseBoolean(valueAsString) ? 1.0d : 0.0d); } + + public static void checkPermissions(UserSession userSession, ComponentDto component) { + if (userSession.hasGlobalPermission(GlobalPermissions.SYSTEM_ADMIN)) { + return; + } + + userSession.checkLoggedIn().checkProjectUuidPermission(UserRole.ADMIN, component.projectUuid()); + } } 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 858020602da5..47e6424d1c42 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,10 +26,8 @@ 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; @@ -38,6 +36,7 @@ import org.sonar.server.user.UserSession; import static com.google.common.base.Preconditions.checkArgument; +import static org.sonar.server.measure.custom.ws.CustomMeasureValidator.checkPermissions; public class MetricsAction implements CustomMeasuresWsAction { public static final String ACTION = "metrics"; @@ -78,7 +77,7 @@ public void handle(Request request, Response response) throws Exception { try { ComponentDto project = searchProject(dbSession, request); - checkPermissions(project); + checkPermissions(userSession, project); List metrics = searchMetrics(dbSession, project); writeResponse(response.newJsonWriter(), metrics); @@ -119,12 +118,4 @@ private ComponentDto searchProject(DbSession dbSession, Request request) { return project; } - - private void checkPermissions(ComponentDto component) { - if (userSession.hasGlobalPermission(GlobalPermissions.SYSTEM_ADMIN)) { - return; - } - - userSession.checkLoggedIn().checkProjectUuidPermission(UserRole.ADMIN, component.projectUuid()); - } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/SearchAction.java index b27e25342919..fecf851b57b9 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/SearchAction.java @@ -42,10 +42,12 @@ import org.sonar.core.persistence.MyBatis; import org.sonar.server.db.DbClient; import org.sonar.server.es.SearchOptions; +import org.sonar.server.user.UserSession; import org.sonar.server.user.index.UserIndex; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.Sets.newHashSet; +import static org.sonar.server.measure.custom.ws.CustomMeasureValidator.checkPermissions; public class SearchAction implements CustomMeasuresWsAction { @@ -56,17 +58,20 @@ public class SearchAction implements CustomMeasuresWsAction { private final DbClient dbClient; private final UserIndex userIndex; private final CustomMeasureJsonWriter customMeasureJsonWriter; + private final UserSession userSession; - public SearchAction(DbClient dbClient, UserIndex userIndex, CustomMeasureJsonWriter customMeasureJsonWriter) { + public SearchAction(DbClient dbClient, UserIndex userIndex, CustomMeasureJsonWriter customMeasureJsonWriter, UserSession userSession) { this.dbClient = dbClient; this.userIndex = userIndex; this.customMeasureJsonWriter = customMeasureJsonWriter; + this.userSession = userSession; } @Override public void define(WebService.NewController context) { WebService.NewAction action = context.createAction(ACTION) - .setDescription("List custom measures. The project id or project key must be provided.") + .setDescription("List custom measures. The project id or project key must be provided.
" + + "Requires 'Administer System' permission or 'Administer' permission on the project.") .setSince("5.2") .addFieldsParam(CustomMeasureJsonWriter.OPTIONAL_FIELDS) .addPagingParams(100) @@ -94,6 +99,7 @@ public void handle(Request request, Response response) throws Exception { DbSession dbSession = dbClient.openSession(false); try { ComponentDto project = searchProject(dbSession, projectUuid, projectKey); + checkPermissions(userSession, project); Long lastAnalysisDateMs = searchLastSnapshot(dbSession, project); List customMeasures = searchCustomMeasures(dbSession, project, searchOptions); int nbCustomMeasures = countTotalOfCustomMeasures(dbSession, project); diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/UpdateAction.java b/server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/UpdateAction.java index 94ffe2e61677..2124aad6f01d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/UpdateAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/custom/ws/UpdateAction.java @@ -27,17 +27,16 @@ import org.sonar.api.user.User; import org.sonar.api.utils.System2; import org.sonar.api.utils.text.JsonWriter; -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.MyBatis; import org.sonar.server.db.DbClient; import org.sonar.server.user.UserSession; import org.sonar.server.user.index.UserIndex; +import static org.sonar.server.measure.custom.ws.CustomMeasureValidator.checkPermissions; import static org.sonar.server.measure.custom.ws.CustomMeasureValueDescription.measureValueDescription; public class UpdateAction implements CustomMeasuresWsAction { @@ -97,7 +96,7 @@ public void handle(Request request, Response response) throws Exception { CustomMeasureDto customMeasure = dbClient.customMeasureDao().selectById(dbSession, id); MetricDto metric = dbClient.metricDao().selectById(dbSession, customMeasure.getMetricId()); ComponentDto component = dbClient.componentDao().selectByUuid(dbSession, customMeasure.getComponentUuid()); - checkPermissions(component); + checkPermissions(userSession, component); User user = userIndex.getByLogin(userSession.getLogin()); setValue(customMeasure, value, metric); @@ -108,7 +107,7 @@ public void handle(Request request, Response response) throws Exception { dbSession.commit(); JsonWriter json = response.newJsonWriter(); - customMeasureJsonWriter.write(json, customMeasure, metric, component, user, true, CustomMeasureJsonWriter.OPTIONAL_FIELDS ); + customMeasureJsonWriter.write(json, customMeasure, metric, component, user, true, CustomMeasureJsonWriter.OPTIONAL_FIELDS); json.close(); } finally { MyBatis.closeQuietly(dbSession); @@ -132,12 +131,4 @@ private static void checkParameters(@Nullable String value, @Nullable String des throw new IllegalArgumentException("Value or description must be provided."); } } - - private void checkPermissions(ComponentDto component) { - if (userSession.hasGlobalPermission(GlobalPermissions.SYSTEM_ADMIN)) { - return; - } - - userSession.checkLoggedIn().checkProjectUuidPermission(UserRole.ADMIN, component.projectUuid()); - } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/custom/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/custom/ws/SearchActionTest.java index bff6d7a27f06..d609b3f12d39 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/measure/custom/ws/SearchActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/measure/custom/ws/SearchActionTest.java @@ -33,9 +33,11 @@ import org.sonar.api.measures.Metric.ValueType; import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.DateUtils; +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; @@ -44,6 +46,7 @@ import org.sonar.server.component.db.SnapshotDao; import org.sonar.server.db.DbClient; import org.sonar.server.es.EsTester; +import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.measure.custom.persistence.CustomMeasureDao; import org.sonar.server.metric.persistence.MetricDao; @@ -92,8 +95,9 @@ public void setUp() { db.truncateTables(); CustomMeasureJsonWriter customMeasureJsonWriter = new CustomMeasureJsonWriter(new UserJsonWriter(userSessionRule)); UserIndex userIndex = new UserIndex(es.client()); - ws = new WsTester(new CustomMeasuresWs(new SearchAction(dbClient, userIndex, customMeasureJsonWriter))); + ws = new WsTester(new CustomMeasuresWs(new SearchAction(dbClient, userIndex, customMeasureJsonWriter, userSessionRule))); defaultProject = insertDefaultProject(); + userSessionRule.login("login").setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN); } @After @@ -207,6 +211,19 @@ public void search_with_more_recent_analysis() throws Exception { assertThat(response).matches(nameValuePattern("pending", "false")); } + @Test + public void search_as_project_admin() throws Exception { + userSessionRule.login("login").addProjectUuidPermissions(UserRole.ADMIN, DEFAULT_PROJECT_UUID); + MetricDto metric1 = insertCustomMetric(1); + insertCustomMeasure(1, metric1); + + String response = newRequest() + .setParam(SearchAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID) + .execute().outputAsString(); + + assertThat(response).contains("text-value-1"); + } + @Test public void empty_json_when_no_measure() throws Exception { WsTester.Result response = newRequest() @@ -242,6 +259,20 @@ public void fail_when_project_not_found_in_db() throws Exception { newRequest().setParam(SearchAction.PARAM_PROJECT_ID, "wrong-project-uuid").execute(); } + @Test + public void fail_when_not_enough_privileges() throws Exception { + expectedException.expect(ForbiddenException.class); + userSessionRule.login("login"); + MetricDto metric1 = insertCustomMetric(1); + insertCustomMeasure(1, metric1); + + String response = newRequest() + .setParam(SearchAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID) + .execute().outputAsString(); + + assertThat(response).contains("text-value-1"); + } + private ComponentDto insertDefaultProject() { return insertProject(DEFAULT_PROJECT_UUID, DEFAULT_PROJECT_KEY); }