123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- /*
- * SonarQube
- * Copyright (C) 2009-2020 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.organization.OrganizationDto;
- import org.sonar.db.permission.OrganizationPermission;
- import org.sonar.server.component.ComponentUpdater;
- import org.sonar.server.component.NewComponent;
- import org.sonar.server.exceptions.BadRequestException;
- import org.sonar.server.exceptions.NotFoundException;
- import org.sonar.server.permission.PermissionTemplateService;
- import org.sonar.server.user.UserSession;
-
- import static com.google.common.base.Preconditions.checkArgument;
- import static java.lang.String.format;
- import static org.apache.commons.lang.StringUtils.defaultIfBlank;
- import static org.sonar.server.component.NewComponent.newComponentBuilder;
- import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
-
- @ServerSide
- public class ReportSubmitter {
-
- private final CeQueue queue;
- private final UserSession userSession;
- private final ComponentUpdater componentUpdater;
- private final PermissionTemplateService permissionTemplateService;
- private final DbClient dbClient;
- private final BranchSupport branchSupport;
-
- public ReportSubmitter(CeQueue queue, UserSession userSession, ComponentUpdater componentUpdater,
- PermissionTemplateService permissionTemplateService, DbClient dbClient, BranchSupport branchSupport) {
- this.queue = queue;
- this.userSession = userSession;
- this.componentUpdater = componentUpdater;
- this.permissionTemplateService = permissionTemplateService;
- this.dbClient = dbClient;
- this.branchSupport = branchSupport;
- }
-
- /**
- * @throws NotFoundException if the organization with the specified key does not exist
- * @throws IllegalArgumentException if the organization with the specified key is not the organization of the specified project (when it already exists in DB)
- */
- public CeTask submit(String organizationKey, String projectKey, @Nullable String projectName, Map<String, String> characteristics, InputStream reportInput) {
- try (DbSession dbSession = dbClient.openSession(false)) {
- boolean projectCreated = false;
- OrganizationDto organizationDto = getOrganizationDtoOrFail(dbSession, organizationKey);
- // 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);
- ensureOrganizationIsConsistent(mainBranchComponent, organizationDto);
- } else {
- mainBranchComponent = createProject(dbSession, organizationDto, componentKey.getMainBranchComponentKey(), projectName);
- projectCreated = true;
- }
-
- BranchDto mainBranch = dbClient.branchDao().selectByUuid(dbSession, mainBranchComponent.projectUuid())
- .orElseThrow(() -> new IllegalStateException("Couldn't find the main branch of the project"));
- ComponentDto branchComponent;
- if (isMainBranch(componentKey, mainBranch)) {
- branchComponent = mainBranchComponent;
- } else {
- branchComponent = dbClient.componentDao().selectByKey(dbSession, componentKey.getDbKey())
- .orElseGet(() -> branchSupport.createBranchComponent(dbSession, componentKey, organizationDto, mainBranchComponent, mainBranch));
- }
-
- if (projectCreated) {
- componentUpdater.commitAndIndex(dbSession, mainBranchComponent);
- } else {
- dbSession.commit();
- }
-
- checkScanPermission(branchComponent);
- return submitReport(dbSession, reportInput, branchComponent, 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 on an organization 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 permission on the organization
- if (!userSession.hasComponentPermission(UserRole.SCAN, project) && !userSession.hasPermission(OrganizationPermission.SCAN)) {
- throw insufficientPrivilegesException();
- }
- }
-
- private OrganizationDto getOrganizationDtoOrFail(DbSession dbSession, String organizationKey) {
- return dbClient.organizationDao().selectByKey(dbSession, organizationKey)
- .orElseThrow(() -> new NotFoundException(format("Organization with key '%s' does not exist", organizationKey)));
- }
-
- 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.projectUuid().equals(component.uuid())) {
- // Project key is already used as a module of another project
- ComponentDto anotherBaseProject = dbClient.componentDao().selectOrFailByUuid(dbSession, component.projectUuid());
- 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 static void ensureOrganizationIsConsistent(ComponentDto project, OrganizationDto organizationDto) {
- checkArgument(project.getOrganizationUuid().equals(organizationDto.getUuid()),
- "Organization of component with key '%s' does not match specified organization '%s'",
- project.getDbKey(), organizationDto.getKey());
- }
-
- private ComponentDto createProject(DbSession dbSession, OrganizationDto organization, BranchSupport.ComponentKey componentKey, @Nullable String projectName) {
- userSession.checkPermission(OrganizationPermission.PROVISION_PROJECTS);
- String userUuid = userSession.getUuid();
-
- boolean wouldCurrentUserHaveScanPermission = permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(
- dbSession, userUuid, componentKey.getDbKey());
- if (!wouldCurrentUserHaveScanPermission) {
- throw insufficientPrivilegesException();
- }
-
- boolean newProjectPrivate = dbClient.organizationDao().getNewProjectPrivate(dbSession, organization);
-
- NewComponent newProject = newComponentBuilder()
- .setOrganizationUuid(organization.getUuid())
- .setKey(componentKey.getKey())
- .setName(defaultIfBlank(projectName, componentKey.getKey()))
- .setQualifier(Qualifiers.PROJECT)
- .setPrivate(newProjectPrivate)
- .build();
- return componentUpdater.createWithoutCommit(dbSession, newProject, userUuid, c -> {
- });
- }
-
- private CeTask submitReport(DbSession dbSession, InputStream reportInput, ComponentDto project, 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(project));
- submit.setSubmitterUuid(userSession.getUuid());
- submit.setCharacteristics(characteristics);
- return queue.submit(submit.build());
- }
-
- }
|