]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-20700 Use auth App installation token to check permissions on GH
authorAurelien Poscia <aurelien.poscia@sonarsource.com>
Wed, 18 Oct 2023 12:57:13 +0000 (14:57 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 20 Oct 2023 20:02:40 +0000 (20:02 +0000)
server/sonar-webserver-webapi/src/it/java/org/sonar/server/almintegration/ws/github/ImportGithubProjectActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/ce/queue/ReportSubmitterIT.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/almsettings/ws/GithubProjectCreationParameters.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/almsettings/ws/GithubProjectCreator.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/almsettings/ws/GithubProjectCreatorFactory.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/ce/queue/ReportSubmitter.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/almsettings/ws/GithubProjectCreatorFactoryTest.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/almsettings/ws/GithubProjectCreatorTest.java

index 78c3c9ec3037b0f45bf085769c98602a8a038645..53074f8f11bba0a28f4ea490cb73ad5f688cf170 100644 (file)
@@ -25,13 +25,16 @@ import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
+import org.sonar.alm.client.github.AppInstallationToken;
 import org.sonar.alm.client.github.GithubApplicationClient;
 import org.sonar.alm.client.github.GithubApplicationClientImpl;
+import org.sonar.alm.client.github.api.GsonRepositoryCollaborator;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.System2;
 import org.sonar.auth.github.GitHubSettings;
 import org.sonar.auth.github.GithubPermissionConverter;
+import org.sonar.auth.github.GsonRepositoryPermissions;
 import org.sonar.core.i18n.I18n;
 import org.sonar.core.platform.EditionProvider;
 import org.sonar.core.platform.PlatformEditionProvider;
@@ -131,7 +134,7 @@ public class ImportGithubProjectActionIT {
 
   private final ManagedProjectService managedProjectService = mock(ManagedProjectService.class);
 
-  private final GithubPermissionConverter githubPermissionConverter = new GithubPermissionConverter();
+  private final GithubPermissionConverter githubPermissionConverter = mock();
   private final GithubProjectCreatorFactory gitHubProjectCreatorFactory = new GithubProjectCreatorFactory(db.getDbClient(),
     null, appClient, projectDefaultVisibility, projectKeyGenerator, userSession, componentUpdater, gitHubSettings, githubPermissionConverter);
   private final WsActionTester ws = new WsActionTester(new ImportGithubProjectAction(db.getDbClient(), managedProjectService, userSession,
@@ -147,7 +150,7 @@ public class ImportGithubProjectActionIT {
   public void importProject_ifProjectWithSameNameDoesNotExist_importSucceed() {
     AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
 
-    GithubApplicationClient.Repository repository = mockGithubInteractions();
+    GithubApplicationClient.Repository repository = mockGithubDevOpsAppInteractions();
 
     Projects.CreateWsResponse response = callWebService(githubAlmSetting);
 
@@ -171,7 +174,7 @@ public class ImportGithubProjectActionIT {
 
     AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
 
-    mockGithubInteractions();
+    mockGithubDevOpsAppInteractions();
 
     Projects.CreateWsResponse response = ws.newRequest()
       .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
@@ -199,7 +202,7 @@ public class ImportGithubProjectActionIT {
 
     AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
 
-    mockGithubInteractions();
+    mockGithubDevOpsAppInteractions();
 
     Projects.CreateWsResponse response = ws.newRequest()
       .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
@@ -230,7 +233,7 @@ public class ImportGithubProjectActionIT {
 
     AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
 
-    mockGithubInteractions();
+    mockGithubDevOpsAppInteractions();
 
     Projects.CreateWsResponse response = ws.newRequest()
       .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
@@ -257,7 +260,7 @@ public class ImportGithubProjectActionIT {
 
     AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
 
-    mockGithubInteractions();
+    mockGithubDevOpsAppInteractions();
 
     Projects.CreateWsResponse response = ws.newRequest()
       .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
@@ -283,7 +286,7 @@ public class ImportGithubProjectActionIT {
     AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
     db.components().insertPublicProject(p -> p.setKey("Hello-World")).getMainBranchComponent();
 
-    GithubApplicationClient.Repository repository = mockGithubInteractions();
+    GithubApplicationClient.Repository repository = mockGithubDevOpsAppInteractions();
 
     Projects.CreateWsResponse response = ws.newRequest()
       .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
@@ -299,7 +302,7 @@ public class ImportGithubProjectActionIT {
   public void importProject_whenGithubProvisioningIsDisabled_shouldApplyPermissionTemplate() {
     AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
 
-    mockGithubInteractions();
+    mockGithubDevOpsAppInteractions();
     when(gitHubSettings.isProvisioningEnabled()).thenReturn(false);
 
     ws.newRequest()
@@ -318,8 +321,9 @@ public class ImportGithubProjectActionIT {
   public void importProject_whenGithubProvisioningIsEnabled_shouldNotApplyPermissionTemplate() {
     AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
 
-    mockGithubInteractions();
     when(gitHubSettings.isProvisioningEnabled()).thenReturn(true);
+    mockGithubDevOpsAppInteractions();
+    mockGithubAuthAppInteractions();
 
     ws.newRequest()
       .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
@@ -330,10 +334,29 @@ public class ImportGithubProjectActionIT {
 
   }
 
+  private void mockGithubAuthAppInteractions() {
+    when(gitHubSettings.appId()).thenReturn("432");
+    when(gitHubSettings.privateKey()).thenReturn("private key");
+    when(gitHubSettings.apiURL()).thenReturn("http://www.url.com");
+
+    AppInstallationToken appInstallationToken = mock();
+
+    when(appClient.getInstallationId(any(), any())).thenReturn(Optional.of(321L));
+    when(appClient.createAppInstallationToken(any(), eq(321L))).thenReturn(Optional.of(appInstallationToken));
+
+    GsonRepositoryCollaborator gsonRepositoryCollaborator = new GsonRepositoryCollaborator("toto", 2, "admin", new GsonRepositoryPermissions(true, true, true, true, true));
+    when(appClient.getRepositoryCollaborators(gitHubSettings.apiURL(), appInstallationToken, "octocat", PROJECT_KEY_NAME)).thenReturn(Set.of(gsonRepositoryCollaborator));
+
+    String role = gsonRepositoryCollaborator.roleName();
+    GsonRepositoryPermissions permissions = gsonRepositoryCollaborator.permissions();
+    when(githubPermissionConverter.toSonarqubeRolesWithFallbackOnRepositoryPermissions(Set.of(), role, permissions)).thenReturn(Set.of("scan"));
+
+  }
+
   @Test
   public void importProject_shouldSetCreationMethodToApi_ifNonBrowserRequest() {
     AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
-    mockGithubInteractions();
+    mockGithubDevOpsAppInteractions();
 
     Projects.CreateWsResponse response = callWebService(githubAlmSetting);
 
@@ -346,7 +369,7 @@ public class ImportGithubProjectActionIT {
   public void importProject_shouldSetCreationMethodToBrowser_ifBrowserRequest() {
     AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
     userSession.flagSessionAsGui();
-    mockGithubInteractions();
+    mockGithubDevOpsAppInteractions();
 
     Projects.CreateWsResponse response = callWebService(githubAlmSetting);
 
@@ -398,8 +421,7 @@ public class ImportGithubProjectActionIT {
   public void importProject_whenNoAlmSettingKeyAndOnlyOneConfig_shouldImport() {
     AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
 
-    mockGithubInteractions();
-    when(gitHubSettings.isProvisioningEnabled()).thenReturn(true);
+    mockGithubDevOpsAppInteractions();
 
     TestRequest request = ws.newRequest()
       .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
@@ -415,7 +437,7 @@ public class ImportGithubProjectActionIT {
       .executeProtobuf(Projects.CreateWsResponse.class);
   }
 
-  private GithubApplicationClient.Repository mockGithubInteractions() {
+  private GithubApplicationClient.Repository mockGithubDevOpsAppInteractions() {
     GithubApplicationClient.Repository repository = new GithubApplicationClient.Repository(1L, PROJECT_KEY_NAME, false,
       "octocat/" + PROJECT_KEY_NAME,
       "https://github.sonarsource.com/api/v3/repos/octocat/" + PROJECT_KEY_NAME, "default-branch");
index dd71c29014fced69bf5eaba9bf7b13e932219425..b56e6f131013be067a2b8ce4c05a2bc20ff237f6 100644 (file)
@@ -53,8 +53,8 @@ import org.sonar.server.almsettings.ws.DelegatingDevOpsProjectCreatorFactory;
 import org.sonar.server.almsettings.ws.DevOpsProjectCreator;
 import org.sonar.server.almsettings.ws.DevOpsProjectCreatorFactory;
 import org.sonar.server.almsettings.ws.DevOpsProjectDescriptor;
-import org.sonar.server.almsettings.ws.GithubProjectCreator;
 import org.sonar.server.almsettings.ws.GithubProjectCreationParameters;
+import org.sonar.server.almsettings.ws.GithubProjectCreator;
 import org.sonar.server.almsettings.ws.GithubProjectCreatorFactory;
 import org.sonar.server.component.ComponentUpdater;
 import org.sonar.server.es.TestIndexers;
@@ -263,22 +263,33 @@ public class ReportSubmitterIT {
 
   @Test
   public void submit_whenReportIsForANewProjectWithoutDevOpsMetadata_createsLocalProject() {
-    userSession.addPermission(GlobalPermission.SCAN).addPermission(PROVISION_PROJECTS);
+    userSession.addPermission(PROVISION_PROJECTS);
     mockSuccessfulPrepareSubmitCall();
-    when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), any(), eq(PROJECT_KEY)))
-      .thenReturn(true);
+    when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), any(), eq(PROJECT_KEY))).thenReturn(true);
 
     underTest.submit(PROJECT_KEY, PROJECT_NAME, emptyMap(), IOUtils.toInputStream("{binary}", UTF_8));
 
     assertLocalProjectWasCreated();
   }
 
-
   @Test
   public void submit_whenReportIsForANewProjectWithValidAlmSettingsAutoProvisioningOnAndPermOnGh_createsProjectWithBinding() {
+    userSession.addPermission(PROVISION_PROJECTS);
+    when(managedInstanceService.isInstanceExternallyManaged()).thenReturn(true);
+    mockSuccessfulPrepareSubmitCall();
+
+    DevOpsProjectCreator devOpsProjectCreator = mockAlmSettingDtoAndDevOpsProjectCreator(CHARACTERISTICS);
+    doReturn(true).when(devOpsProjectCreator).isScanAllowedUsingPermissionsFromDevopsPlatform();
+
+    underTest.submit(PROJECT_KEY, PROJECT_NAME, CHARACTERISTICS, IOUtils.toInputStream("{binary}", UTF_8));
+
+    assertProjectWasCreatedWithBinding();
+  }
+
+  @Test
+  public void submit_whenReportIsForANewProjectWithValidAlmSettingsAutoProvisioningOnNoPermOnGhAndGlobalScanPerm_createsProjectWithBinding() {
     userSession.addPermission(GlobalPermission.SCAN).addPermission(PROVISION_PROJECTS);
     when(managedInstanceService.isInstanceExternallyManaged()).thenReturn(true);
-    when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), any(), eq(PROJECT_KEY))).thenReturn(true);
     mockSuccessfulPrepareSubmitCall();
 
     DevOpsProjectCreator devOpsProjectCreator = mockAlmSettingDtoAndDevOpsProjectCreator(CHARACTERISTICS);
@@ -345,8 +356,11 @@ public class ReportSubmitterIT {
     mockGithubRepository();
 
     DevOpsProjectDescriptor projectDescriptor = new DevOpsProjectDescriptor(ALM.GITHUB, "apiUrl", "orga/repo");
-    GithubProjectCreationParameters githubProjectCreationParameters = new GithubProjectCreationParameters(projectDescriptor, almSettingDto, mock(), true, managedInstanceService.isInstanceExternallyManaged(), userSession);
-    DevOpsProjectCreator devOpsProjectCreator = spy(new GithubProjectCreator(db.getDbClient(), githubApplicationClient, null, projectKeyGenerator, componentUpdater, githubProjectCreationParameters));
+    GithubProjectCreationParameters githubProjectCreationParameters = new GithubProjectCreationParameters(projectDescriptor, almSettingDto, true,
+      managedInstanceService.isInstanceExternallyManaged(), userSession, mock(),
+      null);
+    DevOpsProjectCreator devOpsProjectCreator = spy(
+      new GithubProjectCreator(db.getDbClient(), githubApplicationClient, null, projectKeyGenerator, componentUpdater, githubProjectCreationParameters));
     doReturn(Optional.of(devOpsProjectCreator)).when(devOpsProjectCreatorFactorySpy).getDevOpsProjectCreator(any(), eq(characteristics));
     return devOpsProjectCreator;
   }
index 3f51f1bb2975b78ca71f150752c1cc69e247079d..9803f10f08c7a2f8c6a03fa07fa1d383235475e6 100644 (file)
  */
 package org.sonar.server.almsettings.ws;
 
+import javax.annotation.Nullable;
+import org.sonar.alm.client.github.AppInstallationToken;
 import org.sonar.alm.client.github.security.AccessToken;
 import org.sonar.db.alm.setting.AlmSettingDto;
 import org.sonar.server.user.UserSession;
 
-public record GithubProjectCreationParameters(DevOpsProjectDescriptor devOpsProjectDescriptor, AlmSettingDto almSettingDto, AccessToken appInstallationToken,
-                                              boolean projectsArePrivateByDefault, boolean isProvisioningEnabled, UserSession userSession) {
+public record GithubProjectCreationParameters(DevOpsProjectDescriptor devOpsProjectDescriptor, AlmSettingDto almSettingDto, boolean projectsArePrivateByDefault,
+                                              boolean isProvisioningEnabled, UserSession userSession, AccessToken devOpsAppInstallationToken,
+                                              @Nullable AppInstallationToken authAppInstallationToken) {
 }
index 20a608c353d1d8f7a5c85602915dfe30f65bbccf..303ecdd991eea36341e1b1e1583fba918ad3fdbf 100644 (file)
@@ -21,7 +21,9 @@ package org.sonar.server.almsettings.ws;
 
 import java.util.Optional;
 import java.util.Set;
+import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
+import org.sonar.alm.client.github.AppInstallationToken;
 import org.sonar.alm.client.github.GithubApplicationClient;
 import org.sonar.alm.client.github.api.GsonRepositoryCollaborator;
 import org.sonar.alm.client.github.api.GsonRepositoryTeam;
@@ -47,6 +49,7 @@ import org.sonar.server.user.UserSession;
 import static java.util.Objects.requireNonNull;
 import static java.util.stream.Collectors.toSet;
 import static org.sonar.api.resources.Qualifiers.PROJECT;
+import static org.sonar.api.utils.Preconditions.checkState;
 import static org.sonar.server.component.NewComponent.newComponentBuilder;
 
 public class GithubProjectCreator implements DevOpsProjectCreator {
@@ -58,9 +61,12 @@ public class GithubProjectCreator implements DevOpsProjectCreator {
   private final ComponentUpdater componentUpdater;
   private final GithubProjectCreationParameters githubProjectCreationParameters;
   private final DevOpsProjectDescriptor devOpsProjectDescriptor;
-  private final AccessToken appInstallationToken;
   private final UserSession userSession;
   private final AlmSettingDto almSettingDto;
+  private final AccessToken devOpsAppInstallationToken;
+
+  @CheckForNull
+  private final AppInstallationToken authAppInstallationToken;
 
   public GithubProjectCreator(DbClient dbClient, GithubApplicationClient githubApplicationClient, GithubPermissionConverter githubPermissionConverter,
     ProjectKeyGenerator projectKeyGenerator, ComponentUpdater componentUpdater, GithubProjectCreationParameters githubProjectCreationParameters) {
@@ -71,14 +77,17 @@ public class GithubProjectCreator implements DevOpsProjectCreator {
     this.projectKeyGenerator = projectKeyGenerator;
     this.componentUpdater = componentUpdater;
     this.githubProjectCreationParameters = githubProjectCreationParameters;
-    devOpsProjectDescriptor = githubProjectCreationParameters.devOpsProjectDescriptor();
-    appInstallationToken = githubProjectCreationParameters.appInstallationToken();
     userSession = githubProjectCreationParameters.userSession();
     almSettingDto = githubProjectCreationParameters.almSettingDto();
+    devOpsProjectDescriptor = githubProjectCreationParameters.devOpsProjectDescriptor();
+    devOpsAppInstallationToken = githubProjectCreationParameters.devOpsAppInstallationToken();
+    authAppInstallationToken = githubProjectCreationParameters.authAppInstallationToken();
   }
 
   @Override
   public boolean isScanAllowedUsingPermissionsFromDevopsPlatform() {
+    checkState(githubProjectCreationParameters.authAppInstallationToken() != null, "An auth app token is required in case repository permissions checking is necessary.");
+
     String[] orgaAndRepoTokenified = devOpsProjectDescriptor.projectIdentifier().split("/");
     String organization = orgaAndRepoTokenified[0];
     String repository = orgaAndRepoTokenified[1];
@@ -93,8 +102,8 @@ public class GithubProjectCreator implements DevOpsProjectCreator {
   }
 
   private boolean doesUserHaveScanPermission(String organization, String repository, Set<GithubPermissionsMappingDto> permissionsMappingDtos) {
-    Set<GsonRepositoryCollaborator> repositoryCollaborators = githubApplicationClient.getRepositoryCollaborators(devOpsProjectDescriptor.url(), appInstallationToken, organization,
-      repository);
+    Set<GsonRepositoryCollaborator> repositoryCollaborators = githubApplicationClient.getRepositoryCollaborators(devOpsProjectDescriptor.url(), authAppInstallationToken,
+      organization, repository);
 
     String externalLogin = userSession.getExternalIdentity().map(UserSession.ExternalIdentity::login).orElse(null);
     if (externalLogin == null) {
@@ -109,7 +118,7 @@ public class GithubProjectCreator implements DevOpsProjectCreator {
 
   private boolean doesUserBelongToAGroupWithScanPermission(String organization, String repository,
     Set<GithubPermissionsMappingDto> permissionsMappingDtos) {
-    Set<GsonRepositoryTeam> repositoryTeams = githubApplicationClient.getRepositoryTeams(devOpsProjectDescriptor.url(), appInstallationToken, organization, repository);
+    Set<GsonRepositoryTeam> repositoryTeams = githubApplicationClient.getRepositoryTeams(devOpsProjectDescriptor.url(), authAppInstallationToken, organization, repository);
 
     Set<String> groupsOfUser = findUserMembershipOnSonarQube(organization);
     return repositoryTeams.stream()
@@ -135,7 +144,7 @@ public class GithubProjectCreator implements DevOpsProjectCreator {
   @Override
   public ComponentCreationData createProjectAndBindToDevOpsPlatform(DbSession dbSession, CreationMethod creationMethod, @Nullable String projectKey) {
     String url = requireNonNull(almSettingDto.getUrl(), "DevOps Platform url cannot be null");
-    GithubApplicationClient.Repository repository = githubApplicationClient.getRepository(url, appInstallationToken, devOpsProjectDescriptor.projectIdentifier())
+    GithubApplicationClient.Repository repository = githubApplicationClient.getRepository(url, devOpsAppInstallationToken, devOpsProjectDescriptor.projectIdentifier())
       .orElseThrow(() -> new IllegalStateException(
         String.format("Impossible to find the repository '%s' on GitHub, using the devops config %s", devOpsProjectDescriptor.projectIdentifier(), almSettingDto.getKey())));
 
index 59ca84e9417a2bda429af302a0bdf6727755333c..c7c57272c1de52197eb977bcd3a7d69871bdfc36 100644 (file)
@@ -104,32 +104,49 @@ public class GithubProjectCreatorFactory implements DevOpsProjectCreatorFactory
       .map(appInstallationToken -> createGithubProjectCreator(dbSession, devOpsProjectDescriptor, almSettingDto, appInstallationToken));
   }
 
-  private Optional<Long> findInstallationIdToAccessRepo(GithubAppConfiguration githubAppConfiguration, String repositoryKey) {
-    return githubApplicationClient.getInstallationId(githubAppConfiguration, repositoryKey);
-  }
-
-  private AppInstallationToken generateAppInstallationToken(GithubAppConfiguration githubAppConfiguration, long installationId) {
-    return githubApplicationClient.createAppInstallationToken(githubAppConfiguration, installationId)
-      .orElseThrow(() -> new IllegalStateException(format("Error while generating token for GitHub Api Url %s (installation id: %s)",
-        githubAppConfiguration.getApiEndpoint(), installationId)));
-  }
-
   private GithubProjectCreator createGithubProjectCreator(DbSession dbSession, DevOpsProjectDescriptor devOpsProjectDescriptor, AlmSettingDto almSettingDto,
     AppInstallationToken appInstallationToken) {
     LOG.info("DevOps configuration {} auto-detected for project {}", almSettingDto.getKey(), devOpsProjectDescriptor.projectIdentifier());
+    Optional<AppInstallationToken> authAppInstallationToken = getAuthAppInstallationTokenIfNecessary(devOpsProjectDescriptor);
+
     GithubProjectCreationParameters githubProjectCreationParameters = new GithubProjectCreationParameters(devOpsProjectDescriptor,
-      almSettingDto, appInstallationToken, projectDefaultVisibility.get(dbSession).isPrivate(), gitHubSettings.isProvisioningEnabled(), userSession);
+      almSettingDto, projectDefaultVisibility.get(dbSession).isPrivate(), gitHubSettings.isProvisioningEnabled(), userSession, appInstallationToken,
+      authAppInstallationToken.orElse(null));
     return new GithubProjectCreator(dbClient, githubApplicationClient, githubPermissionConverter, projectKeyGenerator, componentUpdater,
       githubProjectCreationParameters);
   }
 
   public Optional<DevOpsProjectCreator> getDevOpsProjectCreator(DbSession dbSession, AlmSettingDto almSettingDto, AccessToken accessToken,
     DevOpsProjectDescriptor devOpsProjectDescriptor) {
+
+    Optional<AppInstallationToken> authAppInstallationToken = getAuthAppInstallationTokenIfNecessary(devOpsProjectDescriptor);
     GithubProjectCreationParameters githubProjectCreationParameters = new GithubProjectCreationParameters(devOpsProjectDescriptor,
-      almSettingDto, accessToken, projectDefaultVisibility.get(dbSession).isPrivate(), gitHubSettings.isProvisioningEnabled(), userSession);
+      almSettingDto, projectDefaultVisibility.get(dbSession).isPrivate(), gitHubSettings.isProvisioningEnabled(), userSession, accessToken, authAppInstallationToken.orElse(null));
     return Optional.of(
       new GithubProjectCreator(dbClient, githubApplicationClient, githubPermissionConverter, projectKeyGenerator, componentUpdater, githubProjectCreationParameters)
     );
   }
 
+  private Optional<AppInstallationToken> getAuthAppInstallationTokenIfNecessary(DevOpsProjectDescriptor devOpsProjectDescriptor) {
+    if (gitHubSettings.isProvisioningEnabled()) {
+      GithubAppConfiguration githubAppConfiguration = new GithubAppConfiguration(Long.parseLong(gitHubSettings.appId()), gitHubSettings.privateKey(), gitHubSettings.apiURL());
+      long installationId = findInstallationIdToAccessRepo(githubAppConfiguration, devOpsProjectDescriptor.projectIdentifier())
+        .orElseThrow(() -> new IllegalStateException(format("GitHub auto-provisioning is activated. However the repo %s is not in the scope of the authentication application. "
+                                                            + "The permissions can't be checked, and the project can not be breated.",
+          devOpsProjectDescriptor.projectIdentifier())));
+      return Optional.of(generateAppInstallationToken(githubAppConfiguration, installationId));
+    }
+    return Optional.empty();
+  }
+
+  private Optional<Long> findInstallationIdToAccessRepo(GithubAppConfiguration githubAppConfiguration, String repositoryKey) {
+    return githubApplicationClient.getInstallationId(githubAppConfiguration, repositoryKey);
+  }
+
+  private AppInstallationToken generateAppInstallationToken(GithubAppConfiguration githubAppConfiguration, long installationId) {
+    return githubApplicationClient.createAppInstallationToken(githubAppConfiguration, installationId)
+      .orElseThrow(() -> new IllegalStateException(format("Error while generating token for GitHub Api Url %s (installation id: %s)",
+        githubAppConfiguration.getApiEndpoint(), installationId)));
+  }
+
 }
index 4095ba195b2551cd61aafe6165ce5fce2fabb24b..157162b093e5d94e768deef6787e12ec10a0717a 100644 (file)
@@ -52,6 +52,7 @@ import org.sonar.server.user.UserSession;
 
 import static java.lang.String.format;
 import static org.apache.commons.lang.StringUtils.defaultIfBlank;
+import static org.sonar.db.permission.GlobalPermission.SCAN;
 import static org.sonar.db.project.CreationMethod.SCANNER_API;
 import static org.sonar.db.project.CreationMethod.SCANNER_API_DEVOPS_AUTO_CONFIG;
 import static org.sonar.server.component.NewComponent.newComponentBuilder;
@@ -183,6 +184,9 @@ public class ReportSubmitter {
   }
 
   private boolean wouldCurrentUserHaveScanPermission(String projectKey, DbSession dbSession, @Nullable DevOpsProjectCreator devOpsProjectCreator) {
+    if (userSession.hasPermission(SCAN)) {
+      return true;
+    }
     if (managedInstanceService.isInstanceExternallyManaged() && devOpsProjectCreator != null) {
       return devOpsProjectCreator.isScanAllowedUsingPermissionsFromDevopsPlatform();
     }
index acb20e3d19e2bb7a8dc4ea8d70cf8768e980dcc8..53afeb698004203c092385b2a7fa83b445127f58 100644 (file)
@@ -99,6 +99,8 @@ public class GithubProjectCreatorFactoryTest {
 
   @Mock
   private AppInstallationToken appInstallationToken;
+  @Mock
+  private AppInstallationToken authAppInstallationToken;
 
   @InjectMocks
   private GithubProjectCreatorFactory githubProjectCreatorFactory;
@@ -143,13 +145,13 @@ public class GithubProjectCreatorFactoryTest {
     when(githubApplicationClient.createAppInstallationToken(any(), eq(APP_INSTALLATION_ID))).thenReturn(Optional.empty());
 
     assertThatIllegalStateException().isThrownBy(() -> githubProjectCreatorFactory.getDevOpsProjectCreator(dbSession, VALID_GITHUB_PROJECT_COORDINATES))
-      .withMessage("Error while generating token for GitHub Api Url null (installation id: 1)");
+      .withMessage("Error while generating token for GitHub Api Url null (installation id: 534534534543)");
   }
 
   @Test
   public void getDevOpsProjectCreator_whenOneValidAlmSetting_shouldInstantiateDevOpsProjectCreator() {
     AlmSettingDto almSettingDto = mockAlmSettingDto(true);
-    mockSuccesfullGithubInteraction();
+    mockSuccessfulGithubInteraction();
 
     DevOpsProjectCreator devOpsProjectCreator = githubProjectCreatorFactory.getDevOpsProjectCreator(dbSession, VALID_GITHUB_PROJECT_COORDINATES).orElseThrow();
 
@@ -158,12 +160,19 @@ public class GithubProjectCreatorFactoryTest {
   }
 
   @Test
-  public void getDevOpsProjectCreator_whenOneValidAlmSettingAndPublicByDefaultAndAutoProviisoningEnabled_shouldInstantiateDevOpsProjectCreator() {
+  public void getDevOpsProjectCreator_whenOneValidAlmSettingAndPublicByDefaultAndAutoProvisioningEnabled_shouldInstantiateDevOpsProjectCreatorAndDefineAnAuthAppToken() {
+    AlmSettingDto almSettingDto = mockAlmSettingDto(true);
+    mockSuccessfulGithubInteraction();
+
     when(projectDefaultVisibility.get(any()).isPrivate()).thenReturn(true);
     when(gitHubSettings.isProvisioningEnabled()).thenReturn(true);
+    when(gitHubSettings.appId()).thenReturn("4324");
+    when(gitHubSettings.privateKey()).thenReturn("privateKey");
+    when(gitHubSettings.apiURL()).thenReturn(GITHUB_API_URL);
 
-    AlmSettingDto almSettingDto = mockAlmSettingDto(true);
-    mockSuccesfullGithubInteraction();
+    long authAppInstallationId = 32;
+    when(githubApplicationClient.getInstallationId(any(), eq(GITHUB_REPO_FULL_NAME))).thenReturn(Optional.of(authAppInstallationId));
+    when(githubApplicationClient.createAppInstallationToken(any(), eq(authAppInstallationId))).thenReturn(Optional.of(authAppInstallationToken));
 
     DevOpsProjectCreator devOpsProjectCreator = githubProjectCreatorFactory.getDevOpsProjectCreator(dbSession, VALID_GITHUB_PROJECT_COORDINATES).orElseThrow();
 
@@ -177,7 +186,7 @@ public class GithubProjectCreatorFactoryTest {
     AlmSettingDto notMatchingAlmSettingDto = mockAlmSettingDto(false);
     when(dbClient.almSettingDao().selectByAlm(dbSession, ALM.GITHUB)).thenReturn(List.of(notMatchingAlmSettingDto, matchingAlmSettingDto));
 
-    mockSuccesfullGithubInteraction();
+    mockSuccessfulGithubInteraction();
 
     DevOpsProjectCreator devOpsProjectCreator = githubProjectCreatorFactory.getDevOpsProjectCreator(dbSession, VALID_GITHUB_PROJECT_COORDINATES).orElseThrow();
 
@@ -189,7 +198,7 @@ public class GithubProjectCreatorFactoryTest {
   public void getDevOpsProjectCreatorFromImport_shouldInstantiateDevOpsProjectCreator() {
     AlmSettingDto mockAlmSettingDto = mockAlmSettingDto(true);
 
-    mockSuccesfullGithubInteraction();
+    mockSuccessfulGithubInteraction();
 
     DevOpsProjectCreator devOpsProjectCreator = githubProjectCreatorFactory.getDevOpsProjectCreator(dbSession, mockAlmSettingDto, appInstallationToken, GITHUB_PROJECT_DESCRIPTOR)
       .orElseThrow();
@@ -198,15 +207,16 @@ public class GithubProjectCreatorFactoryTest {
     assertThat(devOpsProjectCreator).usingRecursiveComparison().isEqualTo(expectedGithubProjectCreator);
   }
 
-  private void mockSuccesfullGithubInteraction() {
+  private void mockSuccessfulGithubInteraction() {
     when(githubApplicationClient.getInstallationId(any(), eq(GITHUB_REPO_FULL_NAME))).thenReturn(Optional.of(APP_INSTALLATION_ID));
     when(githubApplicationClient.createAppInstallationToken(any(), eq(APP_INSTALLATION_ID))).thenReturn(Optional.of(appInstallationToken));
   }
 
   private GithubProjectCreator getExpectedGithubProjectCreator(AlmSettingDto almSettingDto, boolean projectsArePrivateByDefault, boolean isInstanceManaged) {
     DevOpsProjectDescriptor devOpsProjectDescriptor = new DevOpsProjectDescriptor(ALM.GITHUB, almSettingDto.getUrl(), GITHUB_REPO_FULL_NAME);
+    AppInstallationToken authAppInstallToken = isInstanceManaged ? authAppInstallationToken : null;
     GithubProjectCreationParameters githubProjectCreationParameters = new GithubProjectCreationParameters(devOpsProjectDescriptor,
-      almSettingDto, appInstallationToken, projectsArePrivateByDefault, isInstanceManaged, userSession);
+      almSettingDto, projectsArePrivateByDefault, isInstanceManaged, userSession, appInstallationToken, authAppInstallToken);
     return new GithubProjectCreator(dbClient, githubApplicationClient, githubPermissionConverter, projectKeyGenerator, componentUpdater, githubProjectCreationParameters);
   }
 
index 7cd0fce224614698699a88bc93c6f669a1254d98..7955bea449f86ca4eb940c15c69f8d121e7b6de6 100644 (file)
@@ -30,6 +30,7 @@ import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
+import org.sonar.alm.client.github.AppInstallationToken;
 import org.sonar.alm.client.github.GithubApplicationClient;
 import org.sonar.alm.client.github.api.GsonRepositoryCollaborator;
 import org.sonar.alm.client.github.api.GsonRepositoryTeam;
@@ -90,7 +91,9 @@ public class GithubProjectCreatorTest {
   @Mock
   private GithubProjectCreationParameters githubProjectCreationParameters;
   @Mock
-  private AccessToken appInstallationToken;
+  private AccessToken devOpsAppInstallationToken;
+  @Mock
+  private AppInstallationToken authAppInstallationToken;
   @Mock
   private UserSession userSession;
   @Mock
@@ -113,18 +116,21 @@ public class GithubProjectCreatorTest {
 
     when(githubProjectCreationParameters.devOpsProjectDescriptor()).thenReturn(DEVOPS_PROJECT_DESCRIPTOR);
     when(githubProjectCreationParameters.userSession()).thenReturn(userSession);
-    when(githubProjectCreationParameters.appInstallationToken()).thenReturn(appInstallationToken);
+    when(githubProjectCreationParameters.devOpsAppInstallationToken()).thenReturn(devOpsAppInstallationToken);
+    when(githubProjectCreationParameters.authAppInstallationToken()).thenReturn(authAppInstallationToken);
     when(githubProjectCreationParameters.almSettingDto()).thenReturn(almSettingDto);
 
     githubProjectCreator = new GithubProjectCreator(dbClient, githubApplicationClient, githubPermissionConverter, projectKeyGenerator, componentUpdater,
       githubProjectCreationParameters);
-
-   /* when(githubProjectCreationParameters.almSettingDto()).thenReturn();
-    when(githubProjectCreationParameters.almSettingDto()).thenReturn();
-    when(githubProjectCreationParameters.almSettingDto()).thenReturn();
-    when(githubProjectCreationParameters.almSettingDto()).thenReturn();*/
   }
 
+  @Test
+  public void isScanAllowedUsingPermissionsFromDevopsPlatform_whenNoAuthToken_throws() {
+    when(githubProjectCreationParameters.authAppInstallationToken()).thenReturn(null);
+
+    assertThatIllegalStateException().isThrownBy(() -> githubProjectCreator.isScanAllowedUsingPermissionsFromDevopsPlatform())
+      .withMessage("An auth app token is required in case repository permissions checking is necessary.");
+  }
   @Test
   public void isScanAllowedUsingPermissionsFromDevopsPlatform_whenUserIsNotAGitHubUser_returnsFalse() {
     assertThat(githubProjectCreator.isScanAllowedUsingPermissionsFromDevopsPlatform()).isFalse();
@@ -192,7 +198,7 @@ public class GithubProjectCreatorTest {
 
   private void mockGithubCollaboratorsFromApi(GsonRepositoryCollaborator... repositoryCollaborators) {
     Set<GsonRepositoryCollaborator> collaborators = Arrays.stream(repositoryCollaborators).collect(toSet());
-    when(githubApplicationClient.getRepositoryCollaborators(DEVOPS_PROJECT_DESCRIPTOR.url(), appInstallationToken, ORGANIZATION_NAME, REPOSITORY_NAME)).thenReturn(collaborators);
+    when(githubApplicationClient.getRepositoryCollaborators(DEVOPS_PROJECT_DESCRIPTOR.url(), authAppInstallationToken, ORGANIZATION_NAME, REPOSITORY_NAME)).thenReturn(collaborators);
   }
 
   private GsonRepositoryTeam mockGithubTeam(String name, int id, String role, String... sqPermissions) {
@@ -202,7 +208,7 @@ public class GithubProjectCreatorTest {
   }
 
   private void mockTeamsFromApi(GsonRepositoryTeam... repositoryTeams) {
-    when(githubApplicationClient.getRepositoryTeams(DEVOPS_PROJECT_DESCRIPTOR.url(), appInstallationToken, ORGANIZATION_NAME, REPOSITORY_NAME))
+    when(githubApplicationClient.getRepositoryTeams(DEVOPS_PROJECT_DESCRIPTOR.url(), authAppInstallationToken, ORGANIZATION_NAME, REPOSITORY_NAME))
       .thenReturn(Arrays.stream(repositoryTeams).collect(toSet()));
   }
 
@@ -323,7 +329,7 @@ public class GithubProjectCreatorTest {
     when(repository.getDefaultBranch()).thenReturn(MAIN_BRANCH_NAME);
     when(repository.getName()).thenReturn(REPOSITORY_NAME);
     when(repository.getFullName()).thenReturn(DEVOPS_PROJECT_DESCRIPTOR.projectIdentifier());
-    when(githubApplicationClient.getRepository(DEVOPS_PROJECT_DESCRIPTOR.url(), appInstallationToken, DEVOPS_PROJECT_DESCRIPTOR.projectIdentifier())).thenReturn(
+    when(githubApplicationClient.getRepository(DEVOPS_PROJECT_DESCRIPTOR.url(), devOpsAppInstallationToken, DEVOPS_PROJECT_DESCRIPTOR.projectIdentifier())).thenReturn(
       Optional.of(repository));
     when(projectKeyGenerator.generateUniqueProjectKey(repository.getFullName())).thenReturn("generated_" + DEVOPS_PROJECT_DESCRIPTOR.projectIdentifier());
   }