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.

DefaultUserControllerTest.java 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 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.server.v2.api.user.controller;
  21. import com.google.gson.Gson;
  22. import com.google.gson.GsonBuilder;
  23. import com.google.gson.TypeAdapter;
  24. import com.google.gson.stream.JsonReader;
  25. import com.google.gson.stream.JsonWriter;
  26. import java.util.ArrayList;
  27. import java.util.List;
  28. import java.util.Optional;
  29. import java.util.stream.IntStream;
  30. import org.junit.Rule;
  31. import org.junit.Test;
  32. import org.mockito.ArgumentCaptor;
  33. import org.sonar.db.user.UserDto;
  34. import org.sonar.server.common.SearchResults;
  35. import org.sonar.server.common.user.service.UserInformation;
  36. import org.sonar.server.common.user.service.UserService;
  37. import org.sonar.server.common.user.service.UsersSearchRequest;
  38. import org.sonar.server.exceptions.BadRequestException;
  39. import org.sonar.server.exceptions.NotFoundException;
  40. import org.sonar.server.tester.UserSessionRule;
  41. import org.sonar.server.user.UpdateUser;
  42. import org.sonar.server.v2.api.ControllerTester;
  43. import org.sonar.server.v2.api.model.RestError;
  44. import org.sonar.server.v2.api.response.PageRestResponse;
  45. import org.sonar.server.v2.api.user.converter.UsersSearchRestResponseGenerator;
  46. import org.sonar.server.v2.api.user.request.UserCreateRestRequest;
  47. import org.sonar.server.v2.api.user.response.UserRestResponse;
  48. import org.sonar.server.v2.api.user.response.UserRestResponseForAdmins;
  49. import org.sonar.server.v2.api.user.response.UsersSearchRestResponse;
  50. import org.springframework.http.MediaType;
  51. import org.springframework.test.web.servlet.MockMvc;
  52. import org.springframework.test.web.servlet.MvcResult;
  53. import static org.assertj.core.api.Assertions.assertThat;
  54. import static org.mockito.ArgumentMatchers.any;
  55. import static org.mockito.ArgumentMatchers.eq;
  56. import static org.mockito.Mockito.doThrow;
  57. import static org.mockito.Mockito.mock;
  58. import static org.mockito.Mockito.verify;
  59. import static org.mockito.Mockito.when;
  60. import static org.sonar.api.utils.DateUtils.formatDateTime;
  61. import static org.sonar.api.utils.DateUtils.parseOffsetDateTime;
  62. import static org.sonar.server.v2.WebApiEndpoints.JSON_MERGE_PATCH_CONTENT_TYPE;
  63. import static org.sonar.server.v2.WebApiEndpoints.USER_ENDPOINT;
  64. import static org.sonar.server.v2.api.model.RestPage.DEFAULT_PAGE_INDEX;
  65. import static org.sonar.server.v2.api.model.RestPage.DEFAULT_PAGE_SIZE;
  66. import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
  67. import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
  68. import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
  69. import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
  70. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
  71. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  72. public class DefaultUserControllerTest {
  73. @Rule
  74. public UserSessionRule userSession = UserSessionRule.standalone();
  75. private final UserService userService = mock(UserService.class);
  76. private final UsersSearchRestResponseGenerator responseGenerator = mock(UsersSearchRestResponseGenerator.class);
  77. private final MockMvc mockMvc = ControllerTester.getMockMvc(new DefaultUserController(userSession, userService, responseGenerator));
  78. private static final Gson gson = new GsonBuilder().registerTypeAdapter(UserRestResponse.class, new RestUserDeserializer()).create();
  79. @Test
  80. public void search_whenNoParameters_shouldUseDefaultAndForwardToUserService() throws Exception {
  81. when(userService.findUsers(any())).thenReturn(new SearchResults<>(List.of(), 0));
  82. mockMvc.perform(get(USER_ENDPOINT))
  83. .andExpect(status().isOk());
  84. ArgumentCaptor<UsersSearchRequest> requestCaptor = ArgumentCaptor.forClass(UsersSearchRequest.class);
  85. verify(userService).findUsers(requestCaptor.capture());
  86. assertThat(requestCaptor.getValue().getPageSize()).isEqualTo(Integer.valueOf(DEFAULT_PAGE_SIZE));
  87. assertThat(requestCaptor.getValue().getPage()).isEqualTo(Integer.valueOf(DEFAULT_PAGE_INDEX));
  88. assertThat(requestCaptor.getValue().isDeactivated()).isFalse();
  89. }
  90. @Test
  91. public void search_whenParametersUsed_shouldForwardWithParameters() throws Exception {
  92. when(userService.findUsers(any())).thenReturn(new SearchResults<>(List.of(), 0));
  93. userSession.logIn().setSystemAdministrator();
  94. mockMvc.perform(get(USER_ENDPOINT)
  95. .param("active", "false")
  96. .param("managed", "true")
  97. .param("q", "q")
  98. .param("externalIdentity", "externalIdentity")
  99. .param("sonarQubeLastConnectionDateFrom", "2020-01-01T00:00:00+0100")
  100. .param("sonarQubeLastConnectionDateTo", "2020-01-01T00:00:00+0100")
  101. .param("sonarLintLastConnectionDateFrom", "2020-01-01T00:00:00+0100")
  102. .param("sonarLintLastConnectionDateTo", "2020-01-01T00:00:00+0100")
  103. .param("groupId", "groupId1")
  104. .param("groupId!", "groupId2")
  105. .param("pageSize", "100")
  106. .param("pageIndex", "2"))
  107. .andExpect(status().isOk());
  108. ArgumentCaptor<UsersSearchRequest> requestCaptor = ArgumentCaptor.forClass(UsersSearchRequest.class);
  109. verify(userService).findUsers(requestCaptor.capture());
  110. assertThat(requestCaptor.getValue().isDeactivated()).isTrue();
  111. assertThat(requestCaptor.getValue().isManaged()).isTrue();
  112. assertThat(requestCaptor.getValue().getQuery()).isEqualTo("q");
  113. assertThat(requestCaptor.getValue().getExternalLogin()).contains("externalIdentity");
  114. assertThat(requestCaptor.getValue().getLastConnectionDateFrom()).contains(parseOffsetDateTime("2020-01-01T00:00:00+0100"));
  115. assertThat(requestCaptor.getValue().getLastConnectionDateTo()).contains(parseOffsetDateTime("2020-01-01T00:00:00+0100"));
  116. assertThat(requestCaptor.getValue().getSonarLintLastConnectionDateFrom()).contains(parseOffsetDateTime("2020-01-01T00:00:00+0100"));
  117. assertThat(requestCaptor.getValue().getSonarLintLastConnectionDateTo()).contains(parseOffsetDateTime("2020-01-01T00:00:00+0100"));
  118. assertThat(requestCaptor.getValue().getGroupUuid()).contains("groupId1");
  119. assertThat(requestCaptor.getValue().getExcludedGroupUuid()).contains("groupId2");
  120. assertThat(requestCaptor.getValue().getPageSize()).isEqualTo(100);
  121. assertThat(requestCaptor.getValue().getPage()).isEqualTo(2);
  122. }
  123. @Test
  124. public void search_whenAdminParametersUsedButNotAdmin_shouldFail() throws Exception {
  125. mockMvc.perform(get(USER_ENDPOINT)
  126. .param("sonarQubeLastConnectionDateFrom", "2020-01-01T00:00:00+0100"))
  127. .andExpectAll(
  128. status().isForbidden(),
  129. content().string("{\"message\":\"Parameter sonarQubeLastConnectionDateFrom requires Administer System permission.\"}"));
  130. mockMvc.perform(get(USER_ENDPOINT)
  131. .param("sonarQubeLastConnectionDateTo", "2020-01-01T00:00:00+0100"))
  132. .andExpectAll(
  133. status().isForbidden(),
  134. content().string("{\"message\":\"Parameter sonarQubeLastConnectionDateTo requires Administer System permission.\"}"));
  135. mockMvc.perform(get(USER_ENDPOINT)
  136. .param("sonarLintLastConnectionDateFrom", "2020-01-01T00:00:00+0100"))
  137. .andExpectAll(
  138. status().isForbidden(),
  139. content().string("{\"message\":\"Parameter sonarLintLastConnectionDateFrom requires Administer System permission.\"}"));
  140. mockMvc.perform(get(USER_ENDPOINT)
  141. .param("sonarLintLastConnectionDateTo", "2020-01-01T00:00:00+0100"))
  142. .andExpectAll(
  143. status().isForbidden(),
  144. content().string("{\"message\":\"Parameter sonarLintLastConnectionDateTo requires Administer System permission.\"}"));
  145. mockMvc.perform(get(USER_ENDPOINT)
  146. .param("externalIdentity", "externalIdentity"))
  147. .andExpectAll(
  148. status().isForbidden(),
  149. content().string("{\"message\":\"Parameter externalIdentity requires Administer System permission.\"}"));
  150. mockMvc.perform(get(USER_ENDPOINT)
  151. .param("groupId", "groupId"))
  152. .andExpectAll(
  153. status().isForbidden(),
  154. content().string("{\"message\":\"Parameter groupId requires Administer System permission.\"}"));
  155. mockMvc.perform(get(USER_ENDPOINT)
  156. .param("groupId!", "groupId"))
  157. .andExpectAll(
  158. status().isForbidden(),
  159. content().string("{\"message\":\"Parameter groupId! requires Administer System permission.\"}"));
  160. }
  161. @Test
  162. public void search_whenUserServiceReturnUsers_shouldReturnThem() throws Exception {
  163. UserInformation user1 = generateUserSearchResult("user1", true, true, false, 2, 3);
  164. UserInformation user2 = generateUserSearchResult("user2", true, false, false, 3, 0);
  165. UserInformation user3 = generateUserSearchResult("user3", true, false, true, 1, 1);
  166. UserInformation user4 = generateUserSearchResult("user4", false, true, false, 0, 0);
  167. List<UserInformation> users = List.of(user1, user2, user3, user4);
  168. SearchResults<UserInformation> searchResult = new SearchResults<>(users, users.size());
  169. when(userService.findUsers(any())).thenReturn(searchResult);
  170. List<UserRestResponse> restUserForAdmins = List.of(toRestUser(user1), toRestUser(user2), toRestUser(user3), toRestUser(user4));
  171. when(responseGenerator.toUsersForResponse(eq(searchResult.searchResults()), any())).thenReturn(new UsersSearchRestResponse(restUserForAdmins, new PageRestResponse(1, 50, 4)));
  172. userSession.logIn().setSystemAdministrator();
  173. MvcResult mvcResult = mockMvc.perform(get(USER_ENDPOINT))
  174. .andExpect(status().isOk())
  175. .andReturn();
  176. UsersSearchRestResponse actualUsersSearchRestResponse = gson.fromJson(mvcResult.getResponse().getContentAsString(), UsersSearchRestResponse.class);
  177. assertThat(actualUsersSearchRestResponse.users())
  178. .containsExactlyElementsOf(restUserForAdmins);
  179. assertThat(actualUsersSearchRestResponse.page().total()).isEqualTo(users.size());
  180. }
  181. static class RestUserDeserializer extends TypeAdapter<UserRestResponse> {
  182. @Override
  183. public void write(JsonWriter out, UserRestResponse value) {
  184. throw new IllegalStateException("not implemented");
  185. }
  186. @Override
  187. public UserRestResponse read(JsonReader reader) {
  188. return gson.fromJson(reader, UserRestResponseForAdmins.class);
  189. }
  190. }
  191. private UserInformation generateUserSearchResult(String id, boolean active, boolean local, boolean managed, int groupsCount, int tokensCount) {
  192. UserDto userDto = new UserDto()
  193. .setLogin("login_" + id)
  194. .setUuid("uuid_" + id)
  195. .setName("name_" + id)
  196. .setEmail(id + "@email.com")
  197. .setActive(active)
  198. .setLocal(local)
  199. .setExternalLogin("externalLogin_" + id)
  200. .setExternalId("externalId_" + id)
  201. .setExternalIdentityProvider("externalIdentityProvider_" + id)
  202. .setLastConnectionDate(0L)
  203. .setLastSonarlintConnectionDate(1L);
  204. List<String> groups = new ArrayList<>();
  205. IntStream.range(1, groupsCount).forEach(i -> groups.add("group" + i));
  206. return new UserInformation(userDto, managed, Optional.of("avatar_" + id), groups, tokensCount);
  207. }
  208. private UserRestResponseForAdmins toRestUser(UserInformation userInformation) {
  209. return new UserRestResponseForAdmins(
  210. userInformation.userDto().getLogin(),
  211. userInformation.userDto().getLogin(),
  212. userInformation.userDto().getName(),
  213. userInformation.userDto().getEmail(),
  214. userInformation.userDto().isActive(),
  215. userInformation.userDto().isLocal(),
  216. userInformation.managed(),
  217. userInformation.userDto().getExternalLogin(),
  218. userInformation.userDto().getExternalIdentityProvider(),
  219. userInformation.avatar().orElse(""),
  220. formatDateTime(userInformation.userDto().getLastConnectionDate()),
  221. formatDateTime(userInformation.userDto().getLastSonarlintConnectionDate()),
  222. userInformation.userDto().getSortedScmAccounts());
  223. }
  224. @Test
  225. public void deactivate_whenUserIsNotAdministrator_shouldReturnForbidden() throws Exception {
  226. userSession.logIn().setNonSystemAdministrator();
  227. mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete"))
  228. .andExpectAll(
  229. status().isForbidden(),
  230. content().json("{\"message\":\"Insufficient privileges\"}"));
  231. }
  232. @Test
  233. public void deactivate_whenUserServiceThrowsNotFoundException_shouldReturnNotFound() throws Exception {
  234. userSession.logIn().setSystemAdministrator();
  235. doThrow(new NotFoundException("User not found.")).when(userService).deactivate("userToDelete", false);
  236. mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete"))
  237. .andExpectAll(
  238. status().isNotFound(),
  239. content().json("{\"message\":\"User not found.\"}"));
  240. }
  241. @Test
  242. public void deactivate_whenUserServiceThrowsBadRequestException_shouldReturnBadRequest() throws Exception {
  243. userSession.logIn().setSystemAdministrator();
  244. doThrow(BadRequestException.create("Not allowed")).when(userService).deactivate("userToDelete", false);
  245. mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete"))
  246. .andExpectAll(
  247. status().isBadRequest(),
  248. content().json("{\"message\":\"Not allowed\"}"));
  249. }
  250. @Test
  251. public void deactivate_whenUserTryingToDeactivateThemself_shouldReturnBadRequest() throws Exception {
  252. UserSessionRule userToDelete = userSession.logIn("userToDelete").setSystemAdministrator();
  253. mockMvc.perform(delete(USER_ENDPOINT + "/" + userToDelete.getUuid()))
  254. .andExpectAll(
  255. status().isBadRequest(),
  256. content().json("{\"message\":\"Self-deactivation is not possible\"}"));
  257. }
  258. @Test
  259. public void deactivate_whenAnonymizeParameterIsNotBoolean_shouldReturnBadRequest() throws Exception {
  260. userSession.logIn().setSystemAdministrator();
  261. mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete").param("anonymize", "maybe"))
  262. .andExpect(
  263. status().isBadRequest());
  264. }
  265. @Test
  266. public void deactivate_whenAnonymizeIsNotSpecified_shouldDeactivateUserWithoutAnonymization() throws Exception {
  267. userSession.logIn().setSystemAdministrator();
  268. mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete"))
  269. .andExpect(status().isNoContent());
  270. verify(userService).deactivate("userToDelete", false);
  271. }
  272. @Test
  273. public void deactivate_whenAnonymizeFalse_shouldDeactivateUserWithoutAnonymization() throws Exception {
  274. userSession.logIn().setSystemAdministrator();
  275. mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete").param("anonymize", "false"))
  276. .andExpect(status().isNoContent());
  277. verify(userService).deactivate("userToDelete", false);
  278. }
  279. @Test
  280. public void deactivate_whenAnonymizeTrue_shouldDeactivateUserWithAnonymization() throws Exception {
  281. userSession.logIn().setSystemAdministrator();
  282. mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete").param("anonymize", "true"))
  283. .andExpect(status().isNoContent());
  284. verify(userService).deactivate("userToDelete", true);
  285. }
  286. @Test
  287. public void fetchUser_whenUserServiceThrowsNotFoundException_returnsNotFound() throws Exception {
  288. when(userService.fetchUser("userLogin")).thenThrow(new NotFoundException("Not found"));
  289. mockMvc.perform(get(USER_ENDPOINT + "/userLogin"))
  290. .andExpectAll(
  291. status().isNotFound(),
  292. content().json("{\"message\":\"Not found\"}")
  293. );
  294. }
  295. @Test
  296. public void fetchUser_whenUserExists_shouldReturnUser() throws Exception {
  297. UserInformation user = generateUserSearchResult("user1", true, true, false, 2, 3);
  298. UserRestResponseForAdmins restUserForAdmins = toRestUser(user);
  299. when(userService.fetchUser("userLogin")).thenReturn(user);
  300. when(responseGenerator.toRestUser(user)).thenReturn(restUserForAdmins);
  301. MvcResult mvcResult = mockMvc.perform(get(USER_ENDPOINT + "/userLogin"))
  302. .andExpect(status().isOk())
  303. .andReturn();
  304. UserRestResponseForAdmins responseUser = gson.fromJson(mvcResult.getResponse().getContentAsString(), UserRestResponseForAdmins.class);
  305. assertThat(responseUser).isEqualTo(restUserForAdmins);
  306. }
  307. @Test
  308. public void create_whenNotAnAdmin_shouldReturnForbidden() throws Exception {
  309. userSession.logIn().setNonSystemAdministrator();
  310. mockMvc.perform(
  311. post(USER_ENDPOINT)
  312. .contentType(MediaType.APPLICATION_JSON_VALUE)
  313. .content(gson.toJson(new UserCreateRestRequest(null, null, "login", "name", null, null))))
  314. .andExpectAll(
  315. status().isForbidden(),
  316. content().json("{\"message\":\"Insufficient privileges\"}"));
  317. }
  318. @Test
  319. public void create_whenNoLogin_shouldReturnBadRequest() throws Exception {
  320. userSession.logIn().setSystemAdministrator();
  321. mockMvc.perform(
  322. post(USER_ENDPOINT)
  323. .contentType(MediaType.APPLICATION_JSON_VALUE)
  324. .content(gson.toJson(new UserCreateRestRequest(null, null, null, "name", null, null))))
  325. .andExpectAll(
  326. status().isBadRequest(),
  327. content().json("{\"message\":\"Value {} for field login was rejected. Error: must not be null.\"}"));
  328. }
  329. @Test
  330. public void create_whenNoName_shouldReturnBadRequest() throws Exception {
  331. userSession.logIn().setSystemAdministrator();
  332. mockMvc.perform(
  333. post(USER_ENDPOINT)
  334. .contentType(MediaType.APPLICATION_JSON_VALUE)
  335. .content(gson.toJson(new UserCreateRestRequest(null, null, "login", null, null, null))))
  336. .andExpectAll(
  337. status().isBadRequest(),
  338. content().json("{\"message\":\"Value {} for field name was rejected. Error: must not be null.\"}"));
  339. }
  340. @Test
  341. public void create_whenUserServiceThrow_shouldReturnServerError() throws Exception {
  342. userSession.logIn().setSystemAdministrator();
  343. when(userService.createUser(any())).thenThrow(new IllegalArgumentException("IllegalArgumentException"));
  344. mockMvc.perform(
  345. post(USER_ENDPOINT)
  346. .contentType(MediaType.APPLICATION_JSON_VALUE)
  347. .content(gson.toJson(new UserCreateRestRequest("e@mail.com", true, "login", "name", "password", List.of("scm")))))
  348. .andExpectAll(
  349. status().isBadRequest(),
  350. content().json("{\"message\":\"IllegalArgumentException\"}"));
  351. }
  352. @Test
  353. public void create_whenUserServiceReturnUser_shouldReturnIt() throws Exception {
  354. userSession.logIn().setSystemAdministrator();
  355. UserInformation userInformation = generateUserSearchResult("1", true, true, false, 1, 2);
  356. UserDto userDto = userInformation.userDto();
  357. when(userService.createUser(any())).thenReturn(userInformation);
  358. when(responseGenerator.toRestUser(userInformation)).thenReturn(toRestUser(userInformation));
  359. MvcResult mvcResult = mockMvc.perform(
  360. post(USER_ENDPOINT)
  361. .contentType(MediaType.APPLICATION_JSON_VALUE)
  362. .content(gson.toJson(new UserCreateRestRequest(
  363. userDto.getEmail(), userDto.isLocal(), userDto.getLogin(), userDto.getName(), "password", userDto.getSortedScmAccounts()))))
  364. .andExpect(status().isOk())
  365. .andReturn();
  366. UserRestResponseForAdmins responseUser = gson.fromJson(mvcResult.getResponse().getContentAsString(), UserRestResponseForAdmins.class);
  367. assertThat(responseUser).isEqualTo(toRestUser(userInformation));
  368. }
  369. @Test
  370. public void updateUser_whenUserDoesntExist_shouldReturnNotFound() throws Exception {
  371. userSession.logIn().setSystemAdministrator();
  372. when(userService.updateUser(eq("userLogin"), any(UpdateUser.class))).thenThrow(new NotFoundException("Not found"));
  373. mockMvc.perform(patch(USER_ENDPOINT + "/userLogin")
  374. .contentType(JSON_MERGE_PATCH_CONTENT_TYPE)
  375. .content("{}"))
  376. .andExpectAll(
  377. status().isNotFound(),
  378. content().json("{\"message\":\"Not found\"}"));
  379. }
  380. @Test
  381. public void updateUser_whenCallerIsNotAdmin_shouldReturnForbidden() throws Exception {
  382. userSession.logIn().setNonSystemAdministrator();
  383. mockMvc.perform(
  384. patch(USER_ENDPOINT + "/userLogin")
  385. .contentType(JSON_MERGE_PATCH_CONTENT_TYPE)
  386. .content("{}"))
  387. .andExpectAll(
  388. status().isForbidden(),
  389. content().json("{\"message\":\"Insufficient privileges\"}"));
  390. }
  391. @Test
  392. public void updateUser_whenEmailIsProvided_shouldUpdateUserAndReturnUpdatedValue() throws Exception {
  393. UpdateUser userUpdate = performPatchCallAndVerifyResponse("{\"email\":\"newemail@example.com\"}");
  394. assertThat(userUpdate.email()).isEqualTo("newemail@example.com");
  395. assertThat(userUpdate.name()).isNull();
  396. assertThat(userUpdate.scmAccounts()).isNull();
  397. }
  398. @Test
  399. public void updateUser_whenNameIsProvided_shouldUpdateUserAndReturnUpdatedValue() throws Exception {
  400. UpdateUser userUpdate = performPatchCallAndVerifyResponse("{\"name\":\"new name\"}");
  401. assertThat(userUpdate.email()).isNull();
  402. assertThat(userUpdate.name()).isEqualTo("new name");
  403. assertThat(userUpdate.scmAccounts()).isNull();
  404. }
  405. @Test
  406. public void updateUser_whenScmAccountsAreProvided_shouldUpdateUserAndReturnUpdatedValue() throws Exception {
  407. UpdateUser userUpdate = performPatchCallAndVerifyResponse("{\"scmAccounts\":[\"account1\",\"account2\"]}");
  408. assertThat(userUpdate.email()).isNull();
  409. assertThat(userUpdate.name()).isNull();
  410. assertThat(userUpdate.scmAccounts()).containsExactly("account1", "account2");
  411. }
  412. @Test
  413. public void updateUser_whenEmailIsInvalid_shouldReturnBadRequest() throws Exception {
  414. performPatchCallAndExpectBadRequest("{\"email\":\"notavalidemail\"}", "Value notavalidemail for field email was rejected. Error: must be a well-formed email address.");
  415. }
  416. @Test
  417. public void updateUser_whenEmailIsEmpty_shouldReturnBadRequest() throws Exception {
  418. performPatchCallAndExpectBadRequest("{\"email\":\"\"}", "Value for field email was rejected. Error: size must be between 1 and 100.");
  419. }
  420. @Test
  421. public void updateUser_whenNameIsTooLong_shouldReturnBadRequest() throws Exception {
  422. String tooLong = "toolong".repeat(30);
  423. String payload = "{\"name\":\"" + tooLong + "\"}";
  424. String message = "Value " + tooLong + " for field name was rejected. Error: size must be between 0 and 200.";
  425. performPatchCallAndExpectBadRequest(payload, message);
  426. }
  427. @Test
  428. public void updateUser_whenLoginIsProvided_shouldUpdateLogin() throws Exception {
  429. UpdateUser userUpdate = performPatchCallAndVerifyResponse("{\"login\":\"newLogin\"}");
  430. assertThat(userUpdate.login()).isEqualTo("newLogin");
  431. }
  432. @Test
  433. public void updateUser_whenExternalProviderIsProvided_shouldUpdate() throws Exception {
  434. UpdateUser userUpdate = performPatchCallAndVerifyResponse("{\"externalProvider\":\"newExternalProvider\"}");
  435. assertThat(userUpdate.externalIdentityProvider()).isEqualTo("newExternalProvider");
  436. }
  437. @Test
  438. public void updateUser_whenExternalProviderLoginIsProvided_shouldUpdate() throws Exception {
  439. UpdateUser userUpdate = performPatchCallAndVerifyResponse("{\"externalLogin\":\"newExternalProviderLogin\"}");
  440. assertThat(userUpdate.externalIdentityProviderLogin()).isEqualTo("newExternalProviderLogin");
  441. }
  442. @Test
  443. public void updateUser_whenLoginIsEmpty_shouldReturnBadRequest() throws Exception {
  444. performPatchCallAndExpectBadRequest("{\"login\":\"\"}", "Value for field login was rejected. Error: size must be between 2 and 100.");
  445. }
  446. private void performPatchCallAndExpectBadRequest(String payload, String expectedMessage) throws Exception {
  447. userSession.logIn().setSystemAdministrator();
  448. MvcResult mvcResult = mockMvc.perform(patch(USER_ENDPOINT + "/userLogin")
  449. .contentType(JSON_MERGE_PATCH_CONTENT_TYPE)
  450. .content(payload))
  451. .andExpect(
  452. status().isBadRequest())
  453. .andReturn();
  454. RestError error = gson.fromJson(mvcResult.getResponse().getContentAsString(), RestError.class);
  455. assertThat(error.message()).isEqualTo(expectedMessage);
  456. }
  457. private UpdateUser performPatchCallAndVerifyResponse(String payload) throws Exception {
  458. userSession.logIn().setSystemAdministrator();
  459. UserInformation mock = mock();
  460. when(userService.fetchUser("userUuid")).thenReturn(mock);
  461. UserInformation userInformation = generateUserSearchResult("1", true, true, false, 1, 2);
  462. when(userService.updateUser(eq("userUuid"), any())).thenReturn(userInformation);
  463. when(responseGenerator.toRestUser(userInformation)).thenReturn(toRestUser(userInformation));
  464. MvcResult mvcResult = mockMvc.perform(patch(USER_ENDPOINT + "/userUuid")
  465. .contentType(JSON_MERGE_PATCH_CONTENT_TYPE)
  466. .content(payload))
  467. .andExpect(
  468. status().isOk())
  469. .andReturn();
  470. UserRestResponseForAdmins responseUser = gson.fromJson(mvcResult.getResponse().getContentAsString(), UserRestResponseForAdmins.class);
  471. assertThat(responseUser).isEqualTo(toRestUser(userInformation));
  472. ArgumentCaptor<UpdateUser> updateUserCaptor = ArgumentCaptor.forClass(UpdateUser.class);
  473. verify(userService).updateUser(eq("userUuid"), updateUserCaptor.capture());
  474. return updateUserCaptor.getValue();
  475. }
  476. }