]> source.dussan.org Git - sonarqube.git/blob
68a4b884ce760e165a59b6786ff1abf0463b61dc
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2023 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.github.permissions;
21
22 import com.google.common.collect.Sets;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26 import java.util.function.Consumer;
27 import org.sonar.api.web.UserRole;
28 import org.sonar.core.util.UuidFactory;
29 import org.sonar.db.DbClient;
30 import org.sonar.db.DbSession;
31 import org.sonar.db.provisioning.GithubPermissionsMappingDao;
32 import org.sonar.db.provisioning.GithubPermissionsMappingDto;
33 import org.sonar.server.exceptions.NotFoundException;
34
35 import static java.util.stream.Collectors.groupingBy;
36 import static java.util.stream.Collectors.toSet;
37 import static org.sonar.api.utils.Preconditions.checkArgument;
38 import static org.sonar.server.common.permission.Operation.ADD;
39 import static org.sonar.server.common.permission.Operation.REMOVE;
40
41 public class GithubPermissionsMappingService {
42   public static final String READ_GITHUB_ROLE = "read";
43   public static final String TRIAGE_GITHUB_ROLE = "triage";
44   public static final String WRITE_GITHUB_ROLE = "write";
45   public static final String MAINTAIN_GITHUB_ROLE = "maintain";
46   public static final String ADMIN_GITHUB_ROLE = "admin";
47
48   private static final Set<String> GITHUB_BASE_ROLES = Set.of(
49     READ_GITHUB_ROLE,
50     TRIAGE_GITHUB_ROLE,
51     WRITE_GITHUB_ROLE,
52     MAINTAIN_GITHUB_ROLE,
53     ADMIN_GITHUB_ROLE);
54
55   private static final Map<String, Consumer<SonarqubePermissions.Builder>> permissionAsStringToSonarqubePermission = Map.of(
56     UserRole.USER, builder -> builder.user(true),
57     UserRole.CODEVIEWER, builder -> builder.codeViewer(true),
58     UserRole.ISSUE_ADMIN, builder -> builder.issueAdmin(true),
59     UserRole.SECURITYHOTSPOT_ADMIN, builder -> builder.securityHotspotAdmin(true),
60     UserRole.ADMIN, builder -> builder.admin(true),
61     UserRole.SCAN, builder -> builder.scan(true));
62
63   private final DbClient dbClient;
64   private final GithubPermissionsMappingDao githubPermissionsMappingDao;
65   private final UuidFactory uuidFactory;
66
67   public GithubPermissionsMappingService(DbClient dbClient, GithubPermissionsMappingDao githubPermissionsMappingDao, UuidFactory uuidFactory) {
68     this.dbClient = dbClient;
69     this.githubPermissionsMappingDao = githubPermissionsMappingDao;
70     this.uuidFactory = uuidFactory;
71   }
72
73   public GithubPermissionsMapping getPermissionsMappingForGithubRole(String githubRole) {
74     try (DbSession dbSession = dbClient.openSession(false)) {
75       Set<GithubPermissionsMappingDto> permissionsMappingForGithubRole = getPermissionsMappingForGithubRole(dbSession, githubRole);
76       return toGithubPermissionsMapping(permissionsMappingForGithubRole, githubRole);
77     }
78   }
79
80   public List<GithubPermissionsMapping> getPermissionsMapping() {
81     try (DbSession dbSession = dbClient.openSession(false)) {
82       return toGithubPermissionsMappings(githubPermissionsMappingDao.findAll(dbSession));
83     }
84   }
85
86   private static List<GithubPermissionsMapping> toGithubPermissionsMappings(Set<GithubPermissionsMappingDto> githubPermissionsMappingDtos) {
87     Map<String, Set<GithubPermissionsMappingDto>> githubRoleToGithubPermissionsMappingDto = githubPermissionsMappingDtos.stream()
88       .collect(groupingBy(GithubPermissionsMappingDto::githubRole, toSet()));
89
90     Set<String> allRoles = Sets.union(GITHUB_BASE_ROLES, githubRoleToGithubPermissionsMappingDto.keySet());
91     return allRoles.stream()
92       .map(githubRole -> toGithubPermissionsMapping(githubRoleToGithubPermissionsMappingDto.getOrDefault(githubRole, Set.of()), githubRole))
93       .toList();
94   }
95
96   private static GithubPermissionsMapping toGithubPermissionsMapping(Set<GithubPermissionsMappingDto> githubPermissionsMappingDtos, String githubRole) {
97     boolean isBaseRole = GITHUB_BASE_ROLES.contains(githubRole);
98     SonarqubePermissions sonarqubePermissions = getSonarqubePermissions(githubPermissionsMappingDtos);
99     return new GithubPermissionsMapping(githubRole, isBaseRole, sonarqubePermissions);
100   }
101
102   public void updatePermissionsMappings(Set<PermissionMappingChange> permissionChanges) {
103     try (DbSession dbSession = dbClient.openSession(false)) {
104       Map<String, List<PermissionMappingChange>> githubRolesToChanges = permissionChanges.stream()
105         .collect(groupingBy(PermissionMappingChange::githubRole));
106       githubRolesToChanges.forEach((githubRole, changes) -> updatePermissionsMappings(dbSession, githubRole, changes));
107       dbSession.commit();
108     }
109   }
110
111   public void deletePermissionMappings(String githubRole) {
112     checkArgument(!GITHUB_BASE_ROLES.contains(githubRole), "Deleting permission mapping for GitHub base role '" + githubRole + "' is not allowed.");
113     try (DbSession dbSession = dbClient.openSession(false)) {
114       Set<GithubPermissionsMappingDto> existingPermissions = githubPermissionsMappingDao.findAllForGithubRole(dbSession, githubRole);
115       if (existingPermissions.isEmpty()) {
116         throw new NotFoundException("Role '" + githubRole + "' not found.");
117       }
118       githubPermissionsMappingDao.deleteAllPermissionsForRole(dbSession, githubRole);
119       dbSession.commit();
120     }
121   }
122
123   private void updatePermissionsMappings(DbSession dbSession, String githubRole, List<PermissionMappingChange> permissionChanges) {
124     Set<String> currentPermissionsForRole = getSqPermissionsForGithubRole(dbSession, githubRole);
125     removePermissions(dbSession, permissionChanges, currentPermissionsForRole);
126     addPermissions(dbSession, permissionChanges, currentPermissionsForRole);
127   }
128
129   private Set<String> getSqPermissionsForGithubRole(DbSession dbSession, String githubRole) {
130     return getPermissionsMappingForGithubRole(dbSession, githubRole).stream()
131       .map(GithubPermissionsMappingDto::sonarqubePermission)
132       .collect(toSet());
133   }
134
135   private Set<GithubPermissionsMappingDto> getPermissionsMappingForGithubRole(DbSession dbSession, String githubRole) {
136     return githubPermissionsMappingDao.findAllForGithubRole(dbSession, githubRole);
137   }
138
139   private void removePermissions(DbSession dbSession, List<PermissionMappingChange> permissionChanges, Set<String> currentPermissionsForRole) {
140     permissionChanges.stream()
141       .filter(permissionMappingChange -> REMOVE.equals(permissionMappingChange.operation()))
142       .filter(permissionMappingChange -> currentPermissionsForRole.contains(permissionMappingChange.sonarqubePermission()))
143       .forEach(mapping -> githubPermissionsMappingDao.delete(dbSession, mapping.githubRole(), mapping.sonarqubePermission()));
144   }
145
146   private void addPermissions(DbSession dbSession, List<PermissionMappingChange> permissionChanges, Set<String> currentPermissionsForRole) {
147     permissionChanges.stream()
148       .filter(permissionMappingChange -> ADD.equals(permissionMappingChange.operation()))
149       .filter(permissionMappingChange -> !currentPermissionsForRole.contains(permissionMappingChange.sonarqubePermission()))
150       .forEach(
151         mapping -> githubPermissionsMappingDao.insert(dbSession, new GithubPermissionsMappingDto(uuidFactory.create(), mapping.githubRole(), mapping.sonarqubePermission())));
152   }
153
154   private static SonarqubePermissions getSonarqubePermissions(Set<GithubPermissionsMappingDto> githubPermissionsMappingDtos) {
155     SonarqubePermissions.Builder builder = SonarqubePermissions.Builder.builder();
156     githubPermissionsMappingDtos.stream()
157       .map(GithubPermissionsMappingDto::sonarqubePermission)
158       .map(permissionAsStringToSonarqubePermission::get)
159       .forEach(builderConsumer -> builderConsumer.accept(builder));
160     return builder.build();
161   }
162 }