From c4b18ef9628785693506832d8eb3c2764bbea10c Mon Sep 17 00:00:00 2001 From: Aurelien Poscia Date: Wed, 16 Aug 2023 15:00:20 +0200 Subject: [PATCH] SONAR-20181 ommit from /api/user/search answer the field non-compliant with access-level --- .../UsersSearchRestResponseGenerator.java | 50 +++++++++-------- .../server/v2/api/user/model/RestUser.java | 33 +----------- .../v2/api/user/model/RestUserForAdmins.java | 54 +++++++++++++++++++ .../user/model/RestUserForAnonymousUsers.java | 28 ++++++++++ .../user/model/RestUserForLoggedInUsers.java | 40 ++++++++++++++ .../controller/DefaultUserControllerTest.java | 21 ++++---- .../UsersSearchRestResponseGeneratorTest.java | 51 ++++++------------ 7 files changed, 178 insertions(+), 99 deletions(-) create mode 100644 server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/model/RestUserForAdmins.java create mode 100644 server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/model/RestUserForAnonymousUsers.java create mode 100644 server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/model/RestUserForLoggedInUsers.java diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/converter/UsersSearchRestResponseGenerator.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/converter/UsersSearchRestResponseGenerator.java index 2611a05bfa1..1a83fa56f49 100644 --- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/converter/UsersSearchRestResponseGenerator.java +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/converter/UsersSearchRestResponseGenerator.java @@ -31,6 +31,9 @@ import org.sonar.server.common.user.service.UserSearchResult; import org.sonar.server.user.UserSession; import org.sonar.server.v2.api.response.PageRestResponse; import org.sonar.server.v2.api.user.model.RestUser; +import org.sonar.server.v2.api.user.model.RestUserForAdmins; +import org.sonar.server.v2.api.user.model.RestUserForAnonymousUsers; +import org.sonar.server.v2.api.user.model.RestUserForLoggedInUsers; import org.sonar.server.v2.api.user.response.UsersSearchRestResponse; public class UsersSearchRestResponseGenerator implements UsersSearchResponseGenerator { @@ -72,13 +75,14 @@ public class UsersSearchRestResponseGenerator implements UsersSearchResponseGene Integer tokensCount = null; List scmAccounts = null; - if (userSession.isLoggedIn()) { - avatar = userSearchResult.avatar().orElse(null); - active = userDto.isActive(); - local = userDto.isLocal(); - email = userDto.getEmail(); - externalIdentityProvider = userDto.getExternalIdentityProvider(); + if (!userSession.isLoggedIn()) { + return new RestUserForAnonymousUsers(login, login, name); } + avatar = userSearchResult.avatar().orElse(null); + active = userDto.isActive(); + local = userDto.isLocal(); + email = userDto.getEmail(); + externalIdentityProvider = userDto.getExternalIdentityProvider(); if (userSession.isSystemAdministrator() || Objects.equals(userSession.getUuid(), userDto.getUuid())) { externalLogin = userDto.getExternalLogin(); managed = userSearchResult.managed(); @@ -87,24 +91,24 @@ public class UsersSearchRestResponseGenerator implements UsersSearchResponseGene groupSize = userSearchResult.groups().size(); tokensCount = userSearchResult.tokensCount(); scmAccounts = userSearchResult.userDto().getSortedScmAccounts(); + return new RestUserForAdmins( + login, + login, + name, + email, + active, + local, + managed, + externalLogin, + externalIdentityProvider, + avatar, + sqLastConnectionDate, + slLastConnectionDate, + groupSize, + tokensCount, + scmAccounts); } - - return new RestUser( - login, - login, - name, - email, - active, - local, - managed, - externalLogin, - externalIdentityProvider, - avatar, - sqLastConnectionDate, - slLastConnectionDate, - groupSize, - tokensCount, - scmAccounts); + return new RestUserForLoggedInUsers(login, login, name, email, active, local, externalIdentityProvider, avatar); } private static String toDateTime(@Nullable Long dateTimeMs) { diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/model/RestUser.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/model/RestUser.java index 1815d5598f7..3f596ff68c8 100644 --- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/model/RestUser.java +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/model/RestUser.java @@ -19,36 +19,5 @@ */ package org.sonar.server.v2.api.user.model; -import java.util.List; -import javax.annotation.Nullable; - -public record RestUser( - String id, - String login, - String name, - @Nullable - String email, - @Nullable - Boolean active, - @Nullable - Boolean local, - @Nullable - Boolean managed, - @Nullable - String externalLogin, - @Nullable - String externalProvider, - @Nullable - String avatar, - @Nullable - String sonarQubeLastConnectionDate, - @Nullable - String sonarLintLastConnectionDate, - @Nullable - Integer groupsCount, - @Nullable - Integer tokensCount, - @Nullable - List scmAccounts -) { +public interface RestUser { } diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/model/RestUserForAdmins.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/model/RestUserForAdmins.java new file mode 100644 index 00000000000..cb559458da1 --- /dev/null +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/model/RestUserForAdmins.java @@ -0,0 +1,54 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.v2.api.user.model; + +import java.util.List; +import javax.annotation.Nullable; + +public record RestUserForAdmins( + String id, + String login, + String name, + @Nullable + String email, + @Nullable + Boolean active, + @Nullable + Boolean local, + @Nullable + Boolean managed, + @Nullable + String externalLogin, + @Nullable + String externalProvider, + @Nullable + String avatar, + @Nullable + String sonarQubeLastConnectionDate, + @Nullable + String sonarLintLastConnectionDate, + @Nullable + Integer groupsCount, + @Nullable + Integer tokensCount, + @Nullable + List scmAccounts +) implements RestUser { +} diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/model/RestUserForAnonymousUsers.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/model/RestUserForAnonymousUsers.java new file mode 100644 index 00000000000..5bf3f8aa788 --- /dev/null +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/model/RestUserForAnonymousUsers.java @@ -0,0 +1,28 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.v2.api.user.model; + +public record RestUserForAnonymousUsers( + String id, + String login, + String name + +) implements RestUser { +} diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/model/RestUserForLoggedInUsers.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/model/RestUserForLoggedInUsers.java new file mode 100644 index 00000000000..1466d0468c8 --- /dev/null +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/model/RestUserForLoggedInUsers.java @@ -0,0 +1,40 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.v2.api.user.model; + +import javax.annotation.Nullable; + +public record RestUserForLoggedInUsers( + String id, + String login, + String name, + @Nullable + String email, + @Nullable + Boolean active, + @Nullable + Boolean local, + @Nullable + String externalProvider, + @Nullable + String avatar + +) implements RestUser { +} diff --git a/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/user/controller/DefaultUserControllerTest.java b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/user/controller/DefaultUserControllerTest.java index bcfdd4be4fd..4bbf6121539 100644 --- a/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/user/controller/DefaultUserControllerTest.java +++ b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/user/controller/DefaultUserControllerTest.java @@ -39,6 +39,7 @@ import org.sonar.server.v2.api.ControllerTester; import org.sonar.server.v2.api.response.PageRestResponse; import org.sonar.server.v2.api.user.converter.UsersSearchRestResponseGenerator; import org.sonar.server.v2.api.user.model.RestUser; +import org.sonar.server.v2.api.user.model.RestUserForAdmins; import org.sonar.server.v2.api.user.request.UserCreateRestRequest; import org.sonar.server.v2.api.user.response.UsersSearchRestResponse; import org.springframework.http.MediaType; @@ -146,8 +147,8 @@ public class DefaultUserControllerTest { List users = List.of(user1, user2, user3, user4); SearchResults searchResult = new SearchResults<>(users, users.size()); when(userService.findUsers(any())).thenReturn(searchResult); - List restUsers = List.of(toRestUser(user1), toRestUser(user2), toRestUser(user3), toRestUser(user4)); - when(responseGenerator.toUsersForResponse(eq(searchResult.searchResults()), any())).thenReturn(new UsersSearchRestResponse(restUsers, new PageRestResponse(1, 50, 4))); + List restUserForAdmins = List.of(toRestUser(user1), toRestUser(user2), toRestUser(user3), toRestUser(user4)); + when(responseGenerator.toUsersForResponse(eq(searchResult.searchResults()), any())).thenReturn(new UsersSearchRestResponse(restUserForAdmins, new PageRestResponse(1, 50, 4))); userSession.logIn().setSystemAdministrator(); MvcResult mvcResult = mockMvc.perform(get(USER_ENDPOINT)) @@ -156,7 +157,7 @@ public class DefaultUserControllerTest { UsersSearchRestResponse actualUsersSearchRestResponse = gson.fromJson(mvcResult.getResponse().getContentAsString(), UsersSearchRestResponse.class); assertThat(actualUsersSearchRestResponse.users()) - .containsExactlyElementsOf(restUsers); + .containsExactlyElementsOf(restUserForAdmins); assertThat(actualUsersSearchRestResponse.page().total()).isEqualTo(users.size()); } @@ -181,8 +182,8 @@ public class DefaultUserControllerTest { return new UserSearchResult(userDto, managed, Optional.of("avatar_" + id), groups, tokensCount); } - private RestUser toRestUser(UserSearchResult userSearchResult) { - return new RestUser( + private RestUserForAdmins toRestUser(UserSearchResult userSearchResult) { + return new RestUserForAdmins( userSearchResult.userDto().getLogin(), userSearchResult.userDto().getLogin(), userSearchResult.userDto().getName(), @@ -295,14 +296,14 @@ public class DefaultUserControllerTest { @Test public void fetchUser_whenUserExists_shouldReturnUser() throws Exception { UserSearchResult user = generateUserSearchResult("user1", true, true, false, 2, 3); - RestUser restUser = toRestUser(user); + RestUserForAdmins restUserForAdmins = toRestUser(user); when(userService.fetchUser("userLogin")).thenReturn(user); - when(responseGenerator.toRestUser(user)).thenReturn(restUser); + when(responseGenerator.toRestUser(user)).thenReturn(restUserForAdmins); MvcResult mvcResult = mockMvc.perform(get(USER_ENDPOINT + "/userLogin")) .andExpect(status().isOk()) .andReturn(); - RestUser responseUser = gson.fromJson(mvcResult.getResponse().getContentAsString(), RestUser.class); - assertThat(responseUser).isEqualTo(restUser); + RestUserForAdmins responseUser = gson.fromJson(mvcResult.getResponse().getContentAsString(), RestUserForAdmins.class); + assertThat(responseUser).isEqualTo(restUserForAdmins); } @Test @@ -373,7 +374,7 @@ public class DefaultUserControllerTest { userDto.getEmail(), userDto.isLocal(), userDto.getLogin(), userDto.getName(), "password", userDto.getSortedScmAccounts())))) .andExpect(status().isOk()) .andReturn(); - RestUser responseUser = gson.fromJson(mvcResult.getResponse().getContentAsString(), RestUser.class); + RestUserForAdmins responseUser = gson.fromJson(mvcResult.getResponse().getContentAsString(), RestUserForAdmins.class); assertThat(responseUser).isEqualTo(toRestUser(userSearchResult)); } diff --git a/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/user/converter/UsersSearchRestResponseGeneratorTest.java b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/user/converter/UsersSearchRestResponseGeneratorTest.java index e33ed4283cf..741201135c9 100644 --- a/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/user/converter/UsersSearchRestResponseGeneratorTest.java +++ b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/user/converter/UsersSearchRestResponseGeneratorTest.java @@ -33,7 +33,9 @@ import org.sonar.db.user.UserDto; import org.sonar.server.common.user.service.UserSearchResult; import org.sonar.server.user.UserSession; import org.sonar.server.v2.api.response.PageRestResponse; -import org.sonar.server.v2.api.user.model.RestUser; +import org.sonar.server.v2.api.user.model.RestUserForAdmins; +import org.sonar.server.v2.api.user.model.RestUserForAnonymousUsers; +import org.sonar.server.v2.api.user.model.RestUserForLoggedInUsers; import org.sonar.server.v2.api.user.response.UsersSearchRestResponse; import static org.assertj.core.api.Assertions.assertThat; @@ -72,15 +74,15 @@ public class UsersSearchRestResponseGeneratorTest { UsersSearchRestResponse usersForResponse = usersSearchRestResponseGenerator.toUsersForResponse(List.of(userSearchResult1, userSearchResult2), paging); - RestUser expectUser1 = buildExpectedResponseForAdmin(userSearchResult1); - RestUser expectUser2 = buildExpectedResponseForAdmin(userSearchResult2); + RestUserForAdmins expectUser1 = buildExpectedResponseForAdmin(userSearchResult1); + RestUserForAdmins expectUser2 = buildExpectedResponseForAdmin(userSearchResult2); assertThat(usersForResponse.users()).containsExactly(expectUser1, expectUser2); assertPaginationInformationAreCorrect(paging, usersForResponse.page()); } - private static RestUser buildExpectedResponseForAdmin(UserSearchResult userSearchResult) { + private static RestUserForAdmins buildExpectedResponseForAdmin(UserSearchResult userSearchResult) { UserDto userDto = userSearchResult.userDto(); - return new RestUser( + return new RestUserForAdmins( userDto.getLogin(), userDto.getLogin(), userDto.getName(), @@ -110,30 +112,23 @@ public class UsersSearchRestResponseGeneratorTest { UsersSearchRestResponse usersForResponse = usersSearchRestResponseGenerator.toUsersForResponse(List.of(userSearchResult1, userSearchResult2), paging); - RestUser expectUser1 = buildExpectedResponseForUser(userSearchResult1); - RestUser expectUser2 = buildExpectedResponseForUser(userSearchResult2); + RestUserForLoggedInUsers expectUser1 = buildExpectedResponseForUser(userSearchResult1); + RestUserForLoggedInUsers expectUser2 = buildExpectedResponseForUser(userSearchResult2); assertThat(usersForResponse.users()).containsExactly(expectUser1, expectUser2); assertPaginationInformationAreCorrect(paging, usersForResponse.page()); } - private static RestUser buildExpectedResponseForUser(UserSearchResult userSearchResult) { + private static RestUserForLoggedInUsers buildExpectedResponseForUser(UserSearchResult userSearchResult) { UserDto userDto = userSearchResult.userDto(); - return new RestUser( + return new RestUserForLoggedInUsers( userDto.getLogin(), userDto.getLogin(), userDto.getName(), userDto.getEmail(), userDto.isActive(), userDto.isLocal(), - null, - null, userDto.getExternalIdentityProvider(), - userSearchResult.avatar().orElse(null), - null, - null, - null, - null, - null + userSearchResult.avatar().orElse(null) ); } @@ -146,30 +141,18 @@ public class UsersSearchRestResponseGeneratorTest { UsersSearchRestResponse usersForResponse = usersSearchRestResponseGenerator.toUsersForResponse(List.of(userSearchResult1, userSearchResult2), paging); - RestUser expectUser1 = buildExpectedResponseForAnonymous(userSearchResult1); - RestUser expectUser2 = buildExpectedResponseForAnonymous(userSearchResult2); + RestUserForAnonymousUsers expectUser1 = buildExpectedResponseForAnonymous(userSearchResult1); + RestUserForAnonymousUsers expectUser2 = buildExpectedResponseForAnonymous(userSearchResult2); assertThat(usersForResponse.users()).containsExactly(expectUser1, expectUser2); assertPaginationInformationAreCorrect(paging, usersForResponse.page()); } - private static RestUser buildExpectedResponseForAnonymous(UserSearchResult userSearchResult) { + private static RestUserForAnonymousUsers buildExpectedResponseForAnonymous(UserSearchResult userSearchResult) { UserDto userDto = userSearchResult.userDto(); - return new RestUser( + return new RestUserForAnonymousUsers( userDto.getLogin(), userDto.getLogin(), - userDto.getName(), - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null + userDto.getName() ); } -- 2.39.5