@@ -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"); |
@@ -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; | |||
} |
@@ -19,10 +19,13 @@ | |||
*/ | |||
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) { | |||
} |
@@ -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()))); | |||
@@ -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))); | |||
} | |||
} |
@@ -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(); | |||
} |
@@ -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); | |||
} | |||
@@ -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()); | |||
} |