Add DistributionSetManagementTest and TargetTypeQueryManagementTest (#2727)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-10-08 16:53:37 +03:00
committed by GitHub
parent e23d2aa920
commit 4ba09f127e
5 changed files with 410 additions and 267 deletions

View File

@@ -14,6 +14,7 @@ import java.util.Set;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
@@ -35,6 +36,8 @@ abstract class AbstractAccessControllerTest extends AbstractJpaIntegrationTest {
protected DistributionSetType dsType1;
protected DistributionSetType dsType2;
protected DistributionSetTag dsTag1;
protected DistributionSetTag dsTag2;
protected DistributionSet ds1Type1;
protected DistributionSet ds2Type2;
protected DistributionSet ds3Type2;
@@ -58,12 +61,21 @@ 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<DistributionSetTag> tags = testdataFactory.createDistributionSetTags(2);
dsTag1 = tags.get(0);
dsTag2 = tags.get(1);
ds1Type1 = distributionSetManagement.lock(
testdataFactory.createDistributionSet("Ds1Type1", "1.0", dsType1, List.of(sm1Type1)));
distributionSetManagement.assignTag(
List.of(testdataFactory.createDistributionSet("Ds1Type1", "1.0", dsType1, List.of(sm1Type1)).getId()),
dsTag1.getId()).get(0));
ds2Type2 = distributionSetManagement.lock(
testdataFactory.createDistributionSet("Ds2Type2", "1.0", dsType2, List.of(sm2Type2, sm1Type1)));
distributionSetManagement.assignTag(
List.of(testdataFactory.createDistributionSet("Ds2Type2", "2.0", dsType2, List.of(sm2Type2, sm1Type1)).getId()),
dsTag1.getId()).get(0));
ds3Type2 = distributionSetManagement.lock(
testdataFactory.createDistributionSet("Ds3Type2", "1.0", dsType2, List.of(sm3Type2, sm1Type1)));
distributionSetManagement.assignTag(
List.of(testdataFactory.createDistributionSet("Ds3Type2", "3.0", dsType2, List.of(sm3Type2, sm1Type1)).getId()),
dsTag2.getId()).get(0));
targetType1 = testdataFactory.createTargetType("TargetType1", Set.of(dsType1, dsType2));
targetType2 = testdataFactory.createTargetType("TargetType2", Set.of(dsType2));

View File

@@ -1,264 +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.READ_DISTRIBUTION_SET;
import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_TARGET;
import static org.eclipse.hawkbit.im.authentication.SpPermission.UPDATE_DISTRIBUTION_SET;
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 java.util.Map;
import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
import org.eclipse.hawkbit.repository.Identifiable;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement.AutoAssignDistributionSetUpdate;
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.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Pageable;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
/**
* Note: Still all test gets READ_REPOSITORY since find methods are inherited with request for READ_REPOSITORY. However,
* using READ_DISTRIBUTION_SET scoping - the scopes still work.
* <p/>
* Feature: Component Tests - Access Control<br/>
* Story: Test Distribution Set Access Controller
*/
@TestPropertySource(properties = "hawkbit.acm.access-controller.enabled=true")
class DistributionSetAccessControllerTest extends AbstractJpaIntegrationTest {
/**
* Verifies read access rules for distribution sets
*/
@Test
void verifyDistributionSetReadOperations() {
final DistributionSet permitted = testdataFactory.createDistributionSet();
final DistributionSet hidden = testdataFactory.createDistributionSet();
final Action permittedAction = testdataFactory.performAssignment(permitted);
runAs(withUser("user",
READ_DISTRIBUTION_SET + "/id==" + permitted.getId(),
READ_TARGET +"/controllerId==" + permittedAction.getTarget().getControllerId()), () -> {
final Long permittedActionId = permitted.getId();
// verify distributionSetManagement#findAll
assertThat(distributionSetManagement.findAll(Pageable.unpaged()).get().map(Identifiable::getId).toList())
.containsOnly(permittedActionId);
// verify distributionSetManagement#findByRsql
assertThat(distributionSetManagement.findByRsql("name==*", Pageable.unpaged()).get().map(Identifiable::getId)
.toList()).containsOnly(permittedActionId);
// verify distributionSetManagement#get
assertThat(distributionSetManagement.find(permittedActionId)).isPresent();
final Long hiddenId = hidden.getId();
assertThat(distributionSetManagement.find(hiddenId)).isEmpty();
// verify distributionSetManagement#getWithDetails
assertThat(distributionSetManagement.getWithDetails(permittedActionId)).isNotNull();
assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy(() -> distributionSetManagement.getWithDetails(hiddenId));
// verify distributionSetManagement#get
final List<Long> allActionIds = Arrays.asList(permittedActionId, hiddenId);
assertThatThrownBy(() -> distributionSetManagement.get(allActionIds))
.as("Fail if request hidden.").isInstanceOf(EntityNotFoundException.class);
// verify distributionSetManagement#getByNameAndVersion
assertThat(distributionSetManagement.findByNameAndVersion(permitted.getName(), permitted.getVersion())).isNotNull();
final String hiddenName = hidden.getName();
final String hiddenVersion = hidden.getVersion();
assertThatExceptionOfType(EntityNotFoundException.class)
.isThrownBy(() -> distributionSetManagement.findByNameAndVersion(hiddenName, hiddenVersion));
});
}
/**
* Verifies read access rules for distribution sets
*/
@Test
void verifyDistributionSetUpdates() {
final DistributionSet permitted = testdataFactory.createDistributionSet();
final String mdPresetKey = "metadata.preset";
final String mdPresetValue = "presetValue";
distributionSetManagement.createMetadata(permitted.getId(), Map.of(mdPresetKey, mdPresetValue));
final DistributionSet readOnly = testdataFactory.createDistributionSet();
distributionSetManagement.createMetadata(readOnly.getId(), Map.of(mdPresetKey, mdPresetValue));
final DistributionSet hidden = testdataFactory.createDistributionSet();
distributionSetManagement.createMetadata(hidden.getId(), Map.of(mdPresetKey, mdPresetValue));
final SoftwareModule swModule = testdataFactory.createSoftwareModuleOs();
runAs(withUser("user",
READ_DISTRIBUTION_SET + "/id==" + permitted.getId() + " or id==" + readOnly.getId(),
UPDATE_DISTRIBUTION_SET + "/id==" + permitted.getId()), () -> {
// verify distributionSetManagement#assignSoftwareModules
final List<Long> singleModuleIdList = Collections.singletonList(swModule.getId());
assertThat(distributionSetManagement.assignSoftwareModules(permitted.getId(), singleModuleIdList))
.satisfies(ds -> assertThat(ds.getModules().stream().map(Identifiable::getId).toList()).contains(swModule.getId()));
final Long readOnlyId = readOnly.getId();
assertThatThrownBy(() -> distributionSetManagement.assignSoftwareModules(readOnlyId, singleModuleIdList))
.as("Distribution set not allowed to me modified.")
.isInstanceOf(InsufficientPermissionException.class);
final Long hiddenId = hidden.getId();
assertThatThrownBy(() -> distributionSetManagement.assignSoftwareModules(hiddenId, singleModuleIdList))
.as("Distribution set should not be visible.")
.isInstanceOf(EntityNotFoundException.class);
final Map<String, String> metadata = Map.of("test.create", mdPresetValue);
// verify distributionSetManagement#createMetaData
distributionSetManagement.createMetadata(permitted.getId(), metadata);
assertThatThrownBy(() -> distributionSetManagement.createMetadata(readOnlyId, metadata))
.as("Distribution set not allowed to be modified.")
.isInstanceOf(InsufficientPermissionException.class);
assertThatThrownBy(() -> distributionSetManagement.createMetadata(hiddenId, metadata))
.as("Distribution set should not be visible.")
.isInstanceOf(EntityNotFoundException.class);
// verify distributionSetManagement#updateMetaData
final String newValue = "newValue";
distributionSetManagement.createMetadata(permitted.getId(), mdPresetKey, newValue);
assertThatThrownBy(() -> distributionSetManagement.createMetadata(readOnlyId, mdPresetKey, newValue))
.as("Distribution set not allowed to me modified.")
.isInstanceOf(InsufficientPermissionException.class);
assertThatThrownBy(() -> distributionSetManagement.createMetadata(hiddenId, mdPresetKey, newValue))
.as("Distribution set should not be visible.")
.isInstanceOf(EntityNotFoundException.class);
// verify distributionSetManagement#deleteMetaData
final String metadataKey = metadata.entrySet().stream().findAny().get().getKey();
distributionSetManagement.deleteMetadata(permitted.getId(), metadataKey);
assertThatThrownBy(() -> distributionSetManagement.deleteMetadata(readOnlyId, mdPresetKey))
.as("Distribution set not allowed to me modified.")
.isInstanceOf(InsufficientPermissionException.class);
assertThatThrownBy(() -> distributionSetManagement.deleteMetadata(hiddenId, mdPresetKey))
.as("Distribution set should not be visible.")
.isInstanceOf(EntityNotFoundException.class);
});
}
@Test
void verifyTagFilteringAndManagement() {
final DistributionSet permitted = testdataFactory.createDistributionSet();
final DistributionSet readOnly = testdataFactory.createDistributionSet();
final DistributionSet hidden = testdataFactory.createDistributionSet();
final Long dsTagId = distributionSetTagManagement.create(
DistributionSetTagManagement.Create.builder().name("dsTag").build()).getId();
final Long dsTag2Id = distributionSetTagManagement.create(
DistributionSetTagManagement.Create.builder().name("dsTag2").build()).getId();
// perform tag assignment before setting access rules
distributionSetManagement.assignTag(Arrays.asList(permitted.getId(), readOnly.getId(), hidden.getId()), dsTagId);
runAs(withUser("user",
READ_DISTRIBUTION_SET + "/id==" + permitted.getId() + " or id==" + readOnly.getId(),
UPDATE_DISTRIBUTION_SET + "/id==" + permitted.getId()), () -> {
assertThat(distributionSetManagement.findByTag(dsTagId, Pageable.unpaged()).get().map(Identifiable::getId)
.toList()).containsOnly(permitted.getId(), readOnly.getId());
assertThat(distributionSetManagement.findByRsqlAndTag("name==*", dsTagId, Pageable.unpaged()).get()
.map(Identifiable::getId).toList()).containsOnly(permitted.getId(), readOnly.getId());
// verify distributionSetManagement#unassignTag on permitted target
assertThat(distributionSetManagement
.unassignTag(Collections.singletonList(permitted.getId()), dsTagId))
.size()
.isEqualTo(1);
// verify distributionSetManagement#assignTag on permitted target
assertThat(distributionSetManagement.assignTag(Collections.singletonList(permitted.getId()), dsTagId))
.hasSize(1);
// verify distributionSetManagement#unAssignTag on permitted target
assertThat(distributionSetManagement.unassignTag(List.of(permitted.getId()), dsTagId)
.get(0).getId())
.isEqualTo(permitted.getId());
// assignment is denied for readOnlyTarget (read, but no update permissions)
final List<Long> readOblyList = Collections.singletonList(readOnly.getId());
assertThatThrownBy(() ->
distributionSetManagement.unassignTag(readOblyList, dsTagId))
.as("Missing update permissions for target to toggle tag assignment.")
.isInstanceOf(InsufficientPermissionException.class);
// assignment is denied for readOnlyTarget (read, but no update permissions)
// dsTag2- since - it is tagged with dsTag and won't do anything if assigning dsTag
assertThatThrownBy(() -> distributionSetManagement.assignTag(readOblyList, dsTag2Id))
.as("Missing update permissions for target to toggle tag assignment.")
.isInstanceOf(InsufficientPermissionException.class);
// assignment is denied for hiddenTarget since it's hidden
final List<Long> hiddenList = Collections.singletonList(hidden.getId());
assertThatThrownBy(() -> distributionSetManagement.unassignTag(hiddenList, dsTagId))
.as("Missing update permissions for target to toggle tag assignment.")
.isInstanceOf(EntityNotFoundException.class);
// assignment is denied for hiddenTarget since it's hidden
assertThatThrownBy(() -> distributionSetManagement.assignTag(hiddenList, dsTagId))
.as("Missing update permissions for target to toggle tag assignment.")
.isInstanceOf(EntityNotFoundException.class);
// assignment is denied for hiddenTarget since it's hidden
final List<Long> hiddenIdList = List.of(hidden.getId());
assertThatThrownBy(() -> distributionSetManagement.unassignTag(hiddenIdList, dsTagId))
.as("Missing update permissions for target to toggle tag assignment.")
.isInstanceOf(EntityNotFoundException.class);
});
}
@Test
void verifyAutoAssignmentUsage() {
final DistributionSet permitted = testdataFactory.createDistributionSet();
final DistributionSet readOnly = testdataFactory.createDistributionSet();
final DistributionSet hidden = testdataFactory.createDistributionSet();
// has to lock them, otherwise implicit lock shall be made which require DistributionSet update permissions
distributionSetManagement.lock(permitted);
distributionSetManagement.lock(readOnly);
distributionSetManagement.lock(hidden);
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.create(TargetFilterQueryManagement.Create.builder().name("test").query("id==*").build());
runAs(withUser("user",
READ_DISTRIBUTION_SET + "/id==" + permitted.getId() + " or id==" + readOnly.getId(),
UPDATE_DISTRIBUTION_SET + "/id==" + permitted.getId(),
// read / update target needed to update target filter query
READ_TARGET, UPDATE_TARGET), () -> {
assertThat(targetFilterQueryManagement
.updateAutoAssignDS(new AutoAssignDistributionSetUpdate(targetFilterQuery.getId()).ds(permitted.getId())
.actionType(Action.ActionType.FORCED).confirmationRequired(false))
.getAutoAssignDistributionSet().getId()).isEqualTo(permitted.getId());
targetFilterQueryManagement
.updateAutoAssignDS(new AutoAssignDistributionSetUpdate(targetFilterQuery.getId())
.ds(readOnly.getId()).actionType(Action.ActionType.FORCED).confirmationRequired(false))
.getAutoAssignDistributionSet().getId();
final AutoAssignDistributionSetUpdate autoAssignDistributionSetUpdate =
new AutoAssignDistributionSetUpdate(targetFilterQuery.getId())
.ds(hidden.getId()).actionType(Action.ActionType.FORCED).confirmationRequired(false);
assertThatThrownBy(() -> targetFilterQueryManagement.updateAutoAssignDS(autoAssignDistributionSetUpdate))
.isInstanceOf(EntityNotFoundException.class);
});
}
}

View File

@@ -0,0 +1,202 @@
/**
* 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.DISTRIBUTION_SET;
import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_DISTRIBUTION_SET;
import static org.eclipse.hawkbit.im.authentication.SpPermission.UPDATE_DISTRIBUTION_SET;
import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.runAs;
import java.util.List;
import java.util.Map;
import org.assertj.core.api.Assertions;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.DistributionSetManagement.Create;
import org.eclipse.hawkbit.repository.Identifiable;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.model.DistributionSet;
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 {
@Test
void verifyCreate() {
// permissions to read all and create only type1 ds
runAs(withAuthorities(READ_DISTRIBUTION_SET, CREATE_PREFIX + DISTRIBUTION_SET + "/type.id==" + dsType1.getId()), () -> {
final Create dsType1Create = Create.builder().type(dsType1).name(randomString(16)).version("1.0").build();
final Create dsType2Create = Create.builder().type(dsType2).name(randomString(16)).version("1.0").build();
assertThat(distributionSetManagement.create(dsType1Create)).isNotNull();
assertThatThrownBy(() -> distributionSetManagement.create(dsType2Create)).isInstanceOf(InsufficientPermissionException.class);
final List<Create> dsType1CreateList = List.of(
Create.builder().type(dsType1).name(randomString(16)).version("1.0").build(),
Create.builder().type(dsType1).name(randomString(16)).version("1.0").build());
final List<Create> dsType2CreateList = List.of(
Create.builder().type(dsType2).name(randomString(16)).version("1.0").build(),
Create.builder().type(dsType2).name(randomString(16)).version("1.0").build());
assertThat(distributionSetManagement.create(dsType1CreateList)).hasSize(2);
assertThatThrownBy(() -> distributionSetManagement.create(dsType2CreateList)).isInstanceOf(InsufficientPermissionException.class);
});
}
@Test
void verifyRead() {
// permissions to read only type1 ds
runAs(withAuthorities(READ_DISTRIBUTION_SET + "/type.id==" + dsType1.getId()), () -> {
Assertions.<DistributionSet> assertThat(distributionSetManagement.findAll(UNPAGED)).hasSize(1).containsExactlyInAnyOrder(ds1Type1);
assertThat(distributionSetManagement.count()).isEqualTo(1);
Assertions.<DistributionSet> assertThat(distributionSetManagement.findByRsql("name==*", UNPAGED)).hasSize(1).hasSize(1)
.containsExactly(ds1Type1);
assertThat(distributionSetManagement.countByRsql("name==*")).isEqualTo(1);
Assertions.<DistributionSet> assertThat(distributionSetManagement.findByTag(dsTag1.getId(), UNPAGED)).hasSize(1)
.containsExactly(ds1Type1);
assertThat(distributionSetManagement.findByTag(dsTag2.getId(), UNPAGED)).isEmpty();
Assertions.<DistributionSet> assertThat(distributionSetManagement.findByRsqlAndTag("name==*", dsTag1.getId(), UNPAGED)).hasSize(1)
.containsExactly(ds1Type1);
assertThat(distributionSetManagement.findByRsqlAndTag("name==*", dsTag2.getId(), UNPAGED)).isEmpty();
// perform distributionSetManagement#get and verify
final var ds1Type1Id = ds1Type1.getId();
final Long ds2Type2Id = ds2Type2.getId();
assertThat(distributionSetManagement.get(ds1Type1Id)).isEqualTo(ds1Type1);
assertThat(distributionSetManagement.getWithDetails(ds1Type1Id)).isEqualTo(ds1Type1);
assertThat(distributionSetManagement.find(ds1Type1Id).map(DistributionSet.class::cast)).hasValue(ds1Type1);
assertThatThrownBy(() -> distributionSetManagement.get(ds2Type2Id)).isInstanceOf(EntityNotFoundException.class);
assertThatThrownBy(() -> distributionSetManagement.getWithDetails(ds2Type2Id)).isInstanceOf(EntityNotFoundException.class);
assertThat(distributionSetManagement.find(ds2Type2Id)).isEmpty();
Assertions.<DistributionSet> assertThat(distributionSetManagement.get(List.of(ds1Type1Id))).hasSize(1).contains(ds1Type1);
final List<Long> noPermissionsTestDataDsIdList = List.of(ds2Type2Id);
assertThatThrownBy(() -> distributionSetManagement.get(noPermissionsTestDataDsIdList)).isInstanceOf(EntityNotFoundException.class);
final List<Long> containingNoPermissionTestDataDsIsList = List.of(ds1Type1Id, ds2Type2Id);
assertThatThrownBy(() -> distributionSetManagement.get(containingNoPermissionTestDataDsIsList))
.isInstanceOf(EntityNotFoundException.class);
// perform distributionSetManagement#getByNameAndVersion and verify
assertThat(distributionSetManagement.findByNameAndVersion(ds1Type1.getName(), ds1Type1.getVersion())).isEqualTo(ds1Type1);
final String ds2Type2Name = ds2Type2.getName();
final String ds2Type2Version = ds2Type2.getVersion();
assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy(
() -> distributionSetManagement.findByNameAndVersion(ds2Type2Name, ds2Type2Version));
assertThat(distributionSetManagement.getValidAndComplete(ds1Type1Id)).isEqualTo(ds1Type1);
assertThatThrownBy(() -> distributionSetManagement.getValidAndComplete(ds2Type2Id)).isInstanceOf(EntityNotFoundException.class);
assertThat(distributionSetManagement.getMetadata(ds1Type1Id)).isEmpty();
assertThatThrownBy(() -> distributionSetManagement.getMetadata(ds2Type2Id)).isInstanceOf(EntityNotFoundException.class);
});
}
@Test
void verifyUpdate() {
final String ds2MdKey = "ds2MdKey";
distributionSetManagement.createMetadata(ds2Type2.getId(), ds2MdKey, "ds2MdValue");
// override with updated
final DistributionSet ds2Type2 = distributionSetManagement.get(this.ds2Type2.getId());
// permissions to read all and update only type1 ds
runAs(withAuthorities(READ_DISTRIBUTION_SET, UPDATE_DISTRIBUTION_SET + "/type.id==" + dsType1.getId()), () -> {
assertThat(distributionSetManagement.assignTag(List.of(ds1Type1.getId()), dsTag2.getId())).hasSize(1);
final List<Long> noPermissionsTestDataDsId = List.of(ds3Type2.getId());
final Long tagToAssignId = dsTag1.getId();
assertThatThrownBy(() -> distributionSetManagement.assignTag(noPermissionsTestDataDsId, tagToAssignId))
.isInstanceOf(InsufficientPermissionException.class);
assertThat(distributionSetManagement.unassignTag(List.of(ds1Type1.getId()), dsTag2.getId())).hasSize(1);
final Long tagToRemoveId = dsTag2.getId();
assertThatThrownBy(() -> distributionSetManagement.unassignTag(noPermissionsTestDataDsId, tagToRemoveId))
.isInstanceOf(InsufficientPermissionException.class);
final String metadataKey = "key.dot";
final Map<String, String> metaData = Map.of(metadataKey, "value.dot");
final Long ds2Type2Id = ds2Type2.getId();
distributionSetManagement.createMetadata(ds1Type1.getId(), metaData);
assertThatThrownBy(() -> distributionSetManagement.createMetadata(ds2Type2Id, metaData))
.isInstanceOf(InsufficientPermissionException.class);
final String metadataNewValue = "newValue.dot";
distributionSetManagement.createMetadata(ds1Type1.getId(), metadataKey, metadataNewValue);
assertThatThrownBy(() -> distributionSetManagement.createMetadata(ds2Type2Id, metadataKey, metadataNewValue))
.isInstanceOf(InsufficientPermissionException.class);
distributionSetManagement.deleteMetadata(ds1Type1.getId(), metadataKey);
assertThatThrownBy(() -> distributionSetManagement.deleteMetadata(ds2Type2Id, metadataKey))
.isInstanceOf(EntityNotFoundException.class);
assertThatThrownBy(() -> distributionSetManagement.deleteMetadata(ds2Type2Id, ds2MdKey))
.isInstanceOf(InsufficientPermissionException.class);
final DistributionSetManagement.Update distributionSet1Update = DistributionSetManagement.Update.builder()
.id(ds1Type1.getId()).description(randomString(16)).build();
final DistributionSetManagement.Update distributionSet2Update = DistributionSetManagement.Update.builder()
.id(ds2Type2Id).description(randomString(16)).build();
DistributionSet ds1Type1 = distributionSetManagement.update(distributionSet1Update);
assertThat(ds1Type1).extracting(NamedEntity::getDescription).isEqualTo(distributionSet1Update.getDescription());
assertThatThrownBy(() -> distributionSetManagement.update(distributionSet2Update))
.isInstanceOf(InsufficientPermissionException.class);
distributionSetManagement.unlock(ds1Type1);
ds1Type1 = distributionSetManagement.unassignSoftwareModule(ds1Type1.getId(), sm1Type1.getId());
assertThat(ds1Type1).matches(
updated -> updated.getModules().isEmpty());
ds1Type1 = distributionSetManagement.assignSoftwareModules(ds1Type1.getId(), List.of(sm1Type1.getId()));
assertThat(ds1Type1).matches(updated -> updated.getModules().stream()
.map(Identifiable::getId).anyMatch(sm1Type1.getId()::equals));
try {
SecurityContextSwitch.callAsPrivileged(() -> distributionSetManagement.unlock(ds2Type2));
} catch (Exception e) {
throw new RuntimeException(e);
}
final Long softwareModuleAppId = sm1Type1.getId();
final List<Long> softwareModuleAppIdList = List.of(softwareModuleAppId);
assertThatThrownBy(() -> distributionSetManagement.assignSoftwareModules(ds2Type2Id, softwareModuleAppIdList))
.isInstanceOf(InsufficientPermissionException.class);
assertThatThrownBy(() -> distributionSetManagement.unassignSoftwareModule(ds2Type2Id, softwareModuleAppId))
.isInstanceOf(InsufficientPermissionException.class);
distributionSetManagement.invalidate(ds1Type1);
assertThatThrownBy(() -> distributionSetManagement.invalidate(ds2Type2)).isInstanceOf(InsufficientPermissionException.class);
});
}
//
// private TestData createCompleteDistributionSetWithNewType() {
// // create type
// final DistributionSetType distributionSetType = testdataFactory.findOrCreateDistributionSetType(randomString(16), randomString(16),
// List.of(osType), List.of(appType));
//
// final DistributionSetTag distributionSetTag = distributionSetTagManagement.create(
// DistributionSetTagManagement.Create.builder().name(randomString(16)).build());
//
// final SoftwareModule moduleOs = testdataFactory.createSoftwareModuleOs();
//
// final Long distributionSetId = distributionSetManagement.create(
// Create.builder().type(distributionSetType).name(randomString(16)).version("1.0")
// .modules(Set.of(moduleOs)).build()).getId();
//
// final DistributionSetAssignmentResult assignmentResult = assignDistributionSet(distributionSetId, createTarget().getControllerId());
// assertThat(assignmentResult.getAssignedEntity()).hasSize(1);
//
// return new TestData(distributionSetType,
// distributionSetManagement.assignTag(Collections.singletonList(distributionSetId), distributionSetTag.getId()).get(0),
// distributionSetTag, assignmentResult.getAssignedEntity().get(0));
// }
}

View File

@@ -0,0 +1,132 @@
/**
* 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_PREFIX;
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.UPDATE_PREFIX;
import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.runAs;
import java.util.List;
import java.util.stream.Stream;
import org.assertj.core.api.Assertions;
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.NamedEntity;
import org.junit.jupiter.api.Test;
class DistributionSetTypeManagementTest extends AbstractAccessControllerTest {
@Test
void verifyCreate() {
// permissions to read only type1 ds types 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"))
.isInstanceOf(InsufficientPermissionException.class);
});
}
@Test
void verifyRead() {
// permissions to read only type1 ds types
runAs(withAuthorities(READ_PREFIX + DISTRIBUTION_SET_TYPE + "/id==" + dsType1.getId()), () -> {
// perform distributionSetTypeManagement#findAll and verify
Assertions.<DistributionSetType> assertThat(distributionSetTypeManagement.findAll(UNPAGED))
.hasSize(1).containsExactly(dsType1);
assertThat(distributionSetTypeManagement.count()).isEqualTo(1);
Assertions.<DistributionSetType> assertThat(distributionSetTypeManagement.findByRsql("name==*", UNPAGED))
.hasSize(1).containsExactly(dsType1);
assertThat(distributionSetTypeManagement.countByRsql("name==*")).isEqualTo(1);
assertThat(distributionSetTypeManagement.exists(dsType1.getId())).isTrue();
assertThat(distributionSetTypeManagement.exists(dsType2.getId())).isFalse();
assertThat(distributionSetTypeManagement.find(dsType1.getId()).map(DistributionSetType.class::cast)).hasValue(dsType1);
assertThat(distributionSetTypeManagement.find(dsType2.getId())).isEmpty();
Assertions.<DistributionSetType> assertThat(distributionSetTypeManagement.get(List.of(dsType1.getId())))
.hasSize(1).contains(dsType1);
final List<Long> noPermissionIdList = List.of(dsType2.getId());
assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy(() -> distributionSetTypeManagement.get(noPermissionIdList));
final List<Long> allPermissionsIdList = List.of(dsType1.getId(), dsType2.getId());
assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy(() -> distributionSetTypeManagement.get(allPermissionsIdList));
assertThat(distributionSetTypeManagement.findByKey(dsType1.getKey()).map(DistributionSetType.class::cast)).hasValue(dsType1);
assertThat(distributionSetTypeManagement.findByKey(dsType2.getKey())).isEmpty();
});
}
@Test
void verifyUpdate() {
// permissions to read all and update only type1 ds types
runAs(withAuthorities(READ_PREFIX + DISTRIBUTION_SET_TYPE, UPDATE_PREFIX + DISTRIBUTION_SET_TYPE + "/id==" + dsType1.getId()), () -> {
final String newDescription = randomString(16);
final Long dsType2Id = dsType2.getId();
assertThat(distributionSetTypeManagement.update(
DistributionSetTypeManagement.Update.builder().id(dsType1.getId()).description(newDescription).build()))
.extracting(NamedEntity::getDescription).isEqualTo(newDescription);
final DistributionSetTypeManagement.Update descriptionUpdate = DistributionSetTypeManagement.Update.builder()
.id(dsType2Id).description(newDescription).build();
assertThatThrownBy(() -> distributionSetTypeManagement.update(descriptionUpdate))
.isInstanceOf(InsufficientPermissionException.class);
});
// override types with unassigned in order to have modifiable types
final DistributionSetType dsType1 = testdataFactory.findOrCreateDistributionSetType(
"DsType1_override", "DistributionSetType-1-override", List.of(smType1), List.of());
final DistributionSetType dsType2 = testdataFactory.findOrCreateDistributionSetType(
"DsType2_override", "DistributionSetType-2-override", List.of(smType2), List.of(smType1));
runAs(withAuthorities(READ_PREFIX + DISTRIBUTION_SET_TYPE, UPDATE_PREFIX + DISTRIBUTION_SET_TYPE + "/id==" + dsType1.getId()), () -> {
final List<Long> osAndAppTypeIds = List.of(osType.getId(), appType.getId());
// verify distributionSetTypeManagement#assignCompatibleDistributionSetTypes
DistributionSetType dsType1Up = distributionSetTypeManagement.assignMandatorySoftwareModuleTypes(dsType1.getId(), osAndAppTypeIds);
assertThat(dsType1Up).satisfies(
updatedType -> assertThat(Stream.of(osType, appType).allMatch(updatedType::containsModuleType)).isTrue());
assertThat(dsType1Up.containsModuleType(osType)).isTrue();
assertThat(dsType1Up.containsModuleType(appType)).isTrue();
final Long dsType2Id = dsType2.getId();
assertThatThrownBy(() -> distributionSetTypeManagement.assignMandatorySoftwareModuleTypes(dsType2Id, osAndAppTypeIds))
.isInstanceOf(InsufficientPermissionException.class);
assertThat(distributionSetTypeManagement
.unassignSoftwareModuleType(dsType1.getId(), osType.getId()))
.satisfies(updatedType -> assertThat(updatedType.containsModuleType(osType)).isFalse());
assertThatThrownBy(
() -> distributionSetTypeManagement.unassignSoftwareModuleType(dsType2Id, osType.getId()))
.isInstanceOf(InsufficientPermissionException.class);
});
}
@Test
void verifyDelete() {
// permissions to read all and update only type1 ds types
runAs(withAuthorities(READ_PREFIX + DISTRIBUTION_SET_TYPE, DELETE_PREFIX + DISTRIBUTION_SET_TYPE + "/id==" + dsType1.getId()), () -> {
assertThat(distributionSetTypeManagement.find(dsType1.getId())).isPresent();
distributionSetTypeManagement.delete(dsType1.getId());
// soft delete since dsType1 is assigned to ds1Type1
assertThat(distributionSetTypeManagement.find(dsType1.getId())).hasValueSatisfying(DistributionSetType::isDeleted);
final Long ds2Type2Id = dsType2.getId();
assertThatThrownBy(() -> distributionSetTypeManagement.delete(ds2Type2Id)).isInstanceOf(InsufficientPermissionException.class);
});
}
}

View File

@@ -0,0 +1,61 @@
/**
* 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.assertThatThrownBy;
import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_DISTRIBUTION_SET;
import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_TARGET;
import static org.eclipse.hawkbit.im.authentication.SpPermission.UPDATE_DISTRIBUTION_SET;
import static org.eclipse.hawkbit.im.authentication.SpPermission.UPDATE_TARGET;
import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.runAs;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement.AutoAssignDistributionSetUpdate;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.junit.jupiter.api.Test;
/**
* Note: Still all test gets READ_REPOSITORY since find methods are inherited with request for READ_REPOSITORY. However,
* using READ_DISTRIBUTION_SET scoping - the scopes still work.
* <p/>
* Feature: Component Tests - Access Control<br/>
* Story: Test Distribution Set Access Controller
*/
class TargetTypeQueryManagementTest extends AbstractAccessControllerTest {
@Test
void verifyAutoAssignmentRestrictionByDs() {
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.create(TargetFilterQueryManagement.Create.builder().name("test").query("id==*").build());
runAs(withAuthorities(
READ_DISTRIBUTION_SET + "/type.id==" + dsType1.getId() + " or id==" + ds2Type2.getId(),
UPDATE_DISTRIBUTION_SET + "/type.id==" + dsType1.getId(),
// read / update target needed to update target filter query
READ_TARGET, UPDATE_TARGET), () -> {
assertThat(targetFilterQueryManagement
.updateAutoAssignDS(new AutoAssignDistributionSetUpdate(targetFilterQuery.getId()).ds(ds1Type1.getId())
.actionType(Action.ActionType.FORCED).confirmationRequired(false))
.getAutoAssignDistributionSet().getId()).isEqualTo(ds1Type1.getId());
targetFilterQueryManagement
.updateAutoAssignDS(new AutoAssignDistributionSetUpdate(targetFilterQuery.getId())
.ds(ds2Type2.getId()).actionType(Action.ActionType.FORCED).confirmationRequired(false))
.getAutoAssignDistributionSet().getId();
final AutoAssignDistributionSetUpdate autoAssignDistributionSetUpdate = new AutoAssignDistributionSetUpdate(
targetFilterQuery.getId())
.ds(ds3Type2.getId()).actionType(Action.ActionType.FORCED).confirmationRequired(false);
assertThatThrownBy(() -> targetFilterQueryManagement.updateAutoAssignDS(autoAssignDistributionSetUpdate))
.isInstanceOf(EntityNotFoundException.class);
});
}
}