123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- /*
- * SonarQube
- * Copyright (C) 2009-2024 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
- package org.sonar.server.ce.queue;
-
- import java.io.InputStream;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Map;
- import java.util.Optional;
- import javax.annotation.Nullable;
- import org.sonar.api.resources.Qualifiers;
- import org.sonar.api.resources.Scopes;
- import org.sonar.api.server.ServerSide;
- import org.sonar.api.web.UserRole;
- import org.sonar.ce.queue.CeQueue;
- import org.sonar.ce.queue.CeTaskSubmit;
- import org.sonar.ce.task.CeTask;
- import org.sonar.db.DbClient;
- import org.sonar.db.DbSession;
- import org.sonar.db.ce.CeTaskTypes;
- import org.sonar.db.component.BranchDto;
- import org.sonar.db.component.ComponentDto;
- import org.sonar.db.permission.GlobalPermission;
- import org.sonar.server.almsettings.ws.DevOpsProjectCreator;
- import org.sonar.server.almsettings.ws.DevOpsProjectCreatorFactory;
- import org.sonar.server.component.ComponentCreationData;
- import org.sonar.server.component.ComponentUpdater;
- import org.sonar.server.exceptions.BadRequestException;
- import org.sonar.server.management.ManagedInstanceService;
- import org.sonar.server.permission.PermissionTemplateService;
- import org.sonar.server.project.ws.ProjectCreator;
- import org.sonar.server.user.UserSession;
-
- import static java.lang.String.format;
- import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
- import static org.sonar.db.permission.GlobalPermission.SCAN;
- import static org.sonar.db.project.CreationMethod.SCANNER_API;
- import static org.sonar.db.project.CreationMethod.SCANNER_API_DEVOPS_AUTO_CONFIG;
- import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
-
- @ServerSide
- public class ReportSubmitter {
-
- private final CeQueue queue;
- private final UserSession userSession;
-
- private final ProjectCreator projectCreator;
- private final ComponentUpdater componentUpdater;
- private final PermissionTemplateService permissionTemplateService;
- private final DbClient dbClient;
- private final BranchSupport branchSupport;
- private final DevOpsProjectCreatorFactory devOpsProjectCreatorFactory;
- private final ManagedInstanceService managedInstanceService;
-
- public ReportSubmitter(CeQueue queue, UserSession userSession, ProjectCreator projectCreator, ComponentUpdater componentUpdater,
- PermissionTemplateService permissionTemplateService, DbClient dbClient, BranchSupport branchSupport,
- DevOpsProjectCreatorFactory devOpsProjectCreatorFactory, ManagedInstanceService managedInstanceService) {
- this.queue = queue;
- this.userSession = userSession;
- this.projectCreator = projectCreator;
- this.componentUpdater = componentUpdater;
- this.permissionTemplateService = permissionTemplateService;
- this.dbClient = dbClient;
- this.branchSupport = branchSupport;
- this.devOpsProjectCreatorFactory = devOpsProjectCreatorFactory;
- this.managedInstanceService = managedInstanceService;
- }
-
- public CeTask submit(String projectKey, @Nullable String projectName, Map<String, String> characteristics, InputStream reportInput) {
- try (DbSession dbSession = dbClient.openSession(false)) {
- ComponentCreationData componentCreationData = null;
- // Note: when the main branch is analyzed, the characteristics may or may not have the branch name, so componentKey#isMainBranch is not
- // reliable!
- BranchSupport.ComponentKey componentKey = branchSupport.createComponentKey(projectKey, characteristics);
- Optional<ComponentDto> mainBranchComponentOpt = dbClient.componentDao().selectByKey(dbSession, componentKey.getKey());
- ComponentDto mainBranchComponent;
-
- if (mainBranchComponentOpt.isPresent()) {
- mainBranchComponent = mainBranchComponentOpt.get();
- validateProject(dbSession, mainBranchComponent, projectKey);
- } else {
- componentCreationData = createProject(projectKey, projectName, characteristics, dbSession, componentKey);
- mainBranchComponent = componentCreationData.mainBranchComponent();
- }
-
- BranchDto mainBranch = dbClient.branchDao().selectByUuid(dbSession, mainBranchComponent.branchUuid())
- .orElseThrow(() -> new IllegalStateException("Couldn't find the main branch of the project"));
- ComponentDto branchComponent;
- if (isMainBranch(componentKey, mainBranch)) {
- branchComponent = mainBranchComponent;
- } else if (componentKey.getBranchName().isPresent()) {
- branchComponent = dbClient.componentDao().selectByKeyAndBranch(dbSession, componentKey.getKey(), componentKey.getBranchName().get())
- .orElseGet(() -> branchSupport.createBranchComponent(dbSession, componentKey, mainBranchComponent, mainBranch));
- } else {
- branchComponent = dbClient.componentDao().selectByKeyAndPullRequest(dbSession, componentKey.getKey(), componentKey.getPullRequestKey().get())
- .orElseGet(() -> branchSupport.createBranchComponent(dbSession, componentKey, mainBranchComponent, mainBranch));
- }
-
- if (componentCreationData != null) {
- componentUpdater.commitAndIndex(dbSession, componentCreationData);
- } else {
- dbSession.commit();
- }
-
- checkScanPermission(branchComponent);
- return submitReport(dbSession, reportInput, branchComponent, mainBranch, characteristics);
- }
- }
-
- private static boolean isMainBranch(BranchSupport.ComponentKey componentKey, BranchDto mainBranch) {
- if (componentKey.isMainBranch()) {
- return true;
- }
-
- return componentKey.getBranchName().isPresent() && componentKey.getBranchName().get().equals(mainBranch.getKey());
- }
-
- private void checkScanPermission(ComponentDto project) {
- // this is a specific and inconsistent behavior. For legacy reasons, "technical users"
- // defined with global scan permission should be able to analyze a project even if
- // they don't have the direct permission on the project.
- // That means that dropping the permission on the project does not have any effects
- // if user has still the global permission
- if (!userSession.hasComponentPermission(UserRole.SCAN, project) && !userSession.hasPermission(GlobalPermission.SCAN)) {
- throw insufficientPrivilegesException();
- }
- }
-
- private void validateProject(DbSession dbSession, ComponentDto component, String rawProjectKey) {
- List<String> errors = new ArrayList<>();
-
- if (!Qualifiers.PROJECT.equals(component.qualifier()) || !Scopes.PROJECT.equals(component.scope())) {
- errors.add(format("Component '%s' is not a project", rawProjectKey));
- }
- if (!component.branchUuid().equals(component.uuid())) {
- // Project key is already used as a module of another project
- ComponentDto anotherBaseProject = dbClient.componentDao().selectOrFailByUuid(dbSession, component.branchUuid());
- errors.add(format("The project '%s' is already defined in SonarQube but as a module of project '%s'. "
- + "If you really want to stop directly analysing project '%s', please first delete it from SonarQube and then relaunch the analysis of project '%s'.",
- rawProjectKey, anotherBaseProject.getKey(), anotherBaseProject.getKey(), rawProjectKey));
- }
- if (!errors.isEmpty()) {
- throw BadRequestException.create(errors);
- }
- }
-
- private ComponentCreationData createProject(String projectKey, @Nullable String projectName, Map<String, String> characteristics,
- DbSession dbSession, BranchSupport.ComponentKey componentKey) {
- userSession.checkPermission(GlobalPermission.PROVISION_PROJECTS);
-
- DevOpsProjectCreator devOpsProjectCreator = devOpsProjectCreatorFactory.getDevOpsProjectCreator(dbSession, characteristics).orElse(null);
-
- throwIfCurrentUserWouldNotHaveScanPermission(projectKey, dbSession, devOpsProjectCreator);
-
- if (devOpsProjectCreator != null) {
- return devOpsProjectCreator.createProjectAndBindToDevOpsPlatform(dbSession, SCANNER_API_DEVOPS_AUTO_CONFIG, false, projectKey, projectName);
- }
- return projectCreator.createProject(dbSession, componentKey.getKey(), defaultIfBlank(projectName, projectKey), null, SCANNER_API);
- }
-
- private void throwIfCurrentUserWouldNotHaveScanPermission(String projectKey, DbSession dbSession, @Nullable DevOpsProjectCreator devOpsProjectCreator) {
- if (!wouldCurrentUserHaveScanPermission(projectKey, dbSession, devOpsProjectCreator)) {
- throw insufficientPrivilegesException();
- }
- }
-
- private boolean wouldCurrentUserHaveScanPermission(String projectKey, DbSession dbSession, @Nullable DevOpsProjectCreator devOpsProjectCreator) {
- if (userSession.hasPermission(SCAN)) {
- return true;
- }
- if (managedInstanceService.isInstanceExternallyManaged() && devOpsProjectCreator != null) {
- return devOpsProjectCreator.isScanAllowedUsingPermissionsFromDevopsPlatform();
- }
- return permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(dbSession, userSession.getUuid(), projectKey);
- }
-
- private CeTask submitReport(DbSession dbSession, InputStream reportInput, ComponentDto branch, BranchDto mainBranch, Map<String, String> characteristics) {
- CeTaskSubmit.Builder submit = queue.prepareSubmit();
-
- // the report file must be saved before submitting the task
- dbClient.ceTaskInputDao().insert(dbSession, submit.getUuid(), reportInput);
- dbSession.commit();
- submit.setType(CeTaskTypes.REPORT);
- submit.setComponent(CeTaskSubmit.Component.fromDto(branch.uuid(), mainBranch.getProjectUuid()));
- submit.setSubmitterUuid(userSession.getUuid());
- submit.setCharacteristics(characteristics);
- return queue.submit(submit.build());
- }
-
- }
|