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.

ReportSubmitter.java 9.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  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.util.ArrayList;
  23. import java.util.List;
  24. import java.util.Map;
  25. import java.util.Optional;
  26. import javax.annotation.Nullable;
  27. import org.sonar.api.resources.Qualifiers;
  28. import org.sonar.api.resources.Scopes;
  29. import org.sonar.api.server.ServerSide;
  30. import org.sonar.api.web.UserRole;
  31. import org.sonar.ce.queue.CeQueue;
  32. import org.sonar.ce.queue.CeTaskSubmit;
  33. import org.sonar.ce.task.CeTask;
  34. import org.sonar.db.DbClient;
  35. import org.sonar.db.DbSession;
  36. import org.sonar.db.ce.CeTaskTypes;
  37. import org.sonar.db.component.BranchDto;
  38. import org.sonar.db.component.ComponentDto;
  39. import org.sonar.db.organization.OrganizationDto;
  40. import org.sonar.db.permission.OrganizationPermission;
  41. import org.sonar.server.component.ComponentUpdater;
  42. import org.sonar.server.component.NewComponent;
  43. import org.sonar.server.exceptions.BadRequestException;
  44. import org.sonar.server.exceptions.NotFoundException;
  45. import org.sonar.server.permission.PermissionTemplateService;
  46. import org.sonar.server.user.UserSession;
  47. import static com.google.common.base.Preconditions.checkArgument;
  48. import static java.lang.String.format;
  49. import static org.apache.commons.lang.StringUtils.defaultIfBlank;
  50. import static org.sonar.server.component.NewComponent.newComponentBuilder;
  51. import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
  52. @ServerSide
  53. public class ReportSubmitter {
  54. private final CeQueue queue;
  55. private final UserSession userSession;
  56. private final ComponentUpdater componentUpdater;
  57. private final PermissionTemplateService permissionTemplateService;
  58. private final DbClient dbClient;
  59. private final BranchSupport branchSupport;
  60. public ReportSubmitter(CeQueue queue, UserSession userSession, ComponentUpdater componentUpdater,
  61. PermissionTemplateService permissionTemplateService, DbClient dbClient, BranchSupport branchSupport) {
  62. this.queue = queue;
  63. this.userSession = userSession;
  64. this.componentUpdater = componentUpdater;
  65. this.permissionTemplateService = permissionTemplateService;
  66. this.dbClient = dbClient;
  67. this.branchSupport = branchSupport;
  68. }
  69. /**
  70. * @throws NotFoundException if the organization with the specified key does not exist
  71. * @throws IllegalArgumentException if the organization with the specified key is not the organization of the specified project (when it already exists in DB)
  72. */
  73. public CeTask submit(String organizationKey, String projectKey, @Nullable String projectName,
  74. Map<String, String> characteristics, InputStream reportInput) {
  75. try (DbSession dbSession = dbClient.openSession(false)) {
  76. OrganizationDto organizationDto = getOrganizationDtoOrFail(dbSession, organizationKey);
  77. BranchSupport.ComponentKey componentKey = branchSupport.createComponentKey(projectKey, characteristics);
  78. Optional<ComponentDto> existingComponent = dbClient.componentDao().selectByKey(dbSession, componentKey.getDbKey());
  79. validateProject(dbSession, existingComponent, projectKey);
  80. ensureOrganizationIsConsistent(existingComponent, organizationDto);
  81. ComponentDto component = existingComponent.orElseGet(() -> createComponent(dbSession, organizationDto, componentKey, projectName));
  82. checkScanPermission(component);
  83. return submitReport(dbSession, reportInput, component, characteristics);
  84. }
  85. }
  86. private void checkScanPermission(ComponentDto project) {
  87. // this is a specific and inconsistent behavior. For legacy reasons, "technical users"
  88. // defined on an organization should be able to analyze a project even if
  89. // they don't have the direct permission on the project.
  90. // That means that dropping the permission on the project does not have any effects
  91. // if user has still the permission on the organization
  92. if (!userSession.hasComponentPermission(UserRole.SCAN, project) &&
  93. !userSession.hasPermission(OrganizationPermission.SCAN, project.getOrganizationUuid())) {
  94. throw insufficientPrivilegesException();
  95. }
  96. }
  97. private OrganizationDto getOrganizationDtoOrFail(DbSession dbSession, String organizationKey) {
  98. return dbClient.organizationDao().selectByKey(dbSession, organizationKey)
  99. .orElseThrow(() -> new NotFoundException(format("Organization with key '%s' does not exist", organizationKey)));
  100. }
  101. private void validateProject(DbSession dbSession, Optional<ComponentDto> project, String rawProjectKey) {
  102. List<String> errors = new ArrayList<>();
  103. if (!project.isPresent()) {
  104. return;
  105. }
  106. ComponentDto component = project.get();
  107. if (!Qualifiers.PROJECT.equals(component.qualifier()) || !Scopes.PROJECT.equals(component.scope())) {
  108. errors.add(format("Component '%s' is not a project", rawProjectKey));
  109. }
  110. if (!component.projectUuid().equals(component.uuid())) {
  111. // Project key is already used as a module of another project
  112. ComponentDto anotherBaseProject = dbClient.componentDao().selectOrFailByUuid(dbSession, component.projectUuid());
  113. errors.add(format("The project '%s' is already defined in SonarQube but as a module of project '%s'. "
  114. + "If you really want to stop directly analysing project '%s', please first delete it from SonarQube and then relaunch the analysis of project '%s'.",
  115. rawProjectKey, anotherBaseProject.getKey(), anotherBaseProject.getKey(), rawProjectKey));
  116. }
  117. if (!errors.isEmpty()) {
  118. throw BadRequestException.create(errors);
  119. }
  120. }
  121. private static void ensureOrganizationIsConsistent(Optional<ComponentDto> project, OrganizationDto organizationDto) {
  122. if (project.isPresent()) {
  123. checkArgument(project.get().getOrganizationUuid().equals(organizationDto.getUuid()),
  124. "Organization of component with key '%s' does not match specified organization '%s'",
  125. project.get().getDbKey(), organizationDto.getKey());
  126. }
  127. }
  128. private ComponentDto createComponent(DbSession dbSession, OrganizationDto organization, BranchSupport.ComponentKey componentKey, @Nullable String projectName) {
  129. if (componentKey.isMainBranch()) {
  130. ComponentDto project = createProject(dbSession, organization, componentKey, projectName);
  131. componentUpdater.commitAndIndex(dbSession, project);
  132. return project;
  133. }
  134. Optional<ComponentDto> existingMainComponent = dbClient.componentDao().selectByKey(dbSession, componentKey.getKey());
  135. ComponentDto mainComponentDto = existingMainComponent
  136. .orElseGet(() -> createProject(dbSession, organization, componentKey.getMainBranchComponentKey(), projectName));
  137. BranchDto mainComponentBranchDto = dbClient.branchDao().selectByUuid(dbSession, mainComponentDto.uuid())
  138. .orElseThrow(() -> new IllegalStateException("Branch of main component does not exist"));
  139. ComponentDto branchComponent = branchSupport.createBranchComponent(dbSession, componentKey, organization, mainComponentDto, mainComponentBranchDto);
  140. if (existingMainComponent.isPresent()) {
  141. dbSession.commit();
  142. } else {
  143. componentUpdater.commitAndIndex(dbSession, mainComponentDto);
  144. }
  145. return branchComponent;
  146. }
  147. private ComponentDto createProject(DbSession dbSession, OrganizationDto organization, BranchSupport.ComponentKey componentKey,
  148. @Nullable String projectName) {
  149. userSession.checkPermission(OrganizationPermission.PROVISION_PROJECTS, organization);
  150. Integer userId = userSession.getUserId();
  151. boolean wouldCurrentUserHaveScanPermission = permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(
  152. dbSession, organization.getUuid(), userId, componentKey.getDbKey());
  153. if (!wouldCurrentUserHaveScanPermission) {
  154. throw insufficientPrivilegesException();
  155. }
  156. boolean newProjectPrivate = dbClient.organizationDao().getNewProjectPrivate(dbSession, organization);
  157. NewComponent newProject = newComponentBuilder()
  158. .setOrganizationUuid(organization.getUuid())
  159. .setKey(componentKey.getKey())
  160. .setName(defaultIfBlank(projectName, componentKey.getKey()))
  161. .setQualifier(Qualifiers.PROJECT)
  162. .setPrivate(newProjectPrivate)
  163. .build();
  164. return componentUpdater.createWithoutCommit(dbSession, newProject, userId);
  165. }
  166. private CeTask submitReport(DbSession dbSession, InputStream reportInput, ComponentDto project, Map<String, String> characteristics) {
  167. CeTaskSubmit.Builder submit = queue.prepareSubmit();
  168. // the report file must be saved before submitting the task
  169. dbClient.ceTaskInputDao().insert(dbSession, submit.getUuid(), reportInput);
  170. dbSession.commit();
  171. submit.setType(CeTaskTypes.REPORT);
  172. submit.setComponent(CeTaskSubmit.Component.fromDto(project));
  173. submit.setSubmitterUuid(userSession.getUuid());
  174. submit.setCharacteristics(characteristics);
  175. return queue.submit(submit.build());
  176. }
  177. }