From e7d9ee79909f5aca04f983c1b11a158fe679bbed Mon Sep 17 00:00:00 2001 From: Avgustin Marinov Date: Fri, 10 Oct 2025 08:36:08 +0300 Subject: [PATCH] Add Target(Type)Management tests (#2735) Signed-off-by: Avgustin Marinov --- .../repository/TargetTypeManagement.java | 6 +- ...stractAccessControllerManagementTest.java} | 27 +- .../repository/jpa/acm/AutoAssignTest.java | 2 +- .../jpa/acm/DeploymentManagementTest.java | 2 +- .../acm/DistributionSetManagementTest.java | 2 +- .../DistributionSetTypeManagementTest.java | 4 +- .../jpa/acm/RolloutExecutionTest.java | 7 +- .../jpa/acm/RolloutManagementTest.java | 2 +- .../jpa/acm/SoftwareModuleManagementTest.java | 2 +- .../acm/SoftwareModuleTypeManagementTest.java | 4 +- .../jpa/acm/SystemExecutionTest.java | 116 ++++++ .../jpa/acm/TargetAccessControllerTest.java | 319 ----------------- .../jpa/acm/TargetManagementTest.java | 331 ++++++++++++++++++ .../acm/TargetTypeAccessControllerTest.java | 143 -------- .../jpa/acm/TargetTypeManagementTest.java | 101 ++++++ .../acm/TargetTypeQueryManagementTest.java | 2 +- 16 files changed, 586 insertions(+), 484 deletions(-) rename hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/{AbstractAccessControllerTest.java => AbstractAccessControllerManagementTest.java} (72%) create mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/SystemExecutionTest.java delete mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetAccessControllerTest.java create mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetManagementTest.java delete mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetTypeAccessControllerTest.java create mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetTypeManagementTest.java diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetTypeManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetTypeManagement.java index 0b1fa5c22..09faa6850 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetTypeManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetTypeManagement.java @@ -39,7 +39,7 @@ import org.springframework.security.access.prepost.PreAuthorize; public interface TargetTypeManagement extends RepositoryManagement { - String HAS_UPDATE_TARGET_TYPE_AND_READ_DISTRIBUTION_SET = SpringEvalExpressions.HAS_UPDATE_REPOSITORY + " and hasAuthority('READ_" + SpPermission.DISTRIBUTION_SET + "')"; + String HAS_UPDATE_TARGET_TYPE_AND_READ_DISTRIBUTION_SET_TYPE = SpringEvalExpressions.HAS_UPDATE_REPOSITORY + " and hasAuthority('READ_" + SpPermission.DISTRIBUTION_SET_TYPE + "')"; @Override default String permissionGroup() { @@ -58,7 +58,7 @@ public interface TargetTypeManagement * @param distributionSetTypeIds Distribution set ID * @return Target type */ - @PreAuthorize(HAS_UPDATE_TARGET_TYPE_AND_READ_DISTRIBUTION_SET) + @PreAuthorize(HAS_UPDATE_TARGET_TYPE_AND_READ_DISTRIBUTION_SET_TYPE) TargetType assignCompatibleDistributionSetTypes(long id, @NotEmpty Collection distributionSetTypeIds); /** @@ -66,7 +66,7 @@ public interface TargetTypeManagement * @param distributionSetTypeIds Distribution set ID * @return Target type */ - @PreAuthorize(HAS_UPDATE_TARGET_TYPE_AND_READ_DISTRIBUTION_SET) + @PreAuthorize(HAS_UPDATE_TARGET_TYPE_AND_READ_DISTRIBUTION_SET_TYPE) TargetType unassignDistributionSetType(long id, long distributionSetTypeIds); @SuperBuilder diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/AbstractAccessControllerTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/AbstractAccessControllerManagementTest.java similarity index 72% rename from hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/AbstractAccessControllerTest.java rename to hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/AbstractAccessControllerManagementTest.java index 27166518f..09dc28dd0 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/AbstractAccessControllerTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/AbstractAccessControllerManagementTest.java @@ -19,6 +19,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.Target; +import org.eclipse.hawkbit.repository.model.TargetTag; import org.eclipse.hawkbit.repository.model.TargetType; import org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch; import org.eclipse.hawkbit.repository.test.util.WithUser; @@ -26,7 +27,7 @@ import org.junit.jupiter.api.BeforeEach; import org.springframework.test.context.TestPropertySource; @TestPropertySource(properties = "hawkbit.acm.access-controller.enabled=true") -abstract class AbstractAccessControllerTest extends AbstractJpaIntegrationTest { +abstract class AbstractAccessControllerManagementTest extends AbstractJpaIntegrationTest { protected SoftwareModuleType smType1; protected SoftwareModuleType smType2; @@ -44,10 +45,13 @@ abstract class AbstractAccessControllerTest extends AbstractJpaIntegrationTest { protected TargetType targetType1; protected TargetType targetType2; + protected TargetTag targetTag1; + protected TargetTag targetTag2; protected Target target1Type1; protected Target target2Type2; protected Target target3Type2; + @SuppressWarnings("java:S1117") // java:S1117 - intentional hiding of fiends - they are the same @BeforeEach @Override public void beforeAll() throws Exception { @@ -61,9 +65,9 @@ abstract class AbstractAccessControllerTest extends AbstractJpaIntegrationTest { dsType1 = testdataFactory.findOrCreateDistributionSetType("DsType1", "DistributionSetType-1", List.of(smType1), List.of()); dsType2 = testdataFactory.findOrCreateDistributionSetType("DsType2", "DistributionSetType-2", List.of(smType2), List.of(smType1)); - final List tags = testdataFactory.createDistributionSetTags(2); - dsTag1 = tags.get(0); - dsTag2 = tags.get(1); + final List dsTags = testdataFactory.createDistributionSetTags(2); + dsTag1 = dsTags.get(0); + dsTag2 = dsTags.get(1); ds1Type1 = distributionSetManagement.lock( distributionSetManagement.assignTag( List.of(testdataFactory.createDistributionSet("Ds1Type1", "1.0", dsType1, List.of(sm1Type1)).getId()), @@ -79,9 +83,18 @@ abstract class AbstractAccessControllerTest extends AbstractJpaIntegrationTest { targetType1 = testdataFactory.createTargetType("TargetType1", Set.of(dsType1, dsType2)); targetType2 = testdataFactory.createTargetType("TargetType2", Set.of(dsType2)); - target1Type1 = testdataFactory.createTarget("controller_1", "Controller-1", targetType1); - target2Type2 = testdataFactory.createTarget("controller_2", "Controller-2", targetType2); - target3Type2 = testdataFactory.createTarget("controller_3", "Controller-3", targetType2); + final List targetTags = testdataFactory.createTargetTags(2, "acm_"); + targetTag1 = targetTags.get(0); + targetTag2 = targetTags.get(1); + final Target target1Type1 = testdataFactory.createTarget("controller_1", "Controller-1", targetType1); + final Target target2Type2 = testdataFactory.createTarget("controller_2", "Controller-2", targetType2); + final Target target3Type2 = testdataFactory.createTarget("controller_3", "Controller-3", targetType2); + targetManagement.assignTag(List.of(target1Type1.getControllerId(), target2Type2.getControllerId()), targetTag1.getId()); + targetManagement.assignTag( + List.of(target1Type1.getControllerId(), target2Type2.getControllerId(), target3Type2.getControllerId()), targetTag2.getId()); + this.target1Type1 = targetManagement.get(target1Type1.getId()); + this.target2Type2 = targetManagement.get(target2Type2.getId()); + this.target3Type2 = targetManagement.get(target3Type2.getId()); } protected static WithUser withAuthorities(final String... authorities) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/AutoAssignTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/AutoAssignTest.java index af4fa96a4..cae3c1e5d 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/AutoAssignTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/AutoAssignTest.java @@ -30,7 +30,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Pageable; import org.springframework.integration.support.locks.LockRegistry; -class AutoAssignTest extends AbstractAccessControllerTest { +class AutoAssignTest extends AbstractAccessControllerManagementTest { @Autowired AutoAssignExecutor autoAssignExecutor; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DeploymentManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DeploymentManagementTest.java index 10e418a84..8e5504f9b 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DeploymentManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DeploymentManagementTest.java @@ -27,7 +27,7 @@ import org.eclipse.hawkbit.repository.model.DeploymentRequest; import org.eclipse.hawkbit.repository.model.Target; import org.junit.jupiter.api.Test; -class DeploymentManagementTest extends AbstractAccessControllerTest { +class DeploymentManagementTest extends AbstractAccessControllerManagementTest { @Test void verifyAssignments() { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DistributionSetManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DistributionSetManagementTest.java index b9e85f59f..15818bc4f 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DistributionSetManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DistributionSetManagementTest.java @@ -32,7 +32,7 @@ import org.eclipse.hawkbit.repository.model.NamedEntity; import org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch; import org.junit.jupiter.api.Test; -class DistributionSetManagementTest extends AbstractAccessControllerTest { +class DistributionSetManagementTest extends AbstractAccessControllerManagementTest { @Test void verifyCreate() { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DistributionSetTypeManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DistributionSetTypeManagementTest.java index 5004c1464..88c0c2026 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DistributionSetTypeManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DistributionSetTypeManagementTest.java @@ -30,11 +30,11 @@ import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.NamedEntity; import org.junit.jupiter.api.Test; -class DistributionSetTypeManagementTest extends AbstractAccessControllerTest { +class DistributionSetTypeManagementTest extends AbstractAccessControllerManagementTest { @Test void verifyCreate() { - // permissions to read only type1 ds types and create new type with name 'permitted' + // permissions to read all and create new type with name 'permitted' runAs(withAuthorities(READ_PREFIX + DISTRIBUTION_SET_TYPE, CREATE_PREFIX + DISTRIBUTION_SET_TYPE + "/name==permitted"), () -> { assertThat(testdataFactory.findOrCreateDistributionSetType("newType", "permitted")).isNotNull(); assertThatThrownBy(() -> testdataFactory.findOrCreateDistributionSetType("newType_2", "not_permitted_2")) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/RolloutExecutionTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/RolloutExecutionTest.java index 7b940ade0..66d8c296e 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/RolloutExecutionTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/RolloutExecutionTest.java @@ -18,14 +18,16 @@ import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_TARGET; import static org.eclipse.hawkbit.im.authentication.SpPermission.UPDATE_TARGET; import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.runAs; +import java.util.Arrays; import java.util.Optional; +import org.eclipse.hawkbit.repository.Identifiable; import org.eclipse.hawkbit.repository.jpa.rollout.RolloutScheduler; import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.Target; import org.junit.jupiter.api.Test; -class RolloutExecutionTest extends AbstractAccessControllerTest { +class RolloutExecutionTest extends AbstractAccessControllerManagementTest { @Test void verifyOnlyUpdatableTargetsArePartOfRolloutExecutedByScheduler() { @@ -66,7 +68,8 @@ class RolloutExecutionTest extends AbstractAccessControllerTest { assertThat(rolloutGroupManagement.findByRollout(rollout.getId(), UNPAGED).getContent().stream() .flatMap(rolloutGroup -> rolloutGroupManagement.findTargetsOfRolloutGroup(rolloutGroup.getId(), UNPAGED).stream())) + .extracting(Identifiable::getId) .as("Only updatable targets should be part of the rollout") - .containsExactly(expectedTargets); + .containsExactly(Arrays.stream(expectedTargets).map(Identifiable::getId).toArray(Long[]::new)); } } \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/RolloutManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/RolloutManagementTest.java index ac50b0a84..154360e0f 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/RolloutManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/RolloutManagementTest.java @@ -21,7 +21,7 @@ import org.eclipse.hawkbit.repository.Identifiable; import org.eclipse.hawkbit.repository.model.Rollout; import org.junit.jupiter.api.Test; -class RolloutManagementTest extends AbstractAccessControllerTest { +class RolloutManagementTest extends AbstractAccessControllerManagementTest { /** * Test is verifying that rollout details are showing details without restrictions diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/SoftwareModuleManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/SoftwareModuleManagementTest.java index c2e8cd5e9..7572f0a45 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/SoftwareModuleManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/SoftwareModuleManagementTest.java @@ -28,7 +28,7 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValueCreate; import org.junit.jupiter.api.Test; -class SoftwareModuleManagementTest extends AbstractAccessControllerTest { +class SoftwareModuleManagementTest extends AbstractAccessControllerManagementTest { @Test void verifyRead() { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/SoftwareModuleTypeManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/SoftwareModuleTypeManagementTest.java index bd18ecd08..2a9ef95ef 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/SoftwareModuleTypeManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/SoftwareModuleTypeManagementTest.java @@ -29,11 +29,11 @@ import org.eclipse.hawkbit.repository.model.NamedEntity; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.junit.jupiter.api.Test; -class SoftwareModuleTypeManagementTest extends AbstractAccessControllerTest { +class SoftwareModuleTypeManagementTest extends AbstractAccessControllerManagementTest { @Test void verifyCreate() { - // permissions to read only type1 sm types and create new type with name 'permitted' + // permissions to read all and create new type with name 'permitted' runAs(withAuthorities(READ_PREFIX + SOFTWARE_MODULE_TYPE, CREATE_PREFIX + SOFTWARE_MODULE_TYPE + "/key==permitted"), () -> { assertThat(testdataFactory.findOrCreateSoftwareModuleType("permitted")).isNotNull(); assertThatThrownBy(() -> testdataFactory.findOrCreateSoftwareModuleType("not_permitted_2")) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/SystemExecutionTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/SystemExecutionTest.java new file mode 100644 index 000000000..d33c106d9 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/SystemExecutionTest.java @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.hawkbit.repository.jpa.acm; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.hawkbit.im.authentication.SpPermission.CREATE_PREFIX; +import static org.eclipse.hawkbit.im.authentication.SpPermission.CREATE_TARGET; +import static org.eclipse.hawkbit.im.authentication.SpPermission.DELETE_PREFIX; +import static org.eclipse.hawkbit.im.authentication.SpPermission.DELETE_TARGET; +import static org.eclipse.hawkbit.im.authentication.SpPermission.DISTRIBUTION_SET; +import static org.eclipse.hawkbit.im.authentication.SpPermission.DISTRIBUTION_SET_TYPE; +import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_DISTRIBUTION_SET; +import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_PREFIX; +import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_TARGET; +import static org.eclipse.hawkbit.im.authentication.SpPermission.SOFTWARE_MODULE; +import static org.eclipse.hawkbit.im.authentication.SpPermission.SOFTWARE_MODULE_TYPE; +import static org.eclipse.hawkbit.im.authentication.SpPermission.TARGET_TYPE; +import static org.eclipse.hawkbit.im.authentication.SpPermission.UPDATE_PREFIX; +import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.runAs; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; + +import java.util.List; + +import org.eclipse.hawkbit.repository.jpa.acm.AccessController.Operation; +import org.eclipse.hawkbit.repository.jpa.model.JpaAction; +import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; +import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType; +import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; +import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType; +import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; +import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.domain.Specification; + +class SystemExecutionTest extends AbstractAccessControllerManagementTest { + + @Autowired + protected AccessController softwareModuleAccessController; + @Autowired + protected AccessController softwareModuleTypeAccessController; + @Autowired + protected AccessController distributionSetAccessController; + @Autowired + protected AccessController distributionSetTypeAccessController; + @Autowired + protected AccessController targetAccessController; + @Autowired + protected AccessController targetTypeAccessController; + @Autowired + protected AccessController actionAccessController; + + @Autowired + List> allAccessControllers; + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Test + void verifyWithoutScopes() { + allAccessControllers.forEach(accessController -> { + for (final Operation operation : Operation.values()) { + assertThat(accessController.getAccessRules(operation)).isEmpty(); + } + final Specification specificationMock = mock(Specification.class); + for (final Operation operation : Operation.values()) { + accessController.appendAccessRules(operation, specificationMock); + verifyNoInteractions(specificationMock); + } + }); + } + + @SuppressWarnings("unchecked") + @Test + void verifyWithScope() { + runAs(withAuthorities(READ_PREFIX + SOFTWARE_MODULE + "/type.id==1", UPDATE_PREFIX + SOFTWARE_MODULE), + () -> verifyAccessController(softwareModuleAccessController)); + runAs(withAuthorities(READ_PREFIX + SOFTWARE_MODULE_TYPE + "/id==1", UPDATE_PREFIX + SOFTWARE_MODULE_TYPE), + () -> verifyAccessController(softwareModuleTypeAccessController)); + runAs(withAuthorities(READ_DISTRIBUTION_SET + "/type.id==1", CREATE_PREFIX + DISTRIBUTION_SET), + () -> verifyAccessController(distributionSetAccessController)); + runAs(withAuthorities(READ_PREFIX + DISTRIBUTION_SET_TYPE + "/id==1", DELETE_PREFIX + DISTRIBUTION_SET_TYPE), + () -> verifyAccessController(distributionSetTypeAccessController)); + runAs(withAuthorities(READ_TARGET, DELETE_TARGET + "/type.id==1"), + () -> verifyAccessController(targetAccessController)); + runAs(withAuthorities(UPDATE_PREFIX + TARGET_TYPE + "/id==1"), () -> verifyAccessController(targetTypeAccessController)); + runAs(withAuthorities(CREATE_TARGET + "/type.id==1"), () -> verifyAccessController(actionAccessController)); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private void verifyAccessController(final AccessController accessController) { + final Specification mock = mock(Specification.class); + for (final Operation operation : Operation.values()) { + accessController.appendAccessRules(operation, mock); + } + verify(mock, times(1)).and(any()); // once for every access controller is scoped only + + final Specification mockAsSystem = mock(Specification.class); + for (Operation operation : Operation.values()) { + systemSecurityContext.runAsSystem(() -> { + accessController.appendAccessRules(operation, mockAsSystem); + return null; + }); + } + verifyNoInteractions(mockAsSystem); + } +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetAccessControllerTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetAccessControllerTest.java deleted file mode 100644 index 45be21161..000000000 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetAccessControllerTest.java +++ /dev/null @@ -1,319 +0,0 @@ -/** - * Copyright (c) 2025 Contributors to the Eclipse Foundation - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.hawkbit.repository.jpa.acm; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.eclipse.hawkbit.im.authentication.SpPermission.CREATE_ROLLOUT; -import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_DISTRIBUTION_SET; -import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_ROLLOUT; -import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_TARGET; -import static org.eclipse.hawkbit.im.authentication.SpPermission.UPDATE_TARGET; -import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.runAs; -import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.withUser; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.eclipse.hawkbit.repository.Identifiable; -import org.eclipse.hawkbit.repository.TargetFilterQueryManagement; -import org.eclipse.hawkbit.repository.TargetFilterQueryManagement.AutoAssignDistributionSetUpdate; -import org.eclipse.hawkbit.repository.TargetManagement.Create; -import org.eclipse.hawkbit.repository.TargetTagManagement; -import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; -import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException; -import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest; -import org.eclipse.hawkbit.repository.jpa.autoassign.AutoAssignChecker; -import org.eclipse.hawkbit.repository.model.Action.ActionType; -import org.eclipse.hawkbit.repository.model.DistributionSet; -import org.eclipse.hawkbit.repository.model.Rollout; -import org.eclipse.hawkbit.repository.model.RolloutGroup; -import org.eclipse.hawkbit.repository.model.Target; -import org.eclipse.hawkbit.repository.model.TargetFilterQuery; -import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Pageable; -import org.springframework.test.context.TestPropertySource; - -/** - * Feature: Component Tests - Access Control
- * Story: Test Target Access Controller - */ -@TestPropertySource(properties = "hawkbit.acm.access-controller.enabled=true") -class TargetAccessControllerTest extends AbstractJpaIntegrationTest { - - @Autowired - AutoAssignChecker autoAssignChecker; - - /** - * Verifies read access rules for targets - */ - @Test - void verifyTargetReadOperations() { - final Target permittedTarget = targetManagement - .create(Create.builder().controllerId("device01").updateStatus(TargetUpdateStatus.REGISTERED).build()); - - final Target hiddenTarget = targetManagement - .create(Create.builder().controllerId("device02").updateStatus(TargetUpdateStatus.REGISTERED).build()); - - runAs(withUser("user", READ_TARGET + "/controllerId==" + permittedTarget.getControllerId()), () -> { - // verify targetManagement#findAll - assertThat(targetManagement.findAll(Pageable.unpaged()).get().map(Identifiable::getId).toList()) - .containsExactly(permittedTarget.getId()); - - // verify targetManagement#findByRsql - assertThat(targetManagement.findByRsql("id==*", Pageable.unpaged()).get().map(Identifiable::getId).toList()) - .containsExactly(permittedTarget.getId()); - - // verify targetManagement#getByControllerID - assertThat(targetManagement.getByControllerId(permittedTarget.getControllerId())).isNotNull(); - final String hiddenTargetControllerId = hiddenTarget.getControllerId(); - assertThatThrownBy(() -> targetManagement.getByControllerId(hiddenTargetControllerId)) - .as("Missing read permissions for hidden target.") - .isInstanceOf(InsufficientPermissionException.class); - assertThatThrownBy(() -> targetManagement.findByControllerId(hiddenTargetControllerId)) - .as("Missing read permissions for hidden target.") - .isInstanceOf(InsufficientPermissionException.class); - - // verify targetManagement#getByControllerId - assertThat(targetManagement - .findByControllerId(List.of(permittedTarget.getControllerId(), hiddenTargetControllerId)) - .stream().map(Identifiable::getId).toList()).containsExactly(permittedTarget.getId()); - - // verify targetManagement#get - assertThat(targetManagement.find(permittedTarget.getId())).isPresent(); - assertThat(targetManagement.find(hiddenTarget.getId())).isEmpty(); - - // verify targetManagement#get - final List withHidden = List.of(permittedTarget.getId(), hiddenTarget.getId()); - assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy(() -> targetManagement.get(withHidden)); - - // verify targetManagement#getControllerAttributes - assertThat(targetManagement.getControllerAttributes(permittedTarget.getControllerId())).isEmpty(); - assertThatThrownBy(() -> targetManagement.getControllerAttributes(hiddenTargetControllerId)) - .as("Target should not be found.") - .isInstanceOf(InsufficientPermissionException.class); - }); - } - - @Test - void verifyTagFilteringAndManagement() { - final Target permittedTarget = targetManagement - .create(Create.builder().controllerId("device01").updateStatus(TargetUpdateStatus.REGISTERED).build()); - - final Target readOnlyTarget = targetManagement - .create(Create.builder().controllerId("device02").updateStatus(TargetUpdateStatus.REGISTERED).build()); - final String readOnlyTargetControllerId = readOnlyTarget.getControllerId(); - - final Target hiddenTarget = targetManagement - .create(Create.builder().controllerId("device03").updateStatus(TargetUpdateStatus.REGISTERED).build()); - - final Long myTagId = targetTagManagement.create(TargetTagManagement.Create.builder().name("myTag").build()).getId(); - - // perform tag assignment before setting access rules - targetManagement.assignTag( - List.of(permittedTarget.getControllerId(), readOnlyTargetControllerId, hiddenTarget.getControllerId()), myTagId); - - runAs(withUser("user", - READ_TARGET + "/controllerId==" + permittedTarget.getControllerId() + " or controllerId==" + readOnlyTargetControllerId, - UPDATE_TARGET + "/controllerId==" + permittedTarget.getControllerId()), - () -> { - // verify targetManagement#findByTag - assertThat( - targetManagement.findByTag(myTagId, Pageable.unpaged()).get().map(Identifiable::getId).toList()) - .containsExactlyInAnyOrder(permittedTarget.getId(), readOnlyTarget.getId()); - - // verify targetManagement#findByRsqlAndTag - assertThat(targetManagement.findByRsqlAndTag("id==*", myTagId, Pageable.unpaged()).get() - .map(Identifiable::getId).toList()).containsExactlyInAnyOrder(permittedTarget.getId(), readOnlyTarget.getId()); - - // verify targetManagement#assignTag on permitted target - assertThat(targetManagement.assignTag(Collections.singletonList(permittedTarget.getControllerId()), myTagId)).hasSize(1); - // verify targetManagement#unassignTag on permitted target - assertThat(targetManagement.unassignTag(Collections.singletonList(permittedTarget.getControllerId()), myTagId)).hasSize(1); - // verify targetManagement#assignTag on permitted target - assertThat(targetManagement.assignTag(Collections.singletonList(permittedTarget.getControllerId()), myTagId)) - .hasSize(1); - // verify targetManagement#unAssignTag on permitted target - assertThat(targetManagement.unassignTag(List.of(permittedTarget.getControllerId()), myTagId).get(0).getControllerId()) - .isEqualTo(permittedTarget.getControllerId()); - - // assignment is denied for readOnlyTarget (read, but no update permissions) - final List readTargetControllerIdList = Collections.singletonList(readOnlyTargetControllerId); - assertThatThrownBy(() -> targetManagement.assignTag(readTargetControllerIdList, myTagId)) - .as("Missing update permissions for target to toggle tag assignment.") - .isInstanceOfAny(InsufficientPermissionException.class); - - // assignment is denied for readOnlyTarget (read, but no update permissions) - final List readOnlyTargetControllerIdList = List.of(readOnlyTargetControllerId); - assertThatThrownBy(() -> targetManagement.unassignTag(readOnlyTargetControllerIdList, myTagId)) - .as("Missing update permissions for target to toggle tag assignment.") - .isInstanceOf(InsufficientPermissionException.class); - - // assignment is denied for hiddenTarget since it's hidden - final List hiddenTargetControllerIdList = Collections.singletonList(hiddenTarget.getControllerId()); - assertThatThrownBy(() -> targetManagement.assignTag(hiddenTargetControllerIdList, myTagId)) - .as("Missing update permissions for target to toggle tag assignment.") - .isInstanceOf(InsufficientPermissionException.class); - - // assignment is denied for hiddenTarget since it's hidden - assertThatThrownBy(() -> targetManagement.assignTag(hiddenTargetControllerIdList, myTagId)) - .as("Missing update permissions for target to toggle tag assignment.") - .isInstanceOf(InsufficientPermissionException.class); - - // assignment is denied for hiddenTarget since it's hidden - assertThatThrownBy(() -> targetManagement.unassignTag(hiddenTargetControllerIdList, myTagId)) - .as("Missing update permissions for target to toggle tag assignment.") - .isInstanceOf(InsufficientPermissionException.class); - }); - } - - /** - * Verifies rules for target assignment - */ - @Test - void verifyTargetAssignment() { - final DistributionSet ds = testdataFactory.createDistributionSet("myDs"); - distributionSetManagement.lock(ds); - - final Target permittedTarget = targetManagement - .create(Create.builder().controllerId("device01").updateStatus(TargetUpdateStatus.REGISTERED).build()); - final String hiddenTargetControllerId = targetManagement - .create(Create.builder().controllerId("device02").updateStatus(TargetUpdateStatus.REGISTERED).build()) - .getControllerId(); - - runAs(withUser("user", - READ_DISTRIBUTION_SET, - READ_TARGET + "/controllerId==" + permittedTarget.getControllerId(), - UPDATE_TARGET + "/controllerId==" + permittedTarget.getControllerId()), () -> { - final Long dsId = ds.getId(); - assertThat(assignDistributionSet(dsId, permittedTarget.getControllerId()).getAssigned()).isEqualTo(1); - // assigning of not allowed target behaves as not found - assertThatThrownBy(() -> assignDistributionSet(dsId, hiddenTargetControllerId)).isInstanceOf(AssertionError.class); - }); - } - - /** - * Verifies rules for target assignment - */ - @Test - void verifyTargetAssignmentOnNonUpdatableTarget() { - final DistributionSet firstDs = testdataFactory.createDistributionSet("myDs"); - distributionSetManagement.lock(firstDs); - final DistributionSet secondDs = testdataFactory.createDistributionSet("anotherDs"); - distributionSetManagement.lock(secondDs); - - final Target manageableTarget = targetManagement - .create(Create.builder().controllerId("device01").updateStatus(TargetUpdateStatus.REGISTERED).build()); - final Target readOnlyTarget = targetManagement - .create(Create.builder().controllerId("device02").updateStatus(TargetUpdateStatus.REGISTERED).build()); - - runAs(withUser("user", - READ_DISTRIBUTION_SET, - READ_TARGET + "/controllerId==" + manageableTarget.getControllerId() + " or controllerId==" + readOnlyTarget.getControllerId(), - UPDATE_TARGET + "/controllerId==" + manageableTarget.getControllerId()), () -> { - final Long firstDsId = firstDs.getId(); - // assignment is permitted for manageableTarget - assertThat(assignDistributionSet(firstDsId, manageableTarget.getControllerId()).getAssigned()).isEqualTo(1); - - // assignment is denied for readOnlyTarget (read, but no update permissions) - final var readOnlyTargetControllerId = readOnlyTarget.getControllerId(); - assertThatThrownBy(() -> assignDistributionSet(firstDsId, readOnlyTargetControllerId)).isInstanceOf(AssertionError.class); - - // bunch assignment skips denied since at least one target without update permissions is present - assertThat(assignDistributionSet(secondDs.getId(), - Arrays.asList(readOnlyTargetControllerId, manageableTarget.getControllerId()), - ActionType.FORCED).getAssigned()).isEqualTo(1); - }); - } - - /** - * Verifies only manageable targets are part of the rollout - */ - @Test - void verifyRolloutTargetScope() { - final DistributionSet ds = testdataFactory.createDistributionSet("myDs"); - distributionSetManagement.lock(ds); - - final String[] updateTargetControllerIds = { "update1", "update2", "update3" }; - final List updateTargets = testdataFactory.createTargets(updateTargetControllerIds); - final String[] readTargetControllerIds = { "read1", "read2", "read3", "read4" }; - final List readTargets = testdataFactory.createTargets(readTargetControllerIds); - final List hiddenTargets = testdataFactory.createTargets("hidden1", "hidden2", "hidden3", "hidden4", "hidden5"); - - runAs(withUser("user", - READ_DISTRIBUTION_SET, - READ_TARGET + "/controllerId=in=(" + String.join(", ", List.of(updateTargetControllerIds)) + ")" + - " or controllerId=in=(" + String.join(", ", List.of(readTargetControllerIds)) + ")", - UPDATE_TARGET + "/controllerId=in=(" + String.join(", ", List.of(updateTargetControllerIds)) + ")", - CREATE_ROLLOUT, READ_ROLLOUT), () -> { - final Rollout rollout = testdataFactory.createRolloutByVariables( - "testRollout", "description", updateTargets.size(), "id==*", ds, "50", "5"); - assertThat(rollout.getTotalTargets()).isEqualTo(updateTargets.size()); - - final List content = rolloutGroupManagement.findByRollout(rollout.getId(), Pageable.unpaged()).getContent(); - assertThat(content).hasSize(updateTargets.size()); - - final List rolloutTargets = content.stream().flatMap( - group -> rolloutGroupManagement.findTargetsOfRolloutGroup(group.getId(), Pageable.unpaged()).get()) - .toList(); - - assertThat(rolloutTargets).hasSize(updateTargets.size()).allMatch( - target -> updateTargets.stream().anyMatch(readTarget -> readTarget.getId().equals(target.getId()))) - .noneMatch(target -> readTargets.stream() - .anyMatch(readTarget -> readTarget.getId().equals(target.getId()))) - .noneMatch(target -> hiddenTargets.stream() - .anyMatch(readTarget -> readTarget.getId().equals(target.getId()))); - }); - } - - /** - * Verifies only manageable targets are part of an auto assignment. - */ - @Test - void verifyAutoAssignmentTargetScope() { - final DistributionSet distributionSet = testdataFactory.createDistributionSet(); - distributionSetManagement.lock(distributionSet); - - final String[] updateTargetControllerIds = { "update1", "update2", "update3" }; - final List updateTargets = testdataFactory.createTargets(updateTargetControllerIds); - final String[] readTargetControllerIds = { "read1", "read2", "read3", "read4" }; - final List readTargets = testdataFactory.createTargets(readTargetControllerIds); - final List hiddenTargets = testdataFactory.createTargets("hidden1", "hidden2", "hidden3", "hidden4", "hidden5"); - - final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement - .create(TargetFilterQueryManagement.Create.builder().name("testName").query("id==*").build()); - - runAs(withUser("user", - READ_TARGET + "/controllerId=in=(" + String.join(", ", List.of(updateTargetControllerIds)) + ")" + - " or controllerId=in=(" + String.join(", ", List.of(readTargetControllerIds)) + ")", - UPDATE_TARGET + "/controllerId=in=(" + String.join(", ", List.of(updateTargetControllerIds)) + ")", - READ_DISTRIBUTION_SET + "/id==" + distributionSet.getId()), () -> { - - targetFilterQueryManagement.updateAutoAssignDS( - new AutoAssignDistributionSetUpdate(targetFilterQuery.getId()).ds(distributionSet.getId())); - - autoAssignChecker.checkAllTargets(); - - assertThat(targetManagement.findByAssignedDistributionSet(distributionSet.getId(), Pageable.unpaged()) - .getContent()) - .hasSize(updateTargets.size()) - .allMatch(assignedTarget -> updateTargets.stream() - .anyMatch(updateTarget -> updateTarget.getId().equals(assignedTarget.getId()))) - .noneMatch(assignedTarget -> readTargets.stream() - .anyMatch(updateTarget -> updateTarget.getId().equals(assignedTarget.getId()))) - .noneMatch(assignedTarget -> hiddenTargets.stream() - .anyMatch(updateTarget -> updateTarget.getId().equals(assignedTarget.getId()))); - }); - } -} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetManagementTest.java new file mode 100644 index 000000000..458ed1756 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetManagementTest.java @@ -0,0 +1,331 @@ +/** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.hawkbit.repository.jpa.acm; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.eclipse.hawkbit.im.authentication.SpPermission.CREATE_ROLLOUT; +import static org.eclipse.hawkbit.im.authentication.SpPermission.CREATE_TARGET; +import static org.eclipse.hawkbit.im.authentication.SpPermission.DELETE_TARGET; +import static org.eclipse.hawkbit.im.authentication.SpPermission.HANDLE_ROLLOUT; +import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_DISTRIBUTION_SET; +import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_ROLLOUT; +import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_TARGET; +import static org.eclipse.hawkbit.im.authentication.SpPermission.UPDATE_TARGET; +import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.runAs; +import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.withUser; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.assertj.core.api.Assertions; +import org.eclipse.hawkbit.repository.Identifiable; +import org.eclipse.hawkbit.repository.TargetManagement; +import org.eclipse.hawkbit.repository.TargetManagement.Update; +import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException; +import org.eclipse.hawkbit.repository.model.Action; +import org.eclipse.hawkbit.repository.model.DistributionSet; +import org.eclipse.hawkbit.repository.model.NamedEntity; +import org.eclipse.hawkbit.repository.model.Rollout; +import org.eclipse.hawkbit.repository.model.RolloutGroup; +import org.eclipse.hawkbit.repository.model.Target; +import org.eclipse.hawkbit.repository.model.TargetTag; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.Pageable; + +class TargetManagementTest extends AbstractAccessControllerManagementTest { + + @Test + void verifyCreation() { + // permissions to read all and create only type1 targets + runAs(withAuthorities(READ_TARGET, CREATE_TARGET + "/type.id==" + targetType1.getId()), () -> { + testdataFactory.createTarget("controller_1_create", "Controller-1_create", targetType1); + assertThatThrownBy(() -> testdataFactory.createTarget("controller_2_create", "Controller-2_create", targetType2)) + .isInstanceOf(InsufficientPermissionException.class); + }); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Test + void verifyRead() { + assertThat(assignDistributionSet(ds2Type2, List.of(target1Type1, target2Type2, target3Type2)).getAssigned()).isEqualTo(3); + final Target target1Type1 = targetManagement.get(super.target1Type1.getId()); + final Target target2Type2 = targetManagement.get(super.target2Type2.getId()); + runAs(withAuthorities(READ_TARGET + "/type.id==" + targetType1.getId(), READ_DISTRIBUTION_SET), () -> { + Assertions. assertThat(targetManagement.findAll(UNPAGED)).containsExactly(target1Type1); + assertThat(targetManagement.count()).isEqualTo(1); + + Assertions. assertThat(targetManagement.get(List.of(target1Type1.getId()))).containsExactly(target1Type1); + final List allTargetIds = List.of(target1Type1.getId(), target2Type2.getId()); + assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy(() -> targetManagement.get(allTargetIds)); + + assertThat(((TargetManagement) targetManagement).find(target1Type1.getId())).hasValue(target1Type1); + assertThat(targetManagement.find(target2Type2.getId())).isEmpty(); + + assertThat(targetManagement.findByControllerId(List.of(target1Type1.getControllerId()))).containsExactly(target1Type1); + + assertThat(targetManagement.getByControllerId(target1Type1.getControllerId())).isEqualTo(target1Type1); + assertThat(targetManagement.findByControllerId(target1Type1.getControllerId())).hasValue(target1Type1); + final String target2Type2ControllerId = target2Type2.getControllerId(); + assertThatThrownBy(() -> targetManagement.getByControllerId(target2Type2ControllerId)) + .isInstanceOf(InsufficientPermissionException.class); + assertThatThrownBy(() -> targetManagement.findByControllerId(target2Type2ControllerId)) + .isInstanceOf(InsufficientPermissionException.class); + + assertThat(targetManagement.getControllerAttributes(target1Type1.getControllerId())).isEmpty(); + assertThatThrownBy(() -> targetManagement.getControllerAttributes(target2Type2ControllerId)) + .isInstanceOf(InsufficientPermissionException.class); + + assertThat(targetManagement.getByControllerId(target1Type1.getControllerId())).isEqualTo(target1Type1); + assertThat(targetManagement.findByControllerId(target1Type1.getControllerId())).hasValue(target1Type1); + assertThatThrownBy(() -> targetManagement.getByControllerId(target2Type2ControllerId)) + .isInstanceOf(InsufficientPermissionException.class); + assertThatThrownBy(() -> targetManagement.findByControllerId(target2Type2ControllerId)) + .isInstanceOf(InsufficientPermissionException.class); + + assertThat(targetManagement.countByRsql("id==*")).isEqualTo(1); + assertThat(targetManagement.countByRsql("id==%s".formatted(target1Type1.getControllerId()))).isEqualTo(1); + assertThat(targetManagement.countByRsql("id==%s".formatted(target2Type2.getControllerId()))).isZero(); + Assertions. assertThat(targetManagement.findByRsql("id==*", UNPAGED)) + .hasSize(1) + .containsExactly(target1Type1) + .doesNotContain(target2Type2); + Assertions. assertThat( + targetManagement.findByRsql("id==%s".formatted(target1Type1.getControllerId()), UNPAGED)) + .hasSize(1) + .containsExactly(target1Type1) + .doesNotContain(target2Type2); + assertThat(targetManagement.findByRsql("id==%s".formatted(target2Type2.getControllerId()), UNPAGED)).isEmpty(); + + assertThat(targetManagement.findByAssignedDistributionSet(ds2Type2.getId(), UNPAGED)) + .extracting(Identifiable::getId).containsExactly(target1Type1.getId()); + assertThat(targetManagement.findByAssignedDistributionSetAndRsql(ds2Type2.getId(), "id==*", UNPAGED)) + .extracting(Identifiable::getId).containsExactly(target1Type1.getId()); + + assertThat(targetManagement.countByRsqlAndCompatible("id==*", ds2Type2.getType().getId())).isEqualTo(1); + + assertThat(targetManagement.getMetadata(target1Type1.getControllerId())).isEmpty(); + final String target2Type2ControllerId1 = target2Type2.getControllerId(); + assertThatThrownBy(() -> targetManagement.getMetadata(target2Type2ControllerId1)) + .isInstanceOf(InsufficientPermissionException.class); + + // find by tags + assertThat(targetManagement.findByTag(targetType2.getId(), UNPAGED)) + .extracting(Identifiable::getId) + .containsOnly(target1Type1.getId()); + assertThat(targetManagement.findByRsqlAndTag("id==*", targetType2.getId(), UNPAGED)) + .extracting(Identifiable::getId) + .containsOnly(target1Type1.getId()); + }); + } + + @Disabled + @Test + void verifyReadRolloutRelated() { + assertThat(assignDistributionSet(ds2Type2, List.of(target1Type1, target2Type2, target3Type2)).getAssigned()).isEqualTo(3); + prepareFinishedUpdates(ds2Type2, target1Type1, target2Type2); + final Target target1Type1 = targetManagement.get(super.target1Type1.getId()); + runAs(withAuthorities( + READ_TARGET + "/type.id==" + targetType1.getId(), + READ_DISTRIBUTION_SET, + CREATE_ROLLOUT, READ_ROLLOUT, HANDLE_ROLLOUT), () -> { + assertThat(targetManagement.findByInstalledDistributionSet(ds2Type2.getId(), UNPAGED)) + .extracting(Identifiable::getId).containsExactly(target1Type1.getId()); + assertThat(targetManagement.findByInstalledDistributionSetAndRsql(ds2Type2.getId(), "id==*", UNPAGED)) + .extracting(Identifiable::getId).containsExactly(target1Type1.getId()); + + assertThat(targetManagement.findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable( + ds2Type2.getId(), "id==*", UNPAGED)) + .hasSize(1) + .extracting(Identifiable::getId).containsOnly(target1Type1.getId()); + + assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable( + target1Type1.getControllerId(), ds2Type2.getId(), "id==*")).isTrue(); + assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable( + target2Type2.getControllerId(), ds2Type2.getId(), "id==*")).isFalse(); + + assertThat(targetManagement.findByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable( + List.of(1L), "id==*", ds2Type2.getType(), UNPAGED)) + .hasSize(1) + .extracting(Identifiable::getId).containsOnly(target1Type1.getId()); + assertThat(targetManagement.countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable("id==*", List.of(1L), + ds2Type2.getType())).isEqualTo(1); + + final Rollout rollout = testdataFactory.createRolloutByVariables( + "testRollout", "testDescription", 3, "id==*", ds2Type2, "50", "5"); + + final List foundTargetIds = rolloutGroupManagement.findByRollout(rollout.getId(), UNPAGED).getContent().stream() + .flatMap(rolloutGroup -> targetManagement.findByInRolloutGroupWithoutAction(rolloutGroup.getId(), UNPAGED) + .getContent().stream()).map(Identifiable::getId).toList(); + assertThat(foundTargetIds).hasSize(1).contains(target1Type1.getId()); + + assertThat(targetManagement.findByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable( + List.of(1L), "id==*", ds2Type2.getType(), UNPAGED)) + .hasSize(1) + .extracting(Identifiable::getId).containsOnly(target1Type1.getId()); + assertThat(targetManagement.countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable("id==*", List.of(1L), ds2Type2.getType())) + .isEqualTo(1); + final Rollout runAsSystem = systemSecurityContext.runAsSystem( + () -> testdataFactory.createRolloutByVariables("testRollout", "testDescription", 3, "id==*", ds2Type2, "50", "5")); + assertThat(rolloutGroupManagement.findByRollout(runAsSystem.getId(), UNPAGED).getContent().stream() + .flatMap(rolloutGroup -> targetManagement.findByInRolloutGroupWithoutAction(rolloutGroup.getId(), UNPAGED) + .getContent().stream()).map(Identifiable::getId).toList()) + .hasSize(3); + }); + } + + @Test + void verifyUpdate() { + final TargetTag testTag = testdataFactory.createTargetTags(1, "testTag").get(0); + final Long testTagId = testTag.getId(); + runAs(withAuthorities( + READ_TARGET, UPDATE_TARGET + "/type.id==" + targetType1.getId(), + READ_DISTRIBUTION_SET), + () -> { + assertThat(targetManagement.update( + Update.builder().id(target1Type1.getId()).name("myNewName").build())) + .extracting(NamedEntity::getName).isEqualTo("myNewName"); + + final Update targetUpdate = Update.builder().id(target2Type2.getId()).name("myNewName").build(); + assertThatThrownBy(() -> targetManagement.update(targetUpdate)).isInstanceOf(InsufficientPermissionException.class); + + final String metadataKey = "key.dot"; + final Map metadata = Map.of(metadataKey, "value.dot"); + final String target1Type1ControllerId = target1Type1.getControllerId(); + targetManagement.createMetadata(target1Type1ControllerId, metadata); + final String target2Type2ControllerId = target2Type2.getControllerId(); + assertThatThrownBy(() -> targetManagement.createMetadata(target2Type2ControllerId, metadata)) + .isInstanceOf(InsufficientPermissionException.class); + final String newValue = "newValue"; + targetManagement.createMetadata(target1Type1ControllerId, metadataKey, newValue); + assertThat(targetManagement.getMetadata(target1Type1ControllerId)).containsEntry(metadataKey, newValue); + assertThatThrownBy(() -> targetManagement.createMetadata(target2Type2ControllerId, metadataKey, newValue)) + .isInstanceOf(InsufficientPermissionException.class); + targetManagement.deleteMetadata(target1Type1ControllerId, metadataKey); + assertThatThrownBy(() -> targetManagement.deleteMetadata(target2Type2ControllerId, metadataKey)) + .isInstanceOf(EntityNotFoundException.class); + + // tag assignments + assertThat(targetManagement.assignTag(List.of(target1Type1.getControllerId()), testTagId)) + .extracting(Identifiable::getId).containsExactly(target1Type1.getId()); + final List target2Type2ControllerIdList = List.of(target2Type2.getControllerId()); + assertThatThrownBy(() -> targetManagement.assignTag(target2Type2ControllerIdList, testTagId)) + .isInstanceOf(InsufficientPermissionException.class); + assertThatThrownBy(() -> targetManagement.assignTag(target2Type2ControllerIdList, testTagId)) + .isInstanceOf(InsufficientPermissionException.class); + + assertThat(targetManagement.unassignTag(List.of(target1Type1.getControllerId()), testTagId)).element(0) + .extracting(Identifiable::getId).isEqualTo(target1Type1.getId()); + assertThatThrownBy(() -> targetManagement.unassignTag(target2Type2ControllerIdList, testTagId)) + .isInstanceOf(InsufficientPermissionException.class); + assertThatThrownBy(() -> targetManagement.unassignTag(target2Type2ControllerIdList, testTagId)) + .isInstanceOf(InsufficientPermissionException.class); + + // type + final Long targetType2Id = targetType2.getId(); + assertThatThrownBy(() -> targetManagement.assignType(target2Type2ControllerId, targetType2Id)) + .isInstanceOf(InsufficientPermissionException.class); + assertThatThrownBy(() -> targetManagement.unassignType(target2Type2ControllerId)) + .isInstanceOf(InsufficientPermissionException.class); + final String noPermissionsTestDataControllerId = target2Type2.getControllerId(); + assertThatThrownBy(() -> targetManagement.unassignType(noPermissionsTestDataControllerId)) + .isInstanceOf(InsufficientPermissionException.class); + + assertThat(assignDistributionSet(ds2Type2.getId(), target1Type1.getControllerId()).getAssigned()).isEqualTo(1); + assertThatThrownBy(() -> assignDistributionSet(ds2Type2.getId(), target2Type2ControllerId)).isInstanceOf( + AssertionError.class); + // bunch assignment skips denied since at least one target without update permissions is present + assertThat(assignDistributionSet( + ds3Type2.getId(), List.of(target1Type1.getControllerId(), target2Type2ControllerId), Action.ActionType.FORCED) + .getAssigned()).isEqualTo(1); + + assertThat(targetManagement.findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable(ds1Type1.getId(), "id==*", UNPAGED)) + .extracting(Identifiable::getId).containsExactly(target1Type1.getId()); + assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable( + target1Type1.getControllerId(), ds1Type1.getId(), "id==*")).isTrue(); + assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable( + target2Type2.getControllerId(), ds1Type1.getId(), "id==*")).isFalse(); + }); + } + + @Test + void verifyDeletion() { + runAs(withAuthorities(READ_TARGET, DELETE_TARGET + "/type.id==" + targetType1.getId()), () -> { + final List allTargetIds = List.of(target1Type1.getId(), target2Type2.getId()); + assertThatThrownBy(() -> targetManagement.delete(allTargetIds)).isInstanceOf(InsufficientPermissionException.class); + final List target2Type2IdList = List.of(target2Type2.getId()); + assertThatThrownBy(() -> targetManagement.delete(target2Type2IdList)).isInstanceOf(InsufficientPermissionException.class); + + targetManagement.delete(List.of(target1Type1.getId())); + }); + } + + private void prepareFinishedUpdates(final DistributionSet ds, final Target... targets) { + final List controllerIds = Arrays.stream(targets).map(Target::getControllerId).toList(); + getFirstAssignedTarget(assignDistributionSet(ds.getId(), controllerIds, Action.ActionType.FORCED)); + + controllerIds.stream().map(controllerId -> deploymentManagement.findActiveActionsByTarget(controllerId, PAGE).getContent().get(0)) + .forEach(action -> { + if (action.getStatus() == Action.Status.WAIT_FOR_CONFIRMATION) { + confirmationManagement.confirmAction(action.getId(), null, null); + } + + action = controllerManagement.addUpdateActionStatus( + Action.ActionStatusCreate.builder().actionId(action.getId()).status(Action.Status.RUNNING).build()); + + controllerManagement.addUpdateActionStatus( + Action.ActionStatusCreate.builder().actionId(action.getId()).status(Action.Status.FINISHED).build()); + }); + } + + /** + * Verifies only manageable targets are part of the rollout + */ + @Test + void verifyRolloutTargetScope() { + final DistributionSet ds = testdataFactory.createDistributionSet("myDs"); + distributionSetManagement.lock(ds); + + final String[] updateTargetControllerIds = { "update1", "update2", "update3" }; + final List updateTargets = testdataFactory.createTargets(updateTargetControllerIds); + final String[] readTargetControllerIds = { "read1", "read2", "read3", "read4" }; + final List readTargets = testdataFactory.createTargets(readTargetControllerIds); + final List hiddenTargets = testdataFactory.createTargets("hidden1", "hidden2", "hidden3", "hidden4", "hidden5"); + + runAs(withUser("user", + READ_DISTRIBUTION_SET, + READ_TARGET + "/controllerId=in=(" + String.join(", ", List.of(updateTargetControllerIds)) + ")" + + " or controllerId=in=(" + String.join(", ", List.of(readTargetControllerIds)) + ")", + UPDATE_TARGET + "/controllerId=in=(" + String.join(", ", List.of(updateTargetControllerIds)) + ")", + CREATE_ROLLOUT, READ_ROLLOUT), () -> { + final Rollout rollout = testdataFactory.createRolloutByVariables( + "testRollout", "description", updateTargets.size(), "id==*", ds, "50", "5"); + assertThat(rollout.getTotalTargets()).isEqualTo(updateTargets.size()); + + final List content = rolloutGroupManagement.findByRollout(rollout.getId(), Pageable.unpaged()).getContent(); + assertThat(content).hasSize(updateTargets.size()); + + final List rolloutTargets = content.stream().flatMap( + group -> rolloutGroupManagement.findTargetsOfRolloutGroup(group.getId(), Pageable.unpaged()).get()) + .toList(); + + assertThat(rolloutTargets).hasSize(updateTargets.size()).allMatch( + target -> updateTargets.stream().anyMatch(readTarget -> readTarget.getId().equals(target.getId()))) + .noneMatch(target -> readTargets.stream() + .anyMatch(readTarget -> readTarget.getId().equals(target.getId()))) + .noneMatch(target -> hiddenTargets.stream() + .anyMatch(readTarget -> readTarget.getId().equals(target.getId()))); + }); + } +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetTypeAccessControllerTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetTypeAccessControllerTest.java deleted file mode 100644 index 073e72dc3..000000000 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetTypeAccessControllerTest.java +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Copyright (c) 2023 Bosch.IO GmbH and others - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.hawkbit.repository.jpa.acm; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.eclipse.hawkbit.im.authentication.SpPermission.DELETE_TARGET_TYPE; -import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_TARGET_TYPE; -import static org.eclipse.hawkbit.im.authentication.SpPermission.UPDATE_TARGET_TYPE; -import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.runAs; -import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.withUser; - -import java.util.Arrays; -import java.util.List; - -import org.eclipse.hawkbit.repository.Identifiable; -import org.eclipse.hawkbit.repository.TargetTypeManagement.Create; -import org.eclipse.hawkbit.repository.TargetTypeManagement.Update; -import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; -import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException; -import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest; -import org.eclipse.hawkbit.repository.model.TargetType; -import org.junit.jupiter.api.Test; -import org.springframework.data.domain.Pageable; -import org.springframework.test.context.TestPropertySource; - -/** - * Feature: Component Tests - Access Control
- * Story: Test Target Type Access Controller - */ -@TestPropertySource(properties = "hawkbit.acm.access-controller.enabled=true") -class TargetTypeAccessControllerTest extends AbstractJpaIntegrationTest { - - /** - * Verifies read access rules for target types - */ - @Test - void verifyTargetTypeReadOperations() { - final TargetType permittedTargetType = targetTypeManagement.create(Create.builder().name("type1").build()); - final TargetType hiddenTargetType = targetTypeManagement.create(Create.builder().name("type2").build()); - - runAs(withUser("user", READ_TARGET_TYPE + "/id==" + permittedTargetType.getId()), () -> { - // verify targetTypeManagement#findAll - assertThat(targetTypeManagement.findAll(Pageable.unpaged()).get().map(Identifiable::getId).toList()) - .containsExactly(permittedTargetType.getId()); - - // verify targetTypeManagement#findByRsql - assertThat(targetTypeManagement.findByRsql("name==*", Pageable.unpaged()).get().map(Identifiable::getId).toList()) - .containsExactly(permittedTargetType.getId()); - - // verify targetTypeManagement#count - assertThat(targetTypeManagement.count()).isEqualTo(1); - - // verify targetTypeManagement#get by id - assertThat(targetTypeManagement.find(permittedTargetType.getId())).isPresent(); - final Long hiddenTargetTypeId = hiddenTargetType.getId(); - assertThat(targetTypeManagement.find(hiddenTargetTypeId)).isEmpty(); - - // verify targetTypeManagement#get by ids - final List allEntityIds = Arrays.asList(permittedTargetType.getId(), hiddenTargetTypeId); - assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy(() -> targetTypeManagement.get(allEntityIds)); - - // verify targetTypeManagement#update is not possible. Assert exception thrown. - final Update targetTypeUpdate = Update.builder() - .id(hiddenTargetTypeId).name(hiddenTargetType.getName() + "/new").description("newDesc") - .build(); - assertThatThrownBy(() -> targetTypeManagement.update(targetTypeUpdate)) - .as("Target type update shouldn't be allowed since the target type is not visible.") - .isInstanceOf(InsufficientPermissionException.class); - - // verify targetTypeManagement#delete is not possible. Assert exception thrown. - assertThatThrownBy(() -> targetTypeManagement.delete(hiddenTargetTypeId)) - .as("Target type delete shouldn't be allowed since the target type is not visible.") - .isInstanceOf(InsufficientPermissionException.class); - }); - } - - /** - * Verifies delete access rules for target types - */ - @Test - void verifyTargetTypeDeleteOperations() { - final TargetType manageableTargetType = targetTypeManagement.create(Create.builder().name("type1").build()); - final TargetType readOnlyTargetType = targetTypeManagement.create(Create.builder().name("type2").build()); - - runAs(withUser("user", - READ_TARGET_TYPE + "/id==" + manageableTargetType.getId() + " or id==" + readOnlyTargetType.getId(), - DELETE_TARGET_TYPE + "/id==" + manageableTargetType.getId()), () -> { - // delete the manageableTargetType - targetTypeManagement.delete(manageableTargetType.getId()); - - // verify targetTypeManagement#delete for readOnlyTargetType is not possible - final Long readOnlyTargetTypeId = readOnlyTargetType.getId(); - assertThatThrownBy(() -> targetTypeManagement.delete(readOnlyTargetTypeId)) - .isInstanceOfAny(InsufficientPermissionException.class, EntityNotFoundException.class); - }); - } - - /** - * Verifies update operation for target types - */ - @Test - void verifyTargetTypeUpdateOperations() { - final TargetType manageableTargetType = targetTypeManagement.create(Create.builder().name("type1").build()); - final TargetType readOnlyTargetType = targetTypeManagement.create(Create.builder().name("type2").build()); - - runAs(withUser("user", - READ_TARGET_TYPE + "/id==" + manageableTargetType.getId() + " or id==" + readOnlyTargetType.getId(), - UPDATE_TARGET_TYPE + "/id==" + manageableTargetType.getId()), () -> { - // update the manageableTargetType - targetTypeManagement.update(Update.builder().id(manageableTargetType.getId()) - .name(manageableTargetType.getName() + "/new").description("newDesc").build()); - - // verify targetTypeManagement#update for readOnlyTargetType is not possible - final Update targetTypeUpdate = Update.builder() - .id(readOnlyTargetType.getId()).name(readOnlyTargetType.getName() + "/new").description("newDesc") - .build(); - assertThatThrownBy(() -> targetTypeManagement.update(targetTypeUpdate)).isInstanceOf(InsufficientPermissionException.class); - }); - } - - /** - * Verifies create operation blocked by controller - */ - @Test - void verifyTargetTypeCreationBlockedByAccessController() { - runAs(withUser("user", READ_TARGET_TYPE, UPDATE_TARGET_TYPE), () -> { - // verify targetTypeManagement#create for any type - final Create targetTypeCreate = Create.builder().name("type1").build(); - assertThatThrownBy(() -> targetTypeManagement.create(targetTypeCreate)) - .as("Target type create shouldn't be allowed since the target type is not visible.") - .isInstanceOf(InsufficientPermissionException.class); - }); - } -} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetTypeManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetTypeManagementTest.java new file mode 100644 index 000000000..7c76304fb --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetTypeManagementTest.java @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.hawkbit.repository.jpa.acm; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.eclipse.hawkbit.im.authentication.SpPermission.CREATE_PREFIX; +import static org.eclipse.hawkbit.im.authentication.SpPermission.DELETE_TARGET_TYPE; +import static org.eclipse.hawkbit.im.authentication.SpPermission.DISTRIBUTION_SET_TYPE; +import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_PREFIX; +import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_TARGET_TYPE; +import static org.eclipse.hawkbit.im.authentication.SpPermission.TARGET_TYPE; +import static org.eclipse.hawkbit.im.authentication.SpPermission.UPDATE_TARGET_TYPE; +import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.runAs; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import org.assertj.core.api.Assertions; +import org.eclipse.hawkbit.repository.TargetTypeManagement.Update; +import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException; +import org.eclipse.hawkbit.repository.model.NamedEntity; +import org.eclipse.hawkbit.repository.model.TargetType; +import org.junit.jupiter.api.Test; + +class TargetTypeManagementTest extends AbstractAccessControllerManagementTest { + + @Test + void verifyCreate() { + // permissions to read all and create new type with name 'permitted' + runAs(withAuthorities(READ_TARGET_TYPE, CREATE_PREFIX + TARGET_TYPE + "/name==permitted"), () -> { + assertThat(testdataFactory.findOrCreateTargetType("permitted")).isNotNull(); + assertThatThrownBy(() -> testdataFactory.findOrCreateTargetType("not_permitted_2")) + .isInstanceOf(InsufficientPermissionException.class); + }); + } + + @Test + void verifyRead() { + // permissions to read only type1 target types + runAs(withAuthorities(READ_TARGET_TYPE + "/id==" + targetType1.getId()), () -> { + Assertions. assertThat(targetTypeManagement.findAll(UNPAGED)).containsExactly(targetType1); + assertThat(targetTypeManagement.count()).isEqualTo(1); + + Assertions. assertThat(targetTypeManagement.findByRsql("name==*", UNPAGED)) + .contains(targetType1).doesNotContain(targetType2); + + assertThat((Optional) targetTypeManagement.find(targetType1.getId())).hasValue(targetType1); + assertThat(targetTypeManagement.find(targetType2.getId())).isEmpty(); + + Assertions. assertThat(targetTypeManagement.get(Collections.singletonList(targetType1.getId()))) + .contains(targetType1).doesNotContain(targetType2); + final List noPermissionsEntitiesIds = Collections.singletonList(targetType2.getId()); + assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy(() -> targetTypeManagement.get(noPermissionsEntitiesIds)); + final List allEntitiesIds = List.of(targetType1.getId(), targetType2.getId()); + assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy(() -> targetTypeManagement.get(allEntitiesIds)); + }); + } + + @Test + void verifyUpdate() { + runAs(withAuthorities(READ_TARGET_TYPE, UPDATE_TARGET_TYPE + "/id==" + targetType1.getId(), READ_PREFIX + DISTRIBUTION_SET_TYPE), + () -> { + assertThat(targetTypeManagement.update(Update.builder().id(targetType1.getId()).description("anotherDesc").build())) + .extracting(NamedEntity::getDescription).isEqualTo("anotherDesc"); + final Update targetTypeUpdate = Update.builder().id(targetType2.getId()).description("anotherDesc").build(); + assertThatThrownBy(() -> targetTypeManagement.update(targetTypeUpdate)).isInstanceOf(InsufficientPermissionException.class); + + targetTypeManagement.assignCompatibleDistributionSetTypes(targetType1.getId(), List.of(dsType2.getId())); + final Long targetType2Id = targetType2.getId(); + assertThatThrownBy( + () -> targetTypeManagement.assignCompatibleDistributionSetTypes(targetType2Id, List.of(dsType2.getId()))) + .isInstanceOf(InsufficientPermissionException.class); + + targetTypeManagement.unassignDistributionSetType(targetType1.getId(), dsType2.getId()); + assertThatThrownBy(() -> targetTypeManagement.unassignDistributionSetType(targetType2Id, dsType2.getId())) + .isInstanceOf(InsufficientPermissionException.class); + }); + } + + @Test + void verifyDelete() { + // assigned type can't be deleted, so create dedicated ones + final TargetType targetType4 = testdataFactory.findOrCreateTargetType("toBeDeleted"); + final Long targetType5Id = testdataFactory.findOrCreateTargetType("toBeDeleted2").getId(); + runAs(withAuthorities(READ_TARGET_TYPE, DELETE_TARGET_TYPE + "/id==" + targetType4.getId()), () -> { + targetTypeManagement.delete(targetType4.getId()); + assertThatThrownBy(() -> targetTypeManagement.delete(targetType5Id)).isInstanceOf(InsufficientPermissionException.class); + }); + } +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetTypeQueryManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetTypeQueryManagementTest.java index b72e596ab..c32faba41 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetTypeQueryManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetTypeQueryManagementTest.java @@ -31,7 +31,7 @@ import org.junit.jupiter.api.Test; * Feature: Component Tests - Access Control
* Story: Test Distribution Set Access Controller */ -class TargetTypeQueryManagementTest extends AbstractAccessControllerTest { +class TargetTypeQueryManagementTest extends AbstractAccessControllerManagementTest { @Test void verifyAutoAssignmentRestrictionByDs() {