diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/AbstractAccessControllerTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/AbstractAccessControllerTest.java index 2638b0aea..27166518f 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/AbstractAccessControllerTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/AbstractAccessControllerTest.java @@ -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 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)); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DistributionSetAccessControllerTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DistributionSetAccessControllerTest.java deleted file mode 100644 index f4a1d776c..000000000 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DistributionSetAccessControllerTest.java +++ /dev/null @@ -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. - *

- * Feature: Component Tests - Access Control
- * 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 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 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 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 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 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 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); - }); - } -} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DistributionSetManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DistributionSetManagementTest.java new file mode 100644 index 000000000..d3c6f3ff4 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DistributionSetManagementTest.java @@ -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 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 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. assertThat(distributionSetManagement.findAll(UNPAGED)).hasSize(1).containsExactlyInAnyOrder(ds1Type1); + assertThat(distributionSetManagement.count()).isEqualTo(1); + + Assertions. assertThat(distributionSetManagement.findByRsql("name==*", UNPAGED)).hasSize(1).hasSize(1) + .containsExactly(ds1Type1); + assertThat(distributionSetManagement.countByRsql("name==*")).isEqualTo(1); + + Assertions. assertThat(distributionSetManagement.findByTag(dsTag1.getId(), UNPAGED)).hasSize(1) + .containsExactly(ds1Type1); + assertThat(distributionSetManagement.findByTag(dsTag2.getId(), UNPAGED)).isEmpty(); + + Assertions. 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. assertThat(distributionSetManagement.get(List.of(ds1Type1Id))).hasSize(1).contains(ds1Type1); + final List noPermissionsTestDataDsIdList = List.of(ds2Type2Id); + assertThatThrownBy(() -> distributionSetManagement.get(noPermissionsTestDataDsIdList)).isInstanceOf(EntityNotFoundException.class); + final List 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 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 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 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)); +// } + +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DistributionSetTypeManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DistributionSetTypeManagementTest.java new file mode 100644 index 000000000..59a236b85 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/DistributionSetTypeManagementTest.java @@ -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. assertThat(distributionSetTypeManagement.findAll(UNPAGED)) + .hasSize(1).containsExactly(dsType1); + assertThat(distributionSetTypeManagement.count()).isEqualTo(1); + + Assertions. 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. assertThat(distributionSetTypeManagement.get(List.of(dsType1.getId()))) + .hasSize(1).contains(dsType1); + final List noPermissionIdList = List.of(dsType2.getId()); + assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy(() -> distributionSetTypeManagement.get(noPermissionIdList)); + final List 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 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); + }); + } +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetTypeQueryManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetTypeQueryManagementTest.java new file mode 100644 index 000000000..b72e596ab --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/TargetTypeQueryManagementTest.java @@ -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. + *

+ * Feature: Component Tests - Access Control
+ * 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); + }); + } +} \ No newline at end of file