You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ScimUserDaoIT.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2023 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.db.scim;
  21. import com.tngtech.java.junit.dataprovider.DataProvider;
  22. import com.tngtech.java.junit.dataprovider.DataProviderRunner;
  23. import com.tngtech.java.junit.dataprovider.UseDataProvider;
  24. import java.util.Arrays;
  25. import java.util.Collection;
  26. import java.util.List;
  27. import java.util.Map;
  28. import java.util.Objects;
  29. import java.util.Set;
  30. import java.util.function.Function;
  31. import java.util.stream.Collectors;
  32. import java.util.stream.IntStream;
  33. import org.junit.Rule;
  34. import org.junit.Test;
  35. import org.junit.runner.RunWith;
  36. import org.sonar.db.DbSession;
  37. import org.sonar.db.DbTester;
  38. import org.sonar.db.user.GroupDto;
  39. import org.sonar.db.user.UserDto;
  40. import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
  41. import static org.assertj.core.api.Assertions.assertThat;
  42. import static org.assertj.core.api.Assertions.assertThatCode;
  43. import static org.assertj.core.api.Fail.fail;
  44. @RunWith(DataProviderRunner.class)
  45. public class ScimUserDaoIT {
  46. @Rule
  47. public DbTester db = DbTester.create();
  48. private final DbSession dbSession = db.getSession();
  49. private final ScimUserDao scimUserDao = db.getDbClient().scimUserDao();
  50. @Test
  51. public void findAll_ifNoData_returnsEmptyList() {
  52. assertThat(scimUserDao.findAll(dbSession)).isEmpty();
  53. }
  54. @Test
  55. public void findAll_returnsAllEntries() {
  56. ScimUserTestData scimUser1TestData = insertScimUser("scimUser1");
  57. ScimUserTestData scimUser2TestData = insertScimUser("scimUser2");
  58. List<ScimUserDto> scimUserDtos = scimUserDao.findAll(dbSession);
  59. assertThat(scimUserDtos).hasSize(2)
  60. .map(scimUserDto -> new ScimUserTestData(scimUserDto.getScimUserUuid(), scimUserDto.getUserUuid()))
  61. .containsExactlyInAnyOrder(scimUser1TestData, scimUser2TestData);
  62. }
  63. @Test
  64. public void findByScimUuid_whenScimUuidNotFound_shouldReturnEmptyOptional() {
  65. assertThat(scimUserDao.findByScimUuid(dbSession, "unknownId")).isEmpty();
  66. }
  67. @Test
  68. public void findByScimUuid_whenScimUuidFound_shouldReturnDto() {
  69. ScimUserTestData scimUser1TestData = insertScimUser("scimUser1");
  70. insertScimUser("scimUser2");
  71. ScimUserDto scimUserDto = scimUserDao.findByScimUuid(dbSession, scimUser1TestData.getScimUserUuid())
  72. .orElseGet(() -> fail("User not found"));
  73. assertThat(scimUserDto.getScimUserUuid()).isEqualTo(scimUser1TestData.getScimUserUuid());
  74. assertThat(scimUserDto.getUserUuid()).isEqualTo(scimUser1TestData.getUserUuid());
  75. }
  76. @Test
  77. public void findByUserUuid_whenScimUuidNotFound_shouldReturnEmptyOptional() {
  78. assertThat(scimUserDao.findByUserUuid(dbSession, "unknownId")).isEmpty();
  79. }
  80. @Test
  81. public void findByUserUuid_whenScimUuidFound_shouldReturnDto() {
  82. ScimUserTestData scimUser1TestData = insertScimUser("scimUser1");
  83. insertScimUser("scimUser2");
  84. ScimUserDto scimUserDto = scimUserDao.findByUserUuid(dbSession, scimUser1TestData.getUserUuid())
  85. .orElseGet(() -> fail("User not found"));
  86. assertThat(scimUserDto.getScimUserUuid()).isEqualTo(scimUser1TestData.getScimUserUuid());
  87. assertThat(scimUserDto.getUserUuid()).isEqualTo(scimUser1TestData.getUserUuid());
  88. }
  89. @DataProvider
  90. public static Object[][] paginationData() {
  91. return new Object[][] {
  92. {5, 0, 20, List.of("1", "2", "3", "4", "5")},
  93. {9, 0, 5, List.of("1", "2", "3", "4", "5")},
  94. {9, 3, 3, List.of("4", "5", "6")},
  95. {9, 7, 3, List.of("8", "9")},
  96. {5, 5, 20, List.of()},
  97. {5, 0, 0, List.of()},
  98. };
  99. }
  100. @Test
  101. @UseDataProvider("paginationData")
  102. public void findScimUsers_whenPaginationAndStartIndex_shouldReturnTheCorrectNumberOfScimUsers(int totalScimUsers, int offset, int pageSize, List<String> expectedScimUserUuids) {
  103. generateScimUsers(totalScimUsers);
  104. List<ScimUserDto> scimUserDtos = scimUserDao.findScimUsers(dbSession, ScimUserQuery.empty(), offset, pageSize);
  105. List<String> scimUsersUuids = toScimUsersUuids(scimUserDtos);
  106. assertThat(scimUsersUuids).containsExactlyElementsOf(expectedScimUserUuids);
  107. }
  108. private List<String> toScimUsersUuids(Collection<ScimUserDto> scimUserDtos) {
  109. return scimUserDtos.stream()
  110. .map(ScimUserDto::getScimUserUuid)
  111. .toList();
  112. }
  113. @Test
  114. public void countScimUsers_shouldReturnTheTotalNumberOfScimUsers() {
  115. int totalScimUsers = 15;
  116. generateScimUsers(totalScimUsers);
  117. assertThat(scimUserDao.countScimUsers(dbSession, ScimUserQuery.empty())).isEqualTo(totalScimUsers);
  118. }
  119. @Test
  120. public void countScimUsers_shouldReturnZero_whenNoScimUsers() {
  121. assertThat(scimUserDao.countScimUsers(dbSession, ScimUserQuery.empty())).isZero();
  122. }
  123. @Test
  124. public void countScimUsers_shoudReturnZero_whenNoScimUsersMatchesQuery() {
  125. int totalScimUsers = 15;
  126. generateScimUsers(totalScimUsers);
  127. ScimUserQuery scimUserQuery = ScimUserQuery.builder().userName("jean.okta").build();
  128. assertThat(scimUserDao.countScimUsers(dbSession, scimUserQuery)).isZero();
  129. }
  130. @Test
  131. public void countScimUsers_shoudReturnCorrectNumberOfScimUser_whenFilteredByScimUserName() {
  132. insertScimUsersWithUsers(List.of("TEST_A", "TEST_B", "TEST_B_BIS", "TEST_C", "TEST_D"));
  133. ScimUserQuery scimUserQuery = ScimUserQuery.builder().userName("test_b").build();
  134. assertThat(scimUserDao.countScimUsers(dbSession, scimUserQuery)).isEqualTo(1);
  135. }
  136. private List<ScimUserTestData> generateScimUsers(int totalScimUsers) {
  137. List<String> userNames = IntStream.range(0, totalScimUsers)
  138. .mapToObj(i -> "username_" + i)
  139. .toList();
  140. return insertScimUsersWithUsers(userNames);
  141. }
  142. @Test
  143. public void enableScimForUser_addsUserToScimUsers() {
  144. ScimUserDto scimUserDto = scimUserDao.enableScimForUser(dbSession, "sqUser1");
  145. assertThat(scimUserDto.getScimUserUuid()).isNotBlank();
  146. ScimUserDto actualScimUserDto = scimUserDao.findByScimUuid(dbSession, scimUserDto.getScimUserUuid()).orElseThrow();
  147. assertThat(scimUserDto.getScimUserUuid()).isEqualTo(actualScimUserDto.getScimUserUuid());
  148. assertThat(scimUserDto.getUserUuid()).isEqualTo(actualScimUserDto.getUserUuid());
  149. }
  150. @DataProvider
  151. public static Object[][] filterData() {
  152. return new Object[][] {
  153. {"test_user", List.of("test_user", "Test_USEr", "xxx.test_user.yyy", "test_xxx_user"), List.of("1", "2")},
  154. {"TEST_USER", List.of("test_user", "Test_USEr", "xxx.test_user.yyy", "test_xxx_user"), List.of("1", "2")},
  155. {"test_user_x", List.of("test_user"), List.of()},
  156. {"test_x_user", List.of("test_user"), List.of()},
  157. };
  158. }
  159. @Test
  160. @UseDataProvider("filterData")
  161. public void findScimUsers_whenFilteringByUserName_shouldReturnTheExpectedScimUsers(String search, List<String> userLogins, List<String> expectedScimUserUuids) {
  162. insertScimUsersWithUsers(userLogins);
  163. ScimUserQuery query = ScimUserQuery.builder().userName(search).build();
  164. List<ScimUserDto> scimUsersByQuery = scimUserDao.findScimUsers(dbSession, query, 0, 100);
  165. List<String> scimUsersUuids = toScimUsersUuids(scimUsersByQuery);
  166. assertThat(scimUsersUuids).containsExactlyElementsOf(expectedScimUserUuids);
  167. }
  168. @Test
  169. public void findScimUsers_whenFilteringByGroupUuid_shouldReturnTheExpectedScimUsers() {
  170. List<ScimUserTestData> scimUsersTestData = insertScimUsersWithUsers(List.of("userAInGroupA", "userBInGroupA", "userAInGroupB", "userNotInGroup"));
  171. Map<String, ScimUserTestData> users = scimUsersTestData.stream()
  172. .collect(Collectors.toMap(testData -> testData.getUserDto().getExternalId(), Function.identity()));
  173. GroupDto group1dto = createGroupWithUsers(users.get("userAInGroupA"), users.get("userBInGroupA"));
  174. createGroupWithUsers(users.get("userAInGroupB"));
  175. ScimUserQuery query = ScimUserQuery.builder().groupUuid(group1dto.getUuid()).build();
  176. List<ScimUserDto> scimUsers = scimUserDao.findScimUsers(dbSession, query, 0, 100);
  177. List<String> scimUsersUuids = toScimUsersUuids(scimUsers);
  178. assertThat(scimUsersUuids).containsExactlyInAnyOrder(
  179. users.get("userAInGroupA").getScimUserUuid(),
  180. users.get("userBInGroupA").getScimUserUuid()
  181. );
  182. }
  183. private GroupDto createGroupWithUsers(ScimUserTestData... testUsers) {
  184. GroupDto group = db.users().insertGroup();
  185. UserDto[] userDtos = Arrays.stream(testUsers)
  186. .map(ScimUserTestData::getUserDto)
  187. .toArray(UserDto[]::new);
  188. db.users().insertMembers(group, userDtos);
  189. return group;
  190. }
  191. @Test
  192. public void findScimUsers_whenFilteringByScimUuidsWithLongRange_shouldReturnTheExpectedScimUsers() {
  193. generateScimUsers(3000);
  194. Set<String> expectedScimUserUuids = generateStrings(1, 2050);
  195. ScimUserQuery query = ScimUserQuery.builder().scimUserUuids(expectedScimUserUuids).build();
  196. List<ScimUserDto> scimUsersByQuery = scimUserDao.findScimUsers(dbSession, query, 0, Integer.MAX_VALUE);
  197. List<String> scimUsersUuids = toScimUsersUuids(scimUsersByQuery);
  198. assertThat(scimUsersByQuery)
  199. .hasSameSizeAs(scimUsersUuids)
  200. .extracting(ScimUserDto::getScimUserUuid)
  201. .containsAll(expectedScimUserUuids);
  202. }
  203. @Test
  204. public void findScimUsers_whenFilteringByScimUuidsAndUserName_shouldReturnTheExpectedScimUser() {
  205. Set<String> scimUserUuids = generateScimUsers(10).stream()
  206. .map(ScimUserTestData::getScimUserUuid)
  207. .collect(Collectors.toSet());
  208. ScimUserQuery query = ScimUserQuery.builder().scimUserUuids(scimUserUuids).userName("username_5").build();
  209. List<ScimUserDto> scimUsersByQuery = scimUserDao.findScimUsers(dbSession, query, 0, Integer.MAX_VALUE);
  210. assertThat(scimUsersByQuery).hasSize(1)
  211. .extracting(ScimUserDto::getScimUserUuid)
  212. .contains("6");
  213. }
  214. @Test
  215. public void findScimUsers_whenFilteringByUserUuidsWithLongRange_shouldReturnTheExpectedScimUsers() {
  216. List<ScimUserTestData> scimUsersTestData = generateScimUsers(3000);
  217. Set<String> allUsersUuid = scimUsersTestData.stream()
  218. .map(ScimUserTestData::getUserUuid)
  219. .collect(Collectors.toSet());
  220. ScimUserQuery query = ScimUserQuery.builder().userUuids(allUsersUuid).build();
  221. List<ScimUserDto> scimUsersByQuery = scimUserDao.findScimUsers(dbSession, query, 0, Integer.MAX_VALUE);
  222. assertThat(scimUsersByQuery)
  223. .hasSameSizeAs(allUsersUuid)
  224. .extracting(ScimUserDto::getUserUuid)
  225. .containsAll(allUsersUuid);
  226. }
  227. @Test
  228. public void findScimUsers_whenFilteringByUserUuidsAndUserName_shouldReturnTheExpectedScimUser() {
  229. List<ScimUserTestData> scimUsersTestData = generateScimUsers(10);
  230. Set<String> allUsersUuid = scimUsersTestData.stream()
  231. .map(ScimUserTestData::getUserUuid)
  232. .collect(Collectors.toSet());
  233. ScimUserQuery query = ScimUserQuery.builder().userUuids(allUsersUuid).userName("username_5").build();
  234. List<ScimUserDto> scimUsersByQuery = scimUserDao.findScimUsers(dbSession, query, 0, Integer.MAX_VALUE);
  235. assertThat(scimUsersByQuery).hasSize(1)
  236. .extracting(ScimUserDto::getScimUserUuid)
  237. .contains("6");
  238. }
  239. private static Set<String> generateStrings(int startInclusive, int endExclusive) {
  240. return generateStrings(startInclusive, endExclusive, "");
  241. }
  242. private static Set<String> generateStrings(int startInclusive, int endExclusive, String prefix) {
  243. return IntStream.range(startInclusive, endExclusive)
  244. .mapToObj(String::valueOf)
  245. .map(string -> prefix + string)
  246. .collect(Collectors.toSet());
  247. }
  248. @Test
  249. public void deleteByUserUuid_shouldDeleteScimUser() {
  250. ScimUserTestData scimUserTestData = insertScimUser("scimUser");
  251. scimUserDao.deleteByUserUuid(dbSession, scimUserTestData.getUserUuid());
  252. assertThat(scimUserDao.findAll(dbSession)).isEmpty();
  253. }
  254. @Test
  255. public void deleteByScimUuid_shouldDeleteScimUser() {
  256. ScimUserTestData scimUserTestData = insertScimUser("scimUser");
  257. ScimUserTestData scimUserTestData2 = insertScimUser("scimUser2");
  258. scimUserDao.deleteByScimUuid(dbSession, scimUserTestData.getScimUserUuid());
  259. List<ScimUserDto> remainingUsers = scimUserDao.findAll(dbSession);
  260. assertThat(remainingUsers).hasSize(1);
  261. ScimUserDto remainingUser = remainingUsers.get(0);
  262. assertThat(remainingUser.getScimUserUuid()).isEqualTo(scimUserTestData2.scimUserUuid);
  263. assertThat(remainingUser.getUserUuid()).isEqualTo(scimUserTestData2.userUuid);
  264. }
  265. @Test
  266. public void deleteAll_should_remove_all_ScimUsers(){
  267. insertScimUser("scimUser");
  268. insertScimUser("scimUser2");
  269. scimUserDao.deleteAll(dbSession);
  270. assertThat(scimUserDao.findAll(dbSession)).isEmpty();
  271. }
  272. @Test
  273. public void deleteFromUserUuid_shouldNotFail_whenNoUser() {
  274. assertThatCode(() -> scimUserDao.deleteByUserUuid(dbSession, randomAlphanumeric(6))).doesNotThrowAnyException();
  275. }
  276. private List<ScimUserTestData> insertScimUsersWithUsers(List<String> userLogins) {
  277. return IntStream.range(0, userLogins.size())
  278. .mapToObj(i -> insertScimUserWithUser(userLogins.get(i), String.valueOf(i + 1)))
  279. .toList();
  280. }
  281. private ScimUserTestData insertScimUserWithUser(String userLogin, String scimUuid) {
  282. UserDto userDto = db.users().insertUser(u -> u.setExternalId(userLogin));
  283. ScimUserTestData scimUserTestData = insertScimUser(scimUuid, userDto.getUuid());
  284. scimUserTestData.setUserDto(userDto);
  285. return scimUserTestData;
  286. }
  287. private ScimUserTestData insertScimUser(String scimUserUuid) {
  288. return insertScimUser(scimUserUuid, randomAlphanumeric(40));
  289. }
  290. private ScimUserTestData insertScimUser(String scimUserUuid, String userUuid) {
  291. ScimUserTestData scimUserTestData = new ScimUserTestData(scimUserUuid, userUuid);
  292. Map<String, Object> data = Map.of("scim_uuid", scimUserTestData.getScimUserUuid(), "user_uuid", scimUserTestData.getUserUuid());
  293. db.executeInsert("scim_users", data);
  294. return scimUserTestData;
  295. }
  296. @Test
  297. public void getManagedUserSqlFilter_isNotEmpty() {
  298. String filterManagedUser = scimUserDao.getManagedUserSqlFilter(true);
  299. assertThat(filterManagedUser).isNotEmpty();
  300. String filterNonManagedUser = scimUserDao.getManagedUserSqlFilter(false);
  301. assertThat(filterNonManagedUser).isNotEmpty();
  302. assertThat(filterManagedUser).isNotEqualTo(filterNonManagedUser);
  303. }
  304. @Test
  305. public void getManagedGroupsSqlFilter_whenFilterByManagedIsTrue_returnsCorrectQuery() {
  306. String filterManagedUser = scimUserDao.getManagedUserSqlFilter(true);
  307. assertThat(filterManagedUser).isEqualTo(" exists (select user_uuid from scim_users su where su.user_uuid = uuid)");
  308. }
  309. @Test
  310. public void getManagedGroupsSqlFilter_whenFilterByManagedIsFalse_returnsCorrectQuery() {
  311. String filterNonManagedUser = scimUserDao.getManagedUserSqlFilter(false);
  312. assertThat(filterNonManagedUser).isEqualTo("not exists (select user_uuid from scim_users su where su.user_uuid = uuid)");
  313. }
  314. private static class ScimUserTestData {
  315. private final String scimUserUuid;
  316. private final String userUuid;
  317. private UserDto userDto;
  318. private ScimUserTestData(String scimUserUuid, String userUuid) {
  319. this.scimUserUuid = scimUserUuid;
  320. this.userUuid = userUuid;
  321. }
  322. private String getScimUserUuid() {
  323. return scimUserUuid;
  324. }
  325. private String getUserUuid() {
  326. return userUuid;
  327. }
  328. private UserDto getUserDto() {
  329. return userDto;
  330. }
  331. private void setUserDto(UserDto userDto) {
  332. this.userDto = userDto;
  333. }
  334. @Override
  335. public boolean equals(Object o) {
  336. if (this == o)
  337. return true;
  338. if (o == null || getClass() != o.getClass())
  339. return false;
  340. ScimUserTestData that = (ScimUserTestData) o;
  341. return getScimUserUuid().equals(that.getScimUserUuid()) && getUserUuid().equals(that.getUserUuid());
  342. }
  343. @Override
  344. public int hashCode() {
  345. return Objects.hash(getScimUserUuid(), getUserUuid());
  346. }
  347. }
  348. }