Skip to content

Commit

Permalink
SONAR-6615 ws custom_measures/search requires 'Administer System' per…
Browse files Browse the repository at this point in the history
…mission or 'Administer' permission on the project
  • Loading branch information
teryk committed Jun 26, 2015
1 parent 99897bc commit 568ac96
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 37 deletions.
Expand Up @@ -27,11 +27,9 @@
import org.sonar.api.server.ws.WebService; import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.System2; import org.sonar.api.utils.System2;
import org.sonar.api.utils.text.JsonWriter; import org.sonar.api.utils.text.JsonWriter;
import org.sonar.api.web.UserRole;
import org.sonar.core.component.ComponentDto; import org.sonar.core.component.ComponentDto;
import org.sonar.core.measure.custom.db.CustomMeasureDto; import org.sonar.core.measure.custom.db.CustomMeasureDto;
import org.sonar.core.metric.db.MetricDto; import org.sonar.core.metric.db.MetricDto;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.DbSession;
import org.sonar.core.persistence.MyBatis; import org.sonar.core.persistence.MyBatis;
import org.sonar.server.db.DbClient; import org.sonar.server.db.DbClient;
Expand All @@ -42,6 +40,7 @@
import org.sonar.server.user.index.UserIndex; import org.sonar.server.user.index.UserIndex;


import static com.google.common.base.Preconditions.checkArgument; 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; import static org.sonar.server.measure.custom.ws.CustomMeasureValueDescription.measureValueDescription;


public class CreateAction implements CustomMeasuresWsAction { public class CreateAction implements CustomMeasuresWsAction {
Expand Down Expand Up @@ -116,7 +115,7 @@ public void handle(Request request, Response response) throws Exception {
try { try {
ComponentDto component = searchProject(dbSession, request); ComponentDto component = searchProject(dbSession, request);
MetricDto metric = searchMetric(dbSession, request); MetricDto metric = searchMetric(dbSession, request);
checkPermissions(component); checkPermissions(userSession, component);
checkIsProjectOrModule(component); checkIsProjectOrModule(component);
checkMeasureDoesNotExistAlready(dbSession, component, metric); checkMeasureDoesNotExistAlready(dbSession, component, metric);
UserDoc user = userIndex.getByLogin(userSession.getLogin()); UserDoc user = userIndex.getByLogin(userSession.getLogin());
Expand Down Expand Up @@ -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) { private void checkMeasureDoesNotExistAlready(DbSession dbSession, ComponentDto component, MetricDto metric) {
int nbMeasuresOnSameMetricAndMeasure = dbClient.customMeasureDao().countByComponentIdAndMetricId(dbSession, component.uuid(), metric.getId()); int nbMeasuresOnSameMetricAndMeasure = dbClient.customMeasureDao().countByComponentIdAndMetricId(dbSession, component.uuid(), metric.getId());
if (nbMeasuresOnSameMetricAndMeasure > 0) { if (nbMeasuresOnSameMetricAndMeasure > 0) {
Expand Down
Expand Up @@ -23,8 +23,12 @@
import org.sonar.api.PropertyType; import org.sonar.api.PropertyType;
import org.sonar.api.measures.Metric; import org.sonar.api.measures.Metric;
import org.sonar.api.server.ServerSide; 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.measure.custom.db.CustomMeasureDto;
import org.sonar.core.metric.db.MetricDto; 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; import org.sonar.server.util.TypeValidations;


@ServerSide @ServerSide
Expand Down Expand Up @@ -95,4 +99,12 @@ private void checkAndSetBooleanMeasureValue(CustomMeasureDto measure, String val
typeValidations.validate(valueAsString, PropertyType.BOOLEAN.name(), null); typeValidations.validate(valueAsString, PropertyType.BOOLEAN.name(), null);
measure.setValue(Boolean.parseBoolean(valueAsString) ? 1.0d : 0.0d); 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());
}
} }
Expand Up @@ -26,10 +26,8 @@
import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService; import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.text.JsonWriter; import org.sonar.api.utils.text.JsonWriter;
import org.sonar.api.web.UserRole;
import org.sonar.core.component.ComponentDto; import org.sonar.core.component.ComponentDto;
import org.sonar.core.metric.db.MetricDto; import org.sonar.core.metric.db.MetricDto;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.DbSession;
import org.sonar.core.persistence.MyBatis; import org.sonar.core.persistence.MyBatis;
import org.sonar.server.db.DbClient; import org.sonar.server.db.DbClient;
Expand All @@ -38,6 +36,7 @@
import org.sonar.server.user.UserSession; import org.sonar.server.user.UserSession;


import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static org.sonar.server.measure.custom.ws.CustomMeasureValidator.checkPermissions;


public class MetricsAction implements CustomMeasuresWsAction { public class MetricsAction implements CustomMeasuresWsAction {
public static final String ACTION = "metrics"; public static final String ACTION = "metrics";
Expand Down Expand Up @@ -78,7 +77,7 @@ public void handle(Request request, Response response) throws Exception {


try { try {
ComponentDto project = searchProject(dbSession, request); ComponentDto project = searchProject(dbSession, request);
checkPermissions(project); checkPermissions(userSession, project);
List<MetricDto> metrics = searchMetrics(dbSession, project); List<MetricDto> metrics = searchMetrics(dbSession, project);


writeResponse(response.newJsonWriter(), metrics); writeResponse(response.newJsonWriter(), metrics);
Expand Down Expand Up @@ -119,12 +118,4 @@ private ComponentDto searchProject(DbSession dbSession, Request request) {


return project; return project;
} }

private void checkPermissions(ComponentDto component) {
if (userSession.hasGlobalPermission(GlobalPermissions.SYSTEM_ADMIN)) {
return;
}

userSession.checkLoggedIn().checkProjectUuidPermission(UserRole.ADMIN, component.projectUuid());
}
} }
Expand Up @@ -42,10 +42,12 @@
import org.sonar.core.persistence.MyBatis; import org.sonar.core.persistence.MyBatis;
import org.sonar.server.db.DbClient; import org.sonar.server.db.DbClient;
import org.sonar.server.es.SearchOptions; import org.sonar.server.es.SearchOptions;
import org.sonar.server.user.UserSession;
import org.sonar.server.user.index.UserIndex; import org.sonar.server.user.index.UserIndex;


import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Sets.newHashSet; import static com.google.common.collect.Sets.newHashSet;
import static org.sonar.server.measure.custom.ws.CustomMeasureValidator.checkPermissions;


public class SearchAction implements CustomMeasuresWsAction { public class SearchAction implements CustomMeasuresWsAction {


Expand All @@ -56,17 +58,20 @@ public class SearchAction implements CustomMeasuresWsAction {
private final DbClient dbClient; private final DbClient dbClient;
private final UserIndex userIndex; private final UserIndex userIndex;
private final CustomMeasureJsonWriter customMeasureJsonWriter; 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.dbClient = dbClient;
this.userIndex = userIndex; this.userIndex = userIndex;
this.customMeasureJsonWriter = customMeasureJsonWriter; this.customMeasureJsonWriter = customMeasureJsonWriter;
this.userSession = userSession;
} }


@Override @Override
public void define(WebService.NewController context) { public void define(WebService.NewController context) {
WebService.NewAction action = context.createAction(ACTION) 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.<br />" +
"Requires 'Administer System' permission or 'Administer' permission on the project.")
.setSince("5.2") .setSince("5.2")
.addFieldsParam(CustomMeasureJsonWriter.OPTIONAL_FIELDS) .addFieldsParam(CustomMeasureJsonWriter.OPTIONAL_FIELDS)
.addPagingParams(100) .addPagingParams(100)
Expand Down Expand Up @@ -94,6 +99,7 @@ public void handle(Request request, Response response) throws Exception {
DbSession dbSession = dbClient.openSession(false); DbSession dbSession = dbClient.openSession(false);
try { try {
ComponentDto project = searchProject(dbSession, projectUuid, projectKey); ComponentDto project = searchProject(dbSession, projectUuid, projectKey);
checkPermissions(userSession, project);
Long lastAnalysisDateMs = searchLastSnapshot(dbSession, project); Long lastAnalysisDateMs = searchLastSnapshot(dbSession, project);
List<CustomMeasureDto> customMeasures = searchCustomMeasures(dbSession, project, searchOptions); List<CustomMeasureDto> customMeasures = searchCustomMeasures(dbSession, project, searchOptions);
int nbCustomMeasures = countTotalOfCustomMeasures(dbSession, project); int nbCustomMeasures = countTotalOfCustomMeasures(dbSession, project);
Expand Down
Expand Up @@ -27,17 +27,16 @@
import org.sonar.api.user.User; import org.sonar.api.user.User;
import org.sonar.api.utils.System2; import org.sonar.api.utils.System2;
import org.sonar.api.utils.text.JsonWriter; import org.sonar.api.utils.text.JsonWriter;
import org.sonar.api.web.UserRole;
import org.sonar.core.component.ComponentDto; import org.sonar.core.component.ComponentDto;
import org.sonar.core.measure.custom.db.CustomMeasureDto; import org.sonar.core.measure.custom.db.CustomMeasureDto;
import org.sonar.core.metric.db.MetricDto; import org.sonar.core.metric.db.MetricDto;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.DbSession;
import org.sonar.core.persistence.MyBatis; import org.sonar.core.persistence.MyBatis;
import org.sonar.server.db.DbClient; import org.sonar.server.db.DbClient;
import org.sonar.server.user.UserSession; import org.sonar.server.user.UserSession;
import org.sonar.server.user.index.UserIndex; 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; import static org.sonar.server.measure.custom.ws.CustomMeasureValueDescription.measureValueDescription;


public class UpdateAction implements CustomMeasuresWsAction { public class UpdateAction implements CustomMeasuresWsAction {
Expand Down Expand Up @@ -97,7 +96,7 @@ public void handle(Request request, Response response) throws Exception {
CustomMeasureDto customMeasure = dbClient.customMeasureDao().selectById(dbSession, id); CustomMeasureDto customMeasure = dbClient.customMeasureDao().selectById(dbSession, id);
MetricDto metric = dbClient.metricDao().selectById(dbSession, customMeasure.getMetricId()); MetricDto metric = dbClient.metricDao().selectById(dbSession, customMeasure.getMetricId());
ComponentDto component = dbClient.componentDao().selectByUuid(dbSession, customMeasure.getComponentUuid()); ComponentDto component = dbClient.componentDao().selectByUuid(dbSession, customMeasure.getComponentUuid());
checkPermissions(component); checkPermissions(userSession, component);
User user = userIndex.getByLogin(userSession.getLogin()); User user = userIndex.getByLogin(userSession.getLogin());


setValue(customMeasure, value, metric); setValue(customMeasure, value, metric);
Expand All @@ -108,7 +107,7 @@ public void handle(Request request, Response response) throws Exception {
dbSession.commit(); dbSession.commit();


JsonWriter json = response.newJsonWriter(); 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(); json.close();
} finally { } finally {
MyBatis.closeQuietly(dbSession); MyBatis.closeQuietly(dbSession);
Expand All @@ -132,12 +131,4 @@ private static void checkParameters(@Nullable String value, @Nullable String des
throw new IllegalArgumentException("Value or description must be provided."); 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());
}
} }
Expand Up @@ -33,9 +33,11 @@
import org.sonar.api.measures.Metric.ValueType; import org.sonar.api.measures.Metric.ValueType;
import org.sonar.api.server.ws.WebService; import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.DateUtils; import org.sonar.api.utils.DateUtils;
import org.sonar.api.web.UserRole;
import org.sonar.core.component.ComponentDto; import org.sonar.core.component.ComponentDto;
import org.sonar.core.measure.custom.db.CustomMeasureDto; import org.sonar.core.measure.custom.db.CustomMeasureDto;
import org.sonar.core.metric.db.MetricDto; import org.sonar.core.metric.db.MetricDto;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.DbSession;
import org.sonar.core.persistence.DbTester; import org.sonar.core.persistence.DbTester;
import org.sonar.server.component.ComponentTesting; import org.sonar.server.component.ComponentTesting;
Expand All @@ -44,6 +46,7 @@
import org.sonar.server.component.db.SnapshotDao; import org.sonar.server.component.db.SnapshotDao;
import org.sonar.server.db.DbClient; import org.sonar.server.db.DbClient;
import org.sonar.server.es.EsTester; import org.sonar.server.es.EsTester;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.measure.custom.persistence.CustomMeasureDao; import org.sonar.server.measure.custom.persistence.CustomMeasureDao;
import org.sonar.server.metric.persistence.MetricDao; import org.sonar.server.metric.persistence.MetricDao;
Expand Down Expand Up @@ -92,8 +95,9 @@ public void setUp() {
db.truncateTables(); db.truncateTables();
CustomMeasureJsonWriter customMeasureJsonWriter = new CustomMeasureJsonWriter(new UserJsonWriter(userSessionRule)); CustomMeasureJsonWriter customMeasureJsonWriter = new CustomMeasureJsonWriter(new UserJsonWriter(userSessionRule));
UserIndex userIndex = new UserIndex(es.client()); 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(); defaultProject = insertDefaultProject();
userSessionRule.login("login").setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
} }


@After @After
Expand Down Expand Up @@ -207,6 +211,19 @@ public void search_with_more_recent_analysis() throws Exception {
assertThat(response).matches(nameValuePattern("pending", "false")); 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 @Test
public void empty_json_when_no_measure() throws Exception { public void empty_json_when_no_measure() throws Exception {
WsTester.Result response = newRequest() WsTester.Result response = newRequest()
Expand Down Expand Up @@ -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(); 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() { private ComponentDto insertDefaultProject() {
return insertProject(DEFAULT_PROJECT_UUID, DEFAULT_PROJECT_KEY); return insertProject(DEFAULT_PROJECT_UUID, DEFAULT_PROJECT_KEY);
} }
Expand Down

0 comments on commit 568ac96

Please sign in to comment.