From: Steve Marion Date: Wed, 12 Apr 2023 15:45:26 +0000 (+0200) Subject: [SONAR-18964] make query parameters lastConnectedAfter, lastConnectedBefore, slLastCo... X-Git-Tag: 10.1.0.73491~450 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=0cacb9813afdd9abce8931d03f7e15f5fab40930;p=sonarqube.git [SONAR-18964] make query parameters lastConnectedAfter, lastConnectedBefore, slLastConnectedAfter, slLastConnectedBefore of api/users/search endpoint require Administer System permission. --- diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/user/ws/SearchActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/user/ws/SearchActionIT.java index 7ce5deac990..db71e878fea 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/user/ws/SearchActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/user/ws/SearchActionIT.java @@ -20,9 +20,12 @@ package org.sonar.server.user.ws; import java.time.Instant; +import java.time.OffsetDateTime; import java.time.temporal.ChronoUnit; import java.util.Set; import java.util.stream.IntStream; +import java.util.stream.Stream; +import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.Rule; import org.junit.Test; import org.sonar.api.server.ws.WebService; @@ -34,6 +37,7 @@ import org.sonar.db.scim.ScimUserDao; import org.sonar.db.user.GroupDto; import org.sonar.db.user.UserDto; import org.sonar.server.exceptions.BadRequestException; +import org.sonar.server.exceptions.ServerException; import org.sonar.server.issue.AvatarResolverImpl; import org.sonar.server.management.ManagedInstanceService; import org.sonar.server.tester.UserSessionRule; @@ -50,6 +54,7 @@ import static java.util.function.Function.identity; import static java.util.stream.Collectors.toMap; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.tuple; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -510,18 +515,35 @@ public class SearchActionIT { .extracting(User::getLogin) .containsExactlyInAnyOrder(user.getLogin()); - assertUserWithFilter("lastConnectedAfter", lastConnection.minus(1, ChronoUnit.DAYS), user.getLogin(), true); - assertUserWithFilter("lastConnectedAfter", lastConnection.plus(1, ChronoUnit.DAYS), user.getLogin(), false); - assertUserWithFilter("lastConnectedBefore", lastConnection.minus(1, ChronoUnit.DAYS), user.getLogin(), false); - assertUserWithFilter("lastConnectedBefore", lastConnection.plus(1, ChronoUnit.DAYS), user.getLogin(), true); + assertUserWithFilter(SearchAction.LAST_CONNECTION_DATE_FROM, lastConnection.minus(1, ChronoUnit.DAYS), user.getLogin(), true); + assertUserWithFilter(SearchAction.LAST_CONNECTION_DATE_FROM, lastConnection.plus(1, ChronoUnit.DAYS), user.getLogin(), false); + assertUserWithFilter(SearchAction.LAST_CONNECTION_DATE_TO, lastConnection.minus(1, ChronoUnit.DAYS), user.getLogin(), false); + assertUserWithFilter(SearchAction.LAST_CONNECTION_DATE_TO, lastConnection.plus(1, ChronoUnit.DAYS), user.getLogin(), true); - assertUserWithFilter("slLastConnectedAfter", lastConnection.minus(1, ChronoUnit.DAYS), user.getLogin(), true); - assertUserWithFilter("slLastConnectedAfter", lastConnection.plus(1, ChronoUnit.DAYS), user.getLogin(), false); - assertUserWithFilter("slLastConnectedBefore", lastConnection.minus(1, ChronoUnit.DAYS), user.getLogin(), false); - assertUserWithFilter("slLastConnectedBefore", lastConnection.plus(1, ChronoUnit.DAYS), user.getLogin(), true); + assertUserWithFilter(SearchAction.SONAR_LINT_LAST_CONNECTION_DATE_FROM, lastConnection.minus(1, ChronoUnit.DAYS), user.getLogin(), true); + assertUserWithFilter(SearchAction.SONAR_LINT_LAST_CONNECTION_DATE_FROM, lastConnection.plus(1, ChronoUnit.DAYS), user.getLogin(), false); + assertUserWithFilter(SearchAction.SONAR_LINT_LAST_CONNECTION_DATE_TO, lastConnection.minus(1, ChronoUnit.DAYS), user.getLogin(), false); + assertUserWithFilter(SearchAction.SONAR_LINT_LAST_CONNECTION_DATE_TO, lastConnection.plus(1, ChronoUnit.DAYS), user.getLogin(), true); - assertUserWithFilter("slLastConnectedAfter", lastConnection, user.getLogin(), true); - assertUserWithFilter("slLastConnectedBefore", lastConnection, user.getLogin(), true); + assertUserWithFilter(SearchAction.SONAR_LINT_LAST_CONNECTION_DATE_FROM, lastConnection, user.getLogin(), true); + assertUserWithFilter(SearchAction.SONAR_LINT_LAST_CONNECTION_DATE_TO, lastConnection, user.getLogin(), true); + } + + @Test + public void search_whenNotAdmin_shouldThrowForbidden() { + userSession.logIn(); + + Stream.of(SearchAction.LAST_CONNECTION_DATE_FROM, SearchAction.LAST_CONNECTION_DATE_TO, + SearchAction.SONAR_LINT_LAST_CONNECTION_DATE_FROM, SearchAction.SONAR_LINT_LAST_CONNECTION_DATE_TO) + .map(param -> ws.newRequest().setParam(param, formatDateTime(OffsetDateTime.now()))) + .forEach(SearchActionIT::assertForbiddenException); + } + + private static void assertForbiddenException(TestRequest testRequest) { + assertThatThrownBy(() -> testRequest.executeProtobuf(SearchWsResponse.class)) + .asInstanceOf(InstanceOfAssertFactories.type(ServerException.class)) + .extracting(ServerException::httpCode) + .isEqualTo(403); } private void assertUserWithFilter(String field, Instant filterValue, String userLogin, boolean isExpectedToBeThere) { diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/SearchAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/SearchAction.java index 358efd5315e..1f83f093fde 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/SearchAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/SearchAction.java @@ -73,10 +73,10 @@ public class SearchAction implements UsersWsAction { private static final int MAX_PAGE_SIZE = 500; - private static final String LAST_CONNECTION_DATE_FROM = "lastConnectedAfter"; - private static final String LAST_CONNECTION_DATE_TO = "lastConnectedBefore"; - private static final String SONAR_LINT_LAST_CONNECTION_DATE_FROM = "slLastConnectedAfter"; - private static final String SONAR_LINT_LAST_CONNECTION_DATE_TO = "slLastConnectedBefore"; + static final String LAST_CONNECTION_DATE_FROM = "lastConnectedAfter"; + static final String LAST_CONNECTION_DATE_TO = "lastConnectedBefore"; + static final String SONAR_LINT_LAST_CONNECTION_DATE_FROM = "slLastConnectedAfter"; + static final String SONAR_LINT_LAST_CONNECTION_DATE_TO = "slLastConnectedBefore"; private final UserSession userSession; private final DbClient dbClient; private final AvatarResolver avatarResolver; @@ -108,9 +108,9 @@ public class SearchAction implements UsersWsAction { .setSince("3.6") .setChangelog( new Change("10.1", "New optional parameters " + SONAR_LINT_LAST_CONNECTION_DATE_FROM + - " and " + SONAR_LINT_LAST_CONNECTION_DATE_TO + " to filter users by SonarLint last connection date"), + " and " + SONAR_LINT_LAST_CONNECTION_DATE_TO + " to filter users by SonarLint last connection date. Only available with Administer System permission."), new Change("10.1", "New optional parameters " + LAST_CONNECTION_DATE_FROM + - " and " + LAST_CONNECTION_DATE_TO + " to filter users by SonarQube last connection date"), + " and " + LAST_CONNECTION_DATE_TO + " to filter users by SonarQube last connection date. Only available with Administer System permission."), new Change("10.1", "New field 'sonarLintLastConnectionDate' is added to response"), new Change("10.0", "'q' parameter values is now always performing a case insensitive match"), new Change("10.0", "New parameter 'managed' to optionally search by managed status"), @@ -204,6 +204,12 @@ public class SearchAction implements UsersWsAction { private UserQuery buildUserQuery(SearchRequest request) { UserQuery.UserQueryBuilder builder = UserQuery.builder(); + if(!userSession.isSystemAdministrator()) { + request.getLastConnectionDateFrom().ifPresent(v -> throwForbiddenFor(LAST_CONNECTION_DATE_FROM)); + request.getLastConnectionDateTo().ifPresent(v -> throwForbiddenFor(LAST_CONNECTION_DATE_TO)); + request.getSonarLintLastConnectionDateFrom().ifPresent(v -> throwForbiddenFor(SONAR_LINT_LAST_CONNECTION_DATE_FROM)); + request.getSonarLintLastConnectionDateTo().ifPresent(v -> throwForbiddenFor(SONAR_LINT_LAST_CONNECTION_DATE_TO)); + } request.getLastConnectionDateFrom().ifPresent(builder::lastConnectionDateFrom); request.getLastConnectionDateTo().ifPresent(builder::lastConnectionDateTo); request.getSonarLintLastConnectionDateFrom().ifPresent(builder::sonarLintLastConnectionDateFrom); @@ -224,6 +230,10 @@ public class SearchAction implements UsersWsAction { .build(); } + private static void throwForbiddenFor(String parameterName) { + throw new ServerException(403, "parameter " + parameterName + " requires Administer System permission."); + } + private List findUsersAndSortByLogin(SearchRequest request, DbSession dbSession, UserQuery userQuery) { return dbClient.userDao().selectUsers(dbSession, userQuery, request.getPage(), request.getPageSize()) .stream()