]> source.dussan.org Git - sonarqube.git/blob
7cd0fce224614698699a88bc93c6f669a1254d98
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2023 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 package org.sonar.server.almsettings.ws;
21
22 import java.util.Arrays;
23 import java.util.Optional;
24 import java.util.Set;
25 import org.junit.Before;
26 import org.junit.Test;
27 import org.junit.runner.RunWith;
28 import org.mockito.Answers;
29 import org.mockito.ArgumentCaptor;
30 import org.mockito.Captor;
31 import org.mockito.Mock;
32 import org.mockito.junit.MockitoJUnitRunner;
33 import org.sonar.alm.client.github.GithubApplicationClient;
34 import org.sonar.alm.client.github.api.GsonRepositoryCollaborator;
35 import org.sonar.alm.client.github.api.GsonRepositoryTeam;
36 import org.sonar.alm.client.github.security.AccessToken;
37 import org.sonar.api.resources.Qualifiers;
38 import org.sonar.api.web.UserRole;
39 import org.sonar.auth.github.GithubPermissionConverter;
40 import org.sonar.auth.github.GsonRepositoryPermissions;
41 import org.sonar.db.DbClient;
42 import org.sonar.db.alm.setting.ALM;
43 import org.sonar.db.alm.setting.AlmSettingDto;
44 import org.sonar.db.alm.setting.ProjectAlmSettingDao;
45 import org.sonar.db.alm.setting.ProjectAlmSettingDto;
46 import org.sonar.db.project.CreationMethod;
47 import org.sonar.db.project.ProjectDto;
48 import org.sonar.db.provisioning.GithubPermissionsMappingDto;
49 import org.sonar.db.user.GroupDto;
50 import org.sonar.server.almintegration.ws.ProjectKeyGenerator;
51 import org.sonar.server.component.ComponentCreationData;
52 import org.sonar.server.component.ComponentCreationParameters;
53 import org.sonar.server.component.ComponentUpdater;
54 import org.sonar.server.component.NewComponent;
55 import org.sonar.server.user.UserSession;
56
57 import static java.util.Objects.requireNonNull;
58 import static java.util.stream.Collectors.toSet;
59 import static org.assertj.core.api.Assertions.assertThat;
60 import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
61 import static org.mockito.ArgumentMatchers.any;
62 import static org.mockito.ArgumentMatchers.eq;
63 import static org.mockito.Mockito.mock;
64 import static org.mockito.Mockito.verify;
65 import static org.mockito.Mockito.when;
66 import static org.sonar.db.project.CreationMethod.ALM_IMPORT_API;
67 import static org.sonar.db.project.CreationMethod.SCANNER_API_DEVOPS_AUTO_CONFIG;
68
69 @RunWith(MockitoJUnitRunner.class)
70 public class GithubProjectCreatorTest {
71
72   private static final String ORGANIZATION_NAME = "orga2";
73   private static final String REPOSITORY_NAME = "repo1";
74
75   private static final String MAIN_BRANCH_NAME = "defaultBranch";
76   private static final DevOpsProjectDescriptor DEVOPS_PROJECT_DESCRIPTOR = new DevOpsProjectDescriptor(ALM.GITHUB, "http://api.com", ORGANIZATION_NAME + "/" + REPOSITORY_NAME);
77   private static final String ALM_SETTING_KEY = "github_config_1";
78   private static final String USER_LOGIN = "userLogin";
79   private static final String USER_UUID = "userUuid";
80   @Mock(answer = Answers.RETURNS_DEEP_STUBS)
81   private DbClient dbClient;
82   @Mock
83   private GithubApplicationClient githubApplicationClient;
84   @Mock
85   private GithubPermissionConverter githubPermissionConverter;
86   @Mock
87   private ProjectKeyGenerator projectKeyGenerator;
88   @Mock
89   private ComponentUpdater componentUpdater;
90   @Mock
91   private GithubProjectCreationParameters githubProjectCreationParameters;
92   @Mock
93   private AccessToken appInstallationToken;
94   @Mock
95   private UserSession userSession;
96   @Mock
97   private AlmSettingDto almSettingDto;
98
99   private GithubProjectCreator githubProjectCreator;
100
101   @Captor
102   ArgumentCaptor<ComponentCreationParameters> componentCreationParametersCaptor;
103   @Captor
104   ArgumentCaptor<ProjectAlmSettingDto> projectAlmSettingDtoCaptor;
105
106   @Before
107   public void setup() {
108     when(userSession.getLogin()).thenReturn(USER_LOGIN);
109     when(userSession.getUuid()).thenReturn(USER_UUID);
110
111     when(almSettingDto.getUrl()).thenReturn(DEVOPS_PROJECT_DESCRIPTOR.url());
112     when(almSettingDto.getKey()).thenReturn(ALM_SETTING_KEY);
113
114     when(githubProjectCreationParameters.devOpsProjectDescriptor()).thenReturn(DEVOPS_PROJECT_DESCRIPTOR);
115     when(githubProjectCreationParameters.userSession()).thenReturn(userSession);
116     when(githubProjectCreationParameters.appInstallationToken()).thenReturn(appInstallationToken);
117     when(githubProjectCreationParameters.almSettingDto()).thenReturn(almSettingDto);
118
119     githubProjectCreator = new GithubProjectCreator(dbClient, githubApplicationClient, githubPermissionConverter, projectKeyGenerator, componentUpdater,
120       githubProjectCreationParameters);
121
122    /* when(githubProjectCreationParameters.almSettingDto()).thenReturn();
123     when(githubProjectCreationParameters.almSettingDto()).thenReturn();
124     when(githubProjectCreationParameters.almSettingDto()).thenReturn();
125     when(githubProjectCreationParameters.almSettingDto()).thenReturn();*/
126   }
127
128   @Test
129   public void isScanAllowedUsingPermissionsFromDevopsPlatform_whenUserIsNotAGitHubUser_returnsFalse() {
130     assertThat(githubProjectCreator.isScanAllowedUsingPermissionsFromDevopsPlatform()).isFalse();
131   }
132
133   @Test
134   public void isScanAllowedUsingPermissionsFromDevopsPlatform_whenCollaboratorHasDirectAccessButNoScanPermissions_returnsFalse() {
135     GsonRepositoryCollaborator collaborator1 = mockCollaborator("collaborator1", 1, "role1", "read", "admin");
136     mockGithubCollaboratorsFromApi(collaborator1);
137     bindSessionToCollaborator(collaborator1);
138
139     assertThat(githubProjectCreator.isScanAllowedUsingPermissionsFromDevopsPlatform()).isFalse();
140   }
141
142   @Test
143   public void isScanAllowedUsingPermissionsFromDevopsPlatform_whenCollaboratorHasDirectAccess_returnsTrue() {
144     GsonRepositoryCollaborator collaborator1 = mockCollaborator("collaborator1", 1, "role1", "read", "admin");
145     GsonRepositoryCollaborator collaborator2 = mockCollaborator("collaborator2", 2, "role2", "read", "scan");
146     mockGithubCollaboratorsFromApi(collaborator1, collaborator2);
147     bindSessionToCollaborator(collaborator2);
148
149     assertThat(githubProjectCreator.isScanAllowedUsingPermissionsFromDevopsPlatform()).isTrue();
150   }
151
152   @Test
153   public void isScanAllowedUsingPermissionsFromDevopsPlatform_whenAccessViaTeamButNoScanPermissions_returnsFalse() {
154     GsonRepositoryTeam team2 = mockGithubTeam("team2", 2, "role2", "another_perm", UserRole.ADMIN);
155     mockTeamsFromApi(team2);
156     bindGroupsToUser(team2.name());
157
158     assertThat(githubProjectCreator.isScanAllowedUsingPermissionsFromDevopsPlatform()).isFalse();
159   }
160
161   @Test
162   public void isScanAllowedUsingPermissionsFromDevopsPlatform_whenAccessViaTeam_returnsTrue() {
163     GsonRepositoryTeam team1 = mockGithubTeam("team1", 1, "role1", "read", "another_perm");
164     GsonRepositoryTeam team2 = mockGithubTeam("team2", 2, "role2", "another_perm", UserRole.SCAN);
165     mockTeamsFromApi(team1, team2);
166     bindGroupsToUser(team1.name(), team2.name());
167
168     assertThat(githubProjectCreator.isScanAllowedUsingPermissionsFromDevopsPlatform()).isTrue();
169   }
170
171   @Test
172   public void isScanAllowedUsingPermissionsFromDevopsPlatform_whenAccessViaTeamButUserNotInTeam_returnsFalse() {
173     GsonRepositoryTeam team1 = mockGithubTeam("team1", 1, "role1", "read", "another_perm");
174     GsonRepositoryTeam team2 = mockGithubTeam("team2", 2, "role2", "another_perm", UserRole.SCAN);
175     mockTeamsFromApi(team1, team2);
176     bindGroupsToUser(team1.name());
177
178     assertThat(githubProjectCreator.isScanAllowedUsingPermissionsFromDevopsPlatform()).isFalse();
179   }
180
181   private void bindSessionToCollaborator(GsonRepositoryCollaborator collaborator1) {
182     UserSession.ExternalIdentity externalIdentity = new UserSession.ExternalIdentity(String.valueOf(collaborator1.id()), collaborator1.name());
183     when(userSession.getExternalIdentity()).thenReturn(Optional.of(externalIdentity));
184   }
185
186   private GsonRepositoryCollaborator mockCollaborator(String collaboratorLogin, int id, String role1, String... sqPermissions) {
187     GsonRepositoryCollaborator collaborator = new GsonRepositoryCollaborator(collaboratorLogin, id, role1,
188       new GsonRepositoryPermissions(false, false, false, false, false));
189     mockPermissionsConversion(collaborator, sqPermissions);
190     return collaborator;
191   }
192
193   private void mockGithubCollaboratorsFromApi(GsonRepositoryCollaborator... repositoryCollaborators) {
194     Set<GsonRepositoryCollaborator> collaborators = Arrays.stream(repositoryCollaborators).collect(toSet());
195     when(githubApplicationClient.getRepositoryCollaborators(DEVOPS_PROJECT_DESCRIPTOR.url(), appInstallationToken, ORGANIZATION_NAME, REPOSITORY_NAME)).thenReturn(collaborators);
196   }
197
198   private GsonRepositoryTeam mockGithubTeam(String name, int id, String role, String... sqPermissions) {
199     GsonRepositoryTeam gsonRepositoryTeam = new GsonRepositoryTeam(name, id, name + "slug", role, new GsonRepositoryPermissions(false, false, false, false, false));
200     mockPermissionsConversion(gsonRepositoryTeam, sqPermissions);
201     return gsonRepositoryTeam;
202   }
203
204   private void mockTeamsFromApi(GsonRepositoryTeam... repositoryTeams) {
205     when(githubApplicationClient.getRepositoryTeams(DEVOPS_PROJECT_DESCRIPTOR.url(), appInstallationToken, ORGANIZATION_NAME, REPOSITORY_NAME))
206       .thenReturn(Arrays.stream(repositoryTeams).collect(toSet()));
207   }
208
209   private void mockPermissionsConversion(GsonRepositoryCollaborator collaborator, String... sqPermissions) {
210     Set<GithubPermissionsMappingDto> githubPermissionsMappingDtos = mockPermissionsMappingsDtos();
211     when(githubPermissionConverter.toSonarqubeRolesWithFallbackOnRepositoryPermissions(githubPermissionsMappingDtos, collaborator.roleName(), collaborator.permissions()))
212       .thenReturn(Arrays.stream(sqPermissions).collect(toSet()));
213   }
214
215   private void mockPermissionsConversion(GsonRepositoryTeam team, String... sqPermissions) {
216     Set<GithubPermissionsMappingDto> githubPermissionsMappingDtos = mockPermissionsMappingsDtos();
217     when(githubPermissionConverter.toSonarqubeRolesWithFallbackOnRepositoryPermissions(githubPermissionsMappingDtos, team.permission(), team.permissions()))
218       .thenReturn(Arrays.stream(sqPermissions).collect(toSet()));
219   }
220
221   private Set<GithubPermissionsMappingDto> mockPermissionsMappingsDtos() {
222     Set<GithubPermissionsMappingDto> githubPermissionsMappingDtos = Set.of(mock(GithubPermissionsMappingDto.class));
223     when(dbClient.githubPermissionsMappingDao().findAll(any())).thenReturn(githubPermissionsMappingDtos);
224     return githubPermissionsMappingDtos;
225   }
226
227   private void bindGroupsToUser(String... groupNames) {
228     Set<GroupDto> groupDtos = Arrays.stream(groupNames)
229       .map(groupName -> new GroupDto().setName(ORGANIZATION_NAME + "/" + groupName).setUuid("uuid_" + groupName))
230       .collect(toSet());
231     when(userSession.getGroups()).thenReturn(groupDtos);
232   }
233
234   @Test
235   public void createProjectAndBindToDevOpsPlatform_whenRepoNotFound_throws() {
236     assertThatIllegalStateException().isThrownBy(
237         () -> githubProjectCreator.createProjectAndBindToDevOpsPlatform(mock(), SCANNER_API_DEVOPS_AUTO_CONFIG, null))
238       .withMessage("Impossible to find the repository 'orga2/repo1' on GitHub, using the devops config " + ALM_SETTING_KEY);
239   }
240
241   @Test
242   public void createProjectAndBindToDevOpsPlatformFromScanner_whenRepoFoundOnGitHub_successfullyCreatesProject() {
243     // given
244     mockGitHubRepository();
245
246     ComponentCreationData componentCreationData = mockProjectCreation("generated_orga2/repo1");
247     ProjectAlmSettingDao projectAlmSettingDao = mock();
248     when(dbClient.projectAlmSettingDao()).thenReturn(projectAlmSettingDao);
249
250     // when
251     ComponentCreationData actualComponentCreationData = githubProjectCreator.createProjectAndBindToDevOpsPlatform(dbClient.openSession(true),
252       SCANNER_API_DEVOPS_AUTO_CONFIG, null);
253
254     // then
255     assertThat(actualComponentCreationData).isEqualTo(componentCreationData);
256
257     ComponentCreationParameters componentCreationParameters = componentCreationParametersCaptor.getValue();
258     assertComponentCreationParametersContainsCorrectInformation(componentCreationParameters, "generated_orga2/repo1", SCANNER_API_DEVOPS_AUTO_CONFIG);
259     assertThat(componentCreationParameters.isManaged()).isFalse();
260     assertThat(componentCreationParameters.newComponent().isPrivate()).isFalse();
261
262     verify(projectAlmSettingDao).insertOrUpdate(any(), projectAlmSettingDtoCaptor.capture(), eq(ALM_SETTING_KEY), eq(REPOSITORY_NAME), eq("generated_orga2/repo1"));
263     ProjectAlmSettingDto projectAlmSettingDto = projectAlmSettingDtoCaptor.getValue();
264     assertAlmSettingsDtoContainsCorrectInformation(almSettingDto, requireNonNull(componentCreationData.projectDto()), projectAlmSettingDto);
265   }
266
267   @Test
268   public void createProjectAndBindToDevOpsPlatformFromApi_whenRepoFoundOnGitHub_successfullyCreatesProject() {
269     // given
270     String projectKey = "customProjectKey";
271     mockGitHubRepository();
272
273     ComponentCreationData componentCreationData = mockProjectCreation(projectKey);
274     ProjectAlmSettingDao projectAlmSettingDao = mock();
275     when(dbClient.projectAlmSettingDao()).thenReturn(projectAlmSettingDao);
276
277     // when
278     ComponentCreationData actualComponentCreationData = githubProjectCreator.createProjectAndBindToDevOpsPlatform(dbClient.openSession(true), ALM_IMPORT_API, projectKey);
279
280     // then
281     assertThat(actualComponentCreationData).isEqualTo(componentCreationData);
282
283     ComponentCreationParameters componentCreationParameters = componentCreationParametersCaptor.getValue();
284     assertComponentCreationParametersContainsCorrectInformation(componentCreationParameters, projectKey, ALM_IMPORT_API);
285     assertThat(componentCreationParameters.isManaged()).isFalse();
286     assertThat(componentCreationParameters.newComponent().isPrivate()).isFalse();
287
288     verify(projectAlmSettingDao).insertOrUpdate(any(), projectAlmSettingDtoCaptor.capture(), eq(ALM_SETTING_KEY), eq(REPOSITORY_NAME), eq(projectKey));
289     ProjectAlmSettingDto projectAlmSettingDto = projectAlmSettingDtoCaptor.getValue();
290     assertAlmSettingsDtoContainsCorrectInformation(almSettingDto, requireNonNull(componentCreationData.projectDto()), projectAlmSettingDto);
291   }
292
293   public void createProjectAndBindToDevOpsPlatformFromApi_whenRepoFoundOnGitHubAutoProvisioningOnAndRepoPrivate_successfullyCreatesProject() {
294     // given
295     when(githubProjectCreationParameters.projectsArePrivateByDefault()).thenReturn(true);
296     when(githubProjectCreationParameters.isProvisioningEnabled()).thenReturn(true);
297
298     String projectKey = "customProjectKey";
299     mockGitHubRepository();
300
301     ComponentCreationData componentCreationData = mockProjectCreation(projectKey);
302     ProjectAlmSettingDao projectAlmSettingDao = mock();
303     when(dbClient.projectAlmSettingDao()).thenReturn(projectAlmSettingDao);
304
305     // when
306     ComponentCreationData actualComponentCreationData = githubProjectCreator.createProjectAndBindToDevOpsPlatform(dbClient.openSession(true), ALM_IMPORT_API, projectKey);
307
308     // then
309     assertThat(actualComponentCreationData).isEqualTo(componentCreationData);
310
311     ComponentCreationParameters componentCreationParameters = componentCreationParametersCaptor.getValue();
312     assertComponentCreationParametersContainsCorrectInformation(componentCreationParameters, projectKey, ALM_IMPORT_API);
313     assertThat(componentCreationParameters.isManaged()).isTrue();
314     assertThat(componentCreationParameters.newComponent().isPrivate()).isTrue();
315
316     verify(projectAlmSettingDao).insertOrUpdate(any(), projectAlmSettingDtoCaptor.capture(), eq(ALM_SETTING_KEY), eq(REPOSITORY_NAME), eq(projectKey));
317     ProjectAlmSettingDto projectAlmSettingDto = projectAlmSettingDtoCaptor.getValue();
318     assertAlmSettingsDtoContainsCorrectInformation(almSettingDto, requireNonNull(componentCreationData.projectDto()), projectAlmSettingDto);
319   }
320
321   private void mockGitHubRepository() {
322     GithubApplicationClient.Repository repository = mock();
323     when(repository.getDefaultBranch()).thenReturn(MAIN_BRANCH_NAME);
324     when(repository.getName()).thenReturn(REPOSITORY_NAME);
325     when(repository.getFullName()).thenReturn(DEVOPS_PROJECT_DESCRIPTOR.projectIdentifier());
326     when(githubApplicationClient.getRepository(DEVOPS_PROJECT_DESCRIPTOR.url(), appInstallationToken, DEVOPS_PROJECT_DESCRIPTOR.projectIdentifier())).thenReturn(
327       Optional.of(repository));
328     when(projectKeyGenerator.generateUniqueProjectKey(repository.getFullName())).thenReturn("generated_" + DEVOPS_PROJECT_DESCRIPTOR.projectIdentifier());
329   }
330
331   private ComponentCreationData mockProjectCreation(String projectKey) {
332     ComponentCreationData componentCreationData = mock();
333     ProjectDto projectDto = mockProjectDto(projectKey);
334     when(componentCreationData.projectDto()).thenReturn(projectDto);
335     when(componentUpdater.createWithoutCommit(any(), componentCreationParametersCaptor.capture())).thenReturn(componentCreationData);
336     return componentCreationData;
337   }
338
339   private static ProjectDto mockProjectDto(String projectKey) {
340     ProjectDto projectDto = mock();
341     when(projectDto.getName()).thenReturn(REPOSITORY_NAME);
342     when(projectDto.getKey()).thenReturn(projectKey);
343     when(projectDto.getUuid()).thenReturn("project-uuid-1");
344     return projectDto;
345   }
346
347   private static void assertComponentCreationParametersContainsCorrectInformation(ComponentCreationParameters componentCreationParameters, String expectedKey,
348     CreationMethod expectedCreationMethod) {
349     assertThat(componentCreationParameters.creationMethod()).isEqualTo(expectedCreationMethod);
350     assertThat(componentCreationParameters.mainBranchName()).isEqualTo(MAIN_BRANCH_NAME);
351     assertThat(componentCreationParameters.userLogin()).isEqualTo(USER_LOGIN);
352     assertThat(componentCreationParameters.userUuid()).isEqualTo(USER_UUID);
353
354     NewComponent newComponent = componentCreationParameters.newComponent();
355     assertThat(newComponent.isProject()).isTrue();
356     assertThat(newComponent.qualifier()).isEqualTo(Qualifiers.PROJECT);
357     assertThat(newComponent.key()).isEqualTo(expectedKey);
358     assertThat(newComponent.name()).isEqualTo(REPOSITORY_NAME);
359   }
360
361   private static void assertAlmSettingsDtoContainsCorrectInformation(AlmSettingDto almSettingDto, ProjectDto projectDto, ProjectAlmSettingDto projectAlmSettingDto) {
362     assertThat(projectAlmSettingDto.getAlmRepo()).isEqualTo(DEVOPS_PROJECT_DESCRIPTOR.projectIdentifier());
363     assertThat(projectAlmSettingDto.getAlmSlug()).isNull();
364     assertThat(projectAlmSettingDto.getAlmSettingUuid()).isEqualTo(almSettingDto.getUuid());
365     assertThat(projectAlmSettingDto.getProjectUuid()).isEqualTo(projectDto.getUuid());
366     assertThat(projectAlmSettingDto.getMonorepo()).isFalse();
367     assertThat(projectAlmSettingDto.getSummaryCommentEnabled()).isTrue();
368   }
369 }