]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-20392 Add apiv2 endpoint to fetch the Github permissions mapping
authorAntoine Vigneau <antoine.vigneau@sonarsource.com>
Mon, 11 Sep 2023 18:10:50 +0000 (20:10 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 15 Sep 2023 20:03:05 +0000 (20:03 +0000)
15 files changed:
server/sonar-webserver-common/src/it/java/org/sonar/server/common/github/permissions/GithubPermissionsMappingServiceIT.java [new file with mode: 0644]
server/sonar-webserver-common/src/main/java/org/sonar/server/common/github/permissions/GithubPermissionsMapping.java [new file with mode: 0644]
server/sonar-webserver-common/src/main/java/org/sonar/server/common/github/permissions/GithubPermissionsMappingService.java [new file with mode: 0644]
server/sonar-webserver-common/src/main/java/org/sonar/server/common/github/permissions/SonarqubePermissions.java [new file with mode: 0644]
server/sonar-webserver-common/src/main/java/org/sonar/server/common/github/permissions/package-info.java [new file with mode: 0644]
server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/WebApiEndpoints.java
server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/github/permissions/controller/DefaultGithubPermissionsController.java [new file with mode: 0644]
server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/github/permissions/controller/GithubPermissionsController.java [new file with mode: 0644]
server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/github/permissions/controller/package-info.java [new file with mode: 0644]
server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/github/permissions/model/RestGithubPermissionsMapping.java [new file with mode: 0644]
server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/github/permissions/model/package-info.java [new file with mode: 0644]
server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/github/permissions/response/GithubPermissionsMappingRestResponse.java [new file with mode: 0644]
server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/github/permissions/response/package-info.java [new file with mode: 0644]
server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/PlatformLevel4WebConfig.java
server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/github/permissions/controller/DefaultGithubPermissionsControllerTest.java [new file with mode: 0644]

diff --git a/server/sonar-webserver-common/src/it/java/org/sonar/server/common/github/permissions/GithubPermissionsMappingServiceIT.java b/server/sonar-webserver-common/src/it/java/org/sonar/server/common/github/permissions/GithubPermissionsMappingServiceIT.java
new file mode 100644 (file)
index 0000000..14345c5
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * 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 java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.junit.Rule;
+import org.junit.Test;
+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 static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.sonar.server.common.github.permissions.GithubPermissionsMappingService.ADMIN_GITHUB_ROLE;
+import static org.sonar.server.common.github.permissions.GithubPermissionsMappingService.MAINTAIN_GITHUB_ROLE;
+import static org.sonar.server.common.github.permissions.GithubPermissionsMappingService.READ_GITHUB_ROLE;
+import static org.sonar.server.common.github.permissions.GithubPermissionsMappingService.TRIAGE_GITHUB_ROLE;
+import static org.sonar.server.common.github.permissions.GithubPermissionsMappingService.WRITE_GITHUB_ROLE;
+
+public class GithubPermissionsMappingServiceIT {
+
+  @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);
+
+  @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))
+    );
+
+    assertThat(actualPermissionsMapping).containsAll(expectedPermissionsMapping);
+  }
+
+  @Test
+  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")
+    );
+    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(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))
+    );
+
+    assertThat(actualPermissionsMapping).containsAll(expectedPermissionsMapping);
+  }
+
+  private void persistGithubPermissionsMapping(Map<String, Set<String>> githubRolesToSonarqubePermissions) {
+    for (Map.Entry<String, Set<String>> githubRoleToSonarqubePermissions : githubRolesToSonarqubePermissions.entrySet()) {
+      String githubRole = githubRoleToSonarqubePermissions.getKey();
+      githubRoleToSonarqubePermissions.getValue()
+        .forEach(permission -> githubPermissionsMappingDao.insert(
+          dbSession,
+          new GithubPermissionsMappingDto("uuid_" + githubRole + "_" + permission, githubRole, permission)));
+    }
+    dbSession.commit();
+  }
+
+}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/github/permissions/GithubPermissionsMapping.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/github/permissions/GithubPermissionsMapping.java
new file mode 100644 (file)
index 0000000..a8e8ac0
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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;
+
+public record GithubPermissionsMapping(String roleName, SonarqubePermissions permissions) {
+}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/github/permissions/GithubPermissionsMappingService.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/github/permissions/GithubPermissionsMappingService.java
new file mode 100644 (file)
index 0000000..abbcb18
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * 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 java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+import org.sonar.api.web.UserRole;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.provisioning.GithubPermissionsMappingDao;
+import org.sonar.db.provisioning.GithubPermissionsMappingDto;
+
+import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.Collectors.toSet;
+
+public class GithubPermissionsMappingService {
+  public static final String READ_GITHUB_ROLE = "read";
+  public static final String TRIAGE_GITHUB_ROLE = "triage";
+  public static final String WRITE_GITHUB_ROLE = "write";
+  public static final String MAINTAIN_GITHUB_ROLE = "maintain";
+  public static final String ADMIN_GITHUB_ROLE = "admin";
+
+  private static final Set<String> GITHUB_BASE_ROLE = Set.of(
+    READ_GITHUB_ROLE,
+    TRIAGE_GITHUB_ROLE,
+    WRITE_GITHUB_ROLE,
+    MAINTAIN_GITHUB_ROLE,
+    ADMIN_GITHUB_ROLE
+  );
+
+  private static final Map<String, Consumer<SonarqubePermissions.Builder>> permissionAsStringToSonarqubePermission = Map.of(
+    UserRole.USER, builder -> builder.user(true),
+    UserRole.CODEVIEWER, builder -> builder.codeViewer(true),
+    UserRole.ISSUE_ADMIN, builder -> builder.issueAdmin(true),
+    UserRole.SECURITYHOTSPOT_ADMIN, builder -> builder.securityHotspotAdmin(true),
+    UserRole.ADMIN, builder -> builder.admin(true),
+    UserRole.SCAN, builder -> builder.scan(true)
+  );
+
+  private final DbClient dbClient;
+  private final GithubPermissionsMappingDao githubPermissionsMappingDao;
+
+  public GithubPermissionsMappingService(DbClient dbClient, GithubPermissionsMappingDao githubPermissionsMappingDao) {
+    this.dbClient = dbClient;
+    this.githubPermissionsMappingDao = githubPermissionsMappingDao;
+  }
+
+  public List<GithubPermissionsMapping> getPermissionsMapping() {
+    try (DbSession dbSession = dbClient.openSession(false)) {
+      return toGithubPermissionsMappings(githubPermissionsMappingDao.findAll(dbSession));
+    }
+  }
+
+  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))
+      .toList();
+  }
+
+  private static GithubPermissionsMapping toGithubPermissionsMapping(Set<GithubPermissionsMappingDto> githubPermissionsMappingDtos, String githubRole) {
+    return new GithubPermissionsMapping(githubRole, getSonarqubePermissions(githubPermissionsMappingDtos));
+  }
+
+  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));
+    }
+    return builder.build();
+  }
+
+}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/github/permissions/SonarqubePermissions.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/github/permissions/SonarqubePermissions.java
new file mode 100644 (file)
index 0000000..cfdd0bf
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * 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;
+
+public record SonarqubePermissions(
+  boolean user,
+  boolean codeViewer,
+  boolean issueadmin,
+  boolean securityHotspotAdmin,
+  boolean admin,
+  boolean scan) {
+
+
+  public static final class Builder {
+    private boolean user = false;
+    private boolean codeViewer = false;
+    private boolean issueAdmin = false;
+    private boolean securityHotspotAdmin = false;
+    private boolean admin = false;
+    private boolean scan = false;
+
+    private Builder() {
+    }
+
+    public static Builder builder() {
+      return new Builder();
+    }
+
+    public Builder user(boolean user) {
+      this.user = user;
+      return this;
+    }
+
+    public Builder codeViewer(boolean codeViewer) {
+      this.codeViewer = codeViewer;
+      return this;
+    }
+
+    public Builder issueAdmin(boolean issueAdmin) {
+      this.issueAdmin = issueAdmin;
+      return this;
+    }
+
+    public Builder securityHotspotAdmin(boolean securityHotspotAdmin) {
+      this.securityHotspotAdmin = securityHotspotAdmin;
+      return this;
+    }
+
+    public Builder admin(boolean admin) {
+      this.admin = admin;
+      return this;
+    }
+
+    public Builder scan(boolean scan) {
+      this.scan = scan;
+      return this;
+    }
+
+    public SonarqubePermissions build() {
+      return new SonarqubePermissions(user, codeViewer, issueAdmin, securityHotspotAdmin, admin, scan);
+    }
+  }
+}
diff --git a/server/sonar-webserver-common/src/main/java/org/sonar/server/common/github/permissions/package-info.java b/server/sonar-webserver-common/src/main/java/org/sonar/server/common/github/permissions/package-info.java
new file mode 100644 (file)
index 0000000..bc66c66
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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.github.permissions;
+
+import javax.annotation.ParametersAreNonnullByDefault;
index 665f7e2eff7c3d93593e623d78296ddb373a49f0..15a1ce91933400944ec2a84b94b728e29b112718 100644 (file)
@@ -24,6 +24,7 @@ public class WebApiEndpoints {
   public static final String LIVENESS_ENDPOINT = SYSTEM_ENDPOINTS + "/liveness";
   public static final String HEALTH_ENDPOINT = SYSTEM_ENDPOINTS + "/health";
   public static final String USER_ENDPOINT = "/users";
+  public static final String GITHUB_PERMISSIONS_ENDPOINT = "/github-permission-mappings";
   public static final String JSON_MERGE_PATCH_CONTENT_TYPE = "application/json-merge-patch+json";
 
   private WebApiEndpoints() {
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/github/permissions/controller/DefaultGithubPermissionsController.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/github/permissions/controller/DefaultGithubPermissionsController.java
new file mode 100644 (file)
index 0000000..2268fa2
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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.controller;
+
+import java.util.List;
+import org.sonar.server.common.github.permissions.GithubPermissionsMapping;
+import org.sonar.server.common.github.permissions.GithubPermissionsMappingService;
+import org.sonar.server.user.UserSession;
+import org.sonar.server.v2.api.github.permissions.model.RestGithubPermissionsMapping;
+import org.sonar.server.v2.api.github.permissions.response.GithubPermissionsMappingRestResponse;
+
+public class DefaultGithubPermissionsController implements GithubPermissionsController{
+
+  private UserSession userSession;
+  private GithubPermissionsMappingService githubPermissionsMappingService;
+
+  public DefaultGithubPermissionsController(UserSession userSession, GithubPermissionsMappingService githubPermissionsMappingService) {
+    this.userSession = userSession;
+    this.githubPermissionsMappingService = githubPermissionsMappingService;
+  }
+
+  @Override
+  public GithubPermissionsMappingRestResponse fetchAll() {
+    userSession.checkLoggedIn().checkIsSystemAdministrator();
+    List<GithubPermissionsMapping> permissionsMapping = githubPermissionsMappingService.getPermissionsMapping();
+    return new GithubPermissionsMappingRestResponse(toRestResources(permissionsMapping));
+  }
+
+  private static List<RestGithubPermissionsMapping> toRestResources(List<GithubPermissionsMapping> permissionsMapping) {
+    return permissionsMapping.stream()
+      .map(e -> new RestGithubPermissionsMapping(e.roleName(), e.roleName(), e.permissions()))
+      .toList();
+  }
+
+}
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/github/permissions/controller/GithubPermissionsController.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/github/permissions/controller/GithubPermissionsController.java
new file mode 100644 (file)
index 0000000..78f91f4
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import org.sonar.server.v2.WebApiEndpoints;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+@RequestMapping(WebApiEndpoints.GITHUB_PERMISSIONS_ENDPOINT)
+@RestController
+public interface GithubPermissionsController {
+
+  @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
+  @ResponseStatus(HttpStatus.OK)
+  @Operation(summary = "Fetch the GitHub permissions mapping", description = "Requires Administer System permission.")
+  GithubPermissionsMappingRestResponse fetchAll();
+
+}
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/github/permissions/controller/package-info.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/github/permissions/controller/package-info.java
new file mode 100644 (file)
index 0000000..659fa0d
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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.controller;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/github/permissions/model/RestGithubPermissionsMapping.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/github/permissions/model/RestGithubPermissionsMapping.java
new file mode 100644 (file)
index 0000000..2fb04d2
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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.model;
+
+import org.sonar.server.common.github.permissions.SonarqubePermissions;
+
+public record RestGithubPermissionsMapping(String id, String roleName, SonarqubePermissions permissions) {
+}
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/github/permissions/model/package-info.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/github/permissions/model/package-info.java
new file mode 100644 (file)
index 0000000..a242c4d
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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.model;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/github/permissions/response/GithubPermissionsMappingRestResponse.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/github/permissions/response/GithubPermissionsMappingRestResponse.java
new file mode 100644 (file)
index 0000000..07f8393
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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.response;
+
+import java.util.List;
+import org.sonar.server.v2.api.github.permissions.model.RestGithubPermissionsMapping;
+
+public record GithubPermissionsMappingRestResponse(List<RestGithubPermissionsMapping> githubPermissionsMappings) {
+}
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/github/permissions/response/package-info.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/github/permissions/response/package-info.java
new file mode 100644 (file)
index 0000000..90961cb
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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.response;
+
+import javax.annotation.ParametersAreNonnullByDefault;
index 9231cc10c6b5481a54ef41bc3d4f0fba982a9ac9..b2c0b654ce44bd5af638609c8e78a5738e345bbc 100644 (file)
@@ -20,6 +20,9 @@
 package org.sonar.server.v2.config;
 
 import javax.annotation.Nullable;
+import org.sonar.db.DbClient;
+import org.sonar.db.provisioning.GithubPermissionsMappingDao;
+import org.sonar.server.common.github.permissions.GithubPermissionsMappingService;
 import org.sonar.server.common.health.CeStatusNodeCheck;
 import org.sonar.server.common.health.DbConnectionNodeCheck;
 import org.sonar.server.common.health.EsStatusNodeCheck;
@@ -31,6 +34,7 @@ import org.sonar.server.health.HealthChecker;
 import org.sonar.server.platform.NodeInformation;
 import org.sonar.server.user.SystemPasscode;
 import org.sonar.server.user.UserSession;
+import org.sonar.server.v2.api.github.permissions.controller.DefaultGithubPermissionsController;
 import org.sonar.server.v2.api.system.controller.DefaultLivenessController;
 import org.sonar.server.v2.api.system.controller.HealthController;
 import org.sonar.server.v2.api.system.controller.LivenessController;
@@ -75,4 +79,14 @@ public class PlatformLevel4WebConfig {
     return new DefaultUserController(userSession, userService, usersSearchResponseGenerator);
   }
 
+  @Bean
+  public GithubPermissionsMappingService githubPermissionsMappingService(DbClient dbClient, GithubPermissionsMappingDao githubPermissionsMappingDao) {
+    return new GithubPermissionsMappingService(dbClient, githubPermissionsMappingDao);
+  }
+
+  @Bean
+  public DefaultGithubPermissionsController githubPermissionsController(UserSession userSession, GithubPermissionsMappingService githubPermissionsMappingService) {
+    return new DefaultGithubPermissionsController(userSession, githubPermissionsMappingService);
+  }
+
 }
diff --git a/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/github/permissions/controller/DefaultGithubPermissionsControllerTest.java b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/github/permissions/controller/DefaultGithubPermissionsControllerTest.java
new file mode 100644 (file)
index 0000000..a31f46a
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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.controller;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import java.util.List;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.server.common.github.permissions.GithubPermissionsMapping;
+import org.sonar.server.common.github.permissions.GithubPermissionsMappingService;
+import org.sonar.server.common.github.permissions.SonarqubePermissions;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.v2.api.ControllerTester;
+import org.sonar.server.v2.api.github.permissions.model.RestGithubPermissionsMapping;
+import org.sonar.server.v2.api.github.permissions.response.GithubPermissionsMappingRestResponse;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.server.v2.WebApiEndpoints.GITHUB_PERMISSIONS_ENDPOINT;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+public class DefaultGithubPermissionsControllerTest {
+  @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();
+
+    mockMvc.perform(get(GITHUB_PERMISSIONS_ENDPOINT))
+      .andExpectAll(
+        status().isForbidden(),
+        content().json("{\"message\":\"Insufficient privileges\"}"));
+  }
+
+  @Test
+  public void fetchMapping_whenMappingSet_shouldReturnMapping() throws Exception {
+    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))
+    );
+    when(githubPermissionsMappingService.getPermissionsMapping()).thenReturn(mapping);
+
+    MvcResult mvcResult = mockMvc.perform(get(GITHUB_PERMISSIONS_ENDPOINT))
+      .andExpect(status().isOk())
+      .andReturn();
+
+    GithubPermissionsMappingRestResponse response = gson.fromJson(mvcResult.getResponse().getContentAsString(), GithubPermissionsMappingRestResponse.class);
+    assertThat(response.githubPermissionsMappings()).isEqualTo(toRestResources(mapping));
+  }
+
+  private static List<RestGithubPermissionsMapping> toRestResources(List<GithubPermissionsMapping> permissionsMapping) {
+    return permissionsMapping.stream()
+      .map(e -> new RestGithubPermissionsMapping(e.roleName(), e.roleName(), e.permissions()))
+      .toList();
+  }
+
+}