Browse Source

SONAR-20700 Use auth App installation token to check permissions on GH

tags/10.3.0.82913
Aurelien Poscia 7 months ago
parent
commit
b4d840ef7e

+ 36
- 14
server/sonar-webserver-webapi/src/it/java/org/sonar/server/almintegration/ws/github/ImportGithubProjectActionIT.java View 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");

+ 22
- 8
server/sonar-webserver-webapi/src/it/java/org/sonar/server/ce/queue/ReportSubmitterIT.java View 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;
}

+ 5
- 2
server/sonar-webserver-webapi/src/main/java/org/sonar/server/almsettings/ws/GithubProjectCreationParameters.java View File

@@ -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) {
}

+ 16
- 7
server/sonar-webserver-webapi/src/main/java/org/sonar/server/almsettings/ws/GithubProjectCreator.java View 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())));


+ 29
- 12
server/sonar-webserver-webapi/src/main/java/org/sonar/server/almsettings/ws/GithubProjectCreatorFactory.java View 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)));
}

}

+ 4
- 0
server/sonar-webserver-webapi/src/main/java/org/sonar/server/ce/queue/ReportSubmitter.java View 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();
}

+ 19
- 9
server/sonar-webserver-webapi/src/test/java/org/sonar/server/almsettings/ws/GithubProjectCreatorFactoryTest.java View 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);
}


+ 16
- 10
server/sonar-webserver-webapi/src/test/java/org/sonar/server/almsettings/ws/GithubProjectCreatorTest.java View 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());
}

Loading…
Cancel
Save