assertThat(newValueCaptor.getValue().getSonarqubePermission()).isEqualTo(githubPermissionsMappingDto.sonarqubePermission());
}
+ @Test
+ public void delete_deletesGithubPermissionsMappingDto() {
+ GithubPermissionsMappingDto githubPermissionsMappingDto = new GithubPermissionsMappingDto(MAPPING_UUID, "GH_role", "SQ_role");
+
+ underTest.insert(dbSession, githubPermissionsMappingDto);
+ underTest.delete(dbSession, "GH_role", "SQ_role");
+
+ Set<GithubPermissionsMappingDto> savedGithubPermissionsMappings = underTest.findAll(dbSession);
+ assertThat(savedGithubPermissionsMappings).isEmpty();
+
+ verify(auditPersister).deleteGithubPermissionsMapping(eq(dbSession), newValueCaptor.capture());
+ assertThat(newValueCaptor.getValue().getGithubRole()).isEqualTo("GH_role");
+ assertThat(newValueCaptor.getValue().getSonarqubePermission()).isEqualTo("SQ_role");
+ }
+
@Test
public void findAll_shouldReturnAllGithubOrganizationGroup() {
GithubPermissionsMappingDto mapping1 = new GithubPermissionsMappingDto(MAPPING_UUID, "GH_role", "SQ_role");
assertThat(all).hasSize(2)
.containsExactlyInAnyOrder(
mapping1,
- mapping2
- );
+ mapping2);
+ }
+
+ @Test
+ public void findAllForGithubRole_shouldReturnPermissionsForTheRole() {
+ GithubPermissionsMappingDto mapping1 = new GithubPermissionsMappingDto(MAPPING_UUID, "GH_role", "SQ_role");
+ GithubPermissionsMappingDto mapping2 = new GithubPermissionsMappingDto(MAPPING_UUID + "2", "GH_role2", "SQ_role");
+ GithubPermissionsMappingDto mapping3 = new GithubPermissionsMappingDto(MAPPING_UUID + "3", "GH_role2", "SQ_role2");
+ underTest.insert(dbSession, mapping1);
+ underTest.insert(dbSession, mapping2);
+ underTest.insert(dbSession, mapping3);
+
+ Set<GithubPermissionsMappingDto> forRole2 = underTest.findAllForGithubRole(dbSession, "GH_role2");
+ assertThat(forRole2).hasSize(2)
+ .containsExactlyInAnyOrder(mapping2, mapping3);
+
}
}
return mapper(dbSession).selectAll();
}
+ public Set<GithubPermissionsMappingDto> findAllForGithubRole(DbSession dbSession, String githubRole) {
+ return mapper(dbSession).selectAllForGithubRole(githubRole);
+ }
+
public void insert(DbSession dbSession, GithubPermissionsMappingDto githubPermissionsMappingDto) {
mapper(dbSession).insert(githubPermissionsMappingDto);
- auditPersister.addGithubPermissionsMapping(dbSession, toNewValueForAuditLogs(githubPermissionsMappingDto));
+ auditPersister.addGithubPermissionsMapping(dbSession, toNewValueForAuditLogs(githubPermissionsMappingDto.githubRole(), githubPermissionsMappingDto.sonarqubePermission()));
}
- public void delete(DbSession dbSession, GithubPermissionsMappingDto githubPermissionsMappingDto) {
- // TODO SONAR-20397
- auditPersister.deleteGithubPermissionsMapping(dbSession, toNewValueForAuditLogs(githubPermissionsMappingDto));
+ public void delete(DbSession dbSession, String githubRole, String sonarqubePermission) {
+ mapper(dbSession).delete(githubRole, sonarqubePermission);
+ auditPersister.deleteGithubPermissionsMapping(dbSession, toNewValueForAuditLogs(githubRole, sonarqubePermission));
}
- private static GithubPermissionsMappingNewValue toNewValueForAuditLogs(GithubPermissionsMappingDto githubPermissionsMappingDto) {
- return new GithubPermissionsMappingNewValue(githubPermissionsMappingDto.githubRole(),
- githubPermissionsMappingDto.sonarqubePermission());
+ private static GithubPermissionsMappingNewValue toNewValueForAuditLogs(String githubRole, String sonarqubePermission) {
+ return new GithubPermissionsMappingNewValue(githubRole, sonarqubePermission);
}
private static GithubPermissionsMappingMapper mapper(DbSession session) {
package org.sonar.db.provisioning;
import java.util.Set;
+import org.apache.ibatis.annotations.Param;
public interface GithubPermissionsMappingMapper {
Set<GithubPermissionsMappingDto> selectAll();
+ Set<GithubPermissionsMappingDto> selectAllForGithubRole(String githubRole);
+
void insert(GithubPermissionsMappingDto githubPermissionsMappingDto);
+ void delete(@Param("githubRole") String githubRole, @Param("sonarqubePermission") String sonarqubePermission);
}
)
</insert>
+ <delete id="delete" parameterType="GithubPermissionsMapping">
+ delete from github_perms_mapping
+ where github_role = #{githubRole,jdbcType=VARCHAR} AND sonarqube_permission = #{sonarqubePermission,jdbcType=VARCHAR}
+ </delete>
+
<select id="selectAll" resultType="GithubPermissionsMapping">
SELECT
<include refid="githubPermissionsMappingColumns"/>
FROM github_perms_mapping gpm
</select>
+ <select id="selectAllForGithubRole" resultType="GithubPermissionsMapping">
+ SELECT
+ <include refid="githubPermissionsMappingColumns"/>
+ FROM github_perms_mapping gpm
+ WHERE gpm.github_role = #{githubRole,jdbcType=VARCHAR}
+ </select>
+
</mapper>
import java.util.Set;
import org.junit.Rule;
import org.junit.Test;
+import org.sonar.core.util.SequenceUuidFactory;
+import org.sonar.core.util.UuidFactory;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.audit.AuditPersister;
import org.sonar.db.provisioning.GithubPermissionsMappingDao;
import org.sonar.db.provisioning.GithubPermissionsMappingDto;
+import org.sonar.server.common.permission.Operation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
public class GithubPermissionsMappingServiceIT {
+ private static final SonarqubePermissions NO_SQ_PERMISSIONS = new SonarqubePermissions(false, false, false, false, false, false);
@Rule
public DbTester db = DbTester.create();
private final DbSession dbSession = db.getSession();
private final AuditPersister auditPersister = mock();
private final GithubPermissionsMappingDao githubPermissionsMappingDao = new GithubPermissionsMappingDao(auditPersister);
- private final GithubPermissionsMappingService underTest = new GithubPermissionsMappingService(db.getDbClient(), githubPermissionsMappingDao);
+ private final UuidFactory uuidFactory = new SequenceUuidFactory();
+
+ private final GithubPermissionsMappingService underTest = new GithubPermissionsMappingService(db.getDbClient(), githubPermissionsMappingDao, uuidFactory);
@Test
public void getPermissionsMapping_whenMappingNotDefined_returnMappingEntirelyFalse() {
List<GithubPermissionsMapping> actualPermissionsMapping = underTest.getPermissionsMapping();
List<GithubPermissionsMapping> expectedPermissionsMapping = List.of(
- new GithubPermissionsMapping(READ_GITHUB_ROLE, new SonarqubePermissions(false, false, false, false, false, false)),
- new GithubPermissionsMapping(TRIAGE_GITHUB_ROLE, new SonarqubePermissions(false, false, false, false, false, false)),
- new GithubPermissionsMapping(WRITE_GITHUB_ROLE, new SonarqubePermissions(false, false, false, false, false, false)),
- new GithubPermissionsMapping(MAINTAIN_GITHUB_ROLE, new SonarqubePermissions(false, false, false, false, false, false)),
- new GithubPermissionsMapping(ADMIN_GITHUB_ROLE, new SonarqubePermissions(false, false, false, false, false, false))
- );
+ new GithubPermissionsMapping(READ_GITHUB_ROLE, NO_SQ_PERMISSIONS),
+ new GithubPermissionsMapping(TRIAGE_GITHUB_ROLE, NO_SQ_PERMISSIONS),
+ new GithubPermissionsMapping(WRITE_GITHUB_ROLE, NO_SQ_PERMISSIONS),
+ new GithubPermissionsMapping(MAINTAIN_GITHUB_ROLE, NO_SQ_PERMISSIONS),
+ new GithubPermissionsMapping(ADMIN_GITHUB_ROLE, NO_SQ_PERMISSIONS));
assertThat(actualPermissionsMapping).containsAll(expectedPermissionsMapping);
}
public void getPermissionsMapping_whenMappingDefined_returnMapping() {
Map<String, Set<String>> githubRolesToSqPermissions = Map.of(
READ_GITHUB_ROLE, Set.of("user", "codeviewer"),
- WRITE_GITHUB_ROLE, Set.of("user", "codeviewer", "issueadmin", "securityhotspotadmin", "admin", "scan")
- );
+ WRITE_GITHUB_ROLE, Set.of("user", "codeviewer", "issueadmin", "securityhotspotadmin", "admin", "scan"));
persistGithubPermissionsMapping(githubRolesToSqPermissions);
List<GithubPermissionsMapping> actualPermissionsMapping = underTest.getPermissionsMapping();
List<GithubPermissionsMapping> expectedPermissionsMapping = List.of(
new GithubPermissionsMapping(READ_GITHUB_ROLE, new SonarqubePermissions(true, true, false, false, false, false)),
- new GithubPermissionsMapping(TRIAGE_GITHUB_ROLE, new SonarqubePermissions(false, false, false, false, false, false)),
+ new GithubPermissionsMapping(TRIAGE_GITHUB_ROLE, NO_SQ_PERMISSIONS),
new GithubPermissionsMapping(WRITE_GITHUB_ROLE, new SonarqubePermissions(true, true, true, true, true, true)),
- new GithubPermissionsMapping(MAINTAIN_GITHUB_ROLE, new SonarqubePermissions(false, false, false, false, false, false)),
- new GithubPermissionsMapping(ADMIN_GITHUB_ROLE, new SonarqubePermissions(false, false, false, false, false, false))
- );
+ new GithubPermissionsMapping(MAINTAIN_GITHUB_ROLE, NO_SQ_PERMISSIONS),
+ new GithubPermissionsMapping(ADMIN_GITHUB_ROLE, NO_SQ_PERMISSIONS));
assertThat(actualPermissionsMapping).containsAll(expectedPermissionsMapping);
}
dbSession.commit();
}
+ @Test
+ public void updatePermissionsMappings_shouldAddAndRemovePermissions() {
+ Map<String, Set<String>> githubRolesToSqPermissions = Map.of(READ_GITHUB_ROLE, Set.of("user", "codeviewer"));
+ persistGithubPermissionsMapping(githubRolesToSqPermissions);
+
+ PermissionMappingChange permToAdd1 = new PermissionMappingChange(READ_GITHUB_ROLE, "issueadmin", Operation.ADD);
+ PermissionMappingChange permToAdd2 = new PermissionMappingChange(READ_GITHUB_ROLE, "scan", Operation.ADD);
+ PermissionMappingChange permToRemove1 = new PermissionMappingChange(READ_GITHUB_ROLE, "user", Operation.REMOVE);
+ PermissionMappingChange permToRemove2 = new PermissionMappingChange(READ_GITHUB_ROLE, "codeviewer", Operation.REMOVE);
+
+ underTest.updatePermissionsMappings(Set.of(permToAdd1, permToAdd2, permToRemove1, permToRemove2));
+
+ GithubPermissionsMapping updatedPermissionsMapping = underTest.getPermissionsMappingForGithubRole(READ_GITHUB_ROLE);
+
+ GithubPermissionsMapping expectedPermissionsMapping = new GithubPermissionsMapping(READ_GITHUB_ROLE, new SonarqubePermissions(false, false, true, false, false, true));
+ assertThat(updatedPermissionsMapping).isEqualTo(expectedPermissionsMapping);
+ }
+
+ @Test
+ public void updatePermissionsMappings_whenRemovingNonExistingPermission_isNoOp() {
+ PermissionMappingChange permToRemove1 = new PermissionMappingChange(READ_GITHUB_ROLE, "user", Operation.REMOVE);
+
+ underTest.updatePermissionsMappings(Set.of(permToRemove1));
+
+ GithubPermissionsMapping updatedPermissionsMapping = underTest.getPermissionsMappingForGithubRole(READ_GITHUB_ROLE);
+
+ GithubPermissionsMapping expectedPermissionsMapping = new GithubPermissionsMapping(READ_GITHUB_ROLE, NO_SQ_PERMISSIONS);
+ assertThat(updatedPermissionsMapping).isEqualTo(expectedPermissionsMapping);
+ }
+
+ @Test
+ public void updatePermissionsMappings_whenAddingAlreadyExistingPermission_isNoOp() {
+ Map<String, Set<String>> githubRolesToSqPermissions = Map.of(READ_GITHUB_ROLE, Set.of("user", "codeviewer"));
+ persistGithubPermissionsMapping(githubRolesToSqPermissions);
+ PermissionMappingChange permToAdd1 = new PermissionMappingChange(READ_GITHUB_ROLE, "user", Operation.ADD);
+
+ underTest.updatePermissionsMappings(Set.of(permToAdd1));
+
+ GithubPermissionsMapping updatedPermissionsMapping = underTest.getPermissionsMappingForGithubRole(READ_GITHUB_ROLE);
+
+ GithubPermissionsMapping expectedPermissionsMapping = new GithubPermissionsMapping(READ_GITHUB_ROLE, new SonarqubePermissions(true, true, false, false, false, false));
+ assertThat(updatedPermissionsMapping).isEqualTo(expectedPermissionsMapping);
+ }
+
+ @Test
+ public void updatePermissionsMappings_handlesUpdatesForDifferentRoles() {
+ PermissionMappingChange permToAdd1 = new PermissionMappingChange(READ_GITHUB_ROLE, "user", Operation.ADD);
+ PermissionMappingChange permToAdd2 = new PermissionMappingChange(WRITE_GITHUB_ROLE, "user", Operation.ADD);
+
+ underTest.updatePermissionsMappings(Set.of(permToAdd1, permToAdd2));
+
+ SonarqubePermissions userOnlySqPermission = new SonarqubePermissions(true, false, false, false, false, false);
+
+ GithubPermissionsMapping updatedPermissionsMapping = underTest.getPermissionsMappingForGithubRole(READ_GITHUB_ROLE);
+ assertThat(updatedPermissionsMapping).isEqualTo(new GithubPermissionsMapping(READ_GITHUB_ROLE, userOnlySqPermission));
+
+ updatedPermissionsMapping = underTest.getPermissionsMappingForGithubRole(WRITE_GITHUB_ROLE);
+ assertThat(updatedPermissionsMapping).isEqualTo(new GithubPermissionsMapping(WRITE_GITHUB_ROLE, userOnlySqPermission));
+ }
+
+ @Test
+ public void getPermissionsMappingForGithubRole_shouldReturnMappingOnlyForRole() {
+ Map<String, Set<String>> githubRolesToSqPermissions = Map.of(
+ READ_GITHUB_ROLE, Set.of("user", "codeviewer"),
+ WRITE_GITHUB_ROLE, Set.of("user", "codeviewer", "issueadmin", "securityhotspotadmin", "admin", "scan"));
+ persistGithubPermissionsMapping(githubRolesToSqPermissions);
+
+ GithubPermissionsMapping actualPermissionsMapping = underTest.getPermissionsMappingForGithubRole(READ_GITHUB_ROLE);
+
+ GithubPermissionsMapping expectedPermissionsMapping = new GithubPermissionsMapping(READ_GITHUB_ROLE, new SonarqubePermissions(true, true, false, false, false, false));
+
+ assertThat(actualPermissionsMapping).isEqualTo(expectedPermissionsMapping);
+ }
+
}
import java.util.Set;
import java.util.function.Consumer;
import org.sonar.api.web.UserRole;
+import org.sonar.core.util.UuidFactory;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.provisioning.GithubPermissionsMappingDao;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toSet;
+import static org.sonar.server.common.permission.Operation.ADD;
+import static org.sonar.server.common.permission.Operation.REMOVE;
public class GithubPermissionsMappingService {
public static final String READ_GITHUB_ROLE = "read";
TRIAGE_GITHUB_ROLE,
WRITE_GITHUB_ROLE,
MAINTAIN_GITHUB_ROLE,
- ADMIN_GITHUB_ROLE
- );
+ ADMIN_GITHUB_ROLE);
private static final Map<String, Consumer<SonarqubePermissions.Builder>> permissionAsStringToSonarqubePermission = Map.of(
UserRole.USER, builder -> builder.user(true),
private final DbClient dbClient;
private final GithubPermissionsMappingDao githubPermissionsMappingDao;
+ private final UuidFactory uuidFactory;
- public GithubPermissionsMappingService(DbClient dbClient, GithubPermissionsMappingDao githubPermissionsMappingDao) {
+ public GithubPermissionsMappingService(DbClient dbClient, GithubPermissionsMappingDao githubPermissionsMappingDao, UuidFactory uuidFactory) {
this.dbClient = dbClient;
this.githubPermissionsMappingDao = githubPermissionsMappingDao;
+ this.uuidFactory = uuidFactory;
+ }
+
+ public GithubPermissionsMapping getPermissionsMappingForGithubRole(String githubRole) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ return toGithubPermissionsMapping(getPermissionsMappingForGithubRole(dbSession, githubRole), githubRole);
+ }
}
public List<GithubPermissionsMapping> getPermissionsMapping() {
}
}
+ private static GithubPermissionsMapping toGithubPermissionsMapping(Set<GithubPermissionsMappingDto> githubPermissionsMappingDtos, String githubRole) {
+ return new GithubPermissionsMapping(githubRole, getSonarqubePermissions(githubPermissionsMappingDtos));
+ }
+
private static List<GithubPermissionsMapping> toGithubPermissionsMappings(Set<GithubPermissionsMappingDto> githubPermissionsMappingDtos) {
Map<String, Set<GithubPermissionsMappingDto>> githubRoleToGithubPermissionsMappingDto = githubPermissionsMappingDtos.stream()
.collect(groupingBy(GithubPermissionsMappingDto::githubRole, toSet()));
return GITHUB_BASE_ROLE.stream()
- .map(githubRole -> toGithubPermissionsMapping(githubRoleToGithubPermissionsMappingDto.get(githubRole), githubRole))
+ .map(githubRole -> toGithubPermissionsMapping(githubRoleToGithubPermissionsMappingDto.getOrDefault(githubRole, Set.of()), githubRole))
.toList();
}
- private static GithubPermissionsMapping toGithubPermissionsMapping(Set<GithubPermissionsMappingDto> githubPermissionsMappingDtos, String githubRole) {
- return new GithubPermissionsMapping(githubRole, getSonarqubePermissions(githubPermissionsMappingDtos));
+ public void updatePermissionsMappings(Set<PermissionMappingChange> permissionChanges) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ Map<String, List<PermissionMappingChange>> githubRolesToChanges = permissionChanges.stream()
+ .collect(groupingBy(PermissionMappingChange::githubRole));
+ githubRolesToChanges.forEach((githubRole, changes) -> updatePermissionsMappings(dbSession, githubRole, changes));
+ dbSession.commit();
+ }
+ }
+
+ private void updatePermissionsMappings(DbSession dbSession, String githubRole, List<PermissionMappingChange> permissionChanges) {
+ Set<String> currentPermissionsForRole = getSqPermissionsForGithubRole(dbSession, githubRole);
+ removePermissions(dbSession, permissionChanges, currentPermissionsForRole);
+ addPermissions(dbSession, permissionChanges, currentPermissionsForRole);
+ }
+
+ private Set<String> getSqPermissionsForGithubRole(DbSession dbSession, String githubRole) {
+ return getPermissionsMappingForGithubRole(dbSession, githubRole).stream()
+ .map(GithubPermissionsMappingDto::sonarqubePermission)
+ .collect(toSet());
+ }
+
+ private Set<GithubPermissionsMappingDto> getPermissionsMappingForGithubRole(DbSession dbSession, String githubRole) {
+ return githubPermissionsMappingDao.findAllForGithubRole(dbSession, githubRole);
+ }
+
+ private void removePermissions(DbSession dbSession, List<PermissionMappingChange> permissionChanges, Set<String> currentPermissionsForRole) {
+ permissionChanges.stream()
+ .filter(permissionMappingChange -> REMOVE.equals(permissionMappingChange.operation()))
+ .filter(permissionMappingChange -> currentPermissionsForRole.contains(permissionMappingChange.sonarqubePermission()))
+ .forEach(mapping -> githubPermissionsMappingDao.delete(dbSession, mapping.githubRole(), mapping.sonarqubePermission()));
+ }
+
+ private void addPermissions(DbSession dbSession, List<PermissionMappingChange> permissionChanges, Set<String> currentPermissionsForRole) {
+ permissionChanges.stream()
+ .filter(permissionMappingChange -> ADD.equals(permissionMappingChange.operation()))
+ .filter(permissionMappingChange -> !currentPermissionsForRole.contains(permissionMappingChange.sonarqubePermission()))
+ .forEach(
+ mapping -> githubPermissionsMappingDao.insert(dbSession, new GithubPermissionsMappingDto(uuidFactory.create(), mapping.githubRole(), mapping.sonarqubePermission()))
+ );
}
private static SonarqubePermissions getSonarqubePermissions(Set<GithubPermissionsMappingDto> githubPermissionsMappingDtos) {
SonarqubePermissions.Builder builder = SonarqubePermissions.Builder.builder();
- if (githubPermissionsMappingDtos != null) {
- githubPermissionsMappingDtos.stream()
- .map(GithubPermissionsMappingDto::sonarqubePermission)
- .map(permissionAsStringToSonarqubePermission::get)
- .forEach(builderConsumer -> builderConsumer.accept(builder));
- }
+ githubPermissionsMappingDtos.stream()
+ .map(GithubPermissionsMappingDto::sonarqubePermission)
+ .map(permissionAsStringToSonarqubePermission::get)
+ .forEach(builderConsumer -> builderConsumer.accept(builder));
return builder.build();
}
-
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.common.github.permissions;
+
+import org.sonar.server.common.permission.Operation;
+
+public record PermissionMappingChange(String githubRole, String sonarqubePermission, Operation operation) {
+}
public record SonarqubePermissions(
boolean user,
boolean codeViewer,
- boolean issueadmin,
+ boolean issueAdmin,
boolean securityHotspotAdmin,
boolean admin,
boolean scan) {
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.common.permission;
+
+public enum Operation {
+ ADD, REMOVE
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.common.permission;
+
+import javax.annotation.ParametersAreNonnullByDefault;
*/
package org.sonar.server.v2.api.github.permissions.controller;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
+import javax.validation.Valid;
import org.sonar.server.common.github.permissions.GithubPermissionsMapping;
import org.sonar.server.common.github.permissions.GithubPermissionsMappingService;
+import org.sonar.server.common.github.permissions.PermissionMappingChange;
+import org.sonar.server.common.permission.Operation;
import org.sonar.server.user.UserSession;
import org.sonar.server.v2.api.github.permissions.model.RestGithubPermissionsMapping;
+import org.sonar.server.v2.api.github.permissions.request.GithubPermissionMappingUpdateRequest;
+import org.sonar.server.v2.api.github.permissions.request.PermissionMappingUpdate;
import org.sonar.server.v2.api.github.permissions.response.GithubPermissionsMappingRestResponse;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
-public class DefaultGithubPermissionsController implements GithubPermissionsController{
+import static org.sonar.api.web.UserRole.ADMIN;
+import static org.sonar.api.web.UserRole.CODEVIEWER;
+import static org.sonar.api.web.UserRole.ISSUE_ADMIN;
+import static org.sonar.api.web.UserRole.SCAN;
+import static org.sonar.api.web.UserRole.SECURITYHOTSPOT_ADMIN;
+import static org.sonar.api.web.UserRole.USER;
+
+public class DefaultGithubPermissionsController implements GithubPermissionsController {
private UserSession userSession;
private GithubPermissionsMappingService githubPermissionsMappingService;
@Override
public GithubPermissionsMappingRestResponse fetchAll() {
- userSession.checkLoggedIn().checkIsSystemAdministrator();
+ userSession.checkIsSystemAdministrator();
List<GithubPermissionsMapping> permissionsMapping = githubPermissionsMappingService.getPermissionsMapping();
return new GithubPermissionsMappingRestResponse(toRestResources(permissionsMapping));
}
+ @Override
+ public RestGithubPermissionsMapping updateMapping(@PathVariable("githubRole") String githubRole, @Valid @RequestBody GithubPermissionMappingUpdateRequest request) {
+ userSession.checkIsSystemAdministrator();
+ PermissionMappingUpdate update = request.permissions();
+ Set<PermissionMappingChange> changes = new HashSet<>();
+
+ update.getUser().map(shouldAddPermission -> toPermissionMappingChange(githubRole, USER, shouldAddPermission)).applyIfDefined(changes::add);
+ update.getCodeViewer().map(shouldAddPermission -> toPermissionMappingChange(githubRole, CODEVIEWER, shouldAddPermission)).applyIfDefined(changes::add);
+ update.getIssueAdmin().map(shouldAddPermission -> toPermissionMappingChange(githubRole, ISSUE_ADMIN, shouldAddPermission)).applyIfDefined(changes::add);
+ update.getSecurityHotspotAdmin().map(shouldAddPermission -> toPermissionMappingChange(githubRole, SECURITYHOTSPOT_ADMIN, shouldAddPermission)).applyIfDefined(changes::add);
+ update.getAdmin().map(shouldAddPermission -> toPermissionMappingChange(githubRole, ADMIN, shouldAddPermission)).applyIfDefined(changes::add);
+ update.getScan().map(shouldAddPermission -> toPermissionMappingChange(githubRole, SCAN, shouldAddPermission)).applyIfDefined(changes::add);
+
+ githubPermissionsMappingService.updatePermissionsMappings(changes);
+
+ return toRestGithubPermissionMapping(githubPermissionsMappingService.getPermissionsMappingForGithubRole(githubRole));
+
+ }
+
+ private static PermissionMappingChange toPermissionMappingChange(String githubRole, String sonarqubePermission, boolean shouldAddPermission) {
+ return new PermissionMappingChange(githubRole, sonarqubePermission, shouldAddPermission ? Operation.ADD : Operation.REMOVE);
+ }
+
private static List<RestGithubPermissionsMapping> toRestResources(List<GithubPermissionsMapping> permissionsMapping) {
return permissionsMapping.stream()
- .map(e -> new RestGithubPermissionsMapping(e.roleName(), e.roleName(), e.permissions()))
+ .map(DefaultGithubPermissionsController::toRestGithubPermissionMapping)
.toList();
}
+ private static RestGithubPermissionsMapping toRestGithubPermissionMapping(GithubPermissionsMapping githubPermissionsMapping) {
+ return new RestGithubPermissionsMapping(githubPermissionsMapping.roleName(), githubPermissionsMapping.roleName(), githubPermissionsMapping.permissions());
+ }
+
}
package org.sonar.server.v2.api.github.permissions.controller;
import io.swagger.v3.oas.annotations.Operation;
+import javax.validation.Valid;
import org.sonar.server.v2.WebApiEndpoints;
+import org.sonar.server.v2.api.github.permissions.model.RestGithubPermissionsMapping;
+import org.sonar.server.v2.api.github.permissions.request.GithubPermissionMappingUpdateRequest;
import org.sonar.server.v2.api.github.permissions.response.GithubPermissionsMappingRestResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PatchMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
+import static org.sonar.server.v2.WebApiEndpoints.JSON_MERGE_PATCH_CONTENT_TYPE;
+
@RequestMapping(WebApiEndpoints.GITHUB_PERMISSIONS_ENDPOINT)
@RestController
public interface GithubPermissionsController {
@Operation(summary = "Fetch the GitHub permissions mapping", description = "Requires Administer System permission.")
GithubPermissionsMappingRestResponse fetchAll();
+ @PatchMapping(path = "/{githubRole}", consumes = JSON_MERGE_PATCH_CONTENT_TYPE, produces = MediaType.APPLICATION_JSON_VALUE)
+ @ResponseStatus(HttpStatus.OK)
+ @Operation(summary = "Update a single Github permission mapping", description = "Update a single Github permission mapping")
+ RestGithubPermissionsMapping updateMapping(@PathVariable("githubRole") String githubRole, @Valid @RequestBody GithubPermissionMappingUpdateRequest request);
+
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.v2.api.github.permissions.request;
+
+public record GithubPermissionMappingUpdateRequest(
+ PermissionMappingUpdate permissions) {
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.v2.api.github.permissions.request;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import org.sonar.server.v2.common.model.UpdateField;
+
+public class PermissionMappingUpdate {
+ private UpdateField<Boolean> user = UpdateField.undefined();
+ private UpdateField<Boolean> codeViewer = UpdateField.undefined();
+ private UpdateField<Boolean> issueAdmin = UpdateField.undefined();
+ private UpdateField<Boolean> securityHotspotAdmin = UpdateField.undefined();
+ private UpdateField<Boolean> admin = UpdateField.undefined();
+ private UpdateField<Boolean> scan = UpdateField.undefined();
+
+ @Schema(implementation = Boolean.class)
+ public UpdateField<Boolean> getUser() {
+ return user;
+ }
+
+ public void setUser(Boolean user) {
+ this.user = UpdateField.withValue(user);
+ }
+
+ @Schema(implementation = Boolean.class)
+ public UpdateField<Boolean> getCodeViewer() {
+ return codeViewer;
+ }
+
+ public void setCodeViewer(Boolean codeViewer) {
+ this.codeViewer = UpdateField.withValue(codeViewer);
+ }
+
+ @Schema(implementation = Boolean.class)
+ public UpdateField<Boolean> getIssueAdmin() {
+ return issueAdmin;
+ }
+
+ public void setIssueAdmin(Boolean issueAdmin) {
+ this.issueAdmin = UpdateField.withValue(issueAdmin);
+ }
+
+ @Schema(implementation = Boolean.class)
+ public UpdateField<Boolean> getSecurityHotspotAdmin() {
+ return securityHotspotAdmin;
+ }
+
+ public void setSecurityHotspotAdmin(Boolean securityHotspotAdmin) {
+ this.securityHotspotAdmin = UpdateField.withValue(securityHotspotAdmin);
+ }
+
+ @Schema(implementation = Boolean.class)
+ public UpdateField<Boolean> getAdmin() {
+ return admin;
+ }
+
+ public void setAdmin(Boolean admin) {
+ this.admin = UpdateField.withValue(admin);
+ }
+
+ @Schema(implementation = Boolean.class)
+ public UpdateField<Boolean> getScan() {
+ return scan;
+ }
+
+ public void setScan(Boolean scan) {
+ this.scan = UpdateField.withValue(scan);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.v2.api.github.permissions.request;
+
+import javax.annotation.ParametersAreNonnullByDefault;
@Size(max = 200)
@Schema(description = "User first name and last name", implementation = String.class)
- public UpdateField< String> getName() {
+ public UpdateField<String> getName() {
return name;
}
package org.sonar.server.v2.common.model;
import java.util.function.Consumer;
+import java.util.function.Function;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import javax.validation.valueextraction.UnwrapByDefault;
}
}
+ public <U> UpdateField<U> map(Function<T, U> mappingFunction) {
+ if (isDefined) {
+ return withValue(mappingFunction.apply(value));
+ }
+ return undefined();
+ }
+
@Override
public String toString() {
return value.toString();
package org.sonar.server.v2.config;
import javax.annotation.Nullable;
+import org.sonar.core.util.UuidFactory;
import org.sonar.db.DbClient;
import org.sonar.db.provisioning.GithubPermissionsMappingDao;
import org.sonar.server.common.github.permissions.GithubPermissionsMappingService;
}
@Bean
- public GithubPermissionsMappingService githubPermissionsMappingService(DbClient dbClient, GithubPermissionsMappingDao githubPermissionsMappingDao) {
- return new GithubPermissionsMappingService(dbClient, githubPermissionsMappingDao);
+ public GithubPermissionsMappingService githubPermissionsMappingService(DbClient dbClient, GithubPermissionsMappingDao githubPermissionsMappingDao, UuidFactory uuidFactory) {
+ return new GithubPermissionsMappingService(dbClient, githubPermissionsMappingDao, uuidFactory);
}
@Bean
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.List;
+import java.util.Set;
import org.junit.Rule;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.sonar.server.common.github.permissions.GithubPermissionsMapping;
import org.sonar.server.common.github.permissions.GithubPermissionsMappingService;
+import org.sonar.server.common.github.permissions.PermissionMappingChange;
import org.sonar.server.common.github.permissions.SonarqubePermissions;
+import org.sonar.server.common.permission.Operation;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.v2.api.ControllerTester;
import org.sonar.server.v2.api.github.permissions.model.RestGithubPermissionsMapping;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.sonar.server.v2.WebApiEndpoints.GITHUB_PERMISSIONS_ENDPOINT;
+import static org.sonar.server.v2.WebApiEndpoints.JSON_MERGE_PATCH_CONTENT_TYPE;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
public class DefaultGithubPermissionsControllerTest {
+
+ public static final String GITHUB_ROLE = "role1";
+ private static final Gson gson = new GsonBuilder().create();
+
@Rule
public UserSessionRule userSession = UserSessionRule.standalone();
private final GithubPermissionsMappingService githubPermissionsMappingService = mock();
-
private final MockMvc mockMvc = ControllerTester.getMockMvc(new DefaultGithubPermissionsController(userSession, githubPermissionsMappingService));
- private static final Gson gson = new GsonBuilder().create();
-
-
@Test
public void fetchMapping_whenUserIsNotAdministrator_shouldReturnForbidden() throws Exception {
userSession.logIn().setNonSystemAdministrator();
userSession.logIn().setSystemAdministrator();
List<GithubPermissionsMapping> mapping = List.of(
- new GithubPermissionsMapping("role1", new SonarqubePermissions(true, false, true, false, true, false)),
- new GithubPermissionsMapping("role2", new SonarqubePermissions(false, true, false, true, false, true))
- );
+ new GithubPermissionsMapping(GITHUB_ROLE, new SonarqubePermissions(true, false, true, false, true, false)),
+ new GithubPermissionsMapping("role2", new SonarqubePermissions(false, true, false, true, false, true)));
when(githubPermissionsMappingService.getPermissionsMapping()).thenReturn(mapping);
MvcResult mvcResult = mockMvc.perform(get(GITHUB_PERMISSIONS_ENDPOINT))
.toList();
}
+ @Test
+ public void updateMapping_whenUserIsNotAdministrator_shouldReturnForbidden() throws Exception {
+ userSession.logIn().setNonSystemAdministrator();
+
+ mockMvc.perform(
+ patch(GITHUB_PERMISSIONS_ENDPOINT + "/" + GITHUB_ROLE)
+ .contentType(JSON_MERGE_PATCH_CONTENT_TYPE)
+ .content("""
+ {
+ "user": true,
+ "codeViewer": false,
+ "admin": true
+ }
+ """))
+ .andExpectAll(
+ status().isForbidden(),
+ content().json("{\"message\":\"Insufficient privileges\"}"));
+ }
+
+ @Test
+ public void updateMapping_shouldUpdateMapping() throws Exception {
+ userSession.logIn().setSystemAdministrator();
+ GithubPermissionsMapping updatedRolePermissions = new GithubPermissionsMapping(GITHUB_ROLE, new SonarqubePermissions(true, false, false, true, true, false));
+
+ when(githubPermissionsMappingService.getPermissionsMappingForGithubRole(GITHUB_ROLE)).thenReturn(updatedRolePermissions);
+
+ MvcResult mvcResult = mockMvc.perform(
+ patch(GITHUB_PERMISSIONS_ENDPOINT + "/" + GITHUB_ROLE)
+ .contentType(JSON_MERGE_PATCH_CONTENT_TYPE)
+ .content("""
+ {
+ "permissions": {
+ "user": true,
+ "codeViewer": false,
+ "admin": true
+ }
+ }
+ """))
+ .andExpect(status().isOk())
+ .andReturn();
+
+ RestGithubPermissionsMapping response = gson.fromJson(mvcResult.getResponse().getContentAsString(), RestGithubPermissionsMapping.class);
+
+ RestGithubPermissionsMapping expectedResponse = new RestGithubPermissionsMapping(GITHUB_ROLE, GITHUB_ROLE, new SonarqubePermissions(true, false, false, true, true, false));
+ assertThat(response).isEqualTo(expectedResponse);
+
+ ArgumentCaptor<Set<PermissionMappingChange>> permissionMappingChangesCaptor = ArgumentCaptor.forClass(Set.class);
+ verify(githubPermissionsMappingService).updatePermissionsMappings(permissionMappingChangesCaptor.capture());
+ assertThat(permissionMappingChangesCaptor.getValue())
+ .containsExactlyInAnyOrder(
+ new PermissionMappingChange(GITHUB_ROLE, "codeviewer", Operation.REMOVE),
+ new PermissionMappingChange(GITHUB_ROLE, "user", Operation.ADD),
+ new PermissionMappingChange(GITHUB_ROLE, "admin", Operation.ADD)
+ );
+ }
+
}
import org.sonar.db.project.ProjectDto;
import org.sonar.db.user.GroupDto;
import org.sonar.db.user.UserDto;
+import org.sonar.server.common.permission.Operation;
import org.sonar.server.exceptions.BadRequestException;
import static org.assertj.core.api.Assertions.assertThat;
@Test
public void apply_adds_global_permission_to_group() {
- apply(new GroupPermissionChange(PermissionChange.Operation.ADD, ADMINISTER_QUALITY_PROFILES.getKey(), null, group, permissionService));
+ apply(new GroupPermissionChange(Operation.ADD, ADMINISTER_QUALITY_PROFILES.getKey(), null, group, permissionService));
assertThat(db.users().selectGroupPermissions(group, null)).containsOnly(ADMINISTER_QUALITY_PROFILES.getKey());
}
@Test
public void apply_adds_global_permission_to_group_AnyOne() {
- apply(new GroupPermissionChange(PermissionChange.Operation.ADD, ADMINISTER_QUALITY_PROFILES.getKey(), null, null, permissionService));
+ apply(new GroupPermissionChange(Operation.ADD, ADMINISTER_QUALITY_PROFILES.getKey(), null, null, permissionService));
assertThat(db.users().selectAnyonePermissions(null)).containsOnly(ADMINISTER_QUALITY_PROFILES.getKey());
}
public void apply_fails_with_BadRequestException_when_adding_any_permission_to_group_AnyOne_on_private_project() {
permissionService.getAllProjectPermissions()
.forEach(perm -> {
- GroupPermissionChange change = new GroupPermissionChange(PermissionChange.Operation.ADD, perm, privateProject, null, permissionService);
+ GroupPermissionChange change = new GroupPermissionChange(Operation.ADD, perm, privateProject, null, permissionService);
try {
apply(change);
fail("a BadRequestException should have been thrown");
permissionService.getAllProjectPermissions()
.forEach(perm -> {
- apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, perm, privateProject, null, permissionService));
+ apply(new GroupPermissionChange(Operation.REMOVE, perm, privateProject, null, permissionService));
assertThat(db.users().selectAnyonePermissions(privateProject.getUuid())).contains(perm);
});
private void applyAddsPermissionToGroupOnPrivateProject(String permission) {
- apply(new GroupPermissionChange(PermissionChange.Operation.ADD, permission, privateProject, group, permissionService));
+ apply(new GroupPermissionChange(Operation.ADD, permission, privateProject, group, permissionService));
assertThat(db.users().selectGroupPermissions(group, null)).isEmpty();
assertThat(db.users().selectGroupPermissions(group, privateProject)).containsOnly(permission);
private void applyRemovesPermissionFromGroupOnPrivateProject(String permission) {
db.users().insertEntityPermissionOnGroup(group, permission, privateProject);
- apply(new GroupPermissionChange(PermissionChange.Operation.ADD, permission, privateProject, group, permissionService), permission);
+ apply(new GroupPermissionChange(Operation.ADD, permission, privateProject, group, permissionService), permission);
assertThat(db.users().selectGroupPermissions(group, privateProject)).containsOnly(permission);
}
@Test
public void apply_has_no_effect_when_adding_USER_permission_to_group_AnyOne_on_a_public_project() {
- apply(new GroupPermissionChange(PermissionChange.Operation.ADD, UserRole.USER, publicProject, null, permissionService));
+ apply(new GroupPermissionChange(Operation.ADD, UserRole.USER, publicProject, null, permissionService));
assertThat(db.users().selectAnyonePermissions(publicProject.getUuid())).isEmpty();
}
@Test
public void apply_has_no_effect_when_adding_CODEVIEWER_permission_to_group_AnyOne_on_a_public_project() {
- apply(new GroupPermissionChange(PermissionChange.Operation.ADD, UserRole.CODEVIEWER, publicProject, null, permissionService));
+ apply(new GroupPermissionChange(Operation.ADD, UserRole.CODEVIEWER, publicProject, null, permissionService));
assertThat(db.users().selectAnyonePermissions(publicProject.getUuid())).isEmpty();
}
@Test
public void apply_fails_with_BadRequestException_when_adding_permission_ADMIN_to_group_AnyOne_on_a_public_project() {
- GroupPermissionChange change = new GroupPermissionChange(PermissionChange.Operation.ADD, UserRole.ADMIN, publicProject, null, permissionService);
+ GroupPermissionChange change = new GroupPermissionChange(Operation.ADD, UserRole.ADMIN, publicProject, null, permissionService);
assertThatThrownBy(() -> apply(change))
.isInstanceOf(BadRequestException.class)
.hasMessage("It is not possible to add the 'admin' permission to group 'Anyone'.");
@Test
public void apply_adds_permission_ISSUE_ADMIN_to_group_AnyOne_on_a_public_project() {
- apply(new GroupPermissionChange(PermissionChange.Operation.ADD, UserRole.ISSUE_ADMIN, publicProject, null, permissionService));
+ apply(new GroupPermissionChange(Operation.ADD, UserRole.ISSUE_ADMIN, publicProject, null, permissionService));
assertThat(db.users().selectAnyonePermissions(publicProject.getUuid())).containsOnly(UserRole.ISSUE_ADMIN);
}
@Test
public void apply_adds_permission_SCAN_EXECUTION_to_group_AnyOne_on_a_public_project() {
- apply(new GroupPermissionChange(PermissionChange.Operation.ADD, GlobalPermission.SCAN.getKey(), publicProject, null, permissionService));
+ apply(new GroupPermissionChange(Operation.ADD, GlobalPermission.SCAN.getKey(), publicProject, null, permissionService));
assertThat(db.users().selectAnyonePermissions(publicProject.getUuid())).containsOnly(GlobalPermission.SCAN.getKey());
}
@Test
public void apply_fails_with_BadRequestException_when_removing_USER_permission_from_group_AnyOne_on_a_public_project() {
- GroupPermissionChange change = new GroupPermissionChange(PermissionChange.Operation.REMOVE, UserRole.USER, publicProject, null, permissionService);
+ GroupPermissionChange change = new GroupPermissionChange(Operation.REMOVE, UserRole.USER, publicProject, null, permissionService);
assertThatThrownBy(() -> apply(change))
.isInstanceOf(BadRequestException.class)
.hasMessage("Permission user can't be removed from a public component");
@Test
public void apply_fails_with_BadRequestException_when_removing_CODEVIEWER_permission_from_group_AnyOne_on_a_public_project() {
- GroupPermissionChange change = new GroupPermissionChange(PermissionChange.Operation.REMOVE, UserRole.CODEVIEWER, publicProject, null, permissionService);
+ GroupPermissionChange change = new GroupPermissionChange(Operation.REMOVE, UserRole.CODEVIEWER, publicProject, null, permissionService);
assertThatThrownBy(() -> apply(change))
.isInstanceOf(BadRequestException.class)
.hasMessage("Permission codeviewer can't be removed from a public component");
private void applyRemovesPermissionFromGroupAnyOneOnAPublicProject(String permission) {
db.users().insertEntityPermissionOnAnyone(permission, publicProject);
- apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, permission, publicProject, null, permissionService), permission);
+ apply(new GroupPermissionChange(Operation.REMOVE, permission, publicProject, null, permissionService), permission);
assertThat(db.users().selectAnyonePermissions(publicProject.getUuid())).isEmpty();
}
@Test
public void apply_fails_with_BadRequestException_when_removing_USER_permission_from_a_group_on_a_public_project() {
- GroupPermissionChange change = new GroupPermissionChange(PermissionChange.Operation.REMOVE, UserRole.USER, publicProject, group, permissionService);
+ GroupPermissionChange change = new GroupPermissionChange(Operation.REMOVE, UserRole.USER, publicProject, group, permissionService);
assertThatThrownBy(() -> apply(change))
.isInstanceOf(BadRequestException.class)
.hasMessage("Permission user can't be removed from a public component");
@Test
public void apply_fails_with_BadRequestException_when_removing_CODEVIEWER_permission_from_a_group_on_a_public_project() {
- GroupPermissionChange change = new GroupPermissionChange(PermissionChange.Operation.REMOVE, UserRole.CODEVIEWER, publicProject, group, permissionService);
+ GroupPermissionChange change = new GroupPermissionChange(Operation.REMOVE, UserRole.CODEVIEWER, publicProject, group, permissionService);
assertThatThrownBy(() -> apply(change))
.isInstanceOf(BadRequestException.class)
.hasMessage("Permission codeviewer can't be removed from a public component");
@Test
public void add_permission_to_anyone() {
- apply(new GroupPermissionChange(PermissionChange.Operation.ADD, ADMINISTER_QUALITY_PROFILES.getKey(), null, null, permissionService));
+ apply(new GroupPermissionChange(Operation.ADD, ADMINISTER_QUALITY_PROFILES.getKey(), null, null, permissionService));
assertThat(db.users().selectGroupPermissions(group, null)).isEmpty();
assertThat(db.users().selectAnyonePermissions(null)).containsOnly(ADMINISTER_QUALITY_PROFILES.getKey());
public void do_nothing_when_adding_permission_that_already_exists() {
db.users().insertPermissionOnGroup(group, ADMINISTER_QUALITY_GATES);
- apply(new GroupPermissionChange(PermissionChange.Operation.ADD, ADMINISTER_QUALITY_GATES.getKey(), null, group, permissionService));
+ apply(new GroupPermissionChange(Operation.ADD, ADMINISTER_QUALITY_GATES.getKey(), null, group, permissionService));
assertThat(db.users().selectGroupPermissions(group, null)).containsOnly(ADMINISTER_QUALITY_GATES.getKey());
}
.filter(perm -> !UserRole.ADMIN.equals(perm) && !GlobalPermission.SCAN.getKey().equals(perm))
.forEach(perm -> {
try {
- new GroupPermissionChange(PermissionChange.Operation.ADD, perm, privateProject, group, permissionService);
+ new GroupPermissionChange(Operation.ADD, perm, privateProject, group, permissionService);
fail("a BadRequestException should have been thrown for permission " + perm);
} catch (BadRequestException e) {
assertThat(e).hasMessage("Invalid project permission '" + perm +
.filter(perm -> !UserRole.ADMIN.equals(perm) && !GlobalPermission.SCAN.getKey().equals(perm))
.forEach(perm -> {
try {
- new GroupPermissionChange(PermissionChange.Operation.ADD, perm, publicProject, group, permissionService);
+ new GroupPermissionChange(Operation.ADD, perm, publicProject, group, permissionService);
fail("a BadRequestException should have been thrown for permission " + perm);
} catch (BadRequestException e) {
assertThat(e).hasMessage("Invalid project permission '" + perm +
.filter(perm -> !GlobalPermission.SCAN.getKey().equals(perm) && !GlobalPermission.ADMINISTER.getKey().equals(perm))
.forEach(permission -> {
try {
- new GroupPermissionChange(PermissionChange.Operation.ADD, permission, null, group, permissionService);
+ new GroupPermissionChange(Operation.ADD, permission, null, group, permissionService);
fail("a BadRequestException should have been thrown for permission " + permission);
} catch (BadRequestException e) {
assertThat(e).hasMessage("Invalid global permission '" + permission + "'. Valid values are [admin, gateadmin, profileadmin, provisioning, scan]");
db.users().insertPermissionOnGroup(group, ADMINISTER_QUALITY_GATES);
db.users().insertPermissionOnGroup(group, PROVISION_PROJECTS);
- apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, ADMINISTER_QUALITY_GATES.getKey(), null, group, permissionService), ADMINISTER_QUALITY_GATES.getKey(),
+ apply(new GroupPermissionChange(Operation.REMOVE, ADMINISTER_QUALITY_GATES.getKey(), null, group, permissionService), ADMINISTER_QUALITY_GATES.getKey(),
PROVISION_PROJECTS.getKey());
assertThat(db.users().selectGroupPermissions(group, null)).containsOnly(PROVISION_PROJECTS.getKey());
db.users().insertEntityPermissionOnGroup(group, UserRole.ISSUE_ADMIN, privateProject);
db.users().insertEntityPermissionOnGroup(group, UserRole.CODEVIEWER, privateProject);
- apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, UserRole.ISSUE_ADMIN, privateProject, group, permissionService), UserRole.ISSUE_ADMIN,
+ apply(new GroupPermissionChange(Operation.REMOVE, UserRole.ISSUE_ADMIN, privateProject, group, permissionService), UserRole.ISSUE_ADMIN,
UserRole.CODEVIEWER);
assertThat(db.users().selectGroupPermissions(group, null)).containsOnly(ADMINISTER_QUALITY_GATES.getKey());
@Test
public void do_not_fail_if_removing_a_permission_that_does_not_exist() {
- apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, UserRole.ISSUE_ADMIN, privateProject, group, permissionService));
+ apply(new GroupPermissionChange(Operation.REMOVE, UserRole.ISSUE_ADMIN, privateProject, group, permissionService));
assertThat(db.users().selectGroupPermissions(group, null)).isEmpty();
assertThat(db.users().selectGroupPermissions(group, privateProject)).isEmpty();
public void fail_to_remove_admin_permission_if_no_more_admins() {
db.users().insertPermissionOnGroup(group, ADMINISTER);
- GroupPermissionChange change = new GroupPermissionChange(PermissionChange.Operation.REMOVE, ADMINISTER.getKey(), null, group, permissionService);
+ GroupPermissionChange change = new GroupPermissionChange(Operation.REMOVE, ADMINISTER.getKey(), null, group, permissionService);
Set<String> permission = Set.of("admin");
DbSession session = db.getSession();
assertThatThrownBy(() -> underTest.apply(session, permission, change))
UserDto admin = db.users().insertUser();
db.users().insertGlobalPermissionOnUser(admin, ADMINISTER);
- apply(new GroupPermissionChange(PermissionChange.Operation.REMOVE, ADMINISTER.getKey(), null, group, permissionService), ADMINISTER.getKey());
+ apply(new GroupPermissionChange(Operation.REMOVE, ADMINISTER.getKey(), null, group, permissionService), ADMINISTER.getKey());
assertThat(db.users().selectGroupPermissions(group, null)).isEmpty();
}
import static java.util.stream.Collectors.toSet;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.sonar.server.permission.PermissionChange.Operation.ADD;
-import static org.sonar.server.permission.PermissionChange.Operation.REMOVE;
+import static org.sonar.server.common.permission.Operation.ADD;
+import static org.sonar.server.common.permission.Operation.REMOVE;
import static org.sonar.server.permission.PermissionServiceImpl.ALL_PROJECT_PERMISSIONS;
public class UserPermissionChangerIT {
import org.sonar.db.user.UserDto;
import org.sonar.server.es.Indexers;
import org.sonar.server.favorite.FavoriteUpdater;
-import org.sonar.server.permission.PermissionChange;
+import org.sonar.server.common.permission.Operation;
import org.sonar.server.permission.PermissionService;
import org.sonar.server.permission.PermissionTemplateService;
import org.sonar.server.permission.PermissionUpdater;
}
private UserPermissionChange toUserPermissionChange(String permission, ProjectDto projectDto, UserDto userDto) {
- return new UserPermissionChange(PermissionChange.Operation.ADD, permission, projectDto, userDto, permissionService);
+ return new UserPermissionChange(Operation.ADD, permission, projectDto, userDto, permissionService);
}
private void addToFavourites(DbSession dbSession, ProjectDto projectDto, @Nullable String userUuid, @Nullable String userLogin) {
import javax.annotation.Nullable;
import org.sonar.db.entity.EntityDto;
import org.sonar.db.user.GroupDto;
+import org.sonar.server.common.permission.Operation;
public class GroupPermissionChange extends PermissionChange {
import static com.google.common.base.Preconditions.checkNotNull;
import static java.lang.String.format;
import static org.sonar.server.exceptions.BadRequestException.checkRequest;
-import static org.sonar.server.permission.PermissionChange.Operation.ADD;
-import static org.sonar.server.permission.PermissionChange.Operation.REMOVE;
+import static org.sonar.server.common.permission.Operation.ADD;
+import static org.sonar.server.common.permission.Operation.REMOVE;
public class GroupPermissionChanger implements GranteeTypeSpecificPermissionUpdater<GroupPermissionChange> {
import javax.annotation.Nullable;
import org.sonar.db.entity.EntityDto;
import org.sonar.db.permission.GlobalPermission;
+import org.sonar.server.common.permission.Operation;
import static java.util.Objects.requireNonNull;
import static org.sonar.server.exceptions.BadRequestException.checkRequest;
public abstract class PermissionChange {
- public enum Operation {
- ADD, REMOVE
- }
-
private final Operation operation;
private final String permission;
private final EntityDto entity;
import javax.annotation.Nullable;
import org.sonar.db.entity.EntityDto;
import org.sonar.db.user.UserId;
+import org.sonar.server.common.permission.Operation;
import static java.util.Objects.requireNonNull;
import org.sonar.db.permission.UserPermissionDto;
import static org.sonar.server.exceptions.BadRequestException.checkRequest;
-import static org.sonar.server.permission.PermissionChange.Operation.ADD;
-import static org.sonar.server.permission.PermissionChange.Operation.REMOVE;
+import static org.sonar.server.common.permission.Operation.ADD;
+import static org.sonar.server.common.permission.Operation.REMOVE;
/**
* Adds and removes user permissions. Both global and project scopes are supported.
import org.sonar.db.user.GroupDto;
import org.sonar.server.common.management.ManagedInstanceChecker;
import org.sonar.server.permission.GroupPermissionChange;
-import org.sonar.server.permission.PermissionChange;
+import org.sonar.server.common.permission.Operation;
import org.sonar.server.permission.PermissionService;
import org.sonar.server.permission.PermissionUpdater;
import org.sonar.server.user.UserSession;
}
wsSupport.checkPermissionManagementAccess(userSession, entityDto);
GroupPermissionChange change = new GroupPermissionChange(
- PermissionChange.Operation.ADD,
+ Operation.ADD,
request.mandatoryParam(PARAM_PERMISSION),
entityDto,
groupDto,
import org.sonar.db.entity.EntityDto;
import org.sonar.db.user.UserId;
import org.sonar.server.common.management.ManagedInstanceChecker;
-import org.sonar.server.permission.PermissionChange;
+import org.sonar.server.common.permission.Operation;
import org.sonar.server.permission.PermissionService;
import org.sonar.server.permission.PermissionUpdater;
import org.sonar.server.permission.UserPermissionChange;
UserId user = wsSupport.findUser(dbSession, userLogin);
UserPermissionChange change = new UserPermissionChange(
- PermissionChange.Operation.ADD,
+ Operation.ADD,
request.mandatoryParam(PARAM_PERMISSION),
entityDto,
user,
import org.sonar.server.common.management.ManagedInstanceChecker;
import org.sonar.server.permission.GroupPermissionChange;
import org.sonar.server.permission.GroupUuidOrAnyone;
-import org.sonar.server.permission.PermissionChange;
+import org.sonar.server.common.permission.Operation;
import org.sonar.server.permission.PermissionService;
import org.sonar.server.permission.PermissionUpdater;
import org.sonar.server.user.UserSession;
wsSupport.checkRemovingOwnBrowsePermissionOnPrivateProject(dbSession, userSession, entityDto, permission, GroupUuidOrAnyone.from(groupDto));
GroupPermissionChange change = new GroupPermissionChange(
- PermissionChange.Operation.REMOVE,
+ Operation.REMOVE,
permission,
entityDto,
groupDto,
import org.sonar.db.entity.EntityDto;
import org.sonar.db.user.UserId;
import org.sonar.server.common.management.ManagedInstanceChecker;
-import org.sonar.server.permission.PermissionChange;
+import org.sonar.server.common.permission.Operation;
import org.sonar.server.permission.PermissionService;
import org.sonar.server.permission.PermissionUpdater;
import org.sonar.server.permission.UserPermissionChange;
managedInstanceChecker.throwIfUserAndProjectAreManaged(dbSession, userIdDto.getUuid(), entityDto.getUuid());
}
UserPermissionChange change = new UserPermissionChange(
- PermissionChange.Operation.REMOVE,
+ Operation.REMOVE,
permission,
entityDto,
userIdDto,