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.

ReportSubmitterTest.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2019 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.ce.queue;
  21. import java.io.InputStream;
  22. import java.nio.charset.StandardCharsets;
  23. import java.util.Map;
  24. import java.util.Random;
  25. import java.util.stream.IntStream;
  26. import org.apache.commons.io.IOUtils;
  27. import org.junit.Before;
  28. import org.junit.Rule;
  29. import org.junit.Test;
  30. import org.junit.rules.ExpectedException;
  31. import org.sonar.api.utils.System2;
  32. import org.sonar.ce.queue.CeQueue;
  33. import org.sonar.ce.queue.CeQueueImpl;
  34. import org.sonar.ce.queue.CeTaskSubmit;
  35. import org.sonar.core.i18n.I18n;
  36. import org.sonar.db.DbSession;
  37. import org.sonar.db.DbTester;
  38. import org.sonar.db.ce.CeTaskTypes;
  39. import org.sonar.db.component.ComponentDto;
  40. import org.sonar.db.component.ComponentTesting;
  41. import org.sonar.db.organization.OrganizationDto;
  42. import org.sonar.db.permission.OrganizationPermission;
  43. import org.sonar.db.user.UserDto;
  44. import org.sonar.server.component.ComponentUpdater;
  45. import org.sonar.server.es.TestProjectIndexers;
  46. import org.sonar.server.exceptions.BadRequestException;
  47. import org.sonar.server.exceptions.ForbiddenException;
  48. import org.sonar.server.exceptions.NotFoundException;
  49. import org.sonar.server.favorite.FavoriteUpdater;
  50. import org.sonar.server.permission.PermissionTemplateService;
  51. import org.sonar.server.tester.UserSessionRule;
  52. import static java.lang.String.format;
  53. import static java.nio.charset.StandardCharsets.UTF_8;
  54. import static java.util.Collections.emptyMap;
  55. import static java.util.stream.IntStream.rangeClosed;
  56. import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
  57. import static org.assertj.core.api.Assertions.assertThat;
  58. import static org.junit.Assert.fail;
  59. import static org.mockito.ArgumentMatchers.any;
  60. import static org.mockito.ArgumentMatchers.argThat;
  61. import static org.mockito.ArgumentMatchers.eq;
  62. import static org.mockito.Mockito.mock;
  63. import static org.mockito.Mockito.verify;
  64. import static org.mockito.Mockito.verifyZeroInteractions;
  65. import static org.mockito.Mockito.when;
  66. import static org.sonar.core.permission.GlobalPermissions.SCAN_EXECUTION;
  67. import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
  68. import static org.sonar.db.component.ComponentTesting.newModuleDto;
  69. import static org.sonar.db.permission.OrganizationPermission.PROVISION_PROJECTS;
  70. import static org.sonar.db.permission.OrganizationPermission.SCAN;
  71. public class ReportSubmitterTest {
  72. private static final String PROJECT_KEY = "MY_PROJECT";
  73. private static final String PROJECT_UUID = "P1";
  74. private static final String PROJECT_NAME = "My Project";
  75. private static final String TASK_UUID = "TASK_1";
  76. @Rule
  77. public ExpectedException expectedException = ExpectedException.none();
  78. @Rule
  79. public UserSessionRule userSession = UserSessionRule.standalone();
  80. @Rule
  81. public DbTester db = DbTester.create();
  82. private String defaultOrganizationKey;
  83. private String defaultOrganizationUuid;
  84. private CeQueue queue = mock(CeQueueImpl.class);
  85. private TestProjectIndexers projectIndexers = new TestProjectIndexers();
  86. private PermissionTemplateService permissionTemplateService = mock(PermissionTemplateService.class);
  87. private ComponentUpdater componentUpdater = new ComponentUpdater(db.getDbClient(), mock(I18n.class), mock(System2.class), permissionTemplateService,
  88. new FavoriteUpdater(db.getDbClient()), projectIndexers);
  89. private BranchSupport ossEditionBranchSupport = new BranchSupport();
  90. private ReportSubmitter underTest = new ReportSubmitter(queue, userSession, componentUpdater, permissionTemplateService, db.getDbClient(), ossEditionBranchSupport);
  91. @Before
  92. public void setUp() throws Exception {
  93. defaultOrganizationKey = db.getDefaultOrganization().getKey();
  94. defaultOrganizationUuid = db.getDefaultOrganization().getUuid();
  95. }
  96. @Test
  97. public void submit_with_characteristics_fails_with_ISE_when_no_branch_support_delegate() {
  98. userSession
  99. .addPermission(OrganizationPermission.SCAN, db.getDefaultOrganization().getUuid())
  100. .addPermission(PROVISION_PROJECTS, db.getDefaultOrganization());
  101. mockSuccessfulPrepareSubmitCall();
  102. when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(), eq(defaultOrganizationUuid), any(), eq(PROJECT_KEY)))
  103. .thenReturn(true);
  104. Map<String, String> nonEmptyCharacteristics = IntStream.range(0, 1 + new Random().nextInt(5))
  105. .boxed()
  106. .collect(uniqueIndex(i -> randomAlphabetic(i + 10), i -> randomAlphabetic(i + 20)));
  107. InputStream reportInput = IOUtils.toInputStream("{binary}", UTF_8);
  108. expectedException.expect(IllegalStateException.class);
  109. expectedException.expectMessage("Current edition does not support branch feature");
  110. underTest.submit(defaultOrganizationKey, PROJECT_KEY, PROJECT_NAME, nonEmptyCharacteristics, reportInput);
  111. }
  112. @Test
  113. public void submit_stores_report() {
  114. userSession
  115. .addPermission(OrganizationPermission.SCAN, db.getDefaultOrganization().getUuid())
  116. .addPermission(PROVISION_PROJECTS, db.getDefaultOrganization());
  117. mockSuccessfulPrepareSubmitCall();
  118. when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(), eq(defaultOrganizationUuid), any(), eq(PROJECT_KEY)))
  119. .thenReturn(true);
  120. underTest.submit(defaultOrganizationKey, PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}", UTF_8));
  121. verifyReportIsPersisted(TASK_UUID);
  122. }
  123. @Test
  124. public void submit_a_report_on_existing_project() {
  125. ComponentDto project = db.components().insertPrivateProject(db.getDefaultOrganization());
  126. UserDto user = db.users().insertUser();
  127. userSession.logIn(user).addProjectPermission(SCAN_EXECUTION, project);
  128. mockSuccessfulPrepareSubmitCall();
  129. underTest.submit(defaultOrganizationKey, project.getDbKey(), project.name(), emptyMap(), IOUtils.toInputStream("{binary}", StandardCharsets.UTF_8));
  130. verifyReportIsPersisted(TASK_UUID);
  131. verifyZeroInteractions(permissionTemplateService);
  132. verify(queue).submit(argThat(submit -> submit.getType().equals(CeTaskTypes.REPORT)
  133. && submit.getComponent().filter(cpt -> cpt.getUuid().equals(project.uuid()) && cpt.getMainComponentUuid().equals(project.uuid())).isPresent()
  134. && submit.getSubmitterUuid().equals(user.getUuid())
  135. && submit.getUuid().equals(TASK_UUID)));
  136. }
  137. @Test
  138. public void provision_project_if_does_not_exist() {
  139. OrganizationDto organization = db.organizations().insert();
  140. userSession
  141. .addPermission(OrganizationPermission.SCAN, organization.getUuid())
  142. .addPermission(PROVISION_PROJECTS, organization);
  143. mockSuccessfulPrepareSubmitCall();
  144. when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), eq(organization.getUuid()), any(), eq(PROJECT_KEY))).thenReturn(true);
  145. when(permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(any(DbSession.class), any(ComponentDto.class))).thenReturn(true);
  146. underTest.submit(organization.getKey(), PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}"));
  147. ComponentDto createdProject = db.getDbClient().componentDao().selectByKey(db.getSession(), PROJECT_KEY).get();
  148. verifyReportIsPersisted(TASK_UUID);
  149. verify(queue).submit(argThat(submit -> submit.getType().equals(CeTaskTypes.REPORT)
  150. && submit.getComponent().filter(cpt -> cpt.getUuid().equals(createdProject.uuid()) && cpt.getMainComponentUuid().equals(createdProject.uuid())).isPresent()
  151. && submit.getUuid().equals(TASK_UUID)));
  152. }
  153. @Test
  154. public void add_project_as_favorite_when_project_creator_permission_on_permission_template() {
  155. UserDto user = db.users().insertUser();
  156. OrganizationDto organization = db.organizations().insert();
  157. userSession
  158. .logIn(user)
  159. .addPermission(OrganizationPermission.SCAN, organization.getUuid())
  160. .addPermission(PROVISION_PROJECTS, organization);
  161. mockSuccessfulPrepareSubmitCall();
  162. when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), eq(organization.getUuid()), any(), eq(PROJECT_KEY))).thenReturn(true);
  163. when(permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(any(DbSession.class), any(ComponentDto.class))).thenReturn(true);
  164. underTest.submit(organization.getKey(), PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}"));
  165. ComponentDto createdProject = db.getDbClient().componentDao().selectByKey(db.getSession(), PROJECT_KEY).get();
  166. assertThat(db.favorites().hasFavorite(createdProject, user.getId())).isTrue();
  167. }
  168. @Test
  169. public void do_no_add_favorite_when_no_project_creator_permission_on_permission_template() {
  170. userSession
  171. .addPermission(OrganizationPermission.SCAN, db.getDefaultOrganization().getUuid())
  172. .addPermission(PROVISION_PROJECTS, db.getDefaultOrganization());
  173. when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), eq(defaultOrganizationUuid), any(), eq(PROJECT_KEY)))
  174. .thenReturn(true);
  175. when(permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(any(DbSession.class), any(ComponentDto.class))).thenReturn(false);
  176. mockSuccessfulPrepareSubmitCall();
  177. underTest.submit(defaultOrganizationKey, PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}"));
  178. ComponentDto createdProject = db.getDbClient().componentDao().selectByKey(db.getSession(), PROJECT_KEY).get();
  179. assertThat(db.favorites().hasNoFavorite(createdProject)).isTrue();
  180. }
  181. @Test
  182. public void do_no_add_favorite_when_already_100_favorite_projects_and_no_project_creator_permission_on_permission_template() {
  183. UserDto user = db.users().insertUser();
  184. rangeClosed(1, 100).forEach(i -> db.favorites().add(db.components().insertPrivateProject(), user.getId()));
  185. OrganizationDto organization = db.organizations().insert();
  186. userSession
  187. .logIn(user)
  188. .addPermission(OrganizationPermission.SCAN, organization.getUuid())
  189. .addPermission(PROVISION_PROJECTS, organization);
  190. mockSuccessfulPrepareSubmitCall();
  191. when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), eq(organization.getUuid()), any(), eq(PROJECT_KEY))).thenReturn(true);
  192. when(permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(any(DbSession.class), any(ComponentDto.class))).thenReturn(true);
  193. underTest.submit(organization.getKey(), PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}"));
  194. ComponentDto createdProject = db.getDbClient().componentDao().selectByKey(db.getSession(), PROJECT_KEY).get();
  195. assertThat(db.favorites().hasNoFavorite(createdProject)).isTrue();
  196. }
  197. @Test
  198. public void submit_a_report_on_new_project_with_scan_permission_on_organization() {
  199. userSession
  200. .addPermission(OrganizationPermission.SCAN, db.getDefaultOrganization().getUuid())
  201. .addPermission(PROVISION_PROJECTS, db.getDefaultOrganization());
  202. mockSuccessfulPrepareSubmitCall();
  203. when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), eq(defaultOrganizationUuid), any(), eq(PROJECT_KEY)))
  204. .thenReturn(true);
  205. underTest.submit(defaultOrganizationKey, PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}"));
  206. verify(queue).submit(any(CeTaskSubmit.class));
  207. }
  208. @Test
  209. public void user_with_scan_permission_on_organization_is_allowed_to_submit_a_report_on_existing_project() {
  210. OrganizationDto org = db.organizations().insert();
  211. ComponentDto project = db.components().insertPrivateProject(org);
  212. userSession.addPermission(SCAN, org);
  213. mockSuccessfulPrepareSubmitCall();
  214. underTest.submit(org.getKey(), project.getDbKey(), project.name(), emptyMap(), IOUtils.toInputStream("{binary}"));
  215. verify(queue).submit(any(CeTaskSubmit.class));
  216. }
  217. @Test
  218. public void submit_a_report_on_existing_project_with_project_scan_permission() {
  219. ComponentDto project = db.components().insertPrivateProject(db.getDefaultOrganization());
  220. userSession.addProjectPermission(SCAN_EXECUTION, project);
  221. mockSuccessfulPrepareSubmitCall();
  222. underTest.submit(defaultOrganizationKey, project.getDbKey(), project.name(), emptyMap(), IOUtils.toInputStream("{binary}"));
  223. verify(queue).submit(any(CeTaskSubmit.class));
  224. }
  225. @Test
  226. public void fail_with_NotFoundException_if_organization_with_specified_key_does_not_exist() {
  227. expectedException.expect(NotFoundException.class);
  228. expectedException.expectMessage("Organization with key 'fop' does not exist");
  229. underTest.submit("fop", PROJECT_KEY, null, emptyMap(), null /* method will fail before parameter is used */);
  230. }
  231. @Test
  232. public void fail_with_organizationKey_does_not_match_organization_of_specified_component() {
  233. userSession.logIn().setRoot();
  234. OrganizationDto organization = db.organizations().insert();
  235. ComponentDto project = db.components().insertPrivateProject(organization);
  236. mockSuccessfulPrepareSubmitCall();
  237. underTest.submit(organization.getKey(), project.getDbKey(), project.name(), emptyMap(), IOUtils.toInputStream("{binary}"));
  238. }
  239. @Test
  240. public void fail_if_component_is_not_a_project() {
  241. ComponentDto component = db.components().insertPublicPortfolio(db.getDefaultOrganization());
  242. userSession.logIn().addProjectPermission(SCAN_EXECUTION, component);
  243. mockSuccessfulPrepareSubmitCall();
  244. expectedException.expect(BadRequestException.class);
  245. expectedException.expectMessage(format("Component '%s' is not a project", component.getKey()));
  246. underTest.submit(defaultOrganizationKey, component.getDbKey(), component.name(), emptyMap(), IOUtils.toInputStream("{binary}"));
  247. }
  248. @Test
  249. public void fail_if_project_key_already_exists_as_module() {
  250. ComponentDto project = db.components().insertPrivateProject(db.getDefaultOrganization());
  251. ComponentDto module = db.components().insertComponent(newModuleDto(project));
  252. userSession.logIn().addProjectPermission(SCAN_EXECUTION, project);
  253. mockSuccessfulPrepareSubmitCall();
  254. try {
  255. underTest.submit(defaultOrganizationKey, module.getDbKey(), module.name(), emptyMap(), IOUtils.toInputStream("{binary}"));
  256. fail();
  257. } catch (BadRequestException e) {
  258. assertThat(e.errors()).contains(
  259. format("The project '%s' is already defined in SonarQube but as a module of project '%s'. " +
  260. "If you really want to stop directly analysing project '%s', please first delete it from SonarQube and then relaunch the analysis of project '%s'.",
  261. module.getKey(), project.getKey(), project.getKey(), module.getKey()));
  262. }
  263. }
  264. @Test
  265. public void fail_with_forbidden_exception_when_no_scan_permission() {
  266. expectedException.expect(ForbiddenException.class);
  267. underTest.submit(defaultOrganizationKey, PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}"));
  268. }
  269. @Test
  270. public void fail_with_forbidden_exception_on_new_project_when_only_project_scan_permission() {
  271. ComponentDto component = ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization(), PROJECT_UUID);
  272. userSession.addProjectPermission(SCAN_EXECUTION, component);
  273. mockSuccessfulPrepareSubmitCall();
  274. expectedException.expect(ForbiddenException.class);
  275. underTest.submit(defaultOrganizationKey, PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}"));
  276. }
  277. private void verifyReportIsPersisted(String taskUuid) {
  278. assertThat(db.selectFirst("select task_uuid from ce_task_input where task_uuid='" + taskUuid + "'")).isNotNull();
  279. }
  280. private void mockSuccessfulPrepareSubmitCall() {
  281. when(queue.prepareSubmit()).thenReturn(new CeTaskSubmit.Builder(TASK_UUID));
  282. }
  283. }