aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAntoine Vigneau <antoine.vigneau@sonarsource.com>2023-07-03 11:43:58 +0200
committersonartech <sonartech@sonarsource.com>2023-07-18 20:03:21 +0000
commit5a07fe8d8a5ef407beef94fb410d65cb7d990c98 (patch)
tree57b9d55c065a694cf1300a55d577892fe93bd4ab
parent7eb69f2d9f83b568817107e8064a688246e94299 (diff)
downloadsonarqube-5a07fe8d8a5ef407beef94fb410d65cb7d990c98.tar.gz
sonarqube-5a07fe8d8a5ef407beef94fb410d65cb7d990c98.zip
SONAR-19784 Do not allow visibility update on managed projects
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/entity/EntityDto.java6
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/management/DelegatingManagedInstanceService.java21
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/management/ManagedProjectService.java28
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/management/DelegatingManagedInstanceServiceTest.java44
-rw-r--r--server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/UpdateVisibilityActionIT.java69
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/UpdateVisibilityAction.java11
6 files changed, 165 insertions, 14 deletions
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/entity/EntityDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/entity/EntityDto.java
index 39932908c59..e6d20322ab5 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/entity/EntityDto.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/entity/EntityDto.java
@@ -82,8 +82,12 @@ public class EntityDto {
return Qualifiers.VIEW.equals(qualifier) || Qualifiers.SUBVIEW.equals(qualifier);
}
+ public boolean isProject() {
+ return Qualifiers.PROJECT.equals(qualifier);
+ }
+
public boolean isProjectOrApp() {
- return Qualifiers.APP.equals(qualifier) || Qualifiers.PROJECT.equals(qualifier);
+ return Qualifiers.APP.equals(qualifier) || isProject();
}
@Override
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/management/DelegatingManagedInstanceService.java b/server/sonar-server-common/src/main/java/org/sonar/server/management/DelegatingManagedInstanceService.java
index 98ca712eb60..9b9e109db0e 100644
--- a/server/sonar-server-common/src/main/java/org/sonar/server/management/DelegatingManagedInstanceService.java
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/management/DelegatingManagedInstanceService.java
@@ -34,7 +34,7 @@ import static org.sonar.api.utils.Preconditions.checkState;
@ServerSide
@Priority(ManagedInstanceService.DELEGATING_INSTANCE_PRIORITY)
-public class DelegatingManagedInstanceService implements ManagedInstanceService {
+public class DelegatingManagedInstanceService implements ManagedInstanceService, ManagedProjectService {
private static final IllegalStateException NOT_MANAGED_INSTANCE_EXCEPTION = new IllegalStateException("This instance is not managed.");
private final Set<ManagedInstanceService> delegates;
@@ -58,14 +58,14 @@ public class DelegatingManagedInstanceService implements ManagedInstanceService
public Map<String, Boolean> getUserUuidToManaged(DbSession dbSession, Set<String> userUuids) {
return findManagedInstanceService()
.map(managedInstanceService -> managedInstanceService.getUserUuidToManaged(dbSession, userUuids))
- .orElse(returnNonManagedForAllGroups(userUuids));
+ .orElse(returnNonManagedForAll(userUuids));
}
@Override
public Map<String, Boolean> getGroupUuidToManaged(DbSession dbSession, Set<String> groupUuids) {
return findManagedInstanceService()
.map(managedInstanceService -> managedInstanceService.getGroupUuidToManaged(dbSession, groupUuids))
- .orElse(returnNonManagedForAllGroups(groupUuids));
+ .orElse(returnNonManagedForAll(groupUuids));
}
@Override
@@ -99,7 +99,20 @@ public class DelegatingManagedInstanceService implements ManagedInstanceService
return managedInstanceServices.stream().collect(MoreCollectors.toOptional());
}
- private static Map<String, Boolean> returnNonManagedForAllGroups(Set<String> resourcesUuid) {
+ private static Map<String, Boolean> returnNonManagedForAll(Set<String> resourcesUuid) {
return resourcesUuid.stream().collect(toMap(identity(), any -> false));
}
+
+ @Override
+ public boolean isProjectManaged(DbSession dbSession, String projectKey) {
+ return findManagedProjectService()
+ .map(managedProjectService -> managedProjectService.isProjectManaged(dbSession, projectKey))
+ .orElse(false);
+ }
+
+ private Optional<ManagedProjectService> findManagedProjectService() {
+ return findManagedInstanceService()
+ .filter(ManagedProjectService.class::isInstance)
+ .map(ManagedProjectService.class::cast);
+ }
}
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
new file mode 100644
index 00000000000..b3d765ba0aa
--- /dev/null
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/management/ManagedProjectService.java
@@ -0,0 +1,28 @@
+/*
+ * 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.management;
+
+import org.sonar.db.DbSession;
+
+public interface ManagedProjectService {
+
+ boolean isProjectManaged(DbSession dbSession, String projectKey);
+
+}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/management/DelegatingManagedInstanceServiceTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/management/DelegatingManagedInstanceServiceTest.java
index f2ecf25fd4f..1aa7acd4187 100644
--- a/server/sonar-server-common/src/test/java/org/sonar/server/management/DelegatingManagedInstanceServiceTest.java
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/management/DelegatingManagedInstanceServiceTest.java
@@ -21,6 +21,7 @@ package org.sonar.server.management;
import java.util.Map;
import java.util.Set;
+import org.jetbrains.annotations.NotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -42,9 +43,9 @@ public class DelegatingManagedInstanceServiceTest {
@Test
public void getProviderName_whenNotManaged_shouldThrow() {
- DelegatingManagedInstanceService managedInstanceService = new DelegatingManagedInstanceService(emptySet());
+ DelegatingManagedInstanceService managedInstanceService = noManagedInstanceService();
- assertThatThrownBy(() -> managedInstanceService.getProviderName())
+ assertThatThrownBy(managedInstanceService::getProviderName)
.isInstanceOf(IllegalStateException.class)
.hasMessage("This instance is not managed.");
}
@@ -58,7 +59,7 @@ public class DelegatingManagedInstanceServiceTest {
@Test
public void isInstanceExternallyManaged_whenNoManagedInstanceService_returnsFalse() {
- DelegatingManagedInstanceService managedInstanceService = new DelegatingManagedInstanceService(emptySet());
+ DelegatingManagedInstanceService managedInstanceService = noManagedInstanceService();
assertThat(managedInstanceService.isInstanceExternallyManaged()).isFalse();
}
@@ -81,7 +82,7 @@ public class DelegatingManagedInstanceServiceTest {
@Test
public void getUserUuidToManaged_whenNoDelegates_setAllUsersAsNonManaged() {
Set<String> userUuids = Set.of("a", "b");
- DelegatingManagedInstanceService managedInstanceService = new DelegatingManagedInstanceService(emptySet());
+ DelegatingManagedInstanceService managedInstanceService = noManagedInstanceService();
Map<String, Boolean> userUuidToManaged = managedInstanceService.getUserUuidToManaged(dbSession, userUuids);
@@ -104,7 +105,7 @@ public class DelegatingManagedInstanceServiceTest {
@Test
public void getGroupUuidToManaged_whenNoDelegates_setAllUsersAsNonManaged() {
Set<String> groupUuids = Set.of("a", "b");
- DelegatingManagedInstanceService managedInstanceService = new DelegatingManagedInstanceService(emptySet());
+ DelegatingManagedInstanceService managedInstanceService = noManagedInstanceService();
Map<String, Boolean> groupUuidToManaged = managedInstanceService.getGroupUuidToManaged(dbSession, groupUuids);
@@ -200,7 +201,26 @@ public class DelegatingManagedInstanceServiceTest {
return anotherManagedInstanceService;
}
- private static class NeverManagedInstanceService implements ManagedInstanceService {
+ @Test
+ public void isProjectManaged_whenManagedInstanceServices_shouldDelegatesToRightService() {
+ DelegatingManagedInstanceService managedInstanceService = new DelegatingManagedInstanceService(Set.of(new NeverManagedInstanceService(), new AlwaysManagedInstanceService()));
+
+ assertThat(managedInstanceService.isProjectManaged(dbSession, "project_key")).isTrue();
+ }
+
+ @Test
+ public void isProjectManaged_whenManagedNoInstanceServices_returnsFalse() {
+ DelegatingManagedInstanceService managedInstanceService = noManagedInstanceService();
+
+ assertThat(managedInstanceService.isProjectManaged(dbSession, "project_key")).isFalse();
+ }
+
+ @NotNull
+ private static DelegatingManagedInstanceService noManagedInstanceService() {
+ return new DelegatingManagedInstanceService(emptySet());
+ }
+
+ private static class NeverManagedInstanceService implements ManagedInstanceService, ManagedProjectService {
@Override
public boolean isInstanceExternallyManaged() {
@@ -236,9 +256,14 @@ public class DelegatingManagedInstanceServiceTest {
public boolean isUserManaged(DbSession dbSession, String login) {
return false;
}
+
+ @Override
+ public boolean isProjectManaged(DbSession dbSession, String projectKey) {
+ return false;
+ }
}
- private static class AlwaysManagedInstanceService implements ManagedInstanceService {
+ private static class AlwaysManagedInstanceService implements ManagedInstanceService, ManagedProjectService {
@Override
public boolean isInstanceExternallyManaged() {
@@ -274,6 +299,11 @@ public class DelegatingManagedInstanceServiceTest {
public boolean isUserManaged(DbSession dbSession, String login) {
return true;
}
+
+ @Override
+ public boolean isProjectManaged(DbSession dbSession, String projectKey) {
+ return true;
+ }
}
}
diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/UpdateVisibilityActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/UpdateVisibilityActionIT.java
index 295b305a2ea..7c5bc1a594e 100644
--- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/UpdateVisibilityActionIT.java
+++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/UpdateVisibilityActionIT.java
@@ -20,11 +20,13 @@
package org.sonar.server.project.ws;
import java.util.Arrays;
+import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
+import org.apache.commons.lang.NotImplementedException;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.config.Configuration;
@@ -59,6 +61,9 @@ import org.sonar.server.es.TestIndexers;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.management.DelegatingManagedInstanceService;
+import org.sonar.server.management.ManagedInstanceService;
+import org.sonar.server.management.ManagedProjectService;
import org.sonar.server.permission.PermissionService;
import org.sonar.server.permission.PermissionServiceImpl;
import org.sonar.server.permission.index.FooIndexDefinition;
@@ -82,6 +87,7 @@ public class UpdateVisibilityActionIT {
private static final String PARAM_PROJECT = "project";
private static final String PUBLIC = "public";
private static final String PRIVATE = "private";
+ private static final String MANAGED_PROJECT_KEY = "managed_project_key";
private static final Set<String> GLOBAL_PERMISSIONS_NAME_SET = stream(GlobalPermission.values()).map(GlobalPermission::getKey)
.collect(Collectors.toSet());
@@ -103,8 +109,10 @@ public class UpdateVisibilityActionIT {
private final DbSession dbSession = dbTester.getSession();
private final TestIndexers projectIndexers = new TestIndexers();
private final Configuration configuration = mock(Configuration.class);
+ private final DelegatingManagedInstanceService delegatingManagedInstanceService = new DelegatingManagedInstanceService(Set.of(new ControllableManagedProjectService()));
- private final UpdateVisibilityAction underTest = new UpdateVisibilityAction(dbClient, userSessionRule, projectIndexers, new SequenceUuidFactory(), configuration);
+ private final UpdateVisibilityAction underTest = new UpdateVisibilityAction(dbClient, userSessionRule, projectIndexers, new SequenceUuidFactory(), configuration,
+ delegatingManagedInstanceService);
private final WsActionTester ws = new WsActionTester(underTest);
private final Random random = new Random();
@@ -301,6 +309,18 @@ public class UpdateVisibilityActionIT {
}
@Test
+ public void execute_throws_BadRequestException_if_entity_is_project_and_managed() {
+ ProjectDto project = dbTester.components().insertPublicProject(p -> p.setKey(MANAGED_PROJECT_KEY)).getProjectDto();
+ request.setParam(PARAM_PROJECT, project.getKey())
+ .setParam(PARAM_VISIBILITY, randomVisibility);
+ userSessionRule.addProjectPermission(UserRole.ADMIN, project);
+
+ assertThatThrownBy(request::execute)
+ .isInstanceOf(BadRequestException.class)
+ .hasMessage("Cannot change visibility of a managed project");
+ }
+
+ @Test
public void execute_changes_private_flag_of_specified_project_and_all_children_to_specified_new_visibility() {
ProjectData project = randomPublicOrPrivateProject();
boolean initiallyPrivate = project.getProjectDto().isPrivate();
@@ -695,4 +715,51 @@ public class UpdateVisibilityActionIT {
private ProjectData randomPublicOrPrivateProject() {
return random.nextBoolean() ? dbTester.components().insertPublicProject() : dbTester.components().insertPrivateProject();
}
+
+ private static class ControllableManagedProjectService implements ManagedInstanceService, ManagedProjectService {
+
+ @Override
+ public boolean isInstanceExternallyManaged() {
+ return true;
+ }
+
+ @Override
+ public String getProviderName() {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public Map<String, Boolean> getUserUuidToManaged(DbSession dbSession, Set<String> userUuids) {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public Map<String, Boolean> getGroupUuidToManaged(DbSession dbSession, Set<String> groupUuids) {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public String getManagedUsersSqlFilter(boolean filterByManaged) {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public String getManagedGroupsSqlFilter(boolean filterByManaged) {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public boolean isUserManaged(DbSession dbSession, String login) {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public boolean isProjectManaged(DbSession dbSession, String projectKey) {
+ if (projectKey.equals(MANAGED_PROJECT_KEY)) {
+ return true;
+ }
+ return false;
+ }
+ }
+
}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/UpdateVisibilityAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/UpdateVisibilityAction.java
index ca2ea9bdffe..9c924d89ff5 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/UpdateVisibilityAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/UpdateVisibilityAction.java
@@ -36,6 +36,7 @@ import org.sonar.db.user.GroupDto;
import org.sonar.db.user.UserId;
import org.sonar.server.es.Indexers;
import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.management.DelegatingManagedInstanceService;
import org.sonar.server.project.Visibility;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.client.project.ProjectsWsParameters;
@@ -59,13 +60,16 @@ public class UpdateVisibilityAction implements ProjectsWsAction {
private final Indexers indexers;
private final UuidFactory uuidFactory;
private final Configuration configuration;
+ private final DelegatingManagedInstanceService delegatingManagedInstanceService;
- public UpdateVisibilityAction(DbClient dbClient, UserSession userSession, Indexers indexers, UuidFactory uuidFactory, Configuration configuration) {
+ public UpdateVisibilityAction(DbClient dbClient, UserSession userSession, Indexers indexers, UuidFactory uuidFactory, Configuration configuration,
+ DelegatingManagedInstanceService delegatingManagedInstanceService) {
this.dbClient = dbClient;
this.userSession = userSession;
this.indexers = indexers;
this.uuidFactory = uuidFactory;
this.configuration = configuration;
+ this.delegatingManagedInstanceService = delegatingManagedInstanceService;
}
public void define(WebService.NewController context) {
@@ -95,10 +99,15 @@ public class UpdateVisibilityAction implements ProjectsWsAction {
if (!isProjectAdmin || (!isGlobalAdmin && !allowChangingPermissionsByProjectAdmins)) {
throw insufficientPrivilegesException();
}
+ checkRequest(notManagedProject(dbSession, entityDto), "Cannot change visibility of a managed project");
//This check likely can be removed when we remove the column 'private' from components table
checkRequest(noPendingTask(dbSession, entityDto.getKey()), "Component visibility can't be changed as long as it has background task(s) pending or in progress");
}
+ private boolean notManagedProject(DbSession dbSession, EntityDto entityDto) {
+ return !entityDto.isProject() || !delegatingManagedInstanceService.isProjectManaged(dbSession, entityDto.getKey());
+ }
+
@Override
public void handle(Request request, Response response) throws Exception {
userSession.checkLoggedIn();