]> source.dussan.org Git - sonarqube.git/blob
4bbf6121539cad6e0c1d99e96b656fc3fac661df
[sonarqube.git] /
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.server.v2.api.user.controller;
21
22 import com.google.gson.Gson;
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.Optional;
26 import java.util.stream.IntStream;
27 import org.junit.Rule;
28 import org.junit.Test;
29 import org.mockito.ArgumentCaptor;
30 import org.sonar.db.user.UserDto;
31 import org.sonar.server.common.SearchResults;
32 import org.sonar.server.common.user.service.UserSearchResult;
33 import org.sonar.server.common.user.service.UserService;
34 import org.sonar.server.common.user.service.UsersSearchRequest;
35 import org.sonar.server.exceptions.BadRequestException;
36 import org.sonar.server.exceptions.NotFoundException;
37 import org.sonar.server.tester.UserSessionRule;
38 import org.sonar.server.v2.api.ControllerTester;
39 import org.sonar.server.v2.api.response.PageRestResponse;
40 import org.sonar.server.v2.api.user.converter.UsersSearchRestResponseGenerator;
41 import org.sonar.server.v2.api.user.model.RestUser;
42 import org.sonar.server.v2.api.user.model.RestUserForAdmins;
43 import org.sonar.server.v2.api.user.request.UserCreateRestRequest;
44 import org.sonar.server.v2.api.user.response.UsersSearchRestResponse;
45 import org.springframework.http.MediaType;
46 import org.springframework.test.web.servlet.MockMvc;
47 import org.springframework.test.web.servlet.MvcResult;
48
49 import static org.assertj.core.api.Assertions.assertThat;
50 import static org.mockito.ArgumentMatchers.any;
51 import static org.mockito.ArgumentMatchers.eq;
52 import static org.mockito.Mockito.doThrow;
53 import static org.mockito.Mockito.mock;
54 import static org.mockito.Mockito.verify;
55 import static org.mockito.Mockito.when;
56 import static org.sonar.api.utils.DateUtils.formatDateTime;
57 import static org.sonar.server.v2.WebApiEndpoints.USER_ENDPOINT;
58 import static org.sonar.server.v2.api.model.RestPage.DEFAULT_PAGE_INDEX;
59 import static org.sonar.server.v2.api.model.RestPage.DEFAULT_PAGE_SIZE;
60 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
61 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
62 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
63 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
64 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
65
66 public class DefaultUserControllerTest {
67
68   @Rule
69   public UserSessionRule userSession = UserSessionRule.standalone();
70   private final UserService userService = mock(UserService.class);
71   private final UsersSearchRestResponseGenerator responseGenerator = mock(UsersSearchRestResponseGenerator.class);
72   private final MockMvc mockMvc = ControllerTester.getMockMvc(new DefaultUserController(userSession, userService, responseGenerator));
73
74   private static final Gson gson = new Gson();
75
76   @Test
77   public void search_whenNoParameters_shouldUseDefaultAndForwardToUserService() throws Exception {
78     when(userService.findUsers(any())).thenReturn(new SearchResults<>(List.of(), 0));
79
80     mockMvc.perform(get(USER_ENDPOINT))
81       .andExpect(status().isOk());
82
83     ArgumentCaptor<UsersSearchRequest> requestCaptor = ArgumentCaptor.forClass(UsersSearchRequest.class);
84     verify(userService).findUsers(requestCaptor.capture());
85     assertThat(requestCaptor.getValue().getPageSize()).isEqualTo(Integer.valueOf(DEFAULT_PAGE_SIZE));
86     assertThat(requestCaptor.getValue().getPage()).isEqualTo(Integer.valueOf(DEFAULT_PAGE_INDEX));
87     assertThat(requestCaptor.getValue().isDeactivated()).isFalse();
88   }
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
95     mockMvc.perform(get(USER_ENDPOINT)
96         .param("active", "false")
97         .param("managed", "true")
98         .param("q", "q")
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("pageSize", "100")
104         .param("pageIndex", "2"))
105       .andExpect(status().isOk());
106
107     ArgumentCaptor<UsersSearchRequest> requestCaptor = ArgumentCaptor.forClass(UsersSearchRequest.class);
108     verify(userService).findUsers(requestCaptor.capture());
109     assertThat(requestCaptor.getValue().getPageSize()).isEqualTo(100);
110     assertThat(requestCaptor.getValue().getPage()).isEqualTo(2);
111     assertThat(requestCaptor.getValue().isDeactivated()).isTrue();
112   }
113
114   @Test
115   public void search_whenAdminParametersUsedButNotAdmin_shouldFail() throws Exception {
116     mockMvc.perform(get(USER_ENDPOINT)
117       .param("sonarQubeLastConnectionDateFrom", "2020-01-01T00:00:00+0100"))
118       .andExpectAll(
119         status().isForbidden(),
120         content().string("{\"message\":\"parameter sonarQubeLastConnectionDateFrom requires Administer System permission.\"}"));
121
122     mockMvc.perform(get(USER_ENDPOINT)
123       .param("sonarQubeLastConnectionDateTo", "2020-01-01T00:00:00+0100"))
124       .andExpectAll(
125         status().isForbidden(),
126         content().string("{\"message\":\"parameter sonarQubeLastConnectionDateTo requires Administer System permission.\"}"));
127
128     mockMvc.perform(get(USER_ENDPOINT)
129       .param("sonarLintLastConnectionDateFrom", "2020-01-01T00:00:00+0100"))
130       .andExpectAll(
131         status().isForbidden(),
132         content().string("{\"message\":\"parameter sonarLintLastConnectionDateFrom requires Administer System permission.\"}"));
133
134     mockMvc.perform(get(USER_ENDPOINT)
135       .param("sonarLintLastConnectionDateTo", "2020-01-01T00:00:00+0100"))
136       .andExpectAll(
137         status().isForbidden(),
138         content().string("{\"message\":\"parameter sonarLintLastConnectionDateTo requires Administer System permission.\"}"));
139   }
140
141   @Test
142   public void search_whenUserServiceReturnUsers_shouldReturnThem() throws Exception {
143     UserSearchResult user1 = generateUserSearchResult("user1", true, true, false, 2, 3);
144     UserSearchResult user2 = generateUserSearchResult("user2", true, false, false, 3, 0);
145     UserSearchResult user3 = generateUserSearchResult("user3", true, false, true, 1, 1);
146     UserSearchResult user4 = generateUserSearchResult("user4", false, true, false, 0, 0);
147     List<UserSearchResult> users = List.of(user1, user2, user3, user4);
148     SearchResults<UserSearchResult> searchResult = new SearchResults<>(users, users.size());
149     when(userService.findUsers(any())).thenReturn(searchResult);
150     List<RestUser> restUserForAdmins = List.of(toRestUser(user1), toRestUser(user2), toRestUser(user3), toRestUser(user4));
151     when(responseGenerator.toUsersForResponse(eq(searchResult.searchResults()), any())).thenReturn(new UsersSearchRestResponse(restUserForAdmins, new PageRestResponse(1, 50, 4)));
152     userSession.logIn().setSystemAdministrator();
153
154     MvcResult mvcResult = mockMvc.perform(get(USER_ENDPOINT))
155       .andExpect(status().isOk())
156       .andReturn();
157
158     UsersSearchRestResponse actualUsersSearchRestResponse = gson.fromJson(mvcResult.getResponse().getContentAsString(), UsersSearchRestResponse.class);
159     assertThat(actualUsersSearchRestResponse.users())
160       .containsExactlyElementsOf(restUserForAdmins);
161     assertThat(actualUsersSearchRestResponse.page().total()).isEqualTo(users.size());
162
163   }
164
165   private UserSearchResult generateUserSearchResult(String id, boolean active, boolean local, boolean managed, int groupsCount, int tokensCount) {
166     UserDto userDto = new UserDto()
167       .setLogin("login_" + id)
168       .setUuid("uuid_" + id)
169       .setName("name_" + id)
170       .setEmail(id + "@email.com")
171       .setActive(active)
172       .setLocal(local)
173       .setExternalLogin("externalLogin_" + id)
174       .setExternalId("externalId_" + id)
175       .setExternalIdentityProvider("externalIdentityProvider_" + id)
176       .setLastConnectionDate(0L)
177       .setLastSonarlintConnectionDate(1L);
178
179     List<String> groups = new ArrayList<>();
180     IntStream.range(1, groupsCount).forEach(i -> groups.add("group" + i));
181
182     return new UserSearchResult(userDto, managed, Optional.of("avatar_" + id), groups, tokensCount);
183   }
184
185   private RestUserForAdmins toRestUser(UserSearchResult userSearchResult) {
186     return new RestUserForAdmins(
187       userSearchResult.userDto().getLogin(),
188       userSearchResult.userDto().getLogin(),
189       userSearchResult.userDto().getName(),
190       userSearchResult.userDto().getEmail(),
191       userSearchResult.userDto().isActive(),
192       userSearchResult.userDto().isLocal(),
193       userSearchResult.managed(),
194       userSearchResult.userDto().getExternalLogin(),
195       userSearchResult.userDto().getExternalIdentityProvider(),
196       userSearchResult.avatar().orElse(""),
197       formatDateTime(userSearchResult.userDto().getLastConnectionDate()),
198       formatDateTime(userSearchResult.userDto().getLastSonarlintConnectionDate()),
199       userSearchResult.groups().size(),
200       userSearchResult.tokensCount(),
201       userSearchResult.userDto().getSortedScmAccounts());
202   }
203
204   @Test
205   public void deactivate_whenUserIsNotAdministrator_shouldReturnForbidden() throws Exception {
206     userSession.logIn().setNonSystemAdministrator();
207
208     mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete"))
209       .andExpectAll(
210         status().isForbidden(),
211         content().json("{\"message\":\"Insufficient privileges\"}"));
212   }
213
214   @Test
215   public void deactivate_whenUserServiceThrowsNotFoundException_shouldReturnNotFound() throws Exception {
216     userSession.logIn().setSystemAdministrator();
217     doThrow(new NotFoundException("User not found.")).when(userService).deactivate("userToDelete", false);
218
219     mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete"))
220       .andExpectAll(
221         status().isNotFound(),
222         content().json("{\"message\":\"User not found.\"}"));
223   }
224
225   @Test
226   public void deactivate_whenUserServiceThrowsBadRequestException_shouldReturnBadRequest() throws Exception {
227     userSession.logIn().setSystemAdministrator();
228     doThrow(BadRequestException.create("Not allowed")).when(userService).deactivate("userToDelete", false);
229
230     mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete"))
231       .andExpectAll(
232         status().isBadRequest(),
233         content().json("{\"message\":\"Not allowed\"}"));
234   }
235
236   @Test
237   public void deactivate_whenUserTryingToDeactivateThemself_shouldReturnBadRequest() throws Exception {
238     userSession.logIn("userToDelete").setSystemAdministrator();
239
240     mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete"))
241       .andExpectAll(
242         status().isBadRequest(),
243         content().json("{\"message\":\"Self-deactivation is not possible\"}"));
244   }
245
246   @Test
247   public void deactivate_whenAnonymizeParameterIsNotBoolean_shouldReturnBadRequest() throws Exception {
248     userSession.logIn().setSystemAdministrator();
249
250     mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete").param("anonymize", "maybe"))
251       .andExpect(
252         status().isBadRequest());
253   }
254
255   @Test
256   public void deactivate_whenAnonymizeIsNotSpecified_shouldDeactivateUserWithoutAnonymization() throws Exception {
257     userSession.logIn().setSystemAdministrator();
258
259     mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete"))
260       .andExpect(status().isNoContent());
261
262     verify(userService).deactivate("userToDelete", false);
263   }
264
265   @Test
266   public void deactivate_whenAnonymizeFalse_shouldDeactivateUserWithoutAnonymization() throws Exception {
267     userSession.logIn().setSystemAdministrator();
268
269     mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete").param("anonymize", "false"))
270       .andExpect(status().isNoContent());
271
272     verify(userService).deactivate("userToDelete", false);
273   }
274
275   @Test
276   public void deactivate_whenAnonymizeTrue_shouldDeactivateUserWithAnonymization() throws Exception {
277     userSession.logIn().setSystemAdministrator();
278
279     mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete").param("anonymize", "true"))
280       .andExpect(status().isNoContent());
281
282     verify(userService).deactivate("userToDelete", true);
283   }
284
285   @Test
286   public void fetchUser_whenUserServiceThrowsNotFoundException_returnsNotFound() throws Exception {
287     when(userService.fetchUser("userLogin")).thenThrow(new NotFoundException("Not found"));
288     mockMvc.perform(get(USER_ENDPOINT + "/userLogin"))
289       .andExpectAll(
290         status().isNotFound(),
291         content().json("{\"message\":\"Not found\"}")
292       );
293
294   }
295
296   @Test
297   public void fetchUser_whenUserExists_shouldReturnUser() throws Exception {
298     UserSearchResult user = generateUserSearchResult("user1", true, true, false, 2, 3);
299     RestUserForAdmins restUserForAdmins = toRestUser(user);
300     when(userService.fetchUser("userLogin")).thenReturn(user);
301     when(responseGenerator.toRestUser(user)).thenReturn(restUserForAdmins);
302     MvcResult mvcResult = mockMvc.perform(get(USER_ENDPOINT + "/userLogin"))
303       .andExpect(status().isOk())
304       .andReturn();
305     RestUserForAdmins responseUser = gson.fromJson(mvcResult.getResponse().getContentAsString(), RestUserForAdmins.class);
306     assertThat(responseUser).isEqualTo(restUserForAdmins);
307   }
308
309   @Test
310   public void create_whenNotAnAdmin_shouldReturnForbidden() throws Exception {
311     userSession.logIn().setNonSystemAdministrator();
312
313     mockMvc.perform(
314       post(USER_ENDPOINT)
315         .contentType(MediaType.APPLICATION_JSON_VALUE)
316         .content(gson.toJson(new UserCreateRestRequest(null, null, "login", "name", null, null))))
317       .andExpectAll(
318         status().isForbidden(),
319         content().json("{\"message\":\"Insufficient privileges\"}"));
320   }
321
322   @Test
323   public void create_whenNoLogin_shouldReturnBadRequest() throws Exception {
324     userSession.logIn().setSystemAdministrator();
325
326     mockMvc.perform(
327         post(USER_ENDPOINT)
328           .contentType(MediaType.APPLICATION_JSON_VALUE)
329           .content(gson.toJson(new UserCreateRestRequest(null, null, null, "name", null, null))))
330       .andExpectAll(
331         status().isBadRequest(),
332         content().json("{\"message\":\"Value {} for field login was rejected. Error: must not be null\"}"));
333   }
334
335   @Test
336   public void create_whenNoName_shouldReturnBadRequest() throws Exception {
337     userSession.logIn().setSystemAdministrator();
338
339     mockMvc.perform(
340         post(USER_ENDPOINT)
341           .contentType(MediaType.APPLICATION_JSON_VALUE)
342           .content(gson.toJson(new UserCreateRestRequest(null, null, "login", null, null, null))))
343       .andExpectAll(
344         status().isBadRequest(),
345         content().json("{\"message\":\"Value {} for field name was rejected. Error: must not be null\"}"));
346   }
347
348   @Test
349   public void create_whenUserServiceThrow_shouldReturnServerError() throws Exception {
350     userSession.logIn().setSystemAdministrator();
351     when(userService.createUser(any())).thenThrow(new IllegalArgumentException("IllegalArgumentException"));
352
353     mockMvc.perform(
354         post(USER_ENDPOINT)
355           .contentType(MediaType.APPLICATION_JSON_VALUE)
356           .content(gson.toJson(new UserCreateRestRequest("e@mail.com", true, "login", "name", "password", List.of("scm")))))
357       .andExpectAll(
358         status().isBadRequest(),
359         content().json("{\"message\":\"IllegalArgumentException\"}"));
360   }
361
362   @Test
363   public void create_whenUserServiceReturnUser_shouldReturnIt() throws Exception {
364     userSession.logIn().setSystemAdministrator();
365     UserSearchResult userSearchResult = generateUserSearchResult("1", true, true, false, 1, 2);
366     UserDto userDto = userSearchResult.userDto();
367     when(userService.createUser(any())).thenReturn(userSearchResult);
368     when(responseGenerator.toRestUser(userSearchResult)).thenReturn(toRestUser(userSearchResult));
369
370     MvcResult mvcResult = mockMvc.perform(
371         post(USER_ENDPOINT)
372           .contentType(MediaType.APPLICATION_JSON_VALUE)
373           .content(gson.toJson(new UserCreateRestRequest(
374             userDto.getEmail(), userDto.isLocal(), userDto.getLogin(), userDto.getName(), "password", userDto.getSortedScmAccounts()))))
375       .andExpect(status().isOk())
376       .andReturn();
377     RestUserForAdmins responseUser = gson.fromJson(mvcResult.getResponse().getContentAsString(), RestUserForAdmins.class);
378     assertThat(responseUser).isEqualTo(toRestUser(userSearchResult));
379   }
380
381 }