]> source.dussan.org Git - sonarqube.git/blob
2a1821c2165b0efad780e9df486d60f8b38f8c84
[sonarqube.git] /
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.common.almsettings.github;
21
22 import java.util.Map;
23 import java.util.Optional;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26 import org.sonar.alm.client.github.GithubGlobalSettingsValidator;
27 import org.sonar.alm.client.github.GithubPermissionConverter;
28 import org.sonar.api.server.ServerSide;
29 import org.sonar.auth.github.AppInstallationToken;
30 import org.sonar.auth.github.GitHubSettings;
31 import org.sonar.auth.github.GithubAppConfiguration;
32 import org.sonar.auth.github.client.GithubApplicationClient;
33 import org.sonar.auth.github.security.AccessToken;
34 import org.sonar.auth.github.security.UserAccessToken;
35 import org.sonar.db.DbClient;
36 import org.sonar.db.DbSession;
37 import org.sonar.db.alm.pat.AlmPatDto;
38 import org.sonar.db.alm.setting.ALM;
39 import org.sonar.db.alm.setting.AlmSettingDto;
40 import org.sonar.server.common.almintegration.ProjectKeyGenerator;
41 import org.sonar.server.common.almsettings.DevOpsProjectCreator;
42 import org.sonar.server.common.almsettings.DevOpsProjectCreatorFactory;
43 import org.sonar.server.common.almsettings.DevOpsProjectDescriptor;
44 import org.sonar.server.common.permission.PermissionUpdater;
45 import org.sonar.server.common.permission.UserPermissionChange;
46 import org.sonar.server.common.project.ProjectCreator;
47 import org.sonar.server.exceptions.BadConfigurationException;
48 import org.sonar.server.management.ManagedProjectService;
49 import org.sonar.server.permission.PermissionService;
50 import org.sonar.server.user.UserSession;
51
52 import static java.lang.String.format;
53 import static java.util.Objects.requireNonNull;
54 import static org.sonar.core.ce.CeTaskCharacteristics.DEVOPS_PLATFORM_PROJECT_IDENTIFIER;
55 import static org.sonar.core.ce.CeTaskCharacteristics.DEVOPS_PLATFORM_URL;
56
57 @ServerSide
58 public class GithubProjectCreatorFactory implements DevOpsProjectCreatorFactory {
59   private static final Logger LOG = LoggerFactory.getLogger(GithubProjectCreatorFactory.class);
60
61   private final DbClient dbClient;
62   private final GithubGlobalSettingsValidator githubGlobalSettingsValidator;
63   private final GithubApplicationClient githubApplicationClient;
64   private final ProjectKeyGenerator projectKeyGenerator;
65   private final ProjectCreator projectCreator;
66   private final UserSession userSession;
67   private final GitHubSettings gitHubSettings;
68   private final GithubPermissionConverter githubPermissionConverter;
69   private final PermissionUpdater<UserPermissionChange> permissionUpdater;
70   private final PermissionService permissionService;
71   private final ManagedProjectService managedProjectService;
72
73   public GithubProjectCreatorFactory(DbClient dbClient, GithubGlobalSettingsValidator githubGlobalSettingsValidator,
74     GithubApplicationClient githubApplicationClient, ProjectKeyGenerator projectKeyGenerator, UserSession userSession,
75     ProjectCreator projectCreator, GitHubSettings gitHubSettings, GithubPermissionConverter githubPermissionConverter,
76     PermissionUpdater<UserPermissionChange> permissionUpdater, PermissionService permissionService, ManagedProjectService managedProjectService) {
77     this.dbClient = dbClient;
78     this.githubGlobalSettingsValidator = githubGlobalSettingsValidator;
79     this.githubApplicationClient = githubApplicationClient;
80     this.projectKeyGenerator = projectKeyGenerator;
81     this.userSession = userSession;
82     this.projectCreator = projectCreator;
83     this.gitHubSettings = gitHubSettings;
84     this.githubPermissionConverter = githubPermissionConverter;
85     this.permissionUpdater = permissionUpdater;
86     this.permissionService = permissionService;
87     this.managedProjectService = managedProjectService;
88   }
89
90   @Override
91   public Optional<DevOpsProjectCreator> getDevOpsProjectCreator(DbSession dbSession, Map<String, String> characteristics) {
92     String githubApiUrl = characteristics.get(DEVOPS_PLATFORM_URL);
93     String githubRepository = characteristics.get(DEVOPS_PLATFORM_PROJECT_IDENTIFIER);
94     if (githubApiUrl == null || githubRepository == null) {
95       return Optional.empty();
96     }
97     DevOpsProjectDescriptor devOpsProjectDescriptor = new DevOpsProjectDescriptor(ALM.GITHUB, githubApiUrl, githubRepository);
98
99     return dbClient.almSettingDao().selectByAlm(dbSession, ALM.GITHUB).stream()
100       .filter(almSettingDto -> devOpsProjectDescriptor.url().equals(almSettingDto.getUrl()))
101       .map(almSettingDto -> findInstallationIdAndCreateDevOpsProjectCreator(devOpsProjectDescriptor, almSettingDto))
102       .flatMap(Optional::stream)
103       .findFirst();
104
105   }
106
107   private Optional<DevOpsProjectCreator> findInstallationIdAndCreateDevOpsProjectCreator(DevOpsProjectDescriptor devOpsProjectDescriptor,
108     AlmSettingDto almSettingDto) {
109     GithubAppConfiguration githubAppConfiguration = githubGlobalSettingsValidator.validate(almSettingDto);
110     return findInstallationIdToAccessRepo(githubAppConfiguration, devOpsProjectDescriptor.projectIdentifier())
111       .map(installationId -> generateAppInstallationToken(githubAppConfiguration, installationId))
112       .map(appInstallationToken -> createGithubProjectCreator(devOpsProjectDescriptor, almSettingDto, appInstallationToken));
113   }
114
115   private GithubProjectCreator createGithubProjectCreator(DevOpsProjectDescriptor devOpsProjectDescriptor, AlmSettingDto almSettingDto,
116     AppInstallationToken appInstallationToken) {
117     LOG.info("DevOps configuration {} auto-detected for project {}", almSettingDto.getKey(), devOpsProjectDescriptor.projectIdentifier());
118     Optional<AppInstallationToken> authAppInstallationToken = getAuthAppInstallationTokenIfNecessary(devOpsProjectDescriptor);
119
120     GithubProjectCreationParameters githubProjectCreationParameters = new GithubProjectCreationParameters(devOpsProjectDescriptor, almSettingDto, userSession, appInstallationToken,
121       authAppInstallationToken.orElse(null));
122     return new GithubProjectCreator(dbClient, githubApplicationClient, githubPermissionConverter, projectKeyGenerator, permissionUpdater, permissionService,
123       managedProjectService, projectCreator, githubProjectCreationParameters, gitHubSettings);
124   }
125
126   @Override
127   public Optional<DevOpsProjectCreator> getDevOpsProjectCreator(AlmSettingDto almSettingDto,
128     DevOpsProjectDescriptor devOpsProjectDescriptor) {
129     if (almSettingDto.getAlm() != ALM.GITHUB) {
130       return Optional.empty();
131     }
132     try (DbSession dbSession = dbClient.openSession(false)) {
133       AccessToken accessToken = getAccessToken(dbSession, almSettingDto);
134       Optional<AppInstallationToken> authAppInstallationToken = getAuthAppInstallationTokenIfNecessary(devOpsProjectDescriptor);
135       GithubProjectCreationParameters githubProjectCreationParameters = new GithubProjectCreationParameters(devOpsProjectDescriptor, almSettingDto, userSession, accessToken,
136         authAppInstallationToken.orElse(null));
137       GithubProjectCreator githubProjectCreator = new GithubProjectCreator(dbClient, githubApplicationClient, githubPermissionConverter, projectKeyGenerator, permissionUpdater,
138         permissionService, managedProjectService, this.projectCreator, githubProjectCreationParameters, gitHubSettings);
139       return Optional.of(githubProjectCreator);
140     }
141   }
142
143   private AccessToken getAccessToken(DbSession dbSession, AlmSettingDto almSettingDto) {
144     String userUuid = requireNonNull(userSession.getUuid(), "User UUID cannot be null.");
145     return dbClient.almPatDao().selectByUserAndAlmSetting(dbSession, userUuid, almSettingDto)
146       .map(AlmPatDto::getPersonalAccessToken)
147       .map(UserAccessToken::new)
148       .orElseThrow(() -> new IllegalArgumentException("No personal access token found"));
149   }
150
151   private Optional<AppInstallationToken> getAuthAppInstallationTokenIfNecessary(DevOpsProjectDescriptor devOpsProjectDescriptor) {
152     if (gitHubSettings.isProvisioningEnabled()) {
153       GithubAppConfiguration githubAppConfiguration = new GithubAppConfiguration(Long.parseLong(gitHubSettings.appId()), gitHubSettings.privateKey(), gitHubSettings.apiURL());
154       long installationId = findInstallationIdToAccessRepo(githubAppConfiguration, devOpsProjectDescriptor.projectIdentifier())
155         .orElseThrow(() -> new BadConfigurationException("PROJECT",
156           format("GitHub auto-provisioning is activated. However the repo %s is not in the scope of the authentication application. "
157             + "The permissions can't be checked, and the project can not be created.",
158             devOpsProjectDescriptor.projectIdentifier())));
159       return Optional.of(generateAppInstallationToken(githubAppConfiguration, installationId));
160     }
161     return Optional.empty();
162   }
163
164   private Optional<Long> findInstallationIdToAccessRepo(GithubAppConfiguration githubAppConfiguration, String repositoryKey) {
165     return githubApplicationClient.getInstallationId(githubAppConfiguration, repositoryKey);
166   }
167
168   private AppInstallationToken generateAppInstallationToken(GithubAppConfiguration githubAppConfiguration, long installationId) {
169     return githubApplicationClient.createAppInstallationToken(githubAppConfiguration, installationId)
170       .orElseThrow(() -> new IllegalStateException(format("Error while generating token for GitHub Api Url %s (installation id: %s)",
171         githubAppConfiguration.getApiEndpoint(), installationId)));
172   }
173
174 }