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.

DefaultGitlabConfigurationControllerTest.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  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.gitlab.config;
  21. import com.google.gson.Gson;
  22. import com.google.gson.GsonBuilder;
  23. import java.util.List;
  24. import java.util.Optional;
  25. import java.util.Set;
  26. import org.junit.Before;
  27. import org.junit.Rule;
  28. import org.junit.Test;
  29. import org.sonar.server.common.NonNullUpdatedValue;
  30. import org.sonar.server.common.UpdatedValue;
  31. import org.sonar.server.common.gitlab.config.GitlabConfiguration;
  32. import org.sonar.server.common.gitlab.config.GitlabConfigurationService;
  33. import org.sonar.server.common.gitlab.config.UpdateGitlabConfigurationRequest;
  34. import org.sonar.server.exceptions.NotFoundException;
  35. import org.sonar.server.tester.UserSessionRule;
  36. import org.sonar.server.v2.api.ControllerTester;
  37. import org.sonar.server.v2.api.gitlab.config.controller.DefaultGitlabConfigurationController;
  38. import org.sonar.server.v2.api.gitlab.config.resource.GitlabConfigurationResource;
  39. import org.sonar.server.v2.api.gitlab.config.response.GitlabConfigurationSearchRestResponse;
  40. import org.springframework.http.MediaType;
  41. import org.springframework.test.web.servlet.MockMvc;
  42. import org.springframework.test.web.servlet.MvcResult;
  43. import static org.assertj.core.api.Assertions.assertThat;
  44. import static org.mockito.ArgumentMatchers.any;
  45. import static org.mockito.Mockito.doThrow;
  46. import static org.mockito.Mockito.mock;
  47. import static org.mockito.Mockito.verify;
  48. import static org.mockito.Mockito.when;
  49. import static org.sonar.server.common.gitlab.config.ProvisioningType.AUTO_PROVISIONING;
  50. import static org.sonar.server.common.gitlab.config.ProvisioningType.JIT;
  51. import static org.sonar.server.v2.WebApiEndpoints.GITLAB_CONFIGURATION_ENDPOINT;
  52. import static org.sonar.server.v2.WebApiEndpoints.JSON_MERGE_PATCH_CONTENT_TYPE;
  53. import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
  54. import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
  55. import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
  56. import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
  57. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
  58. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  59. public class DefaultGitlabConfigurationControllerTest {
  60. private static final Gson GSON = new GsonBuilder().create();
  61. private static final GitlabConfiguration GITLAB_CONFIGURATION = new GitlabConfiguration(
  62. "existing-id",
  63. true,
  64. "application-id",
  65. "www.url.com",
  66. "secret",
  67. true,
  68. Set.of("group1", "group2"),
  69. true,
  70. AUTO_PROVISIONING,
  71. "provisioning-token"
  72. );
  73. private static final GitlabConfigurationResource EXPECTED_GITLAB_CONF_RESOURCE = new GitlabConfigurationResource(
  74. GITLAB_CONFIGURATION.id(),
  75. GITLAB_CONFIGURATION.enabled(),
  76. GITLAB_CONFIGURATION.applicationId(),
  77. GITLAB_CONFIGURATION.url(),
  78. GITLAB_CONFIGURATION.synchronizeGroups(),
  79. List.of("group1", "group2"),
  80. GITLAB_CONFIGURATION.allowUsersToSignUp(),
  81. org.sonar.server.v2.api.gitlab.config.resource.ProvisioningType.valueOf(GITLAB_CONFIGURATION.provisioningType().name()),
  82. "error-message");
  83. private static final String EXPECTED_CONFIGURATION = """
  84. {
  85. "id": "existing-id",
  86. "enabled": true,
  87. "applicationId": "application-id",
  88. "url": "www.url.com",
  89. "synchronizeGroups": true,
  90. "allowedGroups": [
  91. "group1",
  92. "group2"
  93. ],
  94. "provisioningType": "AUTO_PROVISIONING",
  95. "allowUsersToSignUp": true,
  96. "errorMessage": "error-message"
  97. }
  98. """;
  99. @Rule
  100. public UserSessionRule userSession = UserSessionRule.standalone();
  101. private final GitlabConfigurationService gitlabConfigurationService = mock();
  102. private final MockMvc mockMvc = ControllerTester.getMockMvc(new DefaultGitlabConfigurationController(userSession, gitlabConfigurationService));
  103. @Before
  104. public void setUp() {
  105. when(gitlabConfigurationService.validate(any())).thenReturn(Optional.of("error-message"));
  106. }
  107. @Test
  108. public void fetchConfiguration_whenUserIsNotAdministrator_shouldReturnForbidden() throws Exception {
  109. userSession.logIn().setNonSystemAdministrator();
  110. mockMvc.perform(get(GITLAB_CONFIGURATION_ENDPOINT + "/1"))
  111. .andExpectAll(
  112. status().isForbidden(),
  113. content().json("{\"message\":\"Insufficient privileges\"}"));
  114. }
  115. @Test
  116. public void fetchConfiguration_whenConfigNotFound_throws() throws Exception {
  117. userSession.logIn().setSystemAdministrator();
  118. when(gitlabConfigurationService.getConfiguration("not-existing")).thenThrow(new NotFoundException("bla"));
  119. mockMvc.perform(get(GITLAB_CONFIGURATION_ENDPOINT + "/not-existing"))
  120. .andExpectAll(
  121. status().isNotFound(),
  122. content().json("{\"message\":\"bla\"}"));
  123. }
  124. @Test
  125. public void fetchConfiguration_whenConfigFound_returnsIt() throws Exception {
  126. userSession.logIn().setSystemAdministrator();
  127. when(gitlabConfigurationService.getConfiguration("existing-id")).thenReturn(GITLAB_CONFIGURATION);
  128. mockMvc.perform(get(GITLAB_CONFIGURATION_ENDPOINT + "/existing-id"))
  129. .andExpectAll(
  130. status().isOk(),
  131. content().json(EXPECTED_CONFIGURATION));
  132. }
  133. @Test
  134. public void search_whenNoParameters_shouldUseDefaultAndForwardToGroupMembershipService() throws Exception {
  135. userSession.logIn().setSystemAdministrator();
  136. when(gitlabConfigurationService.findConfigurations()).thenReturn(Optional.of(GITLAB_CONFIGURATION));
  137. MvcResult mvcResult = mockMvc.perform(get(GITLAB_CONFIGURATION_ENDPOINT))
  138. .andExpect(status().isOk())
  139. .andReturn();
  140. GitlabConfigurationSearchRestResponse gitlabConfigurationResource = GSON.fromJson(mvcResult.getResponse().getContentAsString(), GitlabConfigurationSearchRestResponse.class);
  141. assertThat(gitlabConfigurationResource.page().pageSize()).isEqualTo(1000);
  142. assertThat(gitlabConfigurationResource.page().pageIndex()).isEqualTo(1);
  143. assertThat(gitlabConfigurationResource.page().total()).isEqualTo(1);
  144. assertThat(gitlabConfigurationResource.gitlabConfigurations()).containsExactly(EXPECTED_GITLAB_CONF_RESOURCE);
  145. }
  146. @Test
  147. public void search_whenNoParametersAndNoConfig_shouldReturnEmptyList() throws Exception {
  148. userSession.logIn().setSystemAdministrator();
  149. when(gitlabConfigurationService.findConfigurations()).thenReturn(Optional.empty());
  150. MvcResult mvcResult = mockMvc.perform(get(GITLAB_CONFIGURATION_ENDPOINT))
  151. .andExpect(status().isOk())
  152. .andReturn();
  153. GitlabConfigurationSearchRestResponse gitlabConfigurationResource = GSON.fromJson(mvcResult.getResponse().getContentAsString(), GitlabConfigurationSearchRestResponse.class);
  154. assertThat(gitlabConfigurationResource.page().pageSize()).isEqualTo(1000);
  155. assertThat(gitlabConfigurationResource.page().pageIndex()).isEqualTo(1);
  156. assertThat(gitlabConfigurationResource.page().total()).isZero();
  157. assertThat(gitlabConfigurationResource.gitlabConfigurations()).isEmpty();
  158. }
  159. @Test
  160. public void updateConfiguration_whenUserIsNotAdministrator_shouldReturnForbidden() throws Exception {
  161. userSession.logIn().setNonSystemAdministrator();
  162. mockMvc.perform(patch(GITLAB_CONFIGURATION_ENDPOINT + "/existing-id")
  163. .contentType(JSON_MERGE_PATCH_CONTENT_TYPE)
  164. .content("{}"))
  165. .andExpectAll(
  166. status().isForbidden(),
  167. content().json("{\"message\":\"Insufficient privileges\"}"));
  168. }
  169. @Test
  170. public void updateConfiguration_whenAllFieldsUpdated_performUpdates() throws Exception {
  171. userSession.logIn().setSystemAdministrator();
  172. when(gitlabConfigurationService.updateConfiguration(any())).thenReturn(GITLAB_CONFIGURATION);
  173. String payload = """
  174. {
  175. "enabled": true,
  176. "applicationId": "application-id",
  177. "url": "www.url.com",
  178. "secret": "newSecret",
  179. "synchronizeGroups": true,
  180. "allowedGroups": [
  181. "group1",
  182. "group2"
  183. ],
  184. "provisioningType": "AUTO_PROVISIONING",
  185. "allowUsersToSignUp": true,
  186. "provisioningToken": "token"
  187. }
  188. """;
  189. mockMvc.perform(patch(GITLAB_CONFIGURATION_ENDPOINT + "/existing-id")
  190. .contentType(JSON_MERGE_PATCH_CONTENT_TYPE)
  191. .content(payload))
  192. .andExpectAll(
  193. status().isOk(),
  194. content().json(EXPECTED_CONFIGURATION));
  195. verify(gitlabConfigurationService).updateConfiguration(new UpdateGitlabConfigurationRequest(
  196. "existing-id",
  197. NonNullUpdatedValue.withValueOrThrow(true),
  198. NonNullUpdatedValue.withValueOrThrow("application-id"),
  199. NonNullUpdatedValue.withValueOrThrow("www.url.com"),
  200. NonNullUpdatedValue.withValueOrThrow("newSecret"),
  201. NonNullUpdatedValue.withValueOrThrow(true),
  202. NonNullUpdatedValue.withValueOrThrow(Set.of("group1", "group2")),
  203. NonNullUpdatedValue.withValueOrThrow(true),
  204. UpdatedValue.withValue("token"),
  205. NonNullUpdatedValue.withValueOrThrow(AUTO_PROVISIONING)
  206. ));
  207. }
  208. @Test
  209. public void updateConfiguration_whenSomeFieldsUpdated_performUpdates() throws Exception {
  210. userSession.logIn().setSystemAdministrator();
  211. when(gitlabConfigurationService.updateConfiguration(any())).thenReturn(GITLAB_CONFIGURATION);
  212. String payload = """
  213. {
  214. "enabled": false,
  215. "provisioningType": "JIT",
  216. "allowUsersToSignUp": false,
  217. "provisioningToken": null
  218. }
  219. """;
  220. mockMvc.perform(patch(GITLAB_CONFIGURATION_ENDPOINT + "/existing-id")
  221. .contentType(JSON_MERGE_PATCH_CONTENT_TYPE)
  222. .content(payload))
  223. .andExpectAll(
  224. status().isOk(),
  225. content().json(EXPECTED_CONFIGURATION));
  226. verify(gitlabConfigurationService).updateConfiguration(new UpdateGitlabConfigurationRequest(
  227. "existing-id",
  228. NonNullUpdatedValue.withValueOrThrow(false),
  229. NonNullUpdatedValue.undefined(),
  230. NonNullUpdatedValue.undefined(),
  231. NonNullUpdatedValue.undefined(),
  232. NonNullUpdatedValue.undefined(),
  233. NonNullUpdatedValue.undefined(), NonNullUpdatedValue.withValueOrThrow(false), UpdatedValue.withValue(null), NonNullUpdatedValue.withValueOrThrow(JIT)
  234. ));
  235. }
  236. @Test
  237. public void create_whenUserIsNotAdministrator_shouldReturnForbidden() throws Exception {
  238. userSession.logIn().setNonSystemAdministrator();
  239. mockMvc.perform(
  240. post(GITLAB_CONFIGURATION_ENDPOINT)
  241. .contentType(MediaType.APPLICATION_JSON_VALUE)
  242. .content("""
  243. {
  244. "enabled": true,
  245. "applicationId": "application-id",
  246. "url": "www.url.com",
  247. "secret": "123",
  248. "synchronizeGroups": true,
  249. "allowedGroups": [
  250. "group1",
  251. "group2"
  252. ],
  253. "provisioningType": "AUTO_PROVISIONING",
  254. "allowUsersToSignUp": true
  255. }
  256. """))
  257. .andExpectAll(
  258. status().isForbidden(),
  259. content().json("{\"message\":\"Insufficient privileges\"}"));
  260. }
  261. @Test
  262. public void create_whenConfigCreated_returnsIt() throws Exception {
  263. userSession.logIn().setSystemAdministrator();
  264. when(gitlabConfigurationService.createConfiguration(any())).thenReturn(GITLAB_CONFIGURATION);
  265. mockMvc.perform(
  266. post(GITLAB_CONFIGURATION_ENDPOINT)
  267. .contentType(MediaType.APPLICATION_JSON_VALUE)
  268. .content("""
  269. {
  270. "enabled": true,
  271. "applicationId": "application-id",
  272. "secret": "123",
  273. "url": "www.url.com",
  274. "synchronizeGroups": true,
  275. "allowedGroups": [
  276. "group1",
  277. "group2"
  278. ],
  279. "provisioningType": "AUTO_PROVISIONING",
  280. "allowUsersToSignUp": true
  281. }
  282. """))
  283. .andExpectAll(
  284. status().isOk(),
  285. content().json("""
  286. {
  287. "id": "existing-id",
  288. "enabled": true,
  289. "applicationId": "application-id",
  290. "url": "www.url.com",
  291. "synchronizeGroups": true,
  292. "allowedGroups": [
  293. "group1",
  294. "group2"
  295. ],
  296. "provisioningType": "AUTO_PROVISIONING",
  297. "allowUsersToSignUp": true
  298. }
  299. """));
  300. }
  301. @Test
  302. public void create_whenConfigCreatedWithoutOptionalParams_returnsIt() throws Exception {
  303. userSession.logIn().setSystemAdministrator();
  304. when(gitlabConfigurationService.createConfiguration(any())).thenReturn(GITLAB_CONFIGURATION);
  305. mockMvc.perform(
  306. post(GITLAB_CONFIGURATION_ENDPOINT)
  307. .contentType(MediaType.APPLICATION_JSON_VALUE)
  308. .content("""
  309. {
  310. "enabled": true,
  311. "applicationId": "application-id",
  312. "secret": "123",
  313. "url": "www.url.com",
  314. "synchronizeGroups": true,
  315. "allowedGroups": [
  316. "group1",
  317. "group2"
  318. ],
  319. "provisioningType": "AUTO_PROVISIONING"
  320. }
  321. """))
  322. .andExpectAll(
  323. status().isOk(),
  324. content().json("""
  325. {
  326. "id": "existing-id",
  327. "enabled": true,
  328. "applicationId": "application-id",
  329. "url": "www.url.com",
  330. "synchronizeGroups": true,
  331. "allowedGroups": [
  332. "group1",
  333. "group2"
  334. ],
  335. "provisioningType": "AUTO_PROVISIONING",
  336. "allowUsersToSignUp": true
  337. }
  338. """));
  339. }
  340. @Test
  341. public void create_whenRequiredParameterIsMissing_shouldReturnBadRequest() throws Exception {
  342. userSession.logIn().setSystemAdministrator();
  343. mockMvc.perform(
  344. post(GITLAB_CONFIGURATION_ENDPOINT)
  345. .contentType(MediaType.APPLICATION_JSON_VALUE)
  346. .content("""
  347. {
  348. "enabled": true,
  349. "applicationId": "application-id",
  350. "url": "www.url.com",
  351. "synchronizeGroups": true,
  352. "allowedGroups": [
  353. "group1",
  354. "group2"
  355. ],
  356. "provisioningType": "AUTO_PROVISIONING",
  357. "allowUsersToSignUp": true
  358. }
  359. """))
  360. .andExpectAll(
  361. status().isBadRequest(),
  362. content().json(
  363. "{\"message\":\"Value {} for field secret was rejected. Error: must not be empty.\"}"));
  364. }
  365. @Test
  366. public void delete_whenUserIsNotAdministrator_shouldReturnForbidden() throws Exception {
  367. userSession.logIn().setNonSystemAdministrator();
  368. mockMvc.perform(
  369. delete(GITLAB_CONFIGURATION_ENDPOINT + "/existing-id"))
  370. .andExpectAll(
  371. status().isForbidden(),
  372. content().json("{\"message\":\"Insufficient privileges\"}"));
  373. }
  374. @Test
  375. public void delete_whenConfigIsDeleted_returnsNoContent() throws Exception {
  376. userSession.logIn().setSystemAdministrator();
  377. mockMvc.perform(
  378. delete(GITLAB_CONFIGURATION_ENDPOINT + "/existing-id"))
  379. .andExpectAll(
  380. status().isNoContent());
  381. verify(gitlabConfigurationService).deleteConfiguration("existing-id");
  382. }
  383. @Test
  384. public void delete_whenConfigNotFound_returnsNotFound() throws Exception {
  385. userSession.logIn().setSystemAdministrator();
  386. doThrow(new NotFoundException("Not found")).when(gitlabConfigurationService).deleteConfiguration("not-existing");
  387. mockMvc.perform(
  388. delete(GITLAB_CONFIGURATION_ENDPOINT + "/not-existing"))
  389. .andExpectAll(
  390. status().isNotFound(),
  391. content().json("{\"message\":\"Not found\"}"));
  392. }
  393. }