3 * Copyright (C) 2009-2023 SonarSource SA
4 * mailto:info AT sonarsource DOT com
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.
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.
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.
20 package org.sonar.server.common.github.permissions;
22 import com.google.common.collect.Sets;
23 import java.util.List;
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;
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;
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";
48 private static final Set<String> GITHUB_BASE_ROLES = Set.of(
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));
63 private final DbClient dbClient;
64 private final GithubPermissionsMappingDao githubPermissionsMappingDao;
65 private final UuidFactory uuidFactory;
67 public GithubPermissionsMappingService(DbClient dbClient, GithubPermissionsMappingDao githubPermissionsMappingDao, UuidFactory uuidFactory) {
68 this.dbClient = dbClient;
69 this.githubPermissionsMappingDao = githubPermissionsMappingDao;
70 this.uuidFactory = uuidFactory;
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);
80 public List<GithubPermissionsMapping> getPermissionsMapping() {
81 try (DbSession dbSession = dbClient.openSession(false)) {
82 return toGithubPermissionsMappings(githubPermissionsMappingDao.findAll(dbSession));
86 private static List<GithubPermissionsMapping> toGithubPermissionsMappings(Set<GithubPermissionsMappingDto> githubPermissionsMappingDtos) {
87 Map<String, Set<GithubPermissionsMappingDto>> githubRoleToGithubPermissionsMappingDto = githubPermissionsMappingDtos.stream()
88 .collect(groupingBy(GithubPermissionsMappingDto::githubRole, toSet()));
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))
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);
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));
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.");
118 githubPermissionsMappingDao.deleteAllPermissionsForRole(dbSession, githubRole);
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);
129 private Set<String> getSqPermissionsForGithubRole(DbSession dbSession, String githubRole) {
130 return getPermissionsMappingForGithubRole(dbSession, githubRole).stream()
131 .map(GithubPermissionsMappingDto::sonarqubePermission)
135 private Set<GithubPermissionsMappingDto> getPermissionsMappingForGithubRole(DbSession dbSession, String githubRole) {
136 return githubPermissionsMappingDao.findAllForGithubRole(dbSession, githubRole);
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()));
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()))
151 mapping -> githubPermissionsMappingDao.insert(dbSession, new GithubPermissionsMappingDto(uuidFactory.create(), mapping.githubRole(), mapping.sonarqubePermission())));
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();