]> source.dussan.org Git - sonarqube.git/blob
90890b477fccd2c9ae33ef8ea8a598622820991f
[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.Set;
23 import javax.annotation.CheckForNull;
24 import org.sonar.alm.client.github.GithubPermissionConverter;
25 import org.sonar.api.web.UserRole;
26 import org.sonar.auth.DevOpsPlatformSettings;
27 import org.sonar.auth.github.AppInstallationToken;
28 import org.sonar.auth.github.GsonRepositoryCollaborator;
29 import org.sonar.auth.github.GsonRepositoryPermissions;
30 import org.sonar.auth.github.GsonRepositoryTeam;
31 import org.sonar.auth.github.client.GithubApplicationClient;
32 import org.sonar.db.DbClient;
33 import org.sonar.db.provisioning.GithubPermissionsMappingDto;
34 import org.sonar.db.user.GroupDto;
35 import org.sonar.server.common.almintegration.ProjectKeyGenerator;
36 import org.sonar.server.common.almsettings.DefaultDevOpsProjectCreator;
37 import org.sonar.server.common.almsettings.DevOpsProjectCreationContext;
38 import org.sonar.server.common.permission.PermissionUpdater;
39 import org.sonar.server.common.permission.UserPermissionChange;
40 import org.sonar.server.common.project.ProjectCreator;
41 import org.sonar.server.management.ManagedProjectService;
42 import org.sonar.server.permission.PermissionService;
43 import org.sonar.server.user.UserSession;
44
45 import static java.util.Objects.requireNonNull;
46 import static java.util.stream.Collectors.toSet;
47 import static org.sonar.api.utils.Preconditions.checkState;
48
49 public class GithubProjectCreator extends DefaultDevOpsProjectCreator {
50
51   private final GithubApplicationClient githubApplicationClient;
52   private final GithubPermissionConverter githubPermissionConverter;
53
54   @CheckForNull
55   private final AppInstallationToken authAppInstallationToken;
56
57   public GithubProjectCreator(DbClient dbClient, DevOpsProjectCreationContext devOpsProjectCreationContext,
58     ProjectKeyGenerator projectKeyGenerator,
59     DevOpsPlatformSettings devOpsPlatformSettings, ProjectCreator projectCreator, PermissionService permissionService, PermissionUpdater<UserPermissionChange> permissionUpdater,
60     ManagedProjectService managedProjectService, GithubApplicationClient githubApplicationClient, GithubPermissionConverter githubPermissionConverter,
61     @CheckForNull AppInstallationToken authAppInstallationToken) {
62     super(dbClient, devOpsProjectCreationContext, projectKeyGenerator, devOpsPlatformSettings, projectCreator, permissionService, permissionUpdater,
63       managedProjectService);
64     this.githubApplicationClient = githubApplicationClient;
65     this.githubPermissionConverter = githubPermissionConverter;
66     this.authAppInstallationToken = authAppInstallationToken;
67   }
68
69   @Override
70   public boolean isScanAllowedUsingPermissionsFromDevopsPlatform() {
71     checkState(authAppInstallationToken != null, "An auth app token is required in case repository permissions checking is necessary.");
72
73     String[] orgaAndRepoTokenified = devOpsProjectCreationContext.fullName().split("/");
74     String organization = orgaAndRepoTokenified[0];
75     String repository = orgaAndRepoTokenified[1];
76
77     Set<GithubPermissionsMappingDto> permissionsMappingDtos = dbClient.githubPermissionsMappingDao().findAll(dbClient.openSession(false));
78
79     boolean userHasDirectAccessToRepo = doesUserHaveScanPermission(organization, repository, permissionsMappingDtos);
80     if (userHasDirectAccessToRepo) {
81       return true;
82     }
83     return doesUserBelongToAGroupWithScanPermission(organization, repository, permissionsMappingDtos);
84   }
85
86   private boolean doesUserHaveScanPermission(String organization, String repository, Set<GithubPermissionsMappingDto> permissionsMappingDtos) {
87     String url = requireNonNull(devOpsProjectCreationContext.almSettingDto().getUrl(), "GitHub url not defined");
88     Set<GsonRepositoryCollaborator> repositoryCollaborators = githubApplicationClient.getRepositoryCollaborators(url, authAppInstallationToken, organization, repository);
89
90     UserSession userSession = devOpsProjectCreationContext.userSession();
91     String externalLogin = userSession.getExternalIdentity().map(UserSession.ExternalIdentity::login).orElse(null);
92     if (externalLogin == null) {
93       return false;
94     }
95     return repositoryCollaborators.stream()
96       .filter(gsonRepositoryCollaborator -> externalLogin.equals(gsonRepositoryCollaborator.name()))
97       .findAny()
98       .map(gsonRepositoryCollaborator -> hasScanPermission(permissionsMappingDtos, gsonRepositoryCollaborator.roleName(), gsonRepositoryCollaborator.permissions()))
99       .orElse(false);
100   }
101
102   private boolean doesUserBelongToAGroupWithScanPermission(String organization, String repository,
103     Set<GithubPermissionsMappingDto> permissionsMappingDtos) {
104     String url = requireNonNull(devOpsProjectCreationContext.almSettingDto().getUrl(), "GitHub url not defined");
105     Set<GsonRepositoryTeam> repositoryTeams = githubApplicationClient.getRepositoryTeams(url, authAppInstallationToken, organization, repository);
106
107     Set<String> groupsOfUser = findUserMembershipOnSonarQube(organization);
108     return repositoryTeams.stream()
109       .filter(team -> hasScanPermission(permissionsMappingDtos, team.permission(), team.permissions()))
110       .map(GsonRepositoryTeam::name)
111       .anyMatch(groupsOfUser::contains);
112   }
113
114   private Set<String> findUserMembershipOnSonarQube(String organization) {
115     return devOpsProjectCreationContext.userSession().getGroups().stream()
116       .map(GroupDto::getName)
117       .filter(groupName -> groupName.contains("/"))
118       .map(name -> name.replaceFirst(organization + "/", ""))
119       .collect(toSet());
120   }
121
122   private boolean hasScanPermission(Set<GithubPermissionsMappingDto> permissionsMappingDtos, String role, GsonRepositoryPermissions permissions) {
123     Set<String> sonarqubePermissions = githubPermissionConverter.toSonarqubeRolesWithFallbackOnRepositoryPermissions(permissionsMappingDtos,
124       role, permissions);
125     return sonarqubePermissions.contains(UserRole.SCAN);
126   }
127
128 }