Add Target(Type)Management tests (#2735)
Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -39,7 +39,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
|||||||
public interface TargetTypeManagement<T extends TargetType>
|
public interface TargetTypeManagement<T extends TargetType>
|
||||||
extends RepositoryManagement<T, TargetTypeManagement.Create, TargetTypeManagement.Update> {
|
extends RepositoryManagement<T, TargetTypeManagement.Create, TargetTypeManagement.Update> {
|
||||||
|
|
||||||
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
|
@Override
|
||||||
default String permissionGroup() {
|
default String permissionGroup() {
|
||||||
@@ -58,7 +58,7 @@ public interface TargetTypeManagement<T extends TargetType>
|
|||||||
* @param distributionSetTypeIds Distribution set ID
|
* @param distributionSetTypeIds Distribution set ID
|
||||||
* @return Target type
|
* @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<Long> distributionSetTypeIds);
|
TargetType assignCompatibleDistributionSetTypes(long id, @NotEmpty Collection<Long> distributionSetTypeIds);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,7 +66,7 @@ public interface TargetTypeManagement<T extends TargetType>
|
|||||||
* @param distributionSetTypeIds Distribution set ID
|
* @param distributionSetTypeIds Distribution set ID
|
||||||
* @return Target type
|
* @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);
|
TargetType unassignDistributionSetType(long id, long distributionSetTypeIds);
|
||||||
|
|
||||||
@SuperBuilder
|
@SuperBuilder
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
|||||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
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.model.TargetType;
|
||||||
import org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch;
|
import org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch;
|
||||||
import org.eclipse.hawkbit.repository.test.util.WithUser;
|
import org.eclipse.hawkbit.repository.test.util.WithUser;
|
||||||
@@ -26,7 +27,7 @@ import org.junit.jupiter.api.BeforeEach;
|
|||||||
import org.springframework.test.context.TestPropertySource;
|
import org.springframework.test.context.TestPropertySource;
|
||||||
|
|
||||||
@TestPropertySource(properties = "hawkbit.acm.access-controller.enabled=true")
|
@TestPropertySource(properties = "hawkbit.acm.access-controller.enabled=true")
|
||||||
abstract class AbstractAccessControllerTest extends AbstractJpaIntegrationTest {
|
abstract class AbstractAccessControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||||
|
|
||||||
protected SoftwareModuleType smType1;
|
protected SoftwareModuleType smType1;
|
||||||
protected SoftwareModuleType smType2;
|
protected SoftwareModuleType smType2;
|
||||||
@@ -44,10 +45,13 @@ abstract class AbstractAccessControllerTest extends AbstractJpaIntegrationTest {
|
|||||||
|
|
||||||
protected TargetType targetType1;
|
protected TargetType targetType1;
|
||||||
protected TargetType targetType2;
|
protected TargetType targetType2;
|
||||||
|
protected TargetTag targetTag1;
|
||||||
|
protected TargetTag targetTag2;
|
||||||
protected Target target1Type1;
|
protected Target target1Type1;
|
||||||
protected Target target2Type2;
|
protected Target target2Type2;
|
||||||
protected Target target3Type2;
|
protected Target target3Type2;
|
||||||
|
|
||||||
|
@SuppressWarnings("java:S1117") // java:S1117 - intentional hiding of fiends - they are the same
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
@Override
|
@Override
|
||||||
public void beforeAll() throws Exception {
|
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());
|
dsType1 = testdataFactory.findOrCreateDistributionSetType("DsType1", "DistributionSetType-1", List.of(smType1), List.of());
|
||||||
dsType2 = testdataFactory.findOrCreateDistributionSetType("DsType2", "DistributionSetType-2", List.of(smType2), List.of(smType1));
|
dsType2 = testdataFactory.findOrCreateDistributionSetType("DsType2", "DistributionSetType-2", List.of(smType2), List.of(smType1));
|
||||||
final List<DistributionSetTag> tags = testdataFactory.createDistributionSetTags(2);
|
final List<DistributionSetTag> dsTags = testdataFactory.createDistributionSetTags(2);
|
||||||
dsTag1 = tags.get(0);
|
dsTag1 = dsTags.get(0);
|
||||||
dsTag2 = tags.get(1);
|
dsTag2 = dsTags.get(1);
|
||||||
ds1Type1 = distributionSetManagement.lock(
|
ds1Type1 = distributionSetManagement.lock(
|
||||||
distributionSetManagement.assignTag(
|
distributionSetManagement.assignTag(
|
||||||
List.of(testdataFactory.createDistributionSet("Ds1Type1", "1.0", dsType1, List.of(sm1Type1)).getId()),
|
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));
|
targetType1 = testdataFactory.createTargetType("TargetType1", Set.of(dsType1, dsType2));
|
||||||
targetType2 = testdataFactory.createTargetType("TargetType2", Set.of(dsType2));
|
targetType2 = testdataFactory.createTargetType("TargetType2", Set.of(dsType2));
|
||||||
target1Type1 = testdataFactory.createTarget("controller_1", "Controller-1", targetType1);
|
final List<? extends TargetTag> targetTags = testdataFactory.createTargetTags(2, "acm_");
|
||||||
target2Type2 = testdataFactory.createTarget("controller_2", "Controller-2", targetType2);
|
targetTag1 = targetTags.get(0);
|
||||||
target3Type2 = testdataFactory.createTarget("controller_3", "Controller-3", targetType2);
|
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) {
|
protected static WithUser withAuthorities(final String... authorities) {
|
||||||
@@ -30,7 +30,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.integration.support.locks.LockRegistry;
|
import org.springframework.integration.support.locks.LockRegistry;
|
||||||
|
|
||||||
class AutoAssignTest extends AbstractAccessControllerTest {
|
class AutoAssignTest extends AbstractAccessControllerManagementTest {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
AutoAssignExecutor autoAssignExecutor;
|
AutoAssignExecutor autoAssignExecutor;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import org.eclipse.hawkbit.repository.model.DeploymentRequest;
|
|||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
class DeploymentManagementTest extends AbstractAccessControllerTest {
|
class DeploymentManagementTest extends AbstractAccessControllerManagementTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void verifyAssignments() {
|
void verifyAssignments() {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import org.eclipse.hawkbit.repository.model.NamedEntity;
|
|||||||
import org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch;
|
import org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
class DistributionSetManagementTest extends AbstractAccessControllerTest {
|
class DistributionSetManagementTest extends AbstractAccessControllerManagementTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void verifyCreate() {
|
void verifyCreate() {
|
||||||
|
|||||||
@@ -30,11 +30,11 @@ import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
|||||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
class DistributionSetTypeManagementTest extends AbstractAccessControllerTest {
|
class DistributionSetTypeManagementTest extends AbstractAccessControllerManagementTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void verifyCreate() {
|
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"), () -> {
|
runAs(withAuthorities(READ_PREFIX + DISTRIBUTION_SET_TYPE, CREATE_PREFIX + DISTRIBUTION_SET_TYPE + "/name==permitted"), () -> {
|
||||||
assertThat(testdataFactory.findOrCreateDistributionSetType("newType", "permitted")).isNotNull();
|
assertThat(testdataFactory.findOrCreateDistributionSetType("newType", "permitted")).isNotNull();
|
||||||
assertThatThrownBy(() -> testdataFactory.findOrCreateDistributionSetType("newType_2", "not_permitted_2"))
|
assertThatThrownBy(() -> testdataFactory.findOrCreateDistributionSetType("newType_2", "not_permitted_2"))
|
||||||
|
|||||||
@@ -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.im.authentication.SpPermission.UPDATE_TARGET;
|
||||||
import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.runAs;
|
import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.runAs;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.repository.Identifiable;
|
||||||
import org.eclipse.hawkbit.repository.jpa.rollout.RolloutScheduler;
|
import org.eclipse.hawkbit.repository.jpa.rollout.RolloutScheduler;
|
||||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
class RolloutExecutionTest extends AbstractAccessControllerTest {
|
class RolloutExecutionTest extends AbstractAccessControllerManagementTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void verifyOnlyUpdatableTargetsArePartOfRolloutExecutedByScheduler() {
|
void verifyOnlyUpdatableTargetsArePartOfRolloutExecutedByScheduler() {
|
||||||
@@ -66,7 +68,8 @@ class RolloutExecutionTest extends AbstractAccessControllerTest {
|
|||||||
|
|
||||||
assertThat(rolloutGroupManagement.findByRollout(rollout.getId(), UNPAGED).getContent().stream()
|
assertThat(rolloutGroupManagement.findByRollout(rollout.getId(), UNPAGED).getContent().stream()
|
||||||
.flatMap(rolloutGroup -> rolloutGroupManagement.findTargetsOfRolloutGroup(rolloutGroup.getId(), UNPAGED).stream()))
|
.flatMap(rolloutGroup -> rolloutGroupManagement.findTargetsOfRolloutGroup(rolloutGroup.getId(), UNPAGED).stream()))
|
||||||
|
.extracting(Identifiable::getId)
|
||||||
.as("Only updatable targets should be part of the rollout")
|
.as("Only updatable targets should be part of the rollout")
|
||||||
.containsExactly(expectedTargets);
|
.containsExactly(Arrays.stream(expectedTargets).map(Identifiable::getId).toArray(Long[]::new));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,7 +21,7 @@ import org.eclipse.hawkbit.repository.Identifiable;
|
|||||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||||
import org.junit.jupiter.api.Test;
|
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
|
* Test is verifying that rollout details are showing details without restrictions
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
|||||||
import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValueCreate;
|
import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValueCreate;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
class SoftwareModuleManagementTest extends AbstractAccessControllerTest {
|
class SoftwareModuleManagementTest extends AbstractAccessControllerManagementTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void verifyRead() {
|
void verifyRead() {
|
||||||
|
|||||||
@@ -29,11 +29,11 @@ import org.eclipse.hawkbit.repository.model.NamedEntity;
|
|||||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
class SoftwareModuleTypeManagementTest extends AbstractAccessControllerTest {
|
class SoftwareModuleTypeManagementTest extends AbstractAccessControllerManagementTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void verifyCreate() {
|
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"), () -> {
|
runAs(withAuthorities(READ_PREFIX + SOFTWARE_MODULE_TYPE, CREATE_PREFIX + SOFTWARE_MODULE_TYPE + "/key==permitted"), () -> {
|
||||||
assertThat(testdataFactory.findOrCreateSoftwareModuleType("permitted")).isNotNull();
|
assertThat(testdataFactory.findOrCreateSoftwareModuleType("permitted")).isNotNull();
|
||||||
assertThatThrownBy(() -> testdataFactory.findOrCreateSoftwareModuleType("not_permitted_2"))
|
assertThatThrownBy(() -> testdataFactory.findOrCreateSoftwareModuleType("not_permitted_2"))
|
||||||
|
|||||||
@@ -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<JpaSoftwareModule> softwareModuleAccessController;
|
||||||
|
@Autowired
|
||||||
|
protected AccessController<JpaSoftwareModuleType> softwareModuleTypeAccessController;
|
||||||
|
@Autowired
|
||||||
|
protected AccessController<JpaDistributionSet> distributionSetAccessController;
|
||||||
|
@Autowired
|
||||||
|
protected AccessController<JpaDistributionSetType> distributionSetTypeAccessController;
|
||||||
|
@Autowired
|
||||||
|
protected AccessController<JpaTarget> targetAccessController;
|
||||||
|
@Autowired
|
||||||
|
protected AccessController<JpaTargetType> targetTypeAccessController;
|
||||||
|
@Autowired
|
||||||
|
protected AccessController<JpaAction> actionAccessController;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
List<AccessController<?>> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<br/>
|
|
||||||
* 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<Long> 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<String> 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<String> 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<String> 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<Target> updateTargets = testdataFactory.createTargets(updateTargetControllerIds);
|
|
||||||
final String[] readTargetControllerIds = { "read1", "read2", "read3", "read4" };
|
|
||||||
final List<Target> readTargets = testdataFactory.createTargets(readTargetControllerIds);
|
|
||||||
final List<Target> 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<RolloutGroup> content = rolloutGroupManagement.findByRollout(rollout.getId(), Pageable.unpaged()).getContent();
|
|
||||||
assertThat(content).hasSize(updateTargets.size());
|
|
||||||
|
|
||||||
final List<Target> 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<Target> updateTargets = testdataFactory.createTargets(updateTargetControllerIds);
|
|
||||||
final String[] readTargetControllerIds = { "read1", "read2", "read3", "read4" };
|
|
||||||
final List<Target> readTargets = testdataFactory.createTargets(readTargetControllerIds);
|
|
||||||
final List<Target> 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())));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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.<Target> assertThat(targetManagement.findAll(UNPAGED)).containsExactly(target1Type1);
|
||||||
|
assertThat(targetManagement.count()).isEqualTo(1);
|
||||||
|
|
||||||
|
Assertions.<Target> assertThat(targetManagement.get(List.of(target1Type1.getId()))).containsExactly(target1Type1);
|
||||||
|
final List<Long> 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.<Target> assertThat(targetManagement.findByRsql("id==*", UNPAGED))
|
||||||
|
.hasSize(1)
|
||||||
|
.containsExactly(target1Type1)
|
||||||
|
.doesNotContain(target2Type2);
|
||||||
|
Assertions.<Target> 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<Long> 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<String, String> 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<String> 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<Long> allTargetIds = List.of(target1Type1.getId(), target2Type2.getId());
|
||||||
|
assertThatThrownBy(() -> targetManagement.delete(allTargetIds)).isInstanceOf(InsufficientPermissionException.class);
|
||||||
|
final List<Long> 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<String> 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<Target> updateTargets = testdataFactory.createTargets(updateTargetControllerIds);
|
||||||
|
final String[] readTargetControllerIds = { "read1", "read2", "read3", "read4" };
|
||||||
|
final List<Target> readTargets = testdataFactory.createTargets(readTargetControllerIds);
|
||||||
|
final List<Target> 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<RolloutGroup> content = rolloutGroupManagement.findByRollout(rollout.getId(), Pageable.unpaged()).getContent();
|
||||||
|
assertThat(content).hasSize(updateTargets.size());
|
||||||
|
|
||||||
|
final List<Target> 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())));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<br/>
|
|
||||||
* 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<Long> 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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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.<TargetType> assertThat(targetTypeManagement.findAll(UNPAGED)).containsExactly(targetType1);
|
||||||
|
assertThat(targetTypeManagement.count()).isEqualTo(1);
|
||||||
|
|
||||||
|
Assertions.<TargetType> assertThat(targetTypeManagement.findByRsql("name==*", UNPAGED))
|
||||||
|
.contains(targetType1).doesNotContain(targetType2);
|
||||||
|
|
||||||
|
assertThat((Optional) targetTypeManagement.find(targetType1.getId())).hasValue(targetType1);
|
||||||
|
assertThat(targetTypeManagement.find(targetType2.getId())).isEmpty();
|
||||||
|
|
||||||
|
Assertions.<TargetType> assertThat(targetTypeManagement.get(Collections.singletonList(targetType1.getId())))
|
||||||
|
.contains(targetType1).doesNotContain(targetType2);
|
||||||
|
final List<Long> noPermissionsEntitiesIds = Collections.singletonList(targetType2.getId());
|
||||||
|
assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy(() -> targetTypeManagement.get(noPermissionsEntitiesIds));
|
||||||
|
final List<Long> 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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,7 +31,7 @@ import org.junit.jupiter.api.Test;
|
|||||||
* Feature: Component Tests - Access Control<br/>
|
* Feature: Component Tests - Access Control<br/>
|
||||||
* Story: Test Distribution Set Access Controller
|
* Story: Test Distribution Set Access Controller
|
||||||
*/
|
*/
|
||||||
class TargetTypeQueryManagementTest extends AbstractAccessControllerTest {
|
class TargetTypeQueryManagementTest extends AbstractAccessControllerManagementTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void verifyAutoAssignmentRestrictionByDs() {
|
void verifyAutoAssignmentRestrictionByDs() {
|
||||||
|
|||||||
Reference in New Issue
Block a user