aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorAurelien Poscia <aurelien.poscia@sonarsource.com>2023-08-07 17:12:45 +0200
committersonartech <sonartech@sonarsource.com>2023-08-09 20:03:37 +0000
commit8a7995fb4e79c7fd5b0a22401fcaabb2db6c5a06 (patch)
tree3d11edcbe1c288a84c8478921bdd2929fe43f787 /server
parent2f2b64a42548a18b6fbbbb6549f98555424f5e5c (diff)
downloadsonarqube-8a7995fb4e79c7fd5b0a22401fcaabb2db6c5a06.tar.gz
sonarqube-8a7995fb4e79c7fd5b0a22401fcaabb2db6c5a06.zip
SONAR-19789 schedule permissions sync task upon project onboarding
Diffstat (limited to 'server')
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskTypes.java2
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/management/DelegatingManagedServices.java6
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/management/ManagedProjectService.java1
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/project/VisibilityService.java17
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/management/DelegatingManagedServicesTest.java44
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/project/VisibilityServiceTest.java104
-rw-r--r--server/sonar-webserver-webapi/src/it/java/org/sonar/server/almintegration/ws/github/ImportGithubProjectActionIT.java5
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/github/ImportGithubProjectAction.java10
8 files changed, 173 insertions, 16 deletions
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskTypes.java b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskTypes.java
index 42c7f85ee96..356139f405b 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskTypes.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskTypes.java
@@ -26,6 +26,8 @@ public final class CeTaskTypes {
public static final String REPORT = "REPORT";
public static final String PROJECT_EXPORT = "PROJECT_EXPORT";
+ public static final String GITHUB_PROJECT_PERMISSIONS_PROVISIONING = "GITHUB_PROJECT_PERMISSIONS_PROVISIONING";
+
private CeTaskTypes() {
// only statics
}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/management/DelegatingManagedServices.java b/server/sonar-server-common/src/main/java/org/sonar/server/management/DelegatingManagedServices.java
index 2a76ad5779c..26d0f049341 100644
--- a/server/sonar-server-common/src/main/java/org/sonar/server/management/DelegatingManagedServices.java
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/management/DelegatingManagedServices.java
@@ -124,6 +124,12 @@ public class DelegatingManagedServices implements ManagedInstanceService, Manage
.orElse(false);
}
+ @Override
+ public void queuePermissionSyncTask(String submitterUuid, String componentUuid, String projectUuid) {
+ findManagedProjectService()
+ .ifPresent(managedProjectService -> managedProjectService.queuePermissionSyncTask(submitterUuid, componentUuid, projectUuid));
+ }
+
private Optional<ManagedProjectService> findManagedProjectService() {
return findManagedInstanceService()
.filter(ManagedProjectService.class::isInstance)
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/management/ManagedProjectService.java b/server/sonar-server-common/src/main/java/org/sonar/server/management/ManagedProjectService.java
index 4679195554e..9b4c3b88b80 100644
--- a/server/sonar-server-common/src/main/java/org/sonar/server/management/ManagedProjectService.java
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/management/ManagedProjectService.java
@@ -29,4 +29,5 @@ public interface ManagedProjectService {
boolean isProjectManaged(DbSession dbSession, String projectUuid);
+ void queuePermissionSyncTask(String submitterUuid, String componentUuid, String projectUuid);
}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/project/VisibilityService.java b/server/sonar-server-common/src/main/java/org/sonar/server/project/VisibilityService.java
index 8768b1d5625..2e4eccf5f87 100644
--- a/server/sonar-server-common/src/main/java/org/sonar/server/project/VisibilityService.java
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/project/VisibilityService.java
@@ -19,6 +19,7 @@
*/
package org.sonar.server.project;
+import com.google.common.annotations.VisibleForTesting;
import java.util.Optional;
import org.sonar.api.ce.ComputeEngineSide;
import org.sonar.api.server.ServerSide;
@@ -38,6 +39,7 @@ import static java.util.Collections.singletonList;
import static java.util.Optional.ofNullable;
import static org.sonar.api.utils.Preconditions.checkState;
import static org.sonar.api.web.UserRole.PUBLIC_PERMISSIONS;
+import static org.sonar.db.ce.CeTaskTypes.GITHUB_PROJECT_PERMISSIONS_PROVISIONING;
@ServerSide
@ComputeEngineSide
@@ -66,14 +68,19 @@ public class VisibilityService {
}
}
}
- private void checkNoPendingTasks(DbSession dbSession, EntityDto entityDto) {
- //This check likely can be removed when we remove the column 'private' from components table
- checkState(noPendingTask(dbSession, entityDto.getKey()), "Component visibility can't be changed as long as it has background task(s) pending or in progress");
+
+ @VisibleForTesting
+ void checkNoPendingTasks(DbSession dbSession, EntityDto entityDto) {
+ //This check likely can be removed when we remove the column 'private' from components table in SONAR-20126.
+ checkState(countPendingTask(dbSession, entityDto.getKey()) == 0, "Component visibility can't be changed as long as it has background task(s) pending or in progress");
}
- private boolean noPendingTask(DbSession dbSession, String entityKey) {
+ private long countPendingTask(DbSession dbSession, String entityKey) {
EntityDto entityDto = dbClient.entityDao().selectByKey(dbSession, entityKey).orElseThrow(() -> new IllegalStateException("Can't find entity " + entityKey));
- return dbClient.ceQueueDao().selectByEntityUuid(dbSession, entityDto.getUuid()).isEmpty();
+ return dbClient.ceQueueDao().selectByEntityUuid(dbSession, entityDto.getUuid())
+ .stream()
+ .filter(task -> !task.getTaskType().equals(GITHUB_PROJECT_PERMISSIONS_PROVISIONING))
+ .count();
}
private void setPrivateForRootComponentUuid(DbSession dbSession, EntityDto entity, boolean newIsPrivate) {
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/management/DelegatingManagedServicesTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/management/DelegatingManagedServicesTest.java
index 44ee3c5c674..92e29cb833c 100644
--- a/server/sonar-server-common/src/test/java/org/sonar/server/management/DelegatingManagedServicesTest.java
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/management/DelegatingManagedServicesTest.java
@@ -30,9 +30,14 @@ import org.sonar.db.DbSession;
import static java.util.Collections.emptySet;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
+import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
@@ -46,9 +51,7 @@ public class DelegatingManagedServicesTest {
@Test
public void getProviderName_whenNotManaged_shouldThrow() {
- DelegatingManagedServices managedInstanceService = NO_MANAGED_SERVICES;
-
- assertThatThrownBy(managedInstanceService::getProviderName)
+ assertThatThrownBy(NO_MANAGED_SERVICES::getProviderName)
.isInstanceOf(IllegalStateException.class)
.hasMessage("This instance is not managed.");
}
@@ -62,8 +65,7 @@ public class DelegatingManagedServicesTest {
@Test
public void isInstanceExternallyManaged_whenNoManagedInstanceService_returnsFalse() {
- DelegatingManagedServices managedInstanceService = NO_MANAGED_SERVICES;
- assertThat(managedInstanceService.isInstanceExternallyManaged()).isFalse();
+ assertThat(NO_MANAGED_SERVICES.isInstanceExternallyManaged()).isFalse();
}
@Test
@@ -85,9 +87,8 @@ public class DelegatingManagedServicesTest {
@Test
public void getUserUuidToManaged_whenNoDelegates_setAllUsersAsNonManaged() {
Set<String> userUuids = Set.of("a", "b");
- DelegatingManagedServices managedInstanceService = NO_MANAGED_SERVICES;
- Map<String, Boolean> userUuidToManaged = managedInstanceService.getUserUuidToManaged(dbSession, userUuids);
+ Map<String, Boolean> userUuidToManaged = NO_MANAGED_SERVICES.getUserUuidToManaged(dbSession, userUuids);
assertThat(userUuidToManaged).containsExactlyInAnyOrderEntriesOf(Map.of("a", false, "b", false));
}
@@ -257,9 +258,24 @@ public class DelegatingManagedServicesTest {
@Test
public void isProjectManaged_whenManagedNoInstanceServices_returnsFalse() {
- DelegatingManagedServices managedInstanceService = NO_MANAGED_SERVICES;
+ assertThat(NO_MANAGED_SERVICES.isProjectManaged(dbSession, "whatever")).isFalse();
+ }
+
+ @Test
+ public void queuePermissionSyncTask_whenManagedNoInstanceServices_doesNotFail() {
+ assertThatNoException().isThrownBy(() -> NO_MANAGED_SERVICES.queuePermissionSyncTask("userUuid", "componentUuid", "projectUuid"));
+ }
- assertThat(managedInstanceService.isProjectManaged(dbSession, "whatever")).isFalse();
+ @Test
+ public void queuePermissionSyncTask_whenManagedInstanceServices_shouldDelegatesToRightService() {
+ NeverManagedInstanceService neverManagedInstanceService = spy(new NeverManagedInstanceService());
+ AlwaysManagedInstanceService alwaysManagedInstanceService = spy(new AlwaysManagedInstanceService());
+ Set<ManagedInstanceService> delegates = Set.of(neverManagedInstanceService, alwaysManagedInstanceService);
+ DelegatingManagedServices managedInstanceService = new DelegatingManagedServices(delegates);
+
+ managedInstanceService.queuePermissionSyncTask("userUuid", "componentUuid", "projectUuid");
+ verify(neverManagedInstanceService, never()).queuePermissionSyncTask(anyString(), anyString(), anyString());
+ verify(alwaysManagedInstanceService).queuePermissionSyncTask("userUuid", "componentUuid", "projectUuid");
}
private static class NeverManagedInstanceService implements ManagedInstanceService, ManagedProjectService {
@@ -313,6 +329,11 @@ public class DelegatingManagedServicesTest {
public boolean isProjectManaged(DbSession dbSession, String projectUuid) {
return false;
}
+
+ @Override
+ public void queuePermissionSyncTask(String submitterUuid, String componentUuid, String projectUuid) {
+
+ }
}
private static class AlwaysManagedInstanceService implements ManagedInstanceService, ManagedProjectService {
@@ -366,6 +387,11 @@ public class DelegatingManagedServicesTest {
public boolean isProjectManaged(DbSession dbSession, String projectUuid) {
return true;
}
+
+ @Override
+ public void queuePermissionSyncTask(String submitterUuid, String componentUuid, String projectUuid) {
+
+ }
}
}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/project/VisibilityServiceTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/project/VisibilityServiceTest.java
new file mode 100644
index 00000000000..d31a8658038
--- /dev/null
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/project/VisibilityServiceTest.java
@@ -0,0 +1,104 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.project;
+
+import java.util.List;
+import java.util.Optional;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.ce.CeQueueDto;
+import org.sonar.db.entity.EntityDto;
+
+import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.db.ce.CeTaskTypes.GITHUB_PROJECT_PERMISSIONS_PROVISIONING;
+
+@RunWith(MockitoJUnitRunner.class)
+public class VisibilityServiceTest {
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private DbClient dbClient;
+
+ @Mock
+ private DbSession dbSession;
+
+ @InjectMocks
+ private VisibilityService visibilityService;
+
+ @Test
+ public void checkNoPendingTasks_whenEntityNotFound_throwsIae() {
+ EntityDto entityDto = mockEntityDto();
+ when(dbClient.entityDao().selectByKey(dbSession, entityDto.getKey())).thenReturn(Optional.empty());
+
+ assertThatIllegalStateException()
+ .isThrownBy(() -> visibilityService.checkNoPendingTasks(dbSession, entityDto))
+ .withMessage("Can't find entity entityKey");
+ }
+
+ @Test
+ public void checkNoPendingTasks_whenEntityFoundAndNoTaskInQueue_doesNotThrow() {
+ EntityDto entityDto = mockEntityDto();
+ when(dbClient.entityDao().selectByKey(dbSession, entityDto.getKey())).thenReturn(Optional.of(entityDto));
+
+ visibilityService.checkNoPendingTasks(dbSession, entityDto);
+ }
+
+ @Test
+ public void checkNoPendingTasks_whenOneGithubSyncTaskInQueue_doesNotThrow() {
+ EntityDto entityDto = mockEntityDto();
+ when(dbClient.entityDao().selectByKey(dbSession, entityDto.getKey())).thenReturn(Optional.of(entityDto));
+
+ mockCeQueueDto(GITHUB_PROJECT_PERMISSIONS_PROVISIONING, entityDto.getKey());
+
+ visibilityService.checkNoPendingTasks(dbSession, entityDto);
+ }
+
+ @Test
+ public void checkNoPendingTasks_whenAnyOtherTaskInQueue_throws() {
+ EntityDto entityDto = mockEntityDto();
+ when(dbClient.entityDao().selectByKey(dbSession, entityDto.getKey())).thenReturn(Optional.of(entityDto));
+
+ mockCeQueueDto("ANYTHING", entityDto.getUuid());
+
+ assertThatIllegalStateException()
+ .isThrownBy(() -> visibilityService.checkNoPendingTasks(dbSession, entityDto))
+ .withMessage("Component visibility can't be changed as long as it has background task(s) pending or in progress");
+ }
+
+ private void mockCeQueueDto(String taskType, String entityDto) {
+ CeQueueDto ceQueueDto = mock(CeQueueDto.class);
+ when(ceQueueDto.getTaskType()).thenReturn(taskType);
+ when(dbClient.ceQueueDao().selectByEntityUuid(dbSession, entityDto)).thenReturn(List.of(ceQueueDto));
+ }
+
+ private static EntityDto mockEntityDto() {
+ EntityDto entityDto = mock(EntityDto.class);
+ when(entityDto.getKey()).thenReturn("entityKey");
+ when(entityDto.getUuid()).thenReturn("entityUuid");
+ return entityDto;
+ }
+}
diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/almintegration/ws/github/ImportGithubProjectActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/almintegration/ws/github/ImportGithubProjectActionIT.java
index 140d53a9c91..5552328191c 100644
--- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/almintegration/ws/github/ImportGithubProjectActionIT.java
+++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/almintegration/ws/github/ImportGithubProjectActionIT.java
@@ -54,6 +54,7 @@ import org.sonar.server.es.TestIndexers;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.favorite.FavoriteUpdater;
+import org.sonar.server.management.ManagedProjectService;
import org.sonar.server.newcodeperiod.NewCodeDefinitionResolver;
import org.sonar.server.permission.GroupPermissionChanger;
import org.sonar.server.permission.PermissionService;
@@ -123,7 +124,8 @@ public class ImportGithubProjectActionIT {
private final GitHubSettings gitHubSettings = mock(GitHubSettings.class);
private NewCodeDefinitionResolver newCodeDefinitionResolver = new NewCodeDefinitionResolver(db.getDbClient(), editionProvider);
- private final WsActionTester ws = new WsActionTester(new ImportGithubProjectAction(db.getDbClient(), userSession,
+ private final ManagedProjectService managedProjectService = mock(ManagedProjectService.class);
+ private final WsActionTester ws = new WsActionTester(new ImportGithubProjectAction(db.getDbClient(), managedProjectService, userSession,
projectDefaultVisibility, appClient, componentUpdater, importHelper, projectKeyGenerator, newCodeDefinitionResolver,
defaultBranchNameResolver, gitHubSettings));
@@ -161,6 +163,7 @@ public class ImportGithubProjectActionIT {
assertThat(mainBranch).isPresent();
assertThat(mainBranch.get().getKey()).isEqualTo("default-branch");
+ verify(managedProjectService).queuePermissionSyncTask(userSession.getUuid(), mainBranch.get().getUuid() , projectDto.get().getUuid());
}
@Test
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/github/ImportGithubProjectAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/github/ImportGithubProjectAction.java
index f59b4cf84c6..41e77913588 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/github/ImportGithubProjectAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/github/ImportGithubProjectAction.java
@@ -19,6 +19,7 @@
*/
package org.sonar.server.almintegration.ws.github;
+import java.util.Objects;
import java.util.Optional;
import javax.inject.Inject;
import org.sonar.alm.client.github.GithubApplicationClient;
@@ -44,6 +45,7 @@ import org.sonar.server.component.ComponentCreationData;
import org.sonar.server.component.ComponentUpdater;
import org.sonar.server.component.NewComponent;
import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.management.ManagedProjectService;
import org.sonar.server.newcodeperiod.NewCodeDefinitionResolver;
import org.sonar.server.project.DefaultBranchNameResolver;
import org.sonar.server.project.ProjectDefaultVisibility;
@@ -68,6 +70,8 @@ public class ImportGithubProjectAction implements AlmIntegrationsWsAction {
public static final String PARAM_REPOSITORY_KEY = "repositoryKey";
private final DbClient dbClient;
+
+ private final ManagedProjectService managedProjectService;
private final UserSession userSession;
private final ProjectDefaultVisibility projectDefaultVisibility;
private final GithubApplicationClient githubApplicationClient;
@@ -82,11 +86,12 @@ public class ImportGithubProjectAction implements AlmIntegrationsWsAction {
private final GitHubSettings gitHubSettings;
@Inject
- public ImportGithubProjectAction(DbClient dbClient, UserSession userSession, ProjectDefaultVisibility projectDefaultVisibility,
+ public ImportGithubProjectAction(DbClient dbClient, ManagedProjectService managedProjectService, UserSession userSession, ProjectDefaultVisibility projectDefaultVisibility,
GithubApplicationClientImpl githubApplicationClient, ComponentUpdater componentUpdater, ImportHelper importHelper,
ProjectKeyGenerator projectKeyGenerator, NewCodeDefinitionResolver newCodeDefinitionResolver,
DefaultBranchNameResolver defaultBranchNameResolver, GitHubSettings gitHubSettings) {
this.dbClient = dbClient;
+ this.managedProjectService = managedProjectService;
this.userSession = userSession;
this.projectDefaultVisibility = projectDefaultVisibility;
this.githubApplicationClient = githubApplicationClient;
@@ -172,6 +177,9 @@ public class ImportGithubProjectAction implements AlmIntegrationsWsAction {
componentUpdater.commitAndIndex(dbSession, componentCreationData);
+ String userUuid = Objects.requireNonNull(userSession.getUuid());
+ managedProjectService.queuePermissionSyncTask(userUuid, mainBranchDto.getUuid(), projectDto.getUuid());
+
return toCreateResponse(projectDto);
}
}