From f30ffaf0e0c17299ec7df8a1bda936ed98eb1b8f Mon Sep 17 00:00:00 2001 From: Mohammad Ghazanfar Ali Danish Date: Thu, 19 Sep 2024 15:59:47 +0200 Subject: [PATCH 01/20] Adds dynamic RBAC to SM/CD Rep, SM Reg, and AAS Environment Signed-off-by: Mohammad Ghazanfar Ali Danish --- .github/workflows/basyx_test.yml | 1 + .github/workflows/docker_test.yml | 1 + .../pom.xml | 4 + ...asEnvironmentTargetInformationAdapter.java | 134 +++++++++++++ ...vironmentTargetInformationAdapterTest.java | 184 +++++++++++++++++ .../AasRegistryTargetInformationAdapter.java | 44 ++++- ...sRegistryTargetInformationAdapterTest.java | 27 ++- .../submodel/AasTargetInformationAdapter.java | 41 +++- .../AasTargetInformationAdapterTest.java | 25 ++- .../pom.xml | 4 + ...eptDescriptionRepositoryConfiguration.java | 8 + .../submodel/CDTargetInformationAdapter.java | 114 +++++++++++ .../CDTargetInformationAdapterTest.java | 161 +++++++++++++++ .../pom.xml | 4 + ...thorizedSubmodelRegistryConfiguration.java | 8 + ...modelRegistryTargetInformationAdapter.java | 114 +++++++++++ ...lRegistryTargetInformationAdapterTest.java | 163 +++++++++++++++ .../pom.xml | 4 + ...orizedSubmodelRepositoryConfiguration.java | 8 + .../SubmodelTargetInformationAdapter.java | 130 ++++++++++++ .../SubmodelTargetInformationAdapterTest.java | 185 ++++++++++++++++++ 21 files changed, 1342 insertions(+), 22 deletions(-) create mode 100644 basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/rbac/backend/submodel/AasEnvironmentTargetInformationAdapter.java create mode 100644 basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/AasEnvironmentTargetInformationAdapterTest.java create mode 100644 basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/rbac/backend/submodel/CDTargetInformationAdapter.java create mode 100644 basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/CDTargetInformationAdapterTest.java create mode 100644 basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/feature/authorization/rbac/backend/submodel/SubmodelRegistryTargetInformationAdapter.java create mode 100644 basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/regression/feature/authorization/SubmodelRegistryTargetInformationAdapterTest.java create mode 100644 basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/rbac/backend/submodel/SubmodelTargetInformationAdapter.java create mode 100644 basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/SubmodelTargetInformationAdapterTest.java diff --git a/.github/workflows/basyx_test.yml b/.github/workflows/basyx_test.yml index 1187244a2..2df8360ca 100644 --- a/.github/workflows/basyx_test.yml +++ b/.github/workflows/basyx_test.yml @@ -1,6 +1,7 @@ name: Build and Test BaSyx on: + workflow_dispatch: pull_request: branches: [ main ] paths-ignore: diff --git a/.github/workflows/docker_test.yml b/.github/workflows/docker_test.yml index d10df98fa..33a81fbce 100644 --- a/.github/workflows/docker_test.yml +++ b/.github/workflows/docker_test.yml @@ -4,6 +4,7 @@ name: Build and Start Docker Images on: + workflow_dispatch: pull_request: branches: [ main ] paths-ignore: diff --git a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/pom.xml b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/pom.xml index 637abee7e..532051b99 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/pom.xml +++ b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/pom.xml @@ -81,6 +81,10 @@ org.eclipse.digitaltwin.basyx basyx.authorization.rules.rbac.backend.inmemory + + + org.eclipse.digitaltwin.basyx + basyx.authorization.rules.rbac.backend.submodel diff --git a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/rbac/backend/submodel/AasEnvironmentTargetInformationAdapter.java b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/rbac/backend/submodel/AasEnvironmentTargetInformationAdapter.java new file mode 100644 index 000000000..7465ff28e --- /dev/null +++ b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/rbac/backend/submodel/AasEnvironmentTargetInformationAdapter.java @@ -0,0 +1,134 @@ +/******************************************************************************* + * Copyright (C) 2024 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.aasenvironment.feature.authorization.rbac.backend.submodel; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.digitaltwin.aas4j.v3.model.Property; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementCollection; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementList; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultProperty; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodelElementCollection; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodelElementList; +import org.eclipse.digitaltwin.basyx.aasenvironment.feature.authorization.AasEnvironmentTargetInformation; +import org.eclipse.digitaltwin.basyx.aasrepository.feature.authorization.AasTargetInformation; +import org.eclipse.digitaltwin.basyx.aasrepository.feature.authorization.rbac.backend.submodel.AasTargetInformationAdapter; +import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetInformation; +import org.eclipse.digitaltwin.basyx.authorization.rules.rbac.backend.submodel.TargetInformationAdapter; +import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.feature.authorization.ConceptDescriptionTargetInformation; +import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.feature.authorization.rbac.backend.submodel.CDTargetInformationAdapter; +import org.eclipse.digitaltwin.basyx.core.exceptions.InvalidTargetInformationException; +import org.eclipse.digitaltwin.basyx.submodelrepository.feature.authorization.SubmodelTargetInformation; +import org.eclipse.digitaltwin.basyx.submodelrepository.feature.authorization.rbac.backend.submodel.SubmodelTargetInformationAdapter; + +/** + * An implementation of the {@link TargetInformationAdapter} to adapt with Aas + * {@link TargetInformation} + * + * @author danish + */ +public class AasEnvironmentTargetInformationAdapter implements TargetInformationAdapter { + + @Override + public SubmodelElementCollection adapt(TargetInformation targetInformation) { + + if (targetInformation instanceof AasTargetInformation) + return new AasTargetInformationAdapter().adapt(targetInformation); + + if (targetInformation instanceof SubmodelTargetInformation) + return new SubmodelTargetInformationAdapter().adapt(targetInformation); + + if (targetInformation instanceof ConceptDescriptionTargetInformation) + return new CDTargetInformationAdapter().adapt(targetInformation); + + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation").build(); + + SubmodelElementList aasId = new DefaultSubmodelElementList.Builder().idShort("aasIds").build(); + SubmodelElementList submodelId = new DefaultSubmodelElementList.Builder().idShort("submodelIds").build(); + Property typeProperty = new DefaultProperty.Builder().idShort("@type").value("aas-environment").build(); + + List aasIds = ((AasEnvironmentTargetInformation) targetInformation).getAasIds().stream().map(this::transform).collect(Collectors.toList()); + List submodelIds = ((AasEnvironmentTargetInformation) targetInformation).getSubmodelIds().stream().map(this::transform).collect(Collectors.toList()); + aasId.setValue(aasIds); + submodelId.setValue(submodelIds); + + targetInformationSMC.setValue(Arrays.asList(aasId, submodelId, typeProperty)); + + return targetInformationSMC; + } + + @Override + public TargetInformation adapt(SubmodelElementCollection targetInformation) { + + String targetInformationType = getTargetInformationType(targetInformation); + + if (targetInformationType.equals("aas")) + return new AasTargetInformationAdapter().adapt(targetInformation); + + if (targetInformationType.equals("submodel")) + return new SubmodelTargetInformationAdapter().adapt(targetInformation); + + if (targetInformationType.equals("concept-description")) + return new CDTargetInformationAdapter().adapt(targetInformation); + + if (!targetInformationType.equals("aas-environment")) + throw new InvalidTargetInformationException( + "The TargetInformation @type: " + targetInformationType + " is not compatible with " + + getClass().getName() + "."); + + SubmodelElement aasIdSubmodelElement = targetInformation.getValue().stream().filter(sme -> sme.getIdShort().equals("aasIds")).findAny().orElseThrow( + () -> new InvalidTargetInformationException("The TargetInformation defined in the SubmodelElementCollection Rule with id: " + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName())); + + SubmodelElement submodelIdSubmodelElement = targetInformation.getValue().stream().filter(sme -> sme.getIdShort().equals("submodelIds")).findAny().orElseThrow( + () -> new InvalidTargetInformationException("The TargetInformation defined in the SubmodelElementCollection Rule with id: " + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName())); + + if (!(aasIdSubmodelElement instanceof SubmodelElementList) || !(submodelIdSubmodelElement instanceof SubmodelElementList)) + throw new InvalidTargetInformationException("The TargetInformation defined in the SubmodelElementCollection Rule with id: " + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName()); + + SubmodelElementList aasIdList = (SubmodelElementList) aasIdSubmodelElement; + SubmodelElementList submodelIdList = (SubmodelElementList) submodelIdSubmodelElement; + + List aasIds = aasIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).collect(Collectors.toList()); + List submodelIds = submodelIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).collect(Collectors.toList()); + + return new AasEnvironmentTargetInformation(aasIds, submodelIds); + } + + private String getTargetInformationType(SubmodelElementCollection targetInformation) { + + Property typeProperty = (Property) targetInformation.getValue().stream().filter(sme -> sme.getIdShort().equals("@type")).findAny().orElseThrow(() -> new InvalidTargetInformationException("The TargetInformation defined in the SubmodelElementCollection Rule with id: " + targetInformation.getIdShort() + " does not have @type definition")); + + return typeProperty.getValue(); + } + + private Property transform(String aasId) { + return new DefaultProperty.Builder().value(aasId).build(); + } + +} diff --git a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/AasEnvironmentTargetInformationAdapterTest.java b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/AasEnvironmentTargetInformationAdapterTest.java new file mode 100644 index 000000000..298e3381e --- /dev/null +++ b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/AasEnvironmentTargetInformationAdapterTest.java @@ -0,0 +1,184 @@ +/******************************************************************************* + * Copyright (C) 2024 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.aasenvironment.feature.authorization; + +import static org.junit.Assert.*; +import org.eclipse.digitaltwin.aas4j.v3.model.Property; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementCollection; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementList; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultProperty; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodelElementCollection; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodelElementList; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.digitaltwin.basyx.aasenvironment.feature.authorization.rbac.backend.submodel.AasEnvironmentTargetInformationAdapter; +import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetInformation; +import org.eclipse.digitaltwin.basyx.core.exceptions.InvalidTargetInformationException; + +/** + * Tests {@link AasRegistryTargetInformationAdapter} + * + * @author danish + */ +public class AasEnvironmentTargetInformationAdapterTest { + + private AasEnvironmentTargetInformationAdapter aasEnvironmentTargetInformationAdapter; + + @Before + public void setUp() { + aasEnvironmentTargetInformationAdapter = new AasEnvironmentTargetInformationAdapter(); + } + + @Test + public void testAdaptTargetInformationToSubmodelElementCollection() { + + List aasIds = Arrays.asList("aasId1", "aasId2"); + List submodelIds = Arrays.asList("smId1", "smId2"); + TargetInformation targetInformation = new AasEnvironmentTargetInformation(aasIds, submodelIds); + + SubmodelElementCollection result = aasEnvironmentTargetInformationAdapter.adapt(targetInformation); + + assertEquals("targetInformation", result.getIdShort()); + + List elements = result.getValue(); + assertEquals(3, elements.size()); + + SubmodelElementList aasIdList = (SubmodelElementList) elements.get(0); + assertEquals("aasIds", aasIdList.getIdShort()); + + SubmodelElementList submodelIdList = (SubmodelElementList) elements.get(1); + assertEquals("submodelIds", submodelIdList.getIdShort()); + + Property typeProperty = (Property) elements.get(2); + assertEquals("@type", typeProperty.getIdShort()); + + List actualAasIds = aasIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).map(String::valueOf).collect(Collectors.toList()); + assertEquals(aasIds, actualAasIds); + + List actualSubmodelIds = submodelIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).map(String::valueOf).collect(Collectors.toList()); + assertEquals(submodelIds, actualSubmodelIds); + + String actualType = typeProperty.getValue(); + assertTrue(actualType.equals("aas-environment")); + } + + @Test + public void testAdaptSubmodelElementCollectionToTargetInformation() { + + List expectedAasIds = Arrays.asList("aasId1", "aasId2"); + List expectedSubmodelIds = Arrays.asList("smId1", "smId2"); + String type = "aas-environment"; + + List aasIdProperties = expectedAasIds.stream().map(aasId -> new DefaultProperty.Builder().value(aasId).build()).collect(Collectors.toList()); + List submodelIdProperties = expectedSubmodelIds.stream().map(submodelId -> new DefaultProperty.Builder().value(submodelId).build()).collect(Collectors.toList()); + + SubmodelElementList aasIdList = new DefaultSubmodelElementList.Builder().idShort("aasIds").value(aasIdProperties).build(); + SubmodelElementList submodelIdList = new DefaultSubmodelElementList.Builder().idShort("submodelIds").value(submodelIdProperties).build(); + SubmodelElement typeProperty = createTypeProperty(type); + + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation").value(Arrays.asList(aasIdList, submodelIdList, typeProperty)).build(); + + TargetInformation result = aasEnvironmentTargetInformationAdapter.adapt(targetInformationSMC); + + assertTrue(result instanceof AasEnvironmentTargetInformation); + assertEquals(expectedAasIds, ((AasEnvironmentTargetInformation) result).getAasIds()); + assertEquals(expectedSubmodelIds, ((AasEnvironmentTargetInformation) result).getSubmodelIds()); + } + + @Test + public void testAdaptTargetInformationWithEmptyAasIds() { + + List aasIds = Collections.emptyList(); + List submodelIds = Collections.emptyList(); + + TargetInformation targetInformation = new AasEnvironmentTargetInformation(aasIds, submodelIds); + + SubmodelElementCollection result = aasEnvironmentTargetInformationAdapter.adapt(targetInformation); + + assertEquals("targetInformation", result.getIdShort()); + + List elements = result.getValue(); + assertEquals(3, elements.size()); + + SubmodelElementList aasIdList = (SubmodelElementList) elements.get(0); + assertEquals("aasIds", aasIdList.getIdShort()); + + SubmodelElementList submodelIdList = (SubmodelElementList) elements.get(1); + assertEquals("submodelIds", submodelIdList.getIdShort()); + + Property typeProperty = (Property) elements.get(2); + assertEquals("@type", typeProperty.getIdShort()); + + List actualAasIds = aasIdList.getValue().stream() + .map(Property.class::cast) + .map(Property::getValue) + .map(String::valueOf) + .collect(Collectors.toList()); + assertTrue(actualAasIds.isEmpty()); + + List actualSubmodelIds = submodelIdList.getValue().stream() + .map(Property.class::cast) + .map(Property::getValue) + .map(String::valueOf) + .collect(Collectors.toList()); + assertTrue(actualSubmodelIds.isEmpty()); + + String actualType = typeProperty.getValue(); + assertTrue(actualType.equals("aas-environment")); + } + + @Test(expected = InvalidTargetInformationException.class) + public void testAdaptSubmodelElementCollectionWithInvalidStructure() { + + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation") + .value(Collections.singletonList(new DefaultProperty.Builder().idShort("invalidElement").value("value").build())) + .build(); + + aasEnvironmentTargetInformationAdapter.adapt(targetInformationSMC); + } + + @Test(expected = InvalidTargetInformationException.class) + public void testAdaptSubmodelElementCollectionWithoutAasIds() { + + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation") + .value(Collections.emptyList()) + .build(); + + aasEnvironmentTargetInformationAdapter.adapt(targetInformationSMC); + } + + private SubmodelElement createTypeProperty(String type) { + return new DefaultProperty.Builder().idShort("@type").value(type).build(); + } + +} \ No newline at end of file diff --git a/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/feature/authorization/rbac/backend/submodel/AasRegistryTargetInformationAdapter.java b/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/feature/authorization/rbac/backend/submodel/AasRegistryTargetInformationAdapter.java index ee37eaadf..376eae387 100644 --- a/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/feature/authorization/rbac/backend/submodel/AasRegistryTargetInformationAdapter.java +++ b/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/feature/authorization/rbac/backend/submodel/AasRegistryTargetInformationAdapter.java @@ -52,30 +52,47 @@ public class AasRegistryTargetInformationAdapter implements TargetInformationAda @Override public SubmodelElementCollection adapt(TargetInformation targetInformation) { - SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation").build(); + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder() + .idShort("targetInformation").build(); SubmodelElementList aasId = new DefaultSubmodelElementList.Builder().idShort("aasIds").build(); + Property typeProperty = new DefaultProperty.Builder().idShort("@type").value("aas-registry").build(); - List aasIds = ((AasRegistryTargetInformation) targetInformation).getAasIds().stream().map(this::transform).collect(Collectors.toList()); + List aasIds = ((AasRegistryTargetInformation) targetInformation).getAasIds().stream() + .map(this::transform).collect(Collectors.toList()); aasId.setValue(aasIds); - targetInformationSMC.setValue(Arrays.asList(aasId)); + targetInformationSMC.setValue(Arrays.asList(aasId, typeProperty)); return targetInformationSMC; } @Override public TargetInformation adapt(SubmodelElementCollection targetInformation) { - - SubmodelElement aasIdSubmodelElement = targetInformation.getValue().stream().filter(sme -> sme.getIdShort().equals("aasIds")).findAny().orElseThrow( - () -> new InvalidTargetInformationException("The TargetInformation defined in the SubmodelElementCollection Rule with id: " + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName())); + + String targetInformationType = getTargetInformationType(targetInformation); + + if (!targetInformationType.equals("aas-registry")) + throw new InvalidTargetInformationException( + "The TargetInformation @type: " + targetInformationType + " is not compatible with " + + getClass().getName() + "."); + + SubmodelElement aasIdSubmodelElement = targetInformation.getValue().stream() + .filter(sme -> sme.getIdShort().equals("aasIds")).findAny() + .orElseThrow(() -> new InvalidTargetInformationException( + "The TargetInformation defined in the SubmodelElementCollection Rule with id: " + + targetInformation.getIdShort() + " is not compatible with the " + + getClass().getName())); if (!(aasIdSubmodelElement instanceof SubmodelElementList)) - throw new InvalidTargetInformationException("The TargetInformation defined in the SubmodelElementCollection Rule with id: " + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName()); + throw new InvalidTargetInformationException( + "The TargetInformation defined in the SubmodelElementCollection Rule with id: " + + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName()); SubmodelElementList aasIdList = (SubmodelElementList) aasIdSubmodelElement; - List aasIds = aasIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).collect(Collectors.toList()); + List aasIds = aasIdList.getValue().stream().map(Property.class::cast).map(Property::getValue) + .collect(Collectors.toList()); return new AasRegistryTargetInformation(aasIds); } @@ -84,4 +101,15 @@ private Property transform(String aasId) { return new DefaultProperty.Builder().value(aasId).build(); } + private String getTargetInformationType(SubmodelElementCollection targetInformation) { + + Property typeProperty = (Property) targetInformation.getValue().stream() + .filter(sme -> sme.getIdShort().equals("@type")).findAny() + .orElseThrow(() -> new InvalidTargetInformationException( + "The TargetInformation defined in the SubmodelElementCollection Rule with id: " + + targetInformation.getIdShort() + " does not have @type definition")); + + return typeProperty.getValue(); + } + } diff --git a/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasregistry/regression/feature/authorization/AasRegistryTargetInformationAdapterTest.java b/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasregistry/regression/feature/authorization/AasRegistryTargetInformationAdapterTest.java index d3bac91be..5a4ceb1b7 100644 --- a/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasregistry/regression/feature/authorization/AasRegistryTargetInformationAdapterTest.java +++ b/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasregistry/regression/feature/authorization/AasRegistryTargetInformationAdapterTest.java @@ -71,24 +71,33 @@ public void testAdaptTargetInformationToSubmodelElementCollection() { assertEquals("targetInformation", result.getIdShort()); List elements = result.getValue(); - assertEquals(1, elements.size()); + assertEquals(2, elements.size()); SubmodelElementList aasIdList = (SubmodelElementList) elements.get(0); assertEquals("aasIds", aasIdList.getIdShort()); + + Property typeProperty = (Property) elements.get(1); + assertEquals("@type", typeProperty.getIdShort()); List actualAasIds = aasIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).map(String::valueOf).collect(Collectors.toList()); assertEquals(aasIds, actualAasIds); + + String actualType = typeProperty.getValue(); + assertTrue(actualType.equals("aas-registry")); } @Test public void testAdaptSubmodelElementCollectionToTargetInformation() { List expectedAasIds = Arrays.asList("aasId1", "aasId2"); + String type = "aas-registry"; + List aasIdProperties = expectedAasIds.stream().map(aasId -> new DefaultProperty.Builder().value(aasId).build()).collect(Collectors.toList()); SubmodelElementList aasIdList = new DefaultSubmodelElementList.Builder().idShort("aasIds").value(aasIdProperties).build(); - - SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation").value(Collections.singletonList(aasIdList)).build(); + SubmodelElement typeProperty = createTypeProperty(type); + + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation").value(Arrays.asList(aasIdList, typeProperty)).build(); TargetInformation result = aasRegistryTargetInformationAdapter.adapt(targetInformationSMC); @@ -107,10 +116,13 @@ public void testAdaptTargetInformationWithEmptyAasIds() { assertEquals("targetInformation", result.getIdShort()); List elements = result.getValue(); - assertEquals(1, elements.size()); + assertEquals(2, elements.size()); SubmodelElementList aasIdList = (SubmodelElementList) elements.get(0); assertEquals("aasIds", aasIdList.getIdShort()); + + Property typeProperty = (Property) elements.get(1); + assertEquals("@type", typeProperty.getIdShort()); List actualAasIds = aasIdList.getValue().stream() .map(Property.class::cast) @@ -118,6 +130,9 @@ public void testAdaptTargetInformationWithEmptyAasIds() { .map(String::valueOf) .collect(Collectors.toList()); assertTrue(actualAasIds.isEmpty()); + + String actualType = typeProperty.getValue(); + assertTrue(actualType.equals("aas-registry")); } @Test(expected = InvalidTargetInformationException.class) @@ -139,5 +154,9 @@ public void testAdaptSubmodelElementCollectionWithoutAasIds() { aasRegistryTargetInformationAdapter.adapt(targetInformationSMC); } + + private SubmodelElement createTypeProperty(String type) { + return new DefaultProperty.Builder().idShort("@type").value(type).build(); + } } diff --git a/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/rbac/backend/submodel/AasTargetInformationAdapter.java b/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/rbac/backend/submodel/AasTargetInformationAdapter.java index 1ec0e8fb2..db29c1460 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/rbac/backend/submodel/AasTargetInformationAdapter.java +++ b/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/rbac/backend/submodel/AasTargetInformationAdapter.java @@ -52,14 +52,17 @@ public class AasTargetInformationAdapter implements TargetInformationAdapter { @Override public SubmodelElementCollection adapt(TargetInformation targetInformation) { - SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation").build(); + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder() + .idShort("targetInformation").build(); SubmodelElementList aasId = new DefaultSubmodelElementList.Builder().idShort("aasIds").build(); + Property typeProperty = new DefaultProperty.Builder().idShort("@type").value("aas").build(); - List aasIds = ((AasTargetInformation) targetInformation).getAasIds().stream().map(this::transform).collect(Collectors.toList()); + List aasIds = ((AasTargetInformation) targetInformation).getAasIds().stream() + .map(this::transform).collect(Collectors.toList()); aasId.setValue(aasIds); - targetInformationSMC.setValue(Arrays.asList(aasId)); + targetInformationSMC.setValue(Arrays.asList(aasId, typeProperty)); return targetInformationSMC; } @@ -67,15 +70,28 @@ public SubmodelElementCollection adapt(TargetInformation targetInformation) { @Override public TargetInformation adapt(SubmodelElementCollection targetInformation) { - SubmodelElement aasIdSubmodelElement = targetInformation.getValue().stream().filter(sme -> sme.getIdShort().equals("aasIds")).findAny().orElseThrow( - () -> new InvalidTargetInformationException("The TargetInformation defined in the SubmodelElementCollection Rule with id: " + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName())); + String targetInformationType = getTargetInformationType(targetInformation); + + if (!targetInformationType.equals("aas")) + throw new InvalidTargetInformationException("The TargetInformation @type: " + targetInformationType + + " is not compatible with " + getClass().getName() + "."); + + SubmodelElement aasIdSubmodelElement = targetInformation.getValue().stream() + .filter(sme -> sme.getIdShort().equals("aasIds")).findAny() + .orElseThrow(() -> new InvalidTargetInformationException( + "The TargetInformation defined in the SubmodelElementCollection Rule with id: " + + targetInformation.getIdShort() + " is not compatible with the " + + getClass().getName())); if (!(aasIdSubmodelElement instanceof SubmodelElementList)) - throw new InvalidTargetInformationException("The TargetInformation defined in the SubmodelElementCollection Rule with id: " + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName()); + throw new InvalidTargetInformationException( + "The TargetInformation defined in the SubmodelElementCollection Rule with id: " + + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName()); SubmodelElementList aasIdList = (SubmodelElementList) aasIdSubmodelElement; - List aasIds = aasIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).collect(Collectors.toList()); + List aasIds = aasIdList.getValue().stream().map(Property.class::cast).map(Property::getValue) + .collect(Collectors.toList()); return new AasTargetInformation(aasIds); } @@ -83,5 +99,16 @@ public TargetInformation adapt(SubmodelElementCollection targetInformation) { private Property transform(String aasId) { return new DefaultProperty.Builder().value(aasId).build(); } + + private String getTargetInformationType(SubmodelElementCollection targetInformation) { + + Property typeProperty = (Property) targetInformation.getValue().stream() + .filter(sme -> sme.getIdShort().equals("@type")).findAny() + .orElseThrow(() -> new InvalidTargetInformationException( + "The TargetInformation defined in the SubmodelElementCollection Rule with id: " + + targetInformation.getIdShort() + " does not have @type definition")); + + return typeProperty.getValue(); + } } diff --git a/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/AasTargetInformationAdapterTest.java b/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/AasTargetInformationAdapterTest.java index b7ed798d5..8c7b02ef9 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/AasTargetInformationAdapterTest.java +++ b/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/AasTargetInformationAdapterTest.java @@ -70,24 +70,33 @@ public void testAdaptTargetInformationToSubmodelElementCollection() { assertEquals("targetInformation", result.getIdShort()); List elements = result.getValue(); - assertEquals(1, elements.size()); + assertEquals(2, elements.size()); SubmodelElementList aasIdList = (SubmodelElementList) elements.get(0); assertEquals("aasIds", aasIdList.getIdShort()); + + Property typeProperty = (Property) elements.get(1); + assertEquals("@type", typeProperty.getIdShort()); List actualAasIds = aasIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).map(String::valueOf).collect(Collectors.toList()); assertEquals(aasIds, actualAasIds); + + String actualType = typeProperty.getValue(); + assertTrue(actualType.equals("aas")); } @Test public void testAdaptSubmodelElementCollectionToTargetInformation() { List expectedAasIds = Arrays.asList("aasId1", "aasId2"); + String type = "aas"; + List aasIdProperties = expectedAasIds.stream().map(aasId -> new DefaultProperty.Builder().value(aasId).build()).collect(Collectors.toList()); SubmodelElementList aasIdList = new DefaultSubmodelElementList.Builder().idShort("aasIds").value(aasIdProperties).build(); + SubmodelElement typeProperty = createTypeProperty(type); - SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation").value(Collections.singletonList(aasIdList)).build(); + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation").value(Arrays.asList(aasIdList, typeProperty)).build(); TargetInformation result = aasTargetInformationAdapter.adapt(targetInformationSMC); @@ -106,10 +115,13 @@ public void testAdaptTargetInformationWithEmptyAasIds() { assertEquals("targetInformation", result.getIdShort()); List elements = result.getValue(); - assertEquals(1, elements.size()); + assertEquals(2, elements.size()); SubmodelElementList aasIdList = (SubmodelElementList) elements.get(0); assertEquals("aasIds", aasIdList.getIdShort()); + + Property typeProperty = (Property) elements.get(1); + assertEquals("@type", typeProperty.getIdShort()); List actualAasIds = aasIdList.getValue().stream() .map(Property.class::cast) @@ -117,6 +129,9 @@ public void testAdaptTargetInformationWithEmptyAasIds() { .map(String::valueOf) .collect(Collectors.toList()); assertTrue(actualAasIds.isEmpty()); + + String actualType = typeProperty.getValue(); + assertTrue(actualType.equals("aas")); } @Test(expected = InvalidTargetInformationException.class) @@ -138,5 +153,9 @@ public void testAdaptSubmodelElementCollectionWithoutAasIds() { aasTargetInformationAdapter.adapt(targetInformationSMC); } + + private SubmodelElement createTypeProperty(String type) { + return new DefaultProperty.Builder().idShort("@type").value(type).build(); + } } diff --git a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/pom.xml b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/pom.xml index c80d7491a..f5fa6f5ed 100644 --- a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/pom.xml +++ b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/pom.xml @@ -67,6 +67,10 @@ org.eclipse.digitaltwin.basyx basyx.authorization.rules.rbac.backend.inmemory + + + org.eclipse.digitaltwin.basyx + basyx.authorization.rules.rbac.backend.submodel diff --git a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/AuthorizedConceptDescriptionRepositoryConfiguration.java b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/AuthorizedConceptDescriptionRepositoryConfiguration.java index 8e1be3a70..f07e293fb 100644 --- a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/AuthorizedConceptDescriptionRepositoryConfiguration.java +++ b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/AuthorizedConceptDescriptionRepositoryConfiguration.java @@ -31,7 +31,9 @@ import org.eclipse.digitaltwin.basyx.authorization.rbac.SimpleRbacPermissionResolver; import org.eclipse.digitaltwin.basyx.authorization.rbac.RoleProvider; import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetPermissionVerifier; +import org.eclipse.digitaltwin.basyx.authorization.rules.rbac.backend.submodel.TargetInformationAdapter; import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.feature.authorization.rbac.ConceptDescriptionTargetPermissionVerifier; +import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.feature.authorization.rbac.backend.submodel.CDTargetInformationAdapter; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -55,5 +57,11 @@ public RbacPermissionResolver getCDPermissi return new SimpleRbacPermissionResolver<>(rbacStorage, roleProvider, targetPermissionVerifier); } + + @Bean + public TargetInformationAdapter getAasRegistryTargetInformationAdapter() { + + return new CDTargetInformationAdapter(); + } } diff --git a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/rbac/backend/submodel/CDTargetInformationAdapter.java b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/rbac/backend/submodel/CDTargetInformationAdapter.java new file mode 100644 index 000000000..bb26163fa --- /dev/null +++ b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/rbac/backend/submodel/CDTargetInformationAdapter.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (C) 2024 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.feature.authorization.rbac.backend.submodel; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.digitaltwin.aas4j.v3.model.Property; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementCollection; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementList; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultProperty; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodelElementCollection; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodelElementList; +import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetInformation; +import org.eclipse.digitaltwin.basyx.authorization.rules.rbac.backend.submodel.TargetInformationAdapter; +import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.feature.authorization.ConceptDescriptionTargetInformation; +import org.eclipse.digitaltwin.basyx.core.exceptions.InvalidTargetInformationException; + +/** + * An implementation of the {@link TargetInformationAdapter} to adapt with Aas + * {@link TargetInformation} + * + * @author danish + */ +public class CDTargetInformationAdapter implements TargetInformationAdapter { + + @Override + public SubmodelElementCollection adapt(TargetInformation targetInformation) { + + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder() + .idShort("targetInformation").build(); + + SubmodelElementList aasId = new DefaultSubmodelElementList.Builder().idShort("conceptDescriptionIds").build(); + Property typeProperty = new DefaultProperty.Builder().idShort("@type").value("concept-description").build(); + + List aasIds = ((ConceptDescriptionTargetInformation) targetInformation) + .getConceptDescriptionIds().stream().map(this::transform).collect(Collectors.toList()); + aasId.setValue(aasIds); + + targetInformationSMC.setValue(Arrays.asList(aasId, typeProperty)); + + return targetInformationSMC; + } + + @Override + public TargetInformation adapt(SubmodelElementCollection targetInformation) { + + String targetInformationType = getTargetInformationType(targetInformation); + + if (!targetInformationType.equals("concept-description")) + throw new InvalidTargetInformationException("The TargetInformation @type: " + targetInformationType + + " is not compatible with " + getClass().getName() + "."); + + SubmodelElement aasIdSubmodelElement = targetInformation.getValue().stream() + .filter(sme -> sme.getIdShort().equals("conceptDescriptionIds")).findAny() + .orElseThrow(() -> new InvalidTargetInformationException( + "The TargetInformation defined in the SubmodelElementCollection Rule with id: " + + targetInformation.getIdShort() + " is not compatible with the " + + getClass().getName())); + + if (!(aasIdSubmodelElement instanceof SubmodelElementList)) + throw new InvalidTargetInformationException( + "The TargetInformation defined in the SubmodelElementCollection Rule with id: " + + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName()); + + SubmodelElementList aasIdList = (SubmodelElementList) aasIdSubmodelElement; + + List aasIds = aasIdList.getValue().stream().map(Property.class::cast).map(Property::getValue) + .collect(Collectors.toList()); + + return new ConceptDescriptionTargetInformation(aasIds); + } + + private Property transform(String aasId) { + return new DefaultProperty.Builder().value(aasId).build(); + } + + private String getTargetInformationType(SubmodelElementCollection targetInformation) { + + Property typeProperty = (Property) targetInformation.getValue().stream() + .filter(sme -> sme.getIdShort().equals("@type")).findAny() + .orElseThrow(() -> new InvalidTargetInformationException( + "The TargetInformation defined in the SubmodelElementCollection Rule with id: " + + targetInformation.getIdShort() + " does not have @type definition")); + + return typeProperty.getValue(); + } + +} diff --git a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/CDTargetInformationAdapterTest.java b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/CDTargetInformationAdapterTest.java new file mode 100644 index 000000000..8b90f8b87 --- /dev/null +++ b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/CDTargetInformationAdapterTest.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (C) 2024 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.feature.authorization; + +import static org.junit.Assert.*; +import org.eclipse.digitaltwin.aas4j.v3.model.Property; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementCollection; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementList; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultProperty; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodelElementCollection; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodelElementList; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetInformation; +import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.feature.authorization.rbac.backend.submodel.CDTargetInformationAdapter; +import org.eclipse.digitaltwin.basyx.core.exceptions.InvalidTargetInformationException; + +/** + * Tests {@link AasRegistryTargetInformationAdapter} + * + * @author danish + */ +public class CDTargetInformationAdapterTest { + + private CDTargetInformationAdapter cdTargetInformationAdapter; + + @Before + public void setUp() { + cdTargetInformationAdapter = new CDTargetInformationAdapter(); + } + + @Test + public void testAdaptTargetInformationToSubmodelElementCollection() { + + List conceptDescriptiornIds = Arrays.asList("cdId1", "cdId2"); + TargetInformation targetInformation = new ConceptDescriptionTargetInformation(conceptDescriptiornIds); + + SubmodelElementCollection result = cdTargetInformationAdapter.adapt(targetInformation); + + assertEquals("targetInformation", result.getIdShort()); + + List elements = result.getValue(); + assertEquals(2, elements.size()); + + SubmodelElementList aasIdList = (SubmodelElementList) elements.get(0); + assertEquals("conceptDescriptionIds", aasIdList.getIdShort()); + + Property typeProperty = (Property) elements.get(1); + assertEquals("@type", typeProperty.getIdShort()); + + List actualAasIds = aasIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).map(String::valueOf).collect(Collectors.toList()); + assertEquals(conceptDescriptiornIds, actualAasIds); + + String actualType = typeProperty.getValue(); + assertTrue(actualType.equals("concept-description")); + } + + @Test + public void testAdaptSubmodelElementCollectionToTargetInformation() { + + List expectedCDIds = Arrays.asList("cdId1", "cdId2"); + String type = "concept-description"; + + List aasIdProperties = expectedCDIds.stream().map(aasId -> new DefaultProperty.Builder().value(aasId).build()).collect(Collectors.toList()); + + SubmodelElementList aasIdList = new DefaultSubmodelElementList.Builder().idShort("conceptDescriptionIds").value(aasIdProperties).build(); + SubmodelElement typeProperty = createTypeProperty(type); + + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation").value(Arrays.asList(aasIdList, typeProperty)).build(); + + TargetInformation result = cdTargetInformationAdapter.adapt(targetInformationSMC); + + assertTrue(result instanceof ConceptDescriptionTargetInformation); + assertEquals(expectedCDIds, ((ConceptDescriptionTargetInformation) result).getConceptDescriptionIds()); + } + + @Test + public void testAdaptTargetInformationWithEmptyAasIds() { + + List cdIds = Collections.emptyList(); + TargetInformation targetInformation = new ConceptDescriptionTargetInformation(cdIds); + + SubmodelElementCollection result = cdTargetInformationAdapter.adapt(targetInformation); + + assertEquals("targetInformation", result.getIdShort()); + + List elements = result.getValue(); + assertEquals(2, elements.size()); + + SubmodelElementList aasIdList = (SubmodelElementList) elements.get(0); + assertEquals("conceptDescriptionIds", aasIdList.getIdShort()); + + Property typeProperty = (Property) elements.get(1); + assertEquals("@type", typeProperty.getIdShort()); + + List actualAasIds = aasIdList.getValue().stream() + .map(Property.class::cast) + .map(Property::getValue) + .map(String::valueOf) + .collect(Collectors.toList()); + assertTrue(actualAasIds.isEmpty()); + + String actualType = typeProperty.getValue(); + assertTrue(actualType.equals("concept-description")); + } + + @Test(expected = InvalidTargetInformationException.class) + public void testAdaptSubmodelElementCollectionWithInvalidStructure() { + + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation") + .value(Collections.singletonList(new DefaultProperty.Builder().idShort("invalidElement").value("value").build())) + .build(); + + cdTargetInformationAdapter.adapt(targetInformationSMC); + } + + @Test(expected = InvalidTargetInformationException.class) + public void testAdaptSubmodelElementCollectionWithoutCDIds() { + + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation") + .value(Collections.emptyList()) + .build(); + + cdTargetInformationAdapter.adapt(targetInformationSMC); + } + + private SubmodelElement createTypeProperty(String type) { + return new DefaultProperty.Builder().idShort("@type").value(type).build(); + } + +} \ No newline at end of file diff --git a/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/pom.xml b/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/pom.xml index 64ec4f3e5..9475ffa81 100644 --- a/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/pom.xml +++ b/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/pom.xml @@ -61,5 +61,9 @@ org.eclipse.digitaltwin.basyx basyx.authorization.rules.rbac.backend.inmemory + + org.eclipse.digitaltwin.basyx + basyx.authorization.rules.rbac.backend.submodel + diff --git a/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/feature/authorization/AuthorizedSubmodelRegistryConfiguration.java b/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/feature/authorization/AuthorizedSubmodelRegistryConfiguration.java index 4e8532b5e..17204f7f4 100644 --- a/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/feature/authorization/AuthorizedSubmodelRegistryConfiguration.java +++ b/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/feature/authorization/AuthorizedSubmodelRegistryConfiguration.java @@ -31,7 +31,9 @@ import org.eclipse.digitaltwin.basyx.authorization.rbac.RoleProvider; import org.eclipse.digitaltwin.basyx.authorization.rbac.SimpleRbacPermissionResolver; import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetPermissionVerifier; +import org.eclipse.digitaltwin.basyx.authorization.rules.rbac.backend.submodel.TargetInformationAdapter; import org.eclipse.digitaltwin.basyx.submodelregistry.feature.authorization.rbac.SubmodelRegistryTargetPermissionVerifier; +import org.eclipse.digitaltwin.basyx.submodelregistry.feature.authorization.rbac.backend.submodel.SubmodelRegistryTargetInformationAdapter; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -54,5 +56,11 @@ public TargetPermissionVerifier getSubmodelRe public RbacPermissionResolver getSubmodelRegistryPermissionResolver(RbacStorage rbacStorage, RoleProvider roleProvider, TargetPermissionVerifier targetPermissionVerifier) { return new SimpleRbacPermissionResolver<>(rbacStorage, roleProvider, targetPermissionVerifier); } + + @Bean + public TargetInformationAdapter getSubmdoelRegistryTargetInformationAdapter() { + + return new SubmodelRegistryTargetInformationAdapter(); + } } diff --git a/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/feature/authorization/rbac/backend/submodel/SubmodelRegistryTargetInformationAdapter.java b/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/feature/authorization/rbac/backend/submodel/SubmodelRegistryTargetInformationAdapter.java new file mode 100644 index 000000000..0e9f68c14 --- /dev/null +++ b/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/feature/authorization/rbac/backend/submodel/SubmodelRegistryTargetInformationAdapter.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (C) 2024 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.submodelregistry.feature.authorization.rbac.backend.submodel; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.digitaltwin.aas4j.v3.model.Property; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementCollection; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementList; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultProperty; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodelElementCollection; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodelElementList; +import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetInformation; +import org.eclipse.digitaltwin.basyx.authorization.rules.rbac.backend.submodel.TargetInformationAdapter; +import org.eclipse.digitaltwin.basyx.core.exceptions.InvalidTargetInformationException; +import org.eclipse.digitaltwin.basyx.submodelregistry.feature.authorization.SubmodelRegistryTargetInformation; + +/** + * An implementation of the {@link TargetInformationAdapter} to adapt with Aas + * {@link TargetInformation} + * + * @author danish + */ +public class SubmodelRegistryTargetInformationAdapter implements TargetInformationAdapter { + + @Override + public SubmodelElementCollection adapt(TargetInformation targetInformation) { + + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder() + .idShort("targetInformation").build(); + + SubmodelElementList aasId = new DefaultSubmodelElementList.Builder().idShort("submodelIds").build(); + Property typeProperty = new DefaultProperty.Builder().idShort("@type").value("submodel-registry").build(); + + List aasIds = ((SubmodelRegistryTargetInformation) targetInformation).getSubmodelIds().stream() + .map(this::transform).collect(Collectors.toList()); + aasId.setValue(aasIds); + + targetInformationSMC.setValue(Arrays.asList(aasId, typeProperty)); + + return targetInformationSMC; + } + + @Override + public TargetInformation adapt(SubmodelElementCollection targetInformation) { + + String targetInformationType = getTargetInformationType(targetInformation); + + if (!targetInformationType.equals("submodel-registry")) + throw new InvalidTargetInformationException("The TargetInformation @type: " + targetInformationType + + " is not compatible with " + getClass().getName() + "."); + + SubmodelElement aasIdSubmodelElement = targetInformation.getValue().stream() + .filter(sme -> sme.getIdShort().equals("submodelIds")).findAny() + .orElseThrow(() -> new InvalidTargetInformationException( + "The TargetInformation defined in the SubmodelElementCollection Rule with id: " + + targetInformation.getIdShort() + " is not compatible with the " + + getClass().getName())); + + if (!(aasIdSubmodelElement instanceof SubmodelElementList)) + throw new InvalidTargetInformationException( + "The TargetInformation defined in the SubmodelElementCollection Rule with id: " + + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName()); + + SubmodelElementList aasIdList = (SubmodelElementList) aasIdSubmodelElement; + + List aasIds = aasIdList.getValue().stream().map(Property.class::cast).map(Property::getValue) + .collect(Collectors.toList()); + + return new SubmodelRegistryTargetInformation(aasIds); + } + + private Property transform(String aasId) { + return new DefaultProperty.Builder().value(aasId).build(); + } + + private String getTargetInformationType(SubmodelElementCollection targetInformation) { + + Property typeProperty = (Property) targetInformation.getValue().stream() + .filter(sme -> sme.getIdShort().equals("@type")).findAny() + .orElseThrow(() -> new InvalidTargetInformationException( + "The TargetInformation defined in the SubmodelElementCollection Rule with id: " + + targetInformation.getIdShort() + " does not have @type definition")); + + return typeProperty.getValue(); + } + +} \ No newline at end of file diff --git a/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/regression/feature/authorization/SubmodelRegistryTargetInformationAdapterTest.java b/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/regression/feature/authorization/SubmodelRegistryTargetInformationAdapterTest.java new file mode 100644 index 000000000..1b3836fd6 --- /dev/null +++ b/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/regression/feature/authorization/SubmodelRegistryTargetInformationAdapterTest.java @@ -0,0 +1,163 @@ +/******************************************************************************* + * Copyright (C) 2024 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.submodelregistry.regression.feature.authorization; + +import static org.junit.Assert.*; +import org.eclipse.digitaltwin.aas4j.v3.model.Property; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementCollection; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementList; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultProperty; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodelElementCollection; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodelElementList; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetInformation; +import org.eclipse.digitaltwin.basyx.core.exceptions.InvalidTargetInformationException; +import org.eclipse.digitaltwin.basyx.submodelregistry.feature.authorization.SubmodelRegistryTargetInformation; +import org.eclipse.digitaltwin.basyx.submodelregistry.feature.authorization.rbac.backend.submodel.SubmodelRegistryTargetInformationAdapter; + +/** + * Tests {@link AasRegistryTargetInformationAdapter} + * + * @author danish + */ +public class SubmodelRegistryTargetInformationAdapterTest { + + private SubmodelRegistryTargetInformationAdapter aasRegistryTargetInformationAdapter; + + @Before + public void setUp() { + aasRegistryTargetInformationAdapter = new SubmodelRegistryTargetInformationAdapter(); + } + + @Test + public void testAdaptTargetInformationToSubmodelElementCollection() { + + List aasIds = Arrays.asList("aasId1", "aasId2"); + TargetInformation targetInformation = new SubmodelRegistryTargetInformation(aasIds); + + SubmodelElementCollection result = aasRegistryTargetInformationAdapter.adapt(targetInformation); + + assertEquals("targetInformation", result.getIdShort()); + + List elements = result.getValue(); + assertEquals(2, elements.size()); + + SubmodelElementList aasIdList = (SubmodelElementList) elements.get(0); + assertEquals("submodelIds", aasIdList.getIdShort()); + + Property typeProperty = (Property) elements.get(1); + assertEquals("@type", typeProperty.getIdShort()); + + List actualAasIds = aasIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).map(String::valueOf).collect(Collectors.toList()); + assertEquals(aasIds, actualAasIds); + + String actualType = typeProperty.getValue(); + assertTrue(actualType.equals("submodel-registry")); + } + + @Test + public void testAdaptSubmodelElementCollectionToTargetInformation() { + + List expectedAasIds = Arrays.asList("aasId1", "aasId2"); + String type = "submodel-registry"; + + List aasIdProperties = expectedAasIds.stream().map(aasId -> new DefaultProperty.Builder().value(aasId).build()).collect(Collectors.toList()); + + SubmodelElementList aasIdList = new DefaultSubmodelElementList.Builder().idShort("submodelIds").value(aasIdProperties).build(); + SubmodelElement typeProperty = createTypeProperty(type); + + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation").value(Arrays.asList(aasIdList, typeProperty)).build(); + + TargetInformation result = aasRegistryTargetInformationAdapter.adapt(targetInformationSMC); + + assertTrue(result instanceof SubmodelRegistryTargetInformation); + assertEquals(expectedAasIds, ((SubmodelRegistryTargetInformation) result).getSubmodelIds()); + } + + @Test + public void testAdaptTargetInformationWithEmptyAasIds() { + + List aasIds = Collections.emptyList(); + TargetInformation targetInformation = new SubmodelRegistryTargetInformation(aasIds); + + SubmodelElementCollection result = aasRegistryTargetInformationAdapter.adapt(targetInformation); + + assertEquals("targetInformation", result.getIdShort()); + + List elements = result.getValue(); + assertEquals(2, elements.size()); + + SubmodelElementList aasIdList = (SubmodelElementList) elements.get(0); + assertEquals("submodelIds", aasIdList.getIdShort()); + + Property typeProperty = (Property) elements.get(1); + assertEquals("@type", typeProperty.getIdShort()); + + List actualAasIds = aasIdList.getValue().stream() + .map(Property.class::cast) + .map(Property::getValue) + .map(String::valueOf) + .collect(Collectors.toList()); + assertTrue(actualAasIds.isEmpty()); + + String actualType = typeProperty.getValue(); + assertTrue(actualType.equals("submodel-registry")); + } + + @Test(expected = InvalidTargetInformationException.class) + public void testAdaptSubmodelElementCollectionWithInvalidStructure() { + + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation") + .value(Collections.singletonList(new DefaultProperty.Builder().idShort("invalidElement").value("value").build())) + .build(); + + aasRegistryTargetInformationAdapter.adapt(targetInformationSMC); + } + + @Test(expected = InvalidTargetInformationException.class) + public void testAdaptSubmodelElementCollectionWithoutAasIds() { + + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation") + .value(Collections.emptyList()) + .build(); + + aasRegistryTargetInformationAdapter.adapt(targetInformationSMC); + } + + private SubmodelElement createTypeProperty(String type) { + return new DefaultProperty.Builder().idShort("@type").value(type).build(); + } + + +} diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/pom.xml b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/pom.xml index dccab08e1..bdb7a8008 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/pom.xml +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/pom.xml @@ -63,6 +63,10 @@ org.eclipse.digitaltwin.basyx basyx.authorization.rules.rbac.backend.inmemory + + + org.eclipse.digitaltwin.basyx + basyx.authorization.rules.rbac.backend.submodel org.apache.httpcomponents.client5 diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepositoryConfiguration.java b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepositoryConfiguration.java index 5a6c79fa2..6d10b8f13 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepositoryConfiguration.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepositoryConfiguration.java @@ -31,7 +31,9 @@ import org.eclipse.digitaltwin.basyx.authorization.rbac.SimpleRbacPermissionResolver; import org.eclipse.digitaltwin.basyx.authorization.rbac.RoleProvider; import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetPermissionVerifier; +import org.eclipse.digitaltwin.basyx.authorization.rules.rbac.backend.submodel.TargetInformationAdapter; import org.eclipse.digitaltwin.basyx.submodelrepository.feature.authorization.rbac.SubmodelTargetPermissionVerifier; +import org.eclipse.digitaltwin.basyx.submodelrepository.feature.authorization.rbac.backend.submodel.SubmodelTargetInformationAdapter; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -55,5 +57,11 @@ public RbacPermissionResolver getSubmodelPermissionRe return new SimpleRbacPermissionResolver<>(rbacStorage, roleProvider, targetPermissionVerifier); } + + @Bean + public TargetInformationAdapter getSubmodelTargetInformationAdapter() { + + return new SubmodelTargetInformationAdapter(); + } } diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/rbac/backend/submodel/SubmodelTargetInformationAdapter.java b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/rbac/backend/submodel/SubmodelTargetInformationAdapter.java new file mode 100644 index 000000000..489da521a --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/rbac/backend/submodel/SubmodelTargetInformationAdapter.java @@ -0,0 +1,130 @@ +/******************************************************************************* + * Copyright (C) 2024 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.submodelrepository.feature.authorization.rbac.backend.submodel; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.digitaltwin.aas4j.v3.model.Property; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementCollection; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementList; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultProperty; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodelElementCollection; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodelElementList; +import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetInformation; +import org.eclipse.digitaltwin.basyx.authorization.rules.rbac.backend.submodel.TargetInformationAdapter; +import org.eclipse.digitaltwin.basyx.core.exceptions.InvalidTargetInformationException; +import org.eclipse.digitaltwin.basyx.submodelrepository.feature.authorization.SubmodelTargetInformation; + +/** + * An implementation of the {@link TargetInformationAdapter} to adapt with Aas + * {@link TargetInformation} + * + * @author danish + */ +public class SubmodelTargetInformationAdapter implements TargetInformationAdapter { + + @Override + public SubmodelElementCollection adapt(TargetInformation targetInformation) { + + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder() + .idShort("targetInformation").build(); + + SubmodelElementList submodelId = new DefaultSubmodelElementList.Builder().idShort("submodelIds").build(); + SubmodelElementList submodelElementId = new DefaultSubmodelElementList.Builder() + .idShort("submodelElementIdShortPaths").build(); + Property typeProperty = new DefaultProperty.Builder().idShort("@type").value("submodel").build(); + + List submodelIds = ((SubmodelTargetInformation) targetInformation).getSubmodelIds().stream() + .map(this::transform).collect(Collectors.toList()); + List submodelElementIds = ((SubmodelTargetInformation) targetInformation) + .getSubmodelElementIdShortPaths().stream().map(this::transform).collect(Collectors.toList()); + submodelId.setValue(submodelIds); + submodelElementId.setValue(submodelElementIds); + + targetInformationSMC.setValue(Arrays.asList(submodelId, submodelElementId, typeProperty)); + + return targetInformationSMC; + } + + @Override + public TargetInformation adapt(SubmodelElementCollection targetInformation) { + + String targetInformationType = getTargetInformationType(targetInformation); + + if (!targetInformationType.equals("submodel")) + throw new InvalidTargetInformationException("The TargetInformation @type: " + targetInformationType + + " is not compatible with " + getClass().getName() + "."); + + SubmodelElement submodelIdSubmodelElement = targetInformation.getValue().stream() + .filter(sme -> sme.getIdShort().equals("submodelIds")).findAny() + .orElseThrow(() -> new InvalidTargetInformationException( + "The TargetInformation defined in the SubmodelElementCollection Rule with id: " + + targetInformation.getIdShort() + " is not compatible with the " + + getClass().getName())); + + SubmodelElement smeIdSubmodelElement = targetInformation.getValue().stream() + .filter(sme -> sme.getIdShort().equals("submodelElementIdShortPaths")).findAny() + .orElseThrow(() -> new InvalidTargetInformationException( + "The TargetInformation defined in the SubmodelElementCollection Rule with id: " + + targetInformation.getIdShort() + " is not compatible with the " + + getClass().getName())); + + if (!(submodelIdSubmodelElement instanceof SubmodelElementList) + || !(smeIdSubmodelElement instanceof SubmodelElementList)) + throw new InvalidTargetInformationException( + "The TargetInformation defined in the SubmodelElementCollection Rule with id: " + + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName()); + + SubmodelElementList submodelIdList = (SubmodelElementList) submodelIdSubmodelElement; + SubmodelElementList smeIdList = (SubmodelElementList) smeIdSubmodelElement; + + List submodelIds = submodelIdList.getValue().stream().map(Property.class::cast).map(Property::getValue) + .collect(Collectors.toList()); + List smeIds = smeIdList.getValue().stream().map(Property.class::cast).map(Property::getValue) + .collect(Collectors.toList()); + + return new SubmodelTargetInformation(submodelIds, smeIds); + } + + private Property transform(String aasId) { + return new DefaultProperty.Builder().value(aasId).build(); + } + + private String getTargetInformationType(SubmodelElementCollection targetInformation) { + + Property typeProperty = (Property) targetInformation.getValue().stream() + .filter(sme -> sme.getIdShort().equals("@type")).findAny() + .orElseThrow(() -> new InvalidTargetInformationException( + "The TargetInformation defined in the SubmodelElementCollection Rule with id: " + + targetInformation.getIdShort() + " does not have @type definition")); + + return typeProperty.getValue(); + } + +} diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/SubmodelTargetInformationAdapterTest.java b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/SubmodelTargetInformationAdapterTest.java new file mode 100644 index 000000000..2d6bed23f --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/SubmodelTargetInformationAdapterTest.java @@ -0,0 +1,185 @@ +/******************************************************************************* + * Copyright (C) 2024 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.submodelrepository.feature.authorization; + +import static org.junit.Assert.*; +import org.eclipse.digitaltwin.aas4j.v3.model.Property; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementCollection; +import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementList; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultProperty; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodelElementCollection; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodelElementList; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetInformation; +import org.eclipse.digitaltwin.basyx.core.exceptions.InvalidTargetInformationException; +import org.eclipse.digitaltwin.basyx.submodelrepository.feature.authorization.rbac.backend.submodel.SubmodelTargetInformationAdapter; + +/** + * Tests {@link AasRegistryTargetInformationAdapter} + * + * @author danish + */ +public class SubmodelTargetInformationAdapterTest { + + private SubmodelTargetInformationAdapter submodelTargetInformationAdapter; + + @Before + public void setUp() { + submodelTargetInformationAdapter = new SubmodelTargetInformationAdapter(); + } + + @Test + public void testAdaptTargetInformationToSubmodelElementCollection() { + + List submodelIds = Arrays.asList("aasId1", "aasId2"); + List smeIds = Arrays.asList("sme1.sme2", "sme4.sme5.sme6", "sme7"); + + TargetInformation targetInformation = new SubmodelTargetInformation(submodelIds, smeIds); + + SubmodelElementCollection result = submodelTargetInformationAdapter.adapt(targetInformation); + + assertEquals("targetInformation", result.getIdShort()); + + List elements = result.getValue(); + assertEquals(3, elements.size()); + + SubmodelElementList submodelIdList = (SubmodelElementList) elements.get(0); + assertEquals("submodelIds", submodelIdList.getIdShort()); + + SubmodelElementList smeIdList = (SubmodelElementList) elements.get(1); + assertEquals("submodelElementIdShortPaths", smeIdList.getIdShort()); + + Property typeProperty = (Property) elements.get(2); + assertEquals("@type", typeProperty.getIdShort()); + + List actualSubmodelIds = submodelIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).map(String::valueOf).collect(Collectors.toList()); + List actualSmeIds = smeIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).map(String::valueOf).collect(Collectors.toList()); + assertEquals(submodelIds, actualSubmodelIds); + assertEquals(smeIds, actualSmeIds); + + String actualType = typeProperty.getValue(); + assertTrue(actualType.equals("submodel")); + } + + @Test + public void testAdaptSubmodelElementCollectionToTargetInformation() { + + List expectedSubmodelIds = Arrays.asList("aasId1", "aasId2"); + List expectedSmeIds = Arrays.asList("sme1.sme2", "sme4.sme5.sme6", "sme7"); + String type = "submodel"; + + List submodelIdProperties = expectedSubmodelIds.stream().map(submodelId -> new DefaultProperty.Builder().value(submodelId).build()).collect(Collectors.toList()); + List smeIdProperties = expectedSmeIds.stream().map(smeId -> new DefaultProperty.Builder().value(smeId).build()).collect(Collectors.toList()); + + SubmodelElementList submodelIdList = new DefaultSubmodelElementList.Builder().idShort("submodelIds").value(submodelIdProperties).build(); + SubmodelElementList smeIdList = new DefaultSubmodelElementList.Builder().idShort("submodelElementIdShortPaths").value(smeIdProperties).build(); + SubmodelElement typeProperty = createTypeProperty(type); + + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation").value(Arrays.asList(submodelIdList, smeIdList, typeProperty)).build(); + + TargetInformation result = submodelTargetInformationAdapter.adapt(targetInformationSMC); + + assertTrue(result instanceof SubmodelTargetInformation); + assertEquals(expectedSubmodelIds, ((SubmodelTargetInformation) result).getSubmodelIds()); + assertEquals(expectedSmeIds, ((SubmodelTargetInformation) result).getSubmodelElementIdShortPaths()); + } + + @Test + public void testAdaptTargetInformationWithEmptyAasIds() { + + List submodelIds = Collections.emptyList(); + List smeIds = Collections.emptyList(); + + TargetInformation targetInformation = new SubmodelTargetInformation(submodelIds, smeIds); + + SubmodelElementCollection result = submodelTargetInformationAdapter.adapt(targetInformation); + + assertEquals("targetInformation", result.getIdShort()); + + List elements = result.getValue(); + assertEquals(3, elements.size()); + + SubmodelElementList submodelIdList = (SubmodelElementList) elements.get(0); + assertEquals("submodelIds", submodelIdList.getIdShort()); + + SubmodelElementList smeIdList = (SubmodelElementList) elements.get(1); + assertEquals("submodelElementIdShortPaths", smeIdList.getIdShort()); + + Property typeProperty = (Property) elements.get(2); + assertEquals("@type", typeProperty.getIdShort()); + + List actualSubmodelIds = submodelIdList.getValue().stream() + .map(Property.class::cast) + .map(Property::getValue) + .map(String::valueOf) + .collect(Collectors.toList()); + assertTrue(actualSubmodelIds.isEmpty()); + + List actualSmeIds = smeIdList.getValue().stream() + .map(Property.class::cast) + .map(Property::getValue) + .map(String::valueOf) + .collect(Collectors.toList()); + assertTrue(actualSmeIds.isEmpty()); + + String actualType = typeProperty.getValue(); + assertTrue(actualType.equals("submodel")); + } + + @Test(expected = InvalidTargetInformationException.class) + public void testAdaptSubmodelElementCollectionWithInvalidStructure() { + + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation") + .value(Collections.singletonList(new DefaultProperty.Builder().idShort("invalidElement").value("value").build())) + .build(); + + submodelTargetInformationAdapter.adapt(targetInformationSMC); + } + + @Test(expected = InvalidTargetInformationException.class) + public void testAdaptSubmodelElementCollectionWithoutSubmodelIds() { + + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation") + .value(Collections.emptyList()) + .build(); + + submodelTargetInformationAdapter.adapt(targetInformationSMC); + } + + private SubmodelElement createTypeProperty(String type) { + return new DefaultProperty.Builder().idShort("@type").value(type).build(); + } + + +} From f0bb5d1101e05c30edeb18507447e5ad6fb35072 Mon Sep 17 00:00:00 2001 From: Mohammad Ghazanfar Ali Danish Date: Thu, 19 Sep 2024 16:11:53 +0200 Subject: [PATCH 02/20] Reverts GitHub CI Signed-off-by: Mohammad Ghazanfar Ali Danish --- .github/workflows/basyx_test.yml | 3 ++- .github/workflows/docker_test.yml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/basyx_test.yml b/.github/workflows/basyx_test.yml index 2df8360ca..90143950c 100644 --- a/.github/workflows/basyx_test.yml +++ b/.github/workflows/basyx_test.yml @@ -1,7 +1,8 @@ name: Build and Test BaSyx on: - workflow_dispatch: + push: + branches: [ temp-feature/filter-dyn-rbac ] pull_request: branches: [ main ] paths-ignore: diff --git a/.github/workflows/docker_test.yml b/.github/workflows/docker_test.yml index 33a81fbce..dcd288655 100644 --- a/.github/workflows/docker_test.yml +++ b/.github/workflows/docker_test.yml @@ -4,7 +4,8 @@ name: Build and Start Docker Images on: - workflow_dispatch: + push: + branches: [ temp-feature/filter-dyn-rbac ] pull_request: branches: [ main ] paths-ignore: From af3ebe8271c06f4cacab1520e18800368b5e3dcf Mon Sep 17 00:00:00 2001 From: Mohammad Ghazanfar Ali Danish Date: Thu, 19 Sep 2024 16:26:06 +0200 Subject: [PATCH 03/20] Attempts CI fix Signed-off-by: Mohammad Ghazanfar Ali Danish --- .../basyx.submodelrepository-feature-authorization/pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/pom.xml b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/pom.xml index bdb7a8008..4290945fd 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/pom.xml +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/pom.xml @@ -68,6 +68,10 @@ org.eclipse.digitaltwin.basyx basyx.authorization.rules.rbac.backend.submodel + + org.eclipse.digitaltwin.basyx + basyx.submodelrepository-client + org.apache.httpcomponents.client5 httpclient5 From 8afbe170ea634d6ed381e0c21e873824ab6701e4 Mon Sep 17 00:00:00 2001 From: Mohammad Ghazanfar Ali Danish Date: Thu, 19 Sep 2024 16:30:23 +0200 Subject: [PATCH 04/20] Reverts GitHub CI Signed-off-by: Mohammad Ghazanfar Ali Danish --- .../basyx.submodelrepository-feature-authorization/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/pom.xml b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/pom.xml index 4290945fd..919ce8e11 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/pom.xml +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/pom.xml @@ -70,7 +70,7 @@ org.eclipse.digitaltwin.basyx - basyx.submodelrepository-client + basyx.submodelservice-client org.apache.httpcomponents.client5 From 10f6622a64949c1b394279eba78295e811a93990 Mon Sep 17 00:00:00 2001 From: Mohammad Ghazanfar Ali Danish Date: Thu, 19 Sep 2024 16:32:21 +0200 Subject: [PATCH 05/20] Reverts GitHub CI Signed-off-by: Mohammad Ghazanfar Ali Danish --- .../basyx.submodelrepository-feature-authorization/pom.xml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/pom.xml b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/pom.xml index 919ce8e11..eb444b10e 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/pom.xml +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/pom.xml @@ -67,11 +67,7 @@ org.eclipse.digitaltwin.basyx basyx.authorization.rules.rbac.backend.submodel - - - org.eclipse.digitaltwin.basyx - basyx.submodelservice-client - + org.apache.httpcomponents.client5 httpclient5 From 4f0819effa404befe54405bef56c0a3eab04e2b2 Mon Sep 17 00:00:00 2001 From: Mohammad Ghazanfar Ali Danish Date: Thu, 19 Sep 2024 16:47:21 +0200 Subject: [PATCH 06/20] Adds shading plugin Signed-off-by: Mohammad Ghazanfar Ali Danish --- .../pom.xml | 53 ++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/pom.xml b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/pom.xml index eb444b10e..69706c046 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/pom.xml +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/pom.xml @@ -14,6 +14,57 @@ BaSyx Submodelrepository feature-authorization BaSyx Submodelrepository feature-authorization + + + + org.apache.maven.plugins + maven-shade-plugin + + + install + + shade + + + + + + basyx.submodelrepository-client + + basyx.submodelrepository-shaded-client + + + + basyx.submodelrepository-core + + basyx.submodelrepository-shaded-core + + + + basyx.submodelservice-core + + basyx.submodelservice-shaded-core + + + + basyx.submodelservice-client + + basyx.submodelservice-shaded-client + + + + + + + + + + org.eclipse.digitaltwin.basyx @@ -67,7 +118,7 @@ org.eclipse.digitaltwin.basyx basyx.authorization.rules.rbac.backend.submodel - + org.apache.httpcomponents.client5 httpclient5 From a72c3d18cc2bcd1c07f496353050badbe446e048 Mon Sep 17 00:00:00 2001 From: Mohammad Ghazanfar Ali Danish Date: Thu, 19 Sep 2024 16:50:34 +0200 Subject: [PATCH 07/20] Adds dependency to parent pom Signed-off-by: Mohammad Ghazanfar Ali Danish --- pom.xml | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 1688f0eb9..cdabbde7e 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,8 @@ dash-licenses-snapshots - https://repo.eclipse.org/content/repositories/dash-licenses-snapshots/ + + https://repo.eclipse.org/content/repositories/dash-licenses-snapshots/ true @@ -390,6 +391,26 @@ 5.15.0 test + + org.eclipse.digitaltwin.basyx + basyx.submodelrepository-client + 2.0.0-milestone-03.1 + + + org.eclipse.digitaltwin.basyx + basyx.submodelrepository-core + 2.0.0-milestone-03.1 + + + org.eclipse.digitaltwin.basyx + basyx.submodelservice-core + 2.0.0-milestone-03.1 + + + org.eclipse.digitaltwin.basyx + basyx.submodelservice-client + 2.0.0-milestone-03.1 + @@ -523,7 +544,8 @@ org.eclipse.digitaltwin.basyx - basyx.submodelrepository-feature-operation-delegation + + basyx.submodelrepository-feature-operation-delegation ${revision} @@ -682,7 +704,8 @@ org.eclipse.digitaltwin.basyx - basyx.conceptdescriptionrepository-feature-authorization + + basyx.conceptdescriptionrepository-feature-authorization ${revision} @@ -907,7 +930,8 @@ org.eclipse.digitaltwin.basyx - basyx.submodelrepository-feature-operation-delegation + + basyx.submodelrepository-feature-operation-delegation ${revision} tests @@ -1091,7 +1115,8 @@ org.eclipse.digitaltwin.basyx - basyx.conceptdescriptionrepository-feature-authorization + + basyx.conceptdescriptionrepository-feature-authorization ${revision} tests From 86c66752656d11a432ead80fc9ce79624f8b004d Mon Sep 17 00:00:00 2001 From: Mohammad Ghazanfar Ali Danish Date: Thu, 19 Sep 2024 17:28:18 +0200 Subject: [PATCH 08/20] Excludes SM dependencies Signed-off-by: Mohammad Ghazanfar Ali Danish --- .../pom.xml | 69 +++++-------------- pom.xml | 35 ++-------- 2 files changed, 23 insertions(+), 81 deletions(-) diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/pom.xml b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/pom.xml index 69706c046..e701fdb00 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/pom.xml +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/pom.xml @@ -14,57 +14,6 @@ BaSyx Submodelrepository feature-authorization BaSyx Submodelrepository feature-authorization - - - - org.apache.maven.plugins - maven-shade-plugin - - - install - - shade - - - - - - basyx.submodelrepository-client - - basyx.submodelrepository-shaded-client - - - - basyx.submodelrepository-core - - basyx.submodelrepository-shaded-core - - - - basyx.submodelservice-core - - basyx.submodelservice-shaded-core - - - - basyx.submodelservice-client - - basyx.submodelservice-shaded-client - - - - - - - - - - org.eclipse.digitaltwin.basyx @@ -118,6 +67,24 @@ org.eclipse.digitaltwin.basyx basyx.authorization.rules.rbac.backend.submodel + + + org.eclipse.digitaltwin.basyx + basyx.submodelrepository-client + + + org.eclipse.digitaltwin.basyx + basyx.submodelrepository-core + + + org.eclipse.digitaltwin.basyx + basyx.submodelservice-core + + + org.eclipse.digitaltwin.basyx + basyx.submodelservice-client + + org.apache.httpcomponents.client5 diff --git a/pom.xml b/pom.xml index cdabbde7e..1688f0eb9 100644 --- a/pom.xml +++ b/pom.xml @@ -85,8 +85,7 @@ dash-licenses-snapshots - - https://repo.eclipse.org/content/repositories/dash-licenses-snapshots/ + https://repo.eclipse.org/content/repositories/dash-licenses-snapshots/ true @@ -391,26 +390,6 @@ 5.15.0 test - - org.eclipse.digitaltwin.basyx - basyx.submodelrepository-client - 2.0.0-milestone-03.1 - - - org.eclipse.digitaltwin.basyx - basyx.submodelrepository-core - 2.0.0-milestone-03.1 - - - org.eclipse.digitaltwin.basyx - basyx.submodelservice-core - 2.0.0-milestone-03.1 - - - org.eclipse.digitaltwin.basyx - basyx.submodelservice-client - 2.0.0-milestone-03.1 - @@ -544,8 +523,7 @@ org.eclipse.digitaltwin.basyx - - basyx.submodelrepository-feature-operation-delegation + basyx.submodelrepository-feature-operation-delegation ${revision} @@ -704,8 +682,7 @@ org.eclipse.digitaltwin.basyx - - basyx.conceptdescriptionrepository-feature-authorization + basyx.conceptdescriptionrepository-feature-authorization ${revision} @@ -930,8 +907,7 @@ org.eclipse.digitaltwin.basyx - - basyx.submodelrepository-feature-operation-delegation + basyx.submodelrepository-feature-operation-delegation ${revision} tests @@ -1115,8 +1091,7 @@ org.eclipse.digitaltwin.basyx - - basyx.conceptdescriptionrepository-feature-authorization + basyx.conceptdescriptionrepository-feature-authorization ${revision} tests From 2b14d937ce2e8c3ae868fa6eb3bc90952147b76f Mon Sep 17 00:00:00 2001 From: Mohammad Ghazanfar Ali Danish Date: Fri, 20 Sep 2024 09:44:08 +0200 Subject: [PATCH 09/20] Fixes the TargetInfoAdapter Bean in Aas Env Signed-off-by: Mohammad Ghazanfar Ali Danish --- ...AuthorizedAasEnvironmentConfiguration.java | 10 + .../src/main/resources/application.properties | 17 +- ...eptDescriptionRepositoryConfiguration.java | 2 +- ci/docker-compose.yml | 122 +++--- ci/keycloak/realm/BaSyx-realm.json | 400 +++++++++++------- ci/rules/sm-repo-rbac_rules.json | 155 +++++++ ci/rules/sm-repo.properties | 10 + 7 files changed, 483 insertions(+), 233 deletions(-) create mode 100644 ci/rules/sm-repo-rbac_rules.json create mode 100644 ci/rules/sm-repo.properties diff --git a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/AuthorizedAasEnvironmentConfiguration.java b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/AuthorizedAasEnvironmentConfiguration.java index 79475434a..6dab7228b 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/AuthorizedAasEnvironmentConfiguration.java +++ b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/AuthorizedAasEnvironmentConfiguration.java @@ -27,6 +27,7 @@ import org.eclipse.digitaltwin.basyx.aasenvironment.AasEnvironment; import org.eclipse.digitaltwin.basyx.aasenvironment.feature.authorization.rbac.AasEnvironmentTargetPermissionVerifier; +import org.eclipse.digitaltwin.basyx.aasenvironment.feature.authorization.rbac.backend.submodel.AasEnvironmentTargetInformationAdapter; import org.eclipse.digitaltwin.basyx.aasenvironment.preconfiguration.AasEnvironmentPreconfigurationLoader; import org.eclipse.digitaltwin.basyx.authorization.CommonAuthorizationProperties; import org.eclipse.digitaltwin.basyx.authorization.rbac.RbacPermissionResolver; @@ -34,6 +35,8 @@ import org.eclipse.digitaltwin.basyx.authorization.rbac.SimpleRbacPermissionResolver; import org.eclipse.digitaltwin.basyx.authorization.rbac.RoleProvider; import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetPermissionVerifier; +import org.eclipse.digitaltwin.basyx.authorization.rules.rbac.backend.submodel.TargetInformationAdapter; +import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.feature.authorization.rbac.backend.submodel.CDTargetInformationAdapter; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -67,5 +70,12 @@ public RbacPermissionResolver getAasEnvironment public static AasEnvironmentPreconfigurationLoader getAuthorizedAasEnvironmentPreconfigurationLoader(ResourceLoader resourceLoader, List pathsToLoad) { return new AuthorizedAASEnvironmentPreconfigurationLoader(resourceLoader, pathsToLoad); } + + @Bean + @Primary + public TargetInformationAdapter getAasEnvInformationAdapter() { + + return new AasEnvironmentTargetInformationAdapter(); + } } diff --git a/basyx.aasenvironment/basyx.aasenvironment.component/src/main/resources/application.properties b/basyx.aasenvironment/basyx.aasenvironment.component/src/main/resources/application.properties index aff37727d..aa38ac083 100644 --- a/basyx.aasenvironment/basyx.aasenvironment.component/src/main/resources/application.properties +++ b/basyx.aasenvironment/basyx.aasenvironment.component/src/main/resources/application.properties @@ -33,11 +33,18 @@ basyx.backend = InMemory #################################################################################### # Authorization #################################################################################### -# basyx.feature.authorization.enabled = true -# basyx.feature.authorization.type = rbac -# basyx.feature.authorization.jwtBearerTokenProvider = keycloak -# basyx.feature.authorization.rbac.file = classpath:rbac_rules.json -# spring.security.oauth2.resourceserver.jwt.issuer-uri= http://localhost:9097/realms/BaSyx +basyx.feature.authorization.enabled = true +basyx.feature.authorization.type = rbac +basyx.feature.authorization.jwtBearerTokenProvider = keycloak +basyx.feature.authorization.rbac.file = classpath:rbac_rules.json +spring.security.oauth2.resourceserver.jwt.issuer-uri= http://localhost:9097/realms/BaSyx + +basyx.feature.authorization.rules.backend=Submodel +basyx.feature.authorization.rules.backend.submodel.authorization.endpoint=http://localhost:8055/submodels/N0E3MTA0QkRBQjU3RTE4NA== +basyx.feature.authorization.rules.backend.submodel.authorization.token-endpoint=http://localhost:9097/realms/BaSyx/protocol/openid-connect/token +basyx.feature.authorization.rules.backend.submodel.authorization.grant-type = CLIENT_CREDENTIALS +basyx.feature.authorization.rules.backend.submodel.authorization.client-id=workstation-1 +basyx.feature.authorization.rules.backend.submodel.authorization.client-secret=nY0mjyECF60DGzNmQUjL81XurSl8etom ## This is for preconfiguration of a secured AAS Environment # basyx.aasenvironment.authorization.preconfiguration.token-endpoint=http://localhost:9097/realms/BaSyx/protocol/openid-connect/token diff --git a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/AuthorizedConceptDescriptionRepositoryConfiguration.java b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/AuthorizedConceptDescriptionRepositoryConfiguration.java index f07e293fb..97193f33a 100644 --- a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/AuthorizedConceptDescriptionRepositoryConfiguration.java +++ b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/AuthorizedConceptDescriptionRepositoryConfiguration.java @@ -59,7 +59,7 @@ public RbacPermissionResolver getCDPermissi } @Bean - public TargetInformationAdapter getAasRegistryTargetInformationAdapter() { + public TargetInformationAdapter getCDTargetInformationAdapter() { return new CDTargetInformationAdapter(); } diff --git a/ci/docker-compose.yml b/ci/docker-compose.yml index 92e30a58b..6a8119d17 100644 --- a/ci/docker-compose.yml +++ b/ci/docker-compose.yml @@ -19,60 +19,60 @@ services: networks: - basyx-java-server-sdk - zookeeper: - image: confluentinc/cp-zookeeper:7.5.2 - environment: - ZOOKEEPER_CLIENT_PORT: 2181 - ZOOKEEPER_TICK_TIME: 2000 - healthcheck: - test: nc -z localhost 2181 || exit -1 - interval: 10s - timeout: 5s - retries: 10 - start_period: 10s - networks: - - basyx-java-server-sdk + # zookeeper: + # image: confluentinc/cp-zookeeper:7.5.2 + # environment: + # ZOOKEEPER_CLIENT_PORT: 2181 + # ZOOKEEPER_TICK_TIME: 2000 + # healthcheck: + # test: nc -z localhost 2181 || exit -1 + # interval: 10s + # timeout: 5s + # retries: 10 + # start_period: 10s + # networks: + # - basyx-java-server-sdk - akhq: - image: tchiotludo/akhq:0.24.0 - container_name: akhq - environment: - AKHQ_CONFIGURATION: | - akhq: - connections: - docker-kafka-server: - properties: - bootstrap.servers: "kafka:29092" - ports: - - 8086:8080 - restart: always - depends_on: - - kafka - networks: - - basyx-java-server-sdk + # akhq: + # image: tchiotludo/akhq:0.24.0 + # container_name: akhq + # environment: + # AKHQ_CONFIGURATION: | + # akhq: + # connections: + # docker-kafka-server: + # properties: + # bootstrap.servers: "kafka:29092" + # ports: + # - 8086:8080 + # restart: always + # depends_on: + # - kafka + # networks: + # - basyx-java-server-sdk - kafka: - image: confluentinc/cp-kafka:7.5.2 - ports: - - 9092:9092 - environment: - KAFKA_BROKER_ID: 1 - KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 - KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092 - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT - KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 - healthcheck: - test: nc -z localhost 9092 || exit -1 - interval: 5s - timeout: 10s - retries: 10 - start_period: 15s - depends_on: - zookeeper: - condition: service_healthy - networks: - - basyx-java-server-sdk + # kafka: + # image: confluentinc/cp-kafka:7.5.2 + # ports: + # - 9092:9092 + # environment: + # KAFKA_BROKER_ID: 1 + # KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 + # KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092 + # KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT + # KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT + # KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + # healthcheck: + # test: nc -z localhost 9092 || exit -1 + # interval: 5s + # timeout: 10s + # retries: 10 + # start_period: 15s + # depends_on: + # zookeeper: + # condition: service_healthy + # networks: + # - basyx-java-server-sdk aas-registry-log-mem: image: eclipsebasyx/aas-registry-log-mem:$BASYX_VERSION @@ -141,17 +141,9 @@ services: container_name: configuration-sm-repo ports: - "8055:8081" - environment: - SERVER_SERVLET_CONTEXT_PATH: / - BASYX_CORS_ALLOWED_ORIGINS: '*' - BASYX_CORS_ALLOWED_METHODS: GET,POST,PATCH,DELETE,PUT,OPTIONS,HEAD - BASYX_FEATURE_AUTHORIZATION_ENABLED: true - BASYX_FEATURE_AUTHORIZATION_TYPE: rbac - BASYX_FEATURE_AUTHORIZATION_JWTBEARERTOKENPROVIDER: keycloak - SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: http://keycloak:8080/realms/BaSyx - BASYX_FEATURE_AUTHORIZATION_RBAC_FILE: file:/rbac/rbac_rules.json volumes: - - ./keycloak/rules/rbac_rules-conf-sm-repo.json:/rbac/rbac_rules.json:ro + - ./rules/sm-repo-rbac_rules.json:/application/rbac_rules.json + - ./rules/sm-repo.properties:/application/application.properties restart: always networks: - basyx-java-server-sdk @@ -174,17 +166,17 @@ services: build: context: ./keycloak dockerfile: Dockerfile.keycloak - container_name: keycloak-fixed-uri + container_name: keycloak-fixed environment: KC_HOSTNAME: localhost - KC_SPI_INITIALIZER_ISSUER_BASE_URI: http://keycloak:8080 + KC_SPI_INITIALIZER_ISSUER_BASE_URI: http://keycloak-fixed:8080 KEYCLOAK_ADMIN: admin KEYCLOAK_ADMIN_PASSWORD: keycloak-admin command: ["start-dev", "--import-realm"] ports: - 9097:8080 volumes: - - ./keycloak/realm:/opt/keycloak/data/import:ro + - ./keycloak/realm:/opt/keycloak/data/import networks: - basyx-java-server-sdk diff --git a/ci/keycloak/realm/BaSyx-realm.json b/ci/keycloak/realm/BaSyx-realm.json index b893decf7..7680f78b7 100644 --- a/ci/keycloak/realm/BaSyx-realm.json +++ b/ci/keycloak/realm/BaSyx-realm.json @@ -37,6 +37,7 @@ "editUsernameAllowed" : false, "bruteForceProtected" : false, "permanentLockout" : false, + "maxTemporaryLockouts" : 0, "maxFailureWaitSeconds" : 900, "minimumQuickLoginWaitSeconds" : 60, "waitIncrementSeconds" : 60, @@ -511,14 +512,14 @@ "attributes" : { } } ], "basyx-client-api" : [ { - "id" : "2dd4b9b1-748f-43f3-b62b-048c92ae79d1", + "id" : "2dd4b9b1-748f-43f3-b62b-048c92ae79d1", "name" : "basyx-creator", "description" : "", "composite" : false, "clientRole" : true, "containerId" : "3fb3e5e5-dbd8-4d51-b964-746c5b2181a4", "attributes" : { } - }, { + }, { "id" : "ba077409-1b5d-4fc8-b20e-10389507fb75", "name" : "basyx-admin", "description" : "", @@ -535,6 +536,7 @@ "containerId" : "3fb3e5e5-dbd8-4d51-b964-746c5b2181a4", "attributes" : { } } ], + "basyx-web-ui" : [ ], "security-admin-console" : [ ], "admin-cli" : [ ], "account-console" : [ ], @@ -621,7 +623,7 @@ "clientRole" : true, "containerId" : "049e1323-6efb-4543-bc52-566cd292732a", "attributes" : { } - } ], + } ], "basyx-demo" : [ ], "workstation-1" : [ { "id" : "914a18c6-4f14-418f-99e0-bfdcf604ac01", @@ -629,7 +631,7 @@ "composite" : false, "clientRole" : true, "containerId" : "96031210-9e6c-4252-a22e-e81a47e30d65", - "attributes" : { } + "attributes" : { } } ] } }, @@ -637,10 +639,10 @@ "id" : "606a14f2-6114-4fd3-9ca6-4a53514fffb9", "name" : "BaSyxGroup", "path" : "/BaSyxGroup", + "subGroups" : [ ], "attributes" : { }, "realmRoles" : [ "basyx-deleter", "basyx-creator", "basyx-asset-updater" ], - "clientRoles" : { }, - "subGroups" : [ ] + "clientRoles" : { } } ], "defaultRole" : { "id" : "797d2956-a895-4171-ab44-2fc9dbcf7f4c", @@ -658,7 +660,8 @@ "otpPolicyLookAheadWindow" : 1, "otpPolicyPeriod" : 30, "otpPolicyCodeReusable" : false, - "otpSupportedApplications" : [ "totpAppMicrosoftAuthenticatorName", "totpAppGoogleName", "totpAppFreeOTPName" ], + "otpSupportedApplications" : [ "totpAppFreeOTPName", "totpAppGoogleName", "totpAppMicrosoftAuthenticatorName" ], + "localizationTexts" : { }, "webAuthnPolicyRpEntityName" : "keycloak", "webAuthnPolicySignatureAlgorithms" : [ "ES256" ], "webAuthnPolicyRpId" : "", @@ -669,6 +672,7 @@ "webAuthnPolicyCreateTimeout" : 0, "webAuthnPolicyAvoidSameAuthenticatorRegister" : false, "webAuthnPolicyAcceptableAaguids" : [ ], + "webAuthnPolicyExtraOrigins" : [ ], "webAuthnPolicyPasswordlessRpEntityName" : "keycloak", "webAuthnPolicyPasswordlessSignatureAlgorithms" : [ "ES256" ], "webAuthnPolicyPasswordlessRpId" : "", @@ -679,15 +683,16 @@ "webAuthnPolicyPasswordlessCreateTimeout" : 0, "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister" : false, "webAuthnPolicyPasswordlessAcceptableAaguids" : [ ], + "webAuthnPolicyPasswordlessExtraOrigins" : [ ], "users" : [ { "id" : "856b093b-ef9f-4bd0-92ca-662f680c73cc", - "createdTimestamp" : 1713968958846, "username" : "basyx.aas.discoverer", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1713968958846, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "bc4944be-c8e2-4c91-81cc-9478a0795906", "type" : "password", @@ -703,13 +708,13 @@ "groups" : [ ] }, { "id" : "aef2b331-f694-4503-b0da-1412c77842ba", - "createdTimestamp" : 1702179260496, "username" : "basyx.asset.updater", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1702179260496, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "24db17af-1244-4cea-8af8-1047adcd0753", "type" : "password", @@ -725,13 +730,13 @@ "groups" : [ ] }, { "id" : "4fc75aa9-4745-4bec-846e-de5dbd665b7c", - "createdTimestamp" : 1702179349503, "username" : "basyx.asset.updater.2", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1702179349503, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "db64f531-b8ab-4e33-b7b9-e3ba0e3677d4", "type" : "password", @@ -747,13 +752,13 @@ "groups" : [ ] }, { "id" : "bcbd95b2-2ec4-42a6-9b1d-39dabf0454c3", - "createdTimestamp" : 1713969008326, "username" : "basyx.assetid.creator", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1713969008326, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "81b26fa2-4eda-45d7-a8be-4b6d64b3d813", "type" : "password", @@ -769,13 +774,13 @@ "groups" : [ ] }, { "id" : "2def798c-7547-42fe-8915-be493d740005", - "createdTimestamp" : 1713969038565, "username" : "basyx.assetid.deleter", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1713969038565, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "fa285b2c-9a2d-4400-a524-508b0a16f4c3", "type" : "password", @@ -791,13 +796,13 @@ "groups" : [ ] }, { "id" : "3527f121-116c-429b-bf0d-78bf4a8b5abe", - "createdTimestamp" : 1713968905507, "username" : "basyx.assetid.discoverer", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1713968905507, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "4f3427f8-9da2-4372-a54b-c1e54dc8da68", "type" : "password", @@ -813,13 +818,13 @@ "groups" : [ ] }, { "id" : "f48119f5-7dff-46af-b9db-3ee96cd52550", - "createdTimestamp" : 1702032555719, "username" : "basyx.creator", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "BaSyx", "lastName" : "creator", + "emailVerified" : false, + "createdTimestamp" : 1702032555719, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "393aa50c-70ed-4676-b5d0-b4c6cf930272", "type" : "password", @@ -835,13 +840,13 @@ "groups" : [ ] }, { "id" : "40858874-f6e7-48c8-9667-d585d7c27b57", - "createdTimestamp" : 1702032602188, "username" : "basyx.deleter", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "BaSyx", "lastName" : "Deleter", + "emailVerified" : false, + "createdTimestamp" : 1702032602188, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "ee9224dd-6277-4c3d-8d88-b595d66f25c7", "type" : "password", @@ -857,13 +862,13 @@ "groups" : [ ] }, { "id" : "fcf7ea95-875e-42ef-afe1-f9b6008cbaf9", - "createdTimestamp" : 1702161686852, "username" : "basyx.deleter.2", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1702161686852, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "1b8d0b8c-1f65-43e0-9178-785533746684", "type" : "password", @@ -879,13 +884,13 @@ "groups" : [ ] }, { "id" : "8e820205-13fa-4d61-9513-99f717c15f73", - "createdTimestamp" : 1705326903328, "username" : "basyx.executor", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1705326903328, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "3e891de4-74e6-46cb-840f-6681af0ce397", "type" : "password", @@ -901,13 +906,13 @@ "groups" : [ ] }, { "id" : "3d645000-277e-46f5-b421-85f1fdb064b5", - "createdTimestamp" : 1705326932748, "username" : "basyx.executor.2", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1705326932748, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "d21c920f-5dd0-4ce7-ad2e-737a841bb1f8", "type" : "password", @@ -923,13 +928,13 @@ "groups" : [ ] }, { "id" : "c86047c6-3ab8-4f47-86ba-b26ea80d1986", - "createdTimestamp" : 1705398014689, "username" : "basyx.file.sme.reader", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1705398014689, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "bd374dc3-4fec-4232-be91-a3f5608e373e", "type" : "password", @@ -945,13 +950,13 @@ "groups" : [ ] }, { "id" : "da7acf36-1574-4c0a-aa5b-65829a512b60", - "createdTimestamp" : 1705398962710, "username" : "basyx.file.sme.updater", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1705398962710, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "e5fda50d-9f52-4f60-a917-31a85fc275a7", "type" : "password", @@ -967,13 +972,13 @@ "groups" : [ ] }, { "id" : "abfaa545-3c2f-4ac6-9f41-7c166613ea35", - "createdTimestamp" : 1702032528855, "username" : "basyx.reader", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "BaSyx", "lastName" : "Reader", + "emailVerified" : false, + "createdTimestamp" : 1702032528855, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "eea587f5-439d-487d-85d0-587b226f0683", "type" : "password", @@ -992,13 +997,13 @@ "groups" : [ ] }, { "id" : "37e1ba01-3b9c-428c-b0fe-be85970bf1d9", - "createdTimestamp" : 1702158534562, "username" : "basyx.reader.2", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1702158534562, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "3237f89d-f9f0-4b70-9a08-d31d995c6948", "type" : "password", @@ -1014,13 +1019,13 @@ "groups" : [ ] }, { "id" : "2d7a7bdb-c8d3-4684-8c06-8873aabd9968", - "createdTimestamp" : 1707983800892, "username" : "basyx.reader.serialization", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1707983800892, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "c35c2724-8ae9-4193-bd25-e5245df77f4d", "type" : "password", @@ -1036,13 +1041,13 @@ "groups" : [ ] }, { "id" : "113691d0-fc03-4607-8b64-97b646bded1f", - "createdTimestamp" : 1707983824403, "username" : "basyx.reader.serialization.2", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1707983824403, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "86bd4fdc-077c-4546-b80a-f51788f15b56", "type" : "password", @@ -1058,13 +1063,13 @@ "groups" : [ ] }, { "id" : "3988486a-51eb-447b-bc87-354e5b724c76", - "createdTimestamp" : 1705306854299, "username" : "basyx.sme.reader", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1705306854299, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "d70787d1-9834-43d6-a10d-bca854847fec", "type" : "password", @@ -1080,13 +1085,13 @@ "groups" : [ ] }, { "id" : "ed921577-8b07-439e-af66-cc3579a276ec", - "createdTimestamp" : 1705310445991, "username" : "basyx.sme.reader.2", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1705310445991, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "62446675-7b7e-40e7-bf6c-b375d8542a12", "type" : "password", @@ -1102,13 +1107,13 @@ "groups" : [ ] }, { "id" : "60f02977-d033-43ed-8171-8ac44d14c62f", - "createdTimestamp" : 1705312271325, "username" : "basyx.sme.updater", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1705312271325, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "d224c0bc-7c5d-426d-8e96-9f4dfb141eab", "type" : "password", @@ -1124,13 +1129,13 @@ "groups" : [ ] }, { "id" : "9fd79b98-a230-43c8-a7ff-f88ee381c58c", - "createdTimestamp" : 1705315980352, "username" : "basyx.sme.updater.2", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1705315980352, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "eca8536c-fbf7-49dd-a370-405b00336965", "type" : "password", @@ -1146,13 +1151,13 @@ "groups" : [ ] }, { "id" : "20336dcf-0c1f-4344-b31b-f26048fc7faa", - "createdTimestamp" : 1705324670982, "username" : "basyx.sme.updater.3", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1705324670982, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "d1652462-350a-42e0-9a3e-e04cfa258237", "type" : "password", @@ -1168,13 +1173,13 @@ "groups" : [ ] }, { "id" : "44f291ad-a0de-4035-9938-a092faf810b5", - "createdTimestamp" : 1702032579778, "username" : "basyx.updater", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "BaSyx", "lastName" : "Updater", + "emailVerified" : false, + "createdTimestamp" : 1702032579778, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "28e62a3a-790d-477f-b139-b0ea943abfd7", "type" : "password", @@ -1190,13 +1195,13 @@ "groups" : [ ] }, { "id" : "9bf6b5c8-194f-4672-9313-9f3823cb3019", - "createdTimestamp" : 1702161564532, "username" : "basyx.updater.2", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1702161564532, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "59ef29d6-6dcf-42e6-b946-33d50335e0d1", "type" : "password", @@ -1212,13 +1217,13 @@ "groups" : [ ] }, { "id" : "cb7df854-827d-4d34-a01f-33cdf07f5cea", - "createdTimestamp" : 1708702290219, "username" : "basyx.uploader", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1708702290219, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "7df6170e-1631-434d-8380-62f750c563cf", "type" : "password", @@ -1234,13 +1239,13 @@ "groups" : [ ] }, { "id" : "3c94e1e0-caac-48c3-a31c-c9f555233a46", - "createdTimestamp" : 1708932762171, "username" : "basyx.uploader.2", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1708932762171, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "0d4049cb-293b-4f76-b82b-5ba9ee4530f2", "type" : "password", @@ -1256,13 +1261,13 @@ "groups" : [ ] }, { "id" : "c1a592bd-536f-4a3e-8193-c17d479814f3", - "createdTimestamp" : 1708934121191, "username" : "basyx.uploader.3", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1708934121191, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "7cd8fd3f-8c83-43b9-9fb7-6203e7c7376c", "type" : "password", @@ -1278,13 +1283,13 @@ "groups" : [ ] }, { "id" : "f3ec1793-3d62-41c3-ad34-b7b29ac88528", - "createdTimestamp" : 1702030619322, "username" : "bob.maintainer", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "Bob", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1702030619322, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "a45e5184-e7f3-41bc-be25-c97197852301", "type" : "password", @@ -1300,13 +1305,13 @@ "groups" : [ ] }, { "id" : "e75ac9e8-7093-4898-a203-d9839f854944", - "createdTimestamp" : 1702030567684, "username" : "jane.doe", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "Jane", "lastName" : "Doe", + "emailVerified" : false, + "createdTimestamp" : 1702030567684, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "d1e97a9f-42b6-43d0-a070-1216d03a64b7", "type" : "password", @@ -1322,13 +1327,13 @@ "groups" : [ ] }, { "id" : "caf7499e-4f3d-45fa-9246-99ea8f8b5c94", - "createdTimestamp" : 1701764678734, "username" : "john", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "", "lastName" : "", + "emailVerified" : false, + "createdTimestamp" : 1701764678734, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "136b209f-3b75-45d9-b448-e1ec93dc7ea4", "type" : "password", @@ -1347,13 +1352,13 @@ "groups" : [ ] }, { "id" : "fb833c1b-3a7d-4224-9ab1-672e7203bab5", - "createdTimestamp" : 1702030523698, "username" : "john.doe", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "John", "lastName" : "Doe", + "emailVerified" : false, + "createdTimestamp" : 1702030523698, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "b8b3d5cf-4fa3-46ed-8a3e-18875acecff0", "type" : "password", @@ -1369,13 +1374,13 @@ "groups" : [ ] }, { "id" : "9045c192-428a-4fff-bf7a-b9a9ac16742f", - "createdTimestamp" : 1702030666980, "username" : "paul.visitor", - "enabled" : true, - "totp" : false, - "emailVerified" : false, "firstName" : "Paul", "lastName" : "Visitor", + "emailVerified" : false, + "createdTimestamp" : 1702030666980, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "81d4c206-c0a0-4ee6-9fcb-19f755e66150", "type" : "password", @@ -1389,18 +1394,18 @@ "realmRoles" : [ "visitor", "default-roles-basyx" ], "notBefore" : 0, "groups" : [ ] - }, { + }, { "id" : "a19abcac-34d5-46bb-a604-b07dc234e80f", - "createdTimestamp" : 1715582034760, "username" : "service-account-workstation-1", + "emailVerified" : false, + "createdTimestamp" : 1715582034760, "enabled" : true, "totp" : false, - "emailVerified" : false, "serviceAccountClientId" : "workstation-1", "credentials" : [ ], "disableableCredentialTypes" : [ ], "requiredActions" : [ ], - "realmRoles" : [ "basyx-reader", "basyx-deleter", "basyx-updater", "basyx-creator", "default-roles-basyx", "admin" ], + "realmRoles" : [ "basyx-reader", "basyx-deleter", "basyx-updater", "admin", "basyx-creator", "default-roles-basyx" ], "clientRoles" : { "workstation-1" : [ "uma_protection" ] }, @@ -1408,14 +1413,14 @@ "groups" : [ "/BaSyxGroup" ] }, { "id" : "77957093-d593-44b4-b4e9-bc365e840cdd", - "createdTimestamp" : 1715539640949, "username" : "test.user", - "enabled" : true, - "totp" : false, - "emailVerified" : true, "firstName" : "Test", "lastName" : "User", "email" : "test.user@gmail.com", + "emailVerified" : true, + "createdTimestamp" : 1715539640949, + "enabled" : true, + "totp" : false, "credentials" : [ { "id" : "11672ccf-38b7-421b-8d01-755e0f2197da", "type" : "password", @@ -1428,7 +1433,7 @@ "requiredActions" : [ ], "realmRoles" : [ "default-roles-basyx" ], "notBefore" : 0, - "groups" : [ "/BaSyxGroup" ] + "groups" : [ "/BaSyxGroup" ] } ], "scopeMappings" : [ { "clientScope" : "offline_access", @@ -1575,7 +1580,7 @@ "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ], "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] }, { - "id" : "615f29de-5f3d-4384-8b71-7351ff2c2a32", + "id" : "615f29de-5f3d-4384-8b71-7351ff2c2a32", "clientId" : "basyx-demo", "name" : "", "description" : "", @@ -1601,9 +1606,10 @@ "protocol" : "openid-connect", "attributes" : { "oidc.ciba.grant.enabled" : "false", - "oauth2.device.authorization.grant.enabled" : "false", "client.secret.creation.time" : "1716897911", "backchannel.logout.session.required" : "true", + "post.logout.redirect.uris" : "+", + "oauth2.device.authorization.grant.enabled" : "false", "backchannel.logout.revoke.offline.tokens" : "false" }, "authenticationFlowBindingOverrides" : { }, @@ -1611,7 +1617,44 @@ "nodeReRegistrationTimeout" : -1, "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ], "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] - }, { + }, { + "id" : "047f9c4e-3895-4562-9d8d-87cf3b1c7c05", + "clientId" : "basyx-web-ui", + "name" : "", + "description" : "", + "rootUrl" : "", + "adminUrl" : "", + "baseUrl" : "", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "http://localhost:3000/*" ], + "webOrigins" : [ "http://localhost:3000" ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : true, + "directAccessGrantsEnabled" : true, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : true, + "protocol" : "openid-connect", + "attributes" : { + "oidc.ciba.grant.enabled" : "false", + "backchannel.logout.session.required" : "true", + "post.logout.redirect.uris" : "http://localhost:3000/*", + "oauth2.device.authorization.grant.enabled" : "true", + "display.on.consent.screen" : "false", + "backchannel.logout.revoke.offline.tokens" : "false" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : true, + "nodeReRegistrationTimeout" : -1, + "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { "id" : "45f21c3d-4e85-466f-984f-d7bd47392453", "clientId" : "broker", "name" : "${client_broker}", @@ -1713,7 +1756,7 @@ } ], "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ], "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] - }, { + }, { "id" : "96031210-9e6c-4252-a22e-e81a47e30d65", "clientId" : "workstation-1", "name" : "Workstation 1", @@ -1741,9 +1784,10 @@ "protocol" : "openid-connect", "attributes" : { "oidc.ciba.grant.enabled" : "false", - "oauth2.device.authorization.grant.enabled" : "false", "client.secret.creation.time" : "1715582034", "backchannel.logout.session.required" : "true", + "post.logout.redirect.uris" : "+", + "oauth2.device.authorization.grant.enabled" : "false", "backchannel.logout.revoke.offline.tokens" : "false" }, "authenticationFlowBindingOverrides" : { }, @@ -1757,6 +1801,7 @@ "consentRequired" : false, "config" : { "user.session.note" : "clientAddress", + "userinfo.token.claim" : "true", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "clientAddress", @@ -1770,6 +1815,7 @@ "consentRequired" : false, "config" : { "user.session.note" : "clientHost", + "userinfo.token.claim" : "true", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "clientHost", @@ -1783,6 +1829,7 @@ "consentRequired" : false, "config" : { "user.session.note" : "client_id", + "userinfo.token.claim" : "true", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "client_id", @@ -1790,7 +1837,15 @@ } } ], "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ], - "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ], + "authorizationSettings" : { + "allowRemoteResourceManagement" : true, + "policyEnforcementMode" : "ENFORCING", + "resources" : [ ], + "policies" : [ ], + "scopes" : [ ], + "decisionStrategy" : "UNANIMOUS" + } } ], "clientScopes" : [ { "id" : "e0f355da-f9ff-4104-b305-043b0188747b", @@ -2288,7 +2343,7 @@ "subType" : "anonymous", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "oidc-sha256-pairwise-sub-mapper", "saml-role-list-mapper", "oidc-usermodel-property-mapper", "oidc-full-name-mapper", "oidc-address-mapper", "saml-user-attribute-mapper", "saml-user-property-mapper", "oidc-usermodel-attribute-mapper" ] + "allowed-protocol-mapper-types" : [ "saml-user-attribute-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-address-mapper", "oidc-full-name-mapper", "oidc-usermodel-property-mapper", "saml-role-list-mapper", "oidc-usermodel-attribute-mapper", "saml-user-property-mapper" ] } }, { "id" : "7256d195-1e91-4f63-a9c4-6bef95243a92", @@ -2325,7 +2380,7 @@ "subType" : "authenticated", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper", "saml-user-property-mapper", "oidc-usermodel-property-mapper", "saml-role-list-mapper", "oidc-full-name-mapper", "oidc-address-mapper", "saml-user-attribute-mapper" ] + "allowed-protocol-mapper-types" : [ "saml-user-attribute-mapper", "oidc-usermodel-property-mapper", "oidc-address-mapper", "oidc-full-name-mapper", "saml-role-list-mapper", "oidc-usermodel-attribute-mapper", "saml-user-property-mapper", "oidc-sha256-pairwise-sub-mapper" ] } }, { "id" : "face2c9e-4d23-44e2-9a09-74e1d8448bd3", @@ -2351,6 +2406,14 @@ "allow-default-scopes" : [ "true" ] } } ], + "org.keycloak.userprofile.UserProfileProvider" : [ { + "id" : "2997d5f7-8adc-453d-b672-3d4f01f833ba", + "providerId" : "declarative-user-profile", + "subComponents" : { }, + "config" : { + "kc.user.profile.config" : [ "{\"attributes\":[{\"name\":\"username\",\"displayName\":\"${username}\",\"validations\":{\"length\":{\"min\":3,\"max\":255},\"username-prohibited-characters\":{},\"up-username-not-idn-homograph\":{}},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"email\",\"displayName\":\"${email}\",\"validations\":{\"email\":{},\"length\":{\"max\":255}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"firstName\",\"displayName\":\"${firstName}\",\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"lastName\",\"displayName\":\"${lastName}\",\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false}],\"groups\":[{\"name\":\"user-metadata\",\"displayHeader\":\"User metadata\",\"displayDescription\":\"Attributes, which refer to user metadata\"}],\"unmanagedAttributePolicy\":\"ENABLED\"}" ] + } + } ], "org.keycloak.keys.KeyProvider" : [ { "id" : "9948a63f-b171-4137-bb81-beabd0c049f0", "name" : "aes-generated", @@ -2361,6 +2424,17 @@ "secret" : [ "D82qLVou0ux0UswcrMSTlw" ], "priority" : [ "100" ] } + }, { + "id" : "f594170e-f886-4653-9d9c-a70d87f66ae5", + "name" : "hmac-generated-hs512", + "providerId" : "hmac-generated", + "subComponents" : { }, + "config" : { + "kid" : [ "be9f743e-81d8-4db4-972b-914de6b73ad1" ], + "secret" : [ "LD4EsL3zKuD39yf_HXsNgFLBqiE7pmTTipK3Fg7JCm3QfmdTulIVt7o3LvXVd2Z-6fvBHIFT8DbhHpXuCMu1XY7xiB5TGJSlgs3b5kVb7Dz4xyd4QR5VajswABlcLZDF0_n2LXtpQ72nZmpGBC-DEMSavpvtbMtOb7EJvMlVUws" ], + "priority" : [ "100" ], + "algorithm" : [ "HS512" ] + } }, { "id" : "4a3be057-744a-44c4-9211-9a98d7c6303c", "name" : "rsa-enc-generated", @@ -2390,8 +2464,8 @@ "providerId" : "hmac-generated", "subComponents" : { }, "config" : { - "kid" : [ "6c9fd13c-0afe-46e8-83a6-3fb95ba32dcf" ], - "secret" : [ "f9qfZHpSvHgZ2ti_iZjMfWEM3DyJtlxEpNEzunKXI7qbqnLzwbsMFI4n_vF9YpM4r6mlARVIBnffHgjAwPZvnw" ], + "kid" : [ "8dc274eb-ee41-475b-b397-52fffaf3b558" ], + "secret" : [ "DHWa3cwv5TFTPhjldSbGi44H0qb5UhRJjyE2N2HGwxRptCp4sgot3_0Z8YcTD3fFzXQvsHy5UShsaUwD0i2SaxcksllwigKdUc8kgK28DM1jN-_A98ht-kn_s5mJRqfsTFHnzQ_-Ur2DSzU1d16pNn0J-Gbhb7e6ySF5_LZQwBk" ], "priority" : [ "100" ], "algorithm" : [ "HS256" ] } @@ -2767,13 +2841,6 @@ "priority" : 20, "autheticatorFlow" : false, "userSetupAllowed" : false - }, { - "authenticator" : "registration-profile-action", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 40, - "autheticatorFlow" : false, - "userSetupAllowed" : false }, { "authenticator" : "registration-password-action", "authenticatorFlow" : false, @@ -2918,6 +2985,14 @@ "defaultAction" : false, "priority" : 80, "config" : { } + }, { + "alias" : "delete_credential", + "name" : "Delete Credential", + "providerId" : "delete_credential", + "enabled" : true, + "defaultAction" : false, + "priority" : 100, + "config" : { } }, { "alias" : "update_user_locale", "name" : "Update User Locale", @@ -2933,6 +3008,7 @@ "resetCredentialsFlow" : "reset credentials", "clientAuthenticationFlow" : "clients", "dockerAuthenticationFlow" : "docker auth", + "firstBrokerLoginFlow" : "first broker login", "attributes" : { "cibaBackchannelTokenDeliveryMode" : "poll", "cibaExpiresIn" : "120", @@ -2947,7 +3023,7 @@ "cibaInterval" : "5", "realmReusableOtpCode" : "false" }, - "keycloakVersion" : "22.0.0", + "keycloakVersion" : "24.0.4", "userManagedAccessAllowed" : false, "clientProfiles" : { "profiles" : [ ] diff --git a/ci/rules/sm-repo-rbac_rules.json b/ci/rules/sm-repo-rbac_rules.json new file mode 100644 index 000000000..ef11e18f6 --- /dev/null +++ b/ci/rules/sm-repo-rbac_rules.json @@ -0,0 +1,155 @@ +[ + { + "role": "basyx-reader", + "action": "READ", + "targetInformation": { + "@type": "submodel", + "submodelIds": "*", + "submodelElementIdShortPaths": "*" + } + }, + { + "role": "admin", + "action": ["CREATE", "READ", "UPDATE", "DELETE", "EXECUTE"], + "targetInformation": { + "@type": "submodel", + "submodelIds": "*", + "submodelElementIdShortPaths": "*" + } + }, + { + "role": "basyx-reader-two", + "action": "READ", + "targetInformation": { + "@type": "submodel", + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "*" + } + }, + { + "role": "basyx-sme-reader", + "action": "READ", + "targetInformation": { + "@type": "submodel", + "submodelIds": ["specificSubmodelId", "testSMId1", "testSMId2"], + "submodelElementIdShortPaths": ["testSMEIdShortPath1","smc2.specificSubmodelElementIdShort","testSMEIdShortPath2"] + } + }, + { + "role": "basyx-sme-reader-two", + "action": "READ", + "targetInformation": { + "@type": "submodel", + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "smc2.specificFileSubmodelElementIdShort" + } + }, + { + "role": "basyx-creator", + "action": "CREATE", + "targetInformation": { + "@type": "submodel", + "submodelIds": "*", + "submodelElementIdShortPaths": "*" + } + }, + { + "role": "basyx-updater", + "action": "UPDATE", + "targetInformation": { + "@type": "submodel", + "submodelIds": "*", + "submodelElementIdShortPaths": "*" + } + }, + { + "role": "basyx-updater-two", + "action": "UPDATE", + "targetInformation": { + "@type": "submodel", + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "*" + } + }, + { + "role": "basyx-sme-updater", + "action": "UPDATE", + "targetInformation": { + "@type": "submodel", + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "smc2.specificFileSubmodelElementIdShort" + } + }, + { + "role": "basyx-sme-updater-two", + "action": "UPDATE", + "targetInformation": { + "@type": "submodel", + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "smc2" + } + }, + { + "role": "basyx-sme-updater-three", + "action": "UPDATE", + "targetInformation": { + "@type": "submodel", + "submodelIds": "specificSubmodelId-2", + "submodelElementIdShortPaths": "smc1.specificSubmodelElementIdShort-2" + } + }, + { + "role": "basyx-file-sme-updater", + "action": "UPDATE", + "targetInformation": { + "@type": "submodel", + "submodelIds": "specificSubmodelId-2", + "submodelElementIdShortPaths": "smc2.specificFileSubmodelElementIdShort" + } + }, + { + "role": "basyx-deleter", + "action": "DELETE", + "targetInformation": { + "@type": "submodel", + "submodelIds": "*", + "submodelElementIdShortPaths": "*" + } + }, + { + "role": "basyx-deleter-two", + "action": "DELETE", + "targetInformation": { + "@type": "submodel", + "submodelIds": "specificSubmodelId-2", + "submodelElementIdShortPaths": "*" + } + }, + { + "role": "basyx-executor", + "action": "EXECUTE", + "targetInformation": { + "@type": "submodel", + "submodelIds": "*", + "submodelElementIdShortPaths": "*" + } + }, + { + "role": "basyx-executor-two", + "action": "EXECUTE", + "targetInformation": { + "@type": "submodel", + "submodelIds": "specificSubmodelId", + "submodelElementIdShortPaths": "square" + } + }, + { + "role": "basyx-file-sme-reader", + "action": "READ", + "targetInformation": { + "@type": "submodel", + "submodelIds": "specificSubmodelId-2", + "submodelElementIdShortPaths": "smc2.specificFileSubmodelElementIdShort" + } + } +] \ No newline at end of file diff --git a/ci/rules/sm-repo.properties b/ci/rules/sm-repo.properties new file mode 100644 index 000000000..1229e3457 --- /dev/null +++ b/ci/rules/sm-repo.properties @@ -0,0 +1,10 @@ +server.port=8081 +basyx.backend=InMemory +basyx.cors.allowed-origins=* +basyx.cors.allowed-methods=GET,POST,PATCH,DELETE,PUT,OPTIONS,HEAD + +basyx.feature.authorization.enabled = true +basyx.feature.authorization.type = rbac +basyx.feature.authorization.jwtBearerTokenProvider = keycloak +basyx.feature.authorization.rbac.file = file:/application/rbac_rules.json +spring.security.oauth2.resourceserver.jwt.issuer-uri= http://keycloak-fixed:8080/realms/BaSyx \ No newline at end of file From 76d80127f20c62df42092bb7f9c82e550f850f26 Mon Sep 17 00:00:00 2001 From: Mohammad Ghazanfar Ali Danish Date: Fri, 20 Sep 2024 11:02:21 +0200 Subject: [PATCH 10/20] Adds filtering options in Authorization Signed-off-by: Mohammad Ghazanfar Ali Danish --- .../AuthorizedAasRegistryStorage.java | 30 +++++++++- .../TestAuthorizedAasRegistry.java | 2 +- .../AuthorizedAasRepository.java | 27 ++++++++- .../TestAuthorizedAasRepository.java | 2 +- .../rbac/RbacPermissionResolver.java | 4 ++ .../rbac/SimpleRbacPermissionResolver.java | 9 +++ ...uthorizedConceptDescriptionRepository.java | 27 ++++++++- .../TestAuthorizedCDRepository.java | 2 +- .../AuthorizedSubmodelRegistryStorage.java | 35 +++++++++++- .../TestAuthorizedSubmodelRegistry.java | 2 +- .../AuthorizedSubmodelRepository.java | 55 +++++++++++++++++-- .../TestAuthorizedSubmodelRepository.java | 2 +- 12 files changed, 178 insertions(+), 19 deletions(-) diff --git a/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/feature/authorization/AuthorizedAasRegistryStorage.java b/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/feature/authorization/AuthorizedAasRegistryStorage.java index acacfbeb4..de3babf34 100644 --- a/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/feature/authorization/AuthorizedAasRegistryStorage.java +++ b/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/feature/authorization/AuthorizedAasRegistryStorage.java @@ -28,7 +28,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.Set; +import java.util.TreeMap; +import java.util.stream.Collectors; import org.eclipse.digitaltwin.basyx.aasregistry.feature.authorization.rbac.AasRegistryTargetPermissionVerifier; import org.eclipse.digitaltwin.basyx.aasregistry.model.AssetAdministrationShellDescriptor; @@ -43,9 +46,11 @@ import org.eclipse.digitaltwin.basyx.aasregistry.service.storage.DescriptorFilter; import org.eclipse.digitaltwin.basyx.authorization.rbac.Action; import org.eclipse.digitaltwin.basyx.authorization.rbac.RbacPermissionResolver; +import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetInformation; import org.eclipse.digitaltwin.basyx.core.exceptions.InsufficientPermissionException; import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult; import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; +import org.eclipse.digitaltwin.basyx.core.pagination.PaginationSupport; /** * Decorator for authorized {@link AasRegistryStorage} @@ -64,8 +69,29 @@ public AuthorizedAasRegistryStorage(AasRegistryStorage decorated, RbacPermission @Override public CursorResult> getAllAasDescriptors(PaginationInfo pRequest, DescriptorFilter filter) { - assertHasPermission(Action.READ, getIdAsList(AasRegistryTargetPermissionVerifier.ALL_ALLOWED_WILDCARD)); - return decorated.getAllAasDescriptors(pRequest, filter); + boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new AasRegistryTargetInformation(getIdAsList(AasRegistryTargetPermissionVerifier.ALL_ALLOWED_WILDCARD))); + + if (isAuthorized) + return decorated.getAllAasDescriptors(pRequest, filter); + + List targetInformations = permissionResolver.getMatchingTargetInformationInRules(Action.READ, new AasRegistryTargetInformation(getIdAsList(AasRegistryTargetPermissionVerifier.ALL_ALLOWED_WILDCARD))); + + List allIds = targetInformations.stream().map(AasRegistryTargetInformation.class::cast) + .map(AasRegistryTargetInformation::getAasIds).flatMap(List::stream).collect(Collectors.toList()); + + List aasDescriptors = allIds.stream().map(id -> { + try { + return getAasDescriptor(id); + } catch (Exception e) { + return null; + } + }).filter(Objects::nonNull).collect(Collectors.toList()); + + TreeMap aasMap = aasDescriptors.stream().collect(Collectors.toMap(AssetAdministrationShellDescriptor::getId, aas -> aas, (a, b) -> a, TreeMap::new)); + + PaginationSupport paginationSupport = new PaginationSupport<>(aasMap, AssetAdministrationShellDescriptor::getId); + + return paginationSupport.getPaged(pRequest); } @Override diff --git a/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasregistry/regression/feature/authorization/TestAuthorizedAasRegistry.java b/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasregistry/regression/feature/authorization/TestAuthorizedAasRegistry.java index c78e3831e..4d8eabf33 100644 --- a/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasregistry/regression/feature/authorization/TestAuthorizedAasRegistry.java +++ b/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasregistry/regression/feature/authorization/TestAuthorizedAasRegistry.java @@ -145,7 +145,7 @@ public void getAllAasDescriptorsWithInsufficientPermissionRole() throws IOExcept String accessToken = tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword()); CloseableHttpResponse retrievalResponse = getAllElementsWithAuthorization(aasRegistryBaseUrl, accessToken); - assertEquals(HttpStatus.FORBIDDEN.value(), retrievalResponse.getCode()); + assertEquals(HttpStatus.OK.value(), retrievalResponse.getCode()); } @Test diff --git a/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/AuthorizedAasRepository.java b/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/AuthorizedAasRepository.java index a4f128864..243351917 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/AuthorizedAasRepository.java +++ b/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/AuthorizedAasRepository.java @@ -30,6 +30,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; +import java.util.TreeMap; +import java.util.stream.Collectors; import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell; import org.eclipse.digitaltwin.aas4j.v3.model.AssetInformation; @@ -37,11 +40,13 @@ import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; import org.eclipse.digitaltwin.basyx.authorization.rbac.Action; import org.eclipse.digitaltwin.basyx.authorization.rbac.RbacPermissionResolver; +import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetInformation; import org.eclipse.digitaltwin.basyx.core.exceptions.CollidingIdentifierException; import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException; import org.eclipse.digitaltwin.basyx.core.exceptions.InsufficientPermissionException; import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult; import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; +import org.eclipse.digitaltwin.basyx.core.pagination.PaginationSupport; /** * Decorator for authorized {@link AasRepository} @@ -64,9 +69,27 @@ public AuthorizedAasRepository(AasRepository decorated, RbacPermissionResolver> getAllAas(PaginationInfo pInfo) { boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new AasTargetInformation(getIdAsList("*"))); - throwExceptionIfInsufficientPermission(isAuthorized); + if (isAuthorized) + return decorated.getAllAas(pInfo); + + List targetInformations = permissionResolver.getMatchingTargetInformationInRules(Action.READ, new AasTargetInformation(getIdAsList("*"))); + + List allIds = targetInformations.stream().map(AasTargetInformation.class::cast) + .map(AasTargetInformation::getAasIds).flatMap(List::stream).collect(Collectors.toList()); - return decorated.getAllAas(pInfo); + List aasDescriptors = allIds.stream().map(id -> { + try { + return getAas(id); + } catch (Exception e) { + return null; + } + }).filter(Objects::nonNull).collect(Collectors.toList()); + + TreeMap aasMap = aasDescriptors.stream().collect(Collectors.toMap(AssetAdministrationShell::getId, aas -> aas, (a, b) -> a, TreeMap::new)); + + PaginationSupport paginationSupport = new PaginationSupport<>(aasMap, AssetAdministrationShell::getId); + + return paginationSupport.getPaged(pInfo); } @Override diff --git a/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/TestAuthorizedAasRepository.java b/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/TestAuthorizedAasRepository.java index 76d503397..a38bbe50c 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/TestAuthorizedAasRepository.java +++ b/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/TestAuthorizedAasRepository.java @@ -131,7 +131,7 @@ public void getAllAasWithInsufficientPermissionRole() throws IOException { String accessToken = tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword()); CloseableHttpResponse retrievalResponse = getAllAasWithAuthorization(accessToken); - assertEquals(HttpStatus.FORBIDDEN.value(), retrievalResponse.getCode()); + assertEquals(HttpStatus.OK.value(), retrievalResponse.getCode()); } @Test diff --git a/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/rbac/RbacPermissionResolver.java b/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/rbac/RbacPermissionResolver.java index d8165b807..bc697415e 100644 --- a/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/rbac/RbacPermissionResolver.java +++ b/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/rbac/RbacPermissionResolver.java @@ -25,6 +25,8 @@ package org.eclipse.digitaltwin.basyx.authorization.rbac; +import java.util.List; + /** * An interface for resolving Rbac permissions * @@ -34,4 +36,6 @@ public interface RbacPermissionResolver { public boolean hasPermission(Action action, T targetInformation); + public List getMatchingTargetInformationInRules(Action action, T targetInformation); + } diff --git a/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/rbac/SimpleRbacPermissionResolver.java b/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/rbac/SimpleRbacPermissionResolver.java index f2b4be32c..a760a0f56 100644 --- a/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/rbac/SimpleRbacPermissionResolver.java +++ b/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/rbac/SimpleRbacPermissionResolver.java @@ -79,6 +79,15 @@ public boolean hasPermission(final Action action, final T targetInformation) { return matchingRule.isPresent(); } + + public List getMatchingTargetInformationInRules(final Action action, final T targetInformation) { + + List roles = roleAuthenticator.getRoles(); + + List filteredRbacRulesForTargetInfos = roles.stream().map(role -> RbacRuleKeyGenerator.generateKey(role, action.toString(), targetInformation.getClass().getName())).filter(rbacStorage::exist).map(rbacStorage::getRbacRule).collect(Collectors.toList()); + + return filteredRbacRulesForTargetInfos.stream().map(rbacRule -> rbacRule.getTargetInformation()).collect(Collectors.toList()); + } private Stream getMatchingRules(final List roles, final Action action, final T targetInformation) { diff --git a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/AuthorizedConceptDescriptionRepository.java b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/AuthorizedConceptDescriptionRepository.java index ec181040c..97b6fbcf3 100644 --- a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/AuthorizedConceptDescriptionRepository.java +++ b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/AuthorizedConceptDescriptionRepository.java @@ -28,11 +28,15 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; +import java.util.TreeMap; +import java.util.stream.Collectors; import org.eclipse.digitaltwin.aas4j.v3.model.ConceptDescription; import org.eclipse.digitaltwin.aas4j.v3.model.Reference; import org.eclipse.digitaltwin.basyx.authorization.rbac.Action; import org.eclipse.digitaltwin.basyx.authorization.rbac.RbacPermissionResolver; +import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetInformation; import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.ConceptDescriptionRepository; import org.eclipse.digitaltwin.basyx.core.exceptions.CollidingIdentifierException; import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException; @@ -40,6 +44,7 @@ import org.eclipse.digitaltwin.basyx.core.exceptions.MissingIdentifierException; import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult; import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; +import org.eclipse.digitaltwin.basyx.core.pagination.PaginationSupport; /** * Decorator for authorized {@link ConceptDescriptionRepository} @@ -61,9 +66,27 @@ public AuthorizedConceptDescriptionRepository(ConceptDescriptionRepository decor public CursorResult> getAllConceptDescriptions(PaginationInfo pInfo) { boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new ConceptDescriptionTargetInformation(getIdAsList("*"))); - throwExceptionIfInsufficientPermission(isAuthorized); + if (isAuthorized) + return decorated.getAllConceptDescriptions(pInfo); + + List targetInformations = permissionResolver.getMatchingTargetInformationInRules(Action.READ, new ConceptDescriptionTargetInformation(getIdAsList("*"))); + + List allIds = targetInformations.stream().map(ConceptDescriptionTargetInformation.class::cast) + .map(ConceptDescriptionTargetInformation::getConceptDescriptionIds).flatMap(List::stream).collect(Collectors.toList()); - return decorated.getAllConceptDescriptions(pInfo); + List conceptDesc = allIds.stream().map(id -> { + try { + return getConceptDescription(id); + } catch (Exception e) { + return null; + } + }).filter(Objects::nonNull).collect(Collectors.toList()); + + TreeMap aasMap = conceptDesc.stream().collect(Collectors.toMap(ConceptDescription::getId, aas -> aas, (a, b) -> a, TreeMap::new)); + + PaginationSupport paginationSupport = new PaginationSupport<>(aasMap, ConceptDescription::getId); + + return paginationSupport.getPaged(pInfo); } @Override diff --git a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/TestAuthorizedCDRepository.java b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/TestAuthorizedCDRepository.java index 7956a08ad..80befd8b2 100644 --- a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/TestAuthorizedCDRepository.java +++ b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/TestAuthorizedCDRepository.java @@ -113,7 +113,7 @@ public void getAllCDWithInsufficientPermissionRole() throws IOException { String accessToken = tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword()); CloseableHttpResponse retrievalResponse = getAllCDWithAuthorization(accessToken); - assertEquals(HttpStatus.FORBIDDEN.value(), retrievalResponse.getCode()); + assertEquals(HttpStatus.OK.value(), retrievalResponse.getCode()); } @Test diff --git a/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/feature/authorization/AuthorizedSubmodelRegistryStorage.java b/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/feature/authorization/AuthorizedSubmodelRegistryStorage.java index 74887d22e..4c65a0985 100644 --- a/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/feature/authorization/AuthorizedSubmodelRegistryStorage.java +++ b/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/feature/authorization/AuthorizedSubmodelRegistryStorage.java @@ -28,12 +28,21 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.Set; +import java.util.TreeMap; +import java.util.stream.Collectors; + +import org.eclipse.digitaltwin.basyx.aasregistry.feature.authorization.AasRegistryTargetInformation; +import org.eclipse.digitaltwin.basyx.aasregistry.feature.authorization.rbac.AasRegistryTargetPermissionVerifier; +import org.eclipse.digitaltwin.basyx.aasregistry.model.AssetAdministrationShellDescriptor; import org.eclipse.digitaltwin.basyx.authorization.rbac.Action; import org.eclipse.digitaltwin.basyx.authorization.rbac.RbacPermissionResolver; +import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetInformation; import org.eclipse.digitaltwin.basyx.core.exceptions.InsufficientPermissionException; import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult; import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; +import org.eclipse.digitaltwin.basyx.core.pagination.PaginationSupport; import org.eclipse.digitaltwin.basyx.submodelregistry.feature.authorization.rbac.SubmodelRegistryTargetPermissionVerifier; import org.eclipse.digitaltwin.basyx.submodelregistry.model.SubmodelDescriptor; import org.eclipse.digitaltwin.basyx.submodelregistry.service.errors.SubmodelAlreadyExistsException; @@ -57,8 +66,30 @@ public AuthorizedSubmodelRegistryStorage(SubmodelRegistryStorage decorated, Rbac @Override public CursorResult> getAllSubmodelDescriptors(PaginationInfo pRequest) { - assertHasPermission(Action.READ, getIdAsList(SubmodelRegistryTargetPermissionVerifier.ALL_ALLOWED_WILDCARD)); - return decorated.getAllSubmodelDescriptors(pRequest); + boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelRegistryTargetInformation(getIdAsList(SubmodelRegistryTargetPermissionVerifier.ALL_ALLOWED_WILDCARD))); + throwExceptionIfInsufficientPermission(isAuthorized); + + if (isAuthorized) + return decorated.getAllSubmodelDescriptors(pRequest); + + List targetInformations = permissionResolver.getMatchingTargetInformationInRules(Action.READ, new SubmodelRegistryTargetInformation(getIdAsList(SubmodelRegistryTargetPermissionVerifier.ALL_ALLOWED_WILDCARD))); + + List allIds = targetInformations.stream().map(SubmodelRegistryTargetInformation.class::cast) + .map(SubmodelRegistryTargetInformation::getSubmodelIds).flatMap(List::stream).collect(Collectors.toList()); + + List aasDescriptors = allIds.stream().map(id -> { + try { + return getSubmodelDescriptor(id); + } catch (Exception e) { + return null; + } + }).filter(Objects::nonNull).collect(Collectors.toList()); + + TreeMap aasMap = aasDescriptors.stream().collect(Collectors.toMap(SubmodelDescriptor::getId, aas -> aas, (a, b) -> a, TreeMap::new)); + + PaginationSupport paginationSupport = new PaginationSupport<>(aasMap, SubmodelDescriptor::getId); + + return paginationSupport.getPaged(pRequest); } @Override diff --git a/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/regression/feature/authorization/TestAuthorizedSubmodelRegistry.java b/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/regression/feature/authorization/TestAuthorizedSubmodelRegistry.java index 37f359a32..f72f1345c 100644 --- a/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/regression/feature/authorization/TestAuthorizedSubmodelRegistry.java +++ b/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/regression/feature/authorization/TestAuthorizedSubmodelRegistry.java @@ -144,7 +144,7 @@ public void getAllSubmodelDescriptorsWithInsufficientPermissionRole() throws IOE String accessToken = tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword()); CloseableHttpResponse retrievalResponse = getAllElementsWithAuthorization(submodelRegistryBaseUrl, accessToken); - assertEquals(HttpStatus.FORBIDDEN.value(), retrievalResponse.getCode()); + assertEquals(HttpStatus.OK.value(), retrievalResponse.getCode()); } @Test diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepository.java index e75bc4a2b..ea17a075a 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepository.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepository.java @@ -30,12 +30,16 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; +import java.util.TreeMap; +import java.util.stream.Collectors; import org.eclipse.digitaltwin.aas4j.v3.model.OperationVariable; import org.eclipse.digitaltwin.aas4j.v3.model.Submodel; import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement; import org.eclipse.digitaltwin.basyx.authorization.rbac.Action; import org.eclipse.digitaltwin.basyx.authorization.rbac.RbacPermissionResolver; +import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetInformation; import org.eclipse.digitaltwin.basyx.core.exceptions.CollidingIdentifierException; import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException; import org.eclipse.digitaltwin.basyx.core.exceptions.ElementNotAFileException; @@ -43,6 +47,7 @@ import org.eclipse.digitaltwin.basyx.core.exceptions.InsufficientPermissionException; import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult; import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; +import org.eclipse.digitaltwin.basyx.core.pagination.PaginationSupport; import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository; import org.eclipse.digitaltwin.basyx.submodelservice.value.SubmodelElementValue; import org.eclipse.digitaltwin.basyx.submodelservice.value.SubmodelValueOnly; @@ -68,9 +73,27 @@ public AuthorizedSubmodelRepository(SubmodelRepository decorated, RbacPermission public CursorResult> getAllSubmodels(PaginationInfo pInfo) { boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(getIdAsList(ALL_ALLOWED_WILDCARD), getIdAsList(ALL_ALLOWED_WILDCARD))); - throwExceptionIfInsufficientPermission(isAuthorized); - - return decorated.getAllSubmodels(pInfo); + if (isAuthorized) + return decorated.getAllSubmodels(pInfo); + + List targetInformations = permissionResolver.getMatchingTargetInformationInRules(Action.READ, new SubmodelTargetInformation(getIdAsList(ALL_ALLOWED_WILDCARD), getIdAsList(ALL_ALLOWED_WILDCARD))); + + List allIds = targetInformations.stream().map(SubmodelTargetInformation.class::cast) + .map(SubmodelTargetInformation::getSubmodelIds).flatMap(List::stream).collect(Collectors.toList()); + + List submodels = allIds.stream().map(id -> { + try { + return getSubmodel(id); + } catch (Exception e) { + return null; + } + }).filter(Objects::nonNull).collect(Collectors.toList()); + + TreeMap aasMap = submodels.stream().collect(Collectors.toMap(Submodel::getId, aas -> aas, (a, b) -> a, TreeMap::new)); + + PaginationSupport paginationSupport = new PaginationSupport<>(aasMap, Submodel::getId); + + return paginationSupport.getPaged(pInfo); } @Override @@ -113,9 +136,29 @@ public void deleteSubmodel(String submodelId) throws ElementDoesNotExistExceptio public CursorResult> getSubmodelElements(String submodelId, PaginationInfo pInfo) throws ElementDoesNotExistException { boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(getIdAsList(submodelId), getIdAsList(ALL_ALLOWED_WILDCARD))); - throwExceptionIfInsufficientPermission(isAuthorized); - - return decorated.getSubmodelElements(submodelId, pInfo); + if (isAuthorized) + return decorated.getSubmodelElements(submodelId, pInfo); + + getSubmodel(submodelId); + + List targetInformations = permissionResolver.getMatchingTargetInformationInRules(Action.READ, new SubmodelTargetInformation(getIdAsList(submodelId), getIdAsList(ALL_ALLOWED_WILDCARD))); + + List allIds = targetInformations.stream().map(SubmodelTargetInformation.class::cast) + .map(SubmodelTargetInformation::getSubmodelElementIdShortPaths).flatMap(List::stream).collect(Collectors.toList()); + + List smes = allIds.stream().map(id -> { + try { + return getSubmodelElement(submodelId, id); + } catch (Exception e) { + return null; + } + }).filter(Objects::nonNull).collect(Collectors.toList()); + + TreeMap aasMap = smes.stream().collect(Collectors.toMap(SubmodelElement::getIdShort, aas -> aas, (a, b) -> a, TreeMap::new)); + + PaginationSupport paginationSupport = new PaginationSupport<>(aasMap, SubmodelElement::getIdShort); + + return paginationSupport.getPaged(pInfo); } @Override diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/TestAuthorizedSubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/TestAuthorizedSubmodelRepository.java index f025a03a4..81226a58b 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/TestAuthorizedSubmodelRepository.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/TestAuthorizedSubmodelRepository.java @@ -131,7 +131,7 @@ public void getAllSubmodelsWithInsufficientPermissionRole() throws IOException { String accessToken = tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword()); CloseableHttpResponse retrievalResponse = getAllSubmodelsWithAuthorization(accessToken); - assertEquals(HttpStatus.FORBIDDEN.value(), retrievalResponse.getCode()); + assertEquals(HttpStatus.OK.value(), retrievalResponse.getCode()); } @Test From 9d3ab3ce5d9ec8f8b282f3d4e5a26063aaf91682 Mon Sep 17 00:00:00 2001 From: Mohammad Ghazanfar Ali Danish Date: Fri, 20 Sep 2024 11:11:07 +0200 Subject: [PATCH 11/20] Fixes Keycloak issues Signed-off-by: Mohammad Ghazanfar Ali Danish --- ci/docker-compose.yml | 106 ++++++++++++++--------------- ci/keycloak/realm/BaSyx-realm.json | 1 - 2 files changed, 53 insertions(+), 54 deletions(-) diff --git a/ci/docker-compose.yml b/ci/docker-compose.yml index 6a8119d17..12f05bf21 100644 --- a/ci/docker-compose.yml +++ b/ci/docker-compose.yml @@ -19,60 +19,60 @@ services: networks: - basyx-java-server-sdk - # zookeeper: - # image: confluentinc/cp-zookeeper:7.5.2 - # environment: - # ZOOKEEPER_CLIENT_PORT: 2181 - # ZOOKEEPER_TICK_TIME: 2000 - # healthcheck: - # test: nc -z localhost 2181 || exit -1 - # interval: 10s - # timeout: 5s - # retries: 10 - # start_period: 10s - # networks: - # - basyx-java-server-sdk + zookeeper: + image: confluentinc/cp-zookeeper:7.5.2 + environment: + ZOOKEEPER_CLIENT_PORT: 2181 + ZOOKEEPER_TICK_TIME: 2000 + healthcheck: + test: nc -z localhost 2181 || exit -1 + interval: 10s + timeout: 5s + retries: 10 + start_period: 10s + networks: + - basyx-java-server-sdk - # akhq: - # image: tchiotludo/akhq:0.24.0 - # container_name: akhq - # environment: - # AKHQ_CONFIGURATION: | - # akhq: - # connections: - # docker-kafka-server: - # properties: - # bootstrap.servers: "kafka:29092" - # ports: - # - 8086:8080 - # restart: always - # depends_on: - # - kafka - # networks: - # - basyx-java-server-sdk + akhq: + image: tchiotludo/akhq:0.24.0 + container_name: akhq + environment: + AKHQ_CONFIGURATION: | + akhq: + connections: + docker-kafka-server: + properties: + bootstrap.servers: "kafka:29092" + ports: + - 8086:8080 + restart: always + depends_on: + - kafka + networks: + - basyx-java-server-sdk - # kafka: - # image: confluentinc/cp-kafka:7.5.2 - # ports: - # - 9092:9092 - # environment: - # KAFKA_BROKER_ID: 1 - # KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 - # KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092 - # KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT - # KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT - # KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 - # healthcheck: - # test: nc -z localhost 9092 || exit -1 - # interval: 5s - # timeout: 10s - # retries: 10 - # start_period: 15s - # depends_on: - # zookeeper: - # condition: service_healthy - # networks: - # - basyx-java-server-sdk + kafka: + image: confluentinc/cp-kafka:7.5.2 + ports: + - 9092:9092 + environment: + KAFKA_BROKER_ID: 1 + KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT + KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + healthcheck: + test: nc -z localhost 9092 || exit -1 + interval: 5s + timeout: 10s + retries: 10 + start_period: 15s + depends_on: + zookeeper: + condition: service_healthy + networks: + - basyx-java-server-sdk aas-registry-log-mem: image: eclipsebasyx/aas-registry-log-mem:$BASYX_VERSION @@ -176,7 +176,7 @@ services: ports: - 9097:8080 volumes: - - ./keycloak/realm:/opt/keycloak/data/import + - ./keycloak/realm:/opt/keycloak/data/import:ro networks: - basyx-java-server-sdk diff --git a/ci/keycloak/realm/BaSyx-realm.json b/ci/keycloak/realm/BaSyx-realm.json index 7680f78b7..d4bfe7f8c 100644 --- a/ci/keycloak/realm/BaSyx-realm.json +++ b/ci/keycloak/realm/BaSyx-realm.json @@ -37,7 +37,6 @@ "editUsernameAllowed" : false, "bruteForceProtected" : false, "permanentLockout" : false, - "maxTemporaryLockouts" : 0, "maxFailureWaitSeconds" : 900, "minimumQuickLoginWaitSeconds" : 60, "waitIncrementSeconds" : 60, From b3c3deb4416baf45f67d89a58d0302617e8ff2f7 Mon Sep 17 00:00:00 2001 From: Mohammad Ghazanfar Ali Danish Date: Fri, 20 Sep 2024 11:13:14 +0200 Subject: [PATCH 12/20] Reverts BaSyx Realm Signed-off-by: Mohammad Ghazanfar Ali Danish --- ci/keycloak/realm/BaSyx-realm.json | 399 ++++++++++++----------------- 1 file changed, 162 insertions(+), 237 deletions(-) diff --git a/ci/keycloak/realm/BaSyx-realm.json b/ci/keycloak/realm/BaSyx-realm.json index d4bfe7f8c..b893decf7 100644 --- a/ci/keycloak/realm/BaSyx-realm.json +++ b/ci/keycloak/realm/BaSyx-realm.json @@ -511,14 +511,14 @@ "attributes" : { } } ], "basyx-client-api" : [ { - "id" : "2dd4b9b1-748f-43f3-b62b-048c92ae79d1", + "id" : "2dd4b9b1-748f-43f3-b62b-048c92ae79d1", "name" : "basyx-creator", "description" : "", "composite" : false, "clientRole" : true, "containerId" : "3fb3e5e5-dbd8-4d51-b964-746c5b2181a4", "attributes" : { } - }, { + }, { "id" : "ba077409-1b5d-4fc8-b20e-10389507fb75", "name" : "basyx-admin", "description" : "", @@ -535,7 +535,6 @@ "containerId" : "3fb3e5e5-dbd8-4d51-b964-746c5b2181a4", "attributes" : { } } ], - "basyx-web-ui" : [ ], "security-admin-console" : [ ], "admin-cli" : [ ], "account-console" : [ ], @@ -622,7 +621,7 @@ "clientRole" : true, "containerId" : "049e1323-6efb-4543-bc52-566cd292732a", "attributes" : { } - } ], + } ], "basyx-demo" : [ ], "workstation-1" : [ { "id" : "914a18c6-4f14-418f-99e0-bfdcf604ac01", @@ -630,7 +629,7 @@ "composite" : false, "clientRole" : true, "containerId" : "96031210-9e6c-4252-a22e-e81a47e30d65", - "attributes" : { } + "attributes" : { } } ] } }, @@ -638,10 +637,10 @@ "id" : "606a14f2-6114-4fd3-9ca6-4a53514fffb9", "name" : "BaSyxGroup", "path" : "/BaSyxGroup", - "subGroups" : [ ], "attributes" : { }, "realmRoles" : [ "basyx-deleter", "basyx-creator", "basyx-asset-updater" ], - "clientRoles" : { } + "clientRoles" : { }, + "subGroups" : [ ] } ], "defaultRole" : { "id" : "797d2956-a895-4171-ab44-2fc9dbcf7f4c", @@ -659,8 +658,7 @@ "otpPolicyLookAheadWindow" : 1, "otpPolicyPeriod" : 30, "otpPolicyCodeReusable" : false, - "otpSupportedApplications" : [ "totpAppFreeOTPName", "totpAppGoogleName", "totpAppMicrosoftAuthenticatorName" ], - "localizationTexts" : { }, + "otpSupportedApplications" : [ "totpAppMicrosoftAuthenticatorName", "totpAppGoogleName", "totpAppFreeOTPName" ], "webAuthnPolicyRpEntityName" : "keycloak", "webAuthnPolicySignatureAlgorithms" : [ "ES256" ], "webAuthnPolicyRpId" : "", @@ -671,7 +669,6 @@ "webAuthnPolicyCreateTimeout" : 0, "webAuthnPolicyAvoidSameAuthenticatorRegister" : false, "webAuthnPolicyAcceptableAaguids" : [ ], - "webAuthnPolicyExtraOrigins" : [ ], "webAuthnPolicyPasswordlessRpEntityName" : "keycloak", "webAuthnPolicyPasswordlessSignatureAlgorithms" : [ "ES256" ], "webAuthnPolicyPasswordlessRpId" : "", @@ -682,16 +679,15 @@ "webAuthnPolicyPasswordlessCreateTimeout" : 0, "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister" : false, "webAuthnPolicyPasswordlessAcceptableAaguids" : [ ], - "webAuthnPolicyPasswordlessExtraOrigins" : [ ], "users" : [ { "id" : "856b093b-ef9f-4bd0-92ca-662f680c73cc", - "username" : "basyx.aas.discoverer", - "firstName" : "", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1713968958846, + "username" : "basyx.aas.discoverer", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "", + "lastName" : "", "credentials" : [ { "id" : "bc4944be-c8e2-4c91-81cc-9478a0795906", "type" : "password", @@ -707,13 +703,13 @@ "groups" : [ ] }, { "id" : "aef2b331-f694-4503-b0da-1412c77842ba", - "username" : "basyx.asset.updater", - "firstName" : "", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1702179260496, + "username" : "basyx.asset.updater", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "", + "lastName" : "", "credentials" : [ { "id" : "24db17af-1244-4cea-8af8-1047adcd0753", "type" : "password", @@ -729,13 +725,13 @@ "groups" : [ ] }, { "id" : "4fc75aa9-4745-4bec-846e-de5dbd665b7c", - "username" : "basyx.asset.updater.2", - "firstName" : "", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1702179349503, + "username" : "basyx.asset.updater.2", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "", + "lastName" : "", "credentials" : [ { "id" : "db64f531-b8ab-4e33-b7b9-e3ba0e3677d4", "type" : "password", @@ -751,13 +747,13 @@ "groups" : [ ] }, { "id" : "bcbd95b2-2ec4-42a6-9b1d-39dabf0454c3", - "username" : "basyx.assetid.creator", - "firstName" : "", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1713969008326, + "username" : "basyx.assetid.creator", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "", + "lastName" : "", "credentials" : [ { "id" : "81b26fa2-4eda-45d7-a8be-4b6d64b3d813", "type" : "password", @@ -773,13 +769,13 @@ "groups" : [ ] }, { "id" : "2def798c-7547-42fe-8915-be493d740005", - "username" : "basyx.assetid.deleter", - "firstName" : "", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1713969038565, + "username" : "basyx.assetid.deleter", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "", + "lastName" : "", "credentials" : [ { "id" : "fa285b2c-9a2d-4400-a524-508b0a16f4c3", "type" : "password", @@ -795,13 +791,13 @@ "groups" : [ ] }, { "id" : "3527f121-116c-429b-bf0d-78bf4a8b5abe", - "username" : "basyx.assetid.discoverer", - "firstName" : "", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1713968905507, + "username" : "basyx.assetid.discoverer", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "", + "lastName" : "", "credentials" : [ { "id" : "4f3427f8-9da2-4372-a54b-c1e54dc8da68", "type" : "password", @@ -817,13 +813,13 @@ "groups" : [ ] }, { "id" : "f48119f5-7dff-46af-b9db-3ee96cd52550", - "username" : "basyx.creator", - "firstName" : "BaSyx", - "lastName" : "creator", - "emailVerified" : false, "createdTimestamp" : 1702032555719, + "username" : "basyx.creator", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "BaSyx", + "lastName" : "creator", "credentials" : [ { "id" : "393aa50c-70ed-4676-b5d0-b4c6cf930272", "type" : "password", @@ -839,13 +835,13 @@ "groups" : [ ] }, { "id" : "40858874-f6e7-48c8-9667-d585d7c27b57", - "username" : "basyx.deleter", - "firstName" : "BaSyx", - "lastName" : "Deleter", - "emailVerified" : false, "createdTimestamp" : 1702032602188, + "username" : "basyx.deleter", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "BaSyx", + "lastName" : "Deleter", "credentials" : [ { "id" : "ee9224dd-6277-4c3d-8d88-b595d66f25c7", "type" : "password", @@ -861,13 +857,13 @@ "groups" : [ ] }, { "id" : "fcf7ea95-875e-42ef-afe1-f9b6008cbaf9", - "username" : "basyx.deleter.2", - "firstName" : "", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1702161686852, + "username" : "basyx.deleter.2", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "", + "lastName" : "", "credentials" : [ { "id" : "1b8d0b8c-1f65-43e0-9178-785533746684", "type" : "password", @@ -883,13 +879,13 @@ "groups" : [ ] }, { "id" : "8e820205-13fa-4d61-9513-99f717c15f73", - "username" : "basyx.executor", - "firstName" : "", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1705326903328, + "username" : "basyx.executor", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "", + "lastName" : "", "credentials" : [ { "id" : "3e891de4-74e6-46cb-840f-6681af0ce397", "type" : "password", @@ -905,13 +901,13 @@ "groups" : [ ] }, { "id" : "3d645000-277e-46f5-b421-85f1fdb064b5", - "username" : "basyx.executor.2", - "firstName" : "", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1705326932748, + "username" : "basyx.executor.2", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "", + "lastName" : "", "credentials" : [ { "id" : "d21c920f-5dd0-4ce7-ad2e-737a841bb1f8", "type" : "password", @@ -927,13 +923,13 @@ "groups" : [ ] }, { "id" : "c86047c6-3ab8-4f47-86ba-b26ea80d1986", - "username" : "basyx.file.sme.reader", - "firstName" : "", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1705398014689, + "username" : "basyx.file.sme.reader", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "", + "lastName" : "", "credentials" : [ { "id" : "bd374dc3-4fec-4232-be91-a3f5608e373e", "type" : "password", @@ -949,13 +945,13 @@ "groups" : [ ] }, { "id" : "da7acf36-1574-4c0a-aa5b-65829a512b60", - "username" : "basyx.file.sme.updater", - "firstName" : "", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1705398962710, + "username" : "basyx.file.sme.updater", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "", + "lastName" : "", "credentials" : [ { "id" : "e5fda50d-9f52-4f60-a917-31a85fc275a7", "type" : "password", @@ -971,13 +967,13 @@ "groups" : [ ] }, { "id" : "abfaa545-3c2f-4ac6-9f41-7c166613ea35", - "username" : "basyx.reader", - "firstName" : "BaSyx", - "lastName" : "Reader", - "emailVerified" : false, "createdTimestamp" : 1702032528855, + "username" : "basyx.reader", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "BaSyx", + "lastName" : "Reader", "credentials" : [ { "id" : "eea587f5-439d-487d-85d0-587b226f0683", "type" : "password", @@ -996,13 +992,13 @@ "groups" : [ ] }, { "id" : "37e1ba01-3b9c-428c-b0fe-be85970bf1d9", - "username" : "basyx.reader.2", - "firstName" : "", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1702158534562, + "username" : "basyx.reader.2", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "", + "lastName" : "", "credentials" : [ { "id" : "3237f89d-f9f0-4b70-9a08-d31d995c6948", "type" : "password", @@ -1018,13 +1014,13 @@ "groups" : [ ] }, { "id" : "2d7a7bdb-c8d3-4684-8c06-8873aabd9968", - "username" : "basyx.reader.serialization", - "firstName" : "", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1707983800892, + "username" : "basyx.reader.serialization", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "", + "lastName" : "", "credentials" : [ { "id" : "c35c2724-8ae9-4193-bd25-e5245df77f4d", "type" : "password", @@ -1040,13 +1036,13 @@ "groups" : [ ] }, { "id" : "113691d0-fc03-4607-8b64-97b646bded1f", - "username" : "basyx.reader.serialization.2", - "firstName" : "", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1707983824403, + "username" : "basyx.reader.serialization.2", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "", + "lastName" : "", "credentials" : [ { "id" : "86bd4fdc-077c-4546-b80a-f51788f15b56", "type" : "password", @@ -1062,13 +1058,13 @@ "groups" : [ ] }, { "id" : "3988486a-51eb-447b-bc87-354e5b724c76", - "username" : "basyx.sme.reader", - "firstName" : "", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1705306854299, + "username" : "basyx.sme.reader", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "", + "lastName" : "", "credentials" : [ { "id" : "d70787d1-9834-43d6-a10d-bca854847fec", "type" : "password", @@ -1084,13 +1080,13 @@ "groups" : [ ] }, { "id" : "ed921577-8b07-439e-af66-cc3579a276ec", - "username" : "basyx.sme.reader.2", - "firstName" : "", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1705310445991, + "username" : "basyx.sme.reader.2", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "", + "lastName" : "", "credentials" : [ { "id" : "62446675-7b7e-40e7-bf6c-b375d8542a12", "type" : "password", @@ -1106,13 +1102,13 @@ "groups" : [ ] }, { "id" : "60f02977-d033-43ed-8171-8ac44d14c62f", - "username" : "basyx.sme.updater", - "firstName" : "", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1705312271325, + "username" : "basyx.sme.updater", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "", + "lastName" : "", "credentials" : [ { "id" : "d224c0bc-7c5d-426d-8e96-9f4dfb141eab", "type" : "password", @@ -1128,13 +1124,13 @@ "groups" : [ ] }, { "id" : "9fd79b98-a230-43c8-a7ff-f88ee381c58c", - "username" : "basyx.sme.updater.2", - "firstName" : "", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1705315980352, + "username" : "basyx.sme.updater.2", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "", + "lastName" : "", "credentials" : [ { "id" : "eca8536c-fbf7-49dd-a370-405b00336965", "type" : "password", @@ -1150,13 +1146,13 @@ "groups" : [ ] }, { "id" : "20336dcf-0c1f-4344-b31b-f26048fc7faa", - "username" : "basyx.sme.updater.3", - "firstName" : "", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1705324670982, + "username" : "basyx.sme.updater.3", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "", + "lastName" : "", "credentials" : [ { "id" : "d1652462-350a-42e0-9a3e-e04cfa258237", "type" : "password", @@ -1172,13 +1168,13 @@ "groups" : [ ] }, { "id" : "44f291ad-a0de-4035-9938-a092faf810b5", - "username" : "basyx.updater", - "firstName" : "BaSyx", - "lastName" : "Updater", - "emailVerified" : false, "createdTimestamp" : 1702032579778, + "username" : "basyx.updater", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "BaSyx", + "lastName" : "Updater", "credentials" : [ { "id" : "28e62a3a-790d-477f-b139-b0ea943abfd7", "type" : "password", @@ -1194,13 +1190,13 @@ "groups" : [ ] }, { "id" : "9bf6b5c8-194f-4672-9313-9f3823cb3019", - "username" : "basyx.updater.2", - "firstName" : "", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1702161564532, + "username" : "basyx.updater.2", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "", + "lastName" : "", "credentials" : [ { "id" : "59ef29d6-6dcf-42e6-b946-33d50335e0d1", "type" : "password", @@ -1216,13 +1212,13 @@ "groups" : [ ] }, { "id" : "cb7df854-827d-4d34-a01f-33cdf07f5cea", - "username" : "basyx.uploader", - "firstName" : "", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1708702290219, + "username" : "basyx.uploader", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "", + "lastName" : "", "credentials" : [ { "id" : "7df6170e-1631-434d-8380-62f750c563cf", "type" : "password", @@ -1238,13 +1234,13 @@ "groups" : [ ] }, { "id" : "3c94e1e0-caac-48c3-a31c-c9f555233a46", - "username" : "basyx.uploader.2", - "firstName" : "", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1708932762171, + "username" : "basyx.uploader.2", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "", + "lastName" : "", "credentials" : [ { "id" : "0d4049cb-293b-4f76-b82b-5ba9ee4530f2", "type" : "password", @@ -1260,13 +1256,13 @@ "groups" : [ ] }, { "id" : "c1a592bd-536f-4a3e-8193-c17d479814f3", - "username" : "basyx.uploader.3", - "firstName" : "", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1708934121191, + "username" : "basyx.uploader.3", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "", + "lastName" : "", "credentials" : [ { "id" : "7cd8fd3f-8c83-43b9-9fb7-6203e7c7376c", "type" : "password", @@ -1282,13 +1278,13 @@ "groups" : [ ] }, { "id" : "f3ec1793-3d62-41c3-ad34-b7b29ac88528", - "username" : "bob.maintainer", - "firstName" : "Bob", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1702030619322, + "username" : "bob.maintainer", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "Bob", + "lastName" : "", "credentials" : [ { "id" : "a45e5184-e7f3-41bc-be25-c97197852301", "type" : "password", @@ -1304,13 +1300,13 @@ "groups" : [ ] }, { "id" : "e75ac9e8-7093-4898-a203-d9839f854944", - "username" : "jane.doe", - "firstName" : "Jane", - "lastName" : "Doe", - "emailVerified" : false, "createdTimestamp" : 1702030567684, + "username" : "jane.doe", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "Jane", + "lastName" : "Doe", "credentials" : [ { "id" : "d1e97a9f-42b6-43d0-a070-1216d03a64b7", "type" : "password", @@ -1326,13 +1322,13 @@ "groups" : [ ] }, { "id" : "caf7499e-4f3d-45fa-9246-99ea8f8b5c94", - "username" : "john", - "firstName" : "", - "lastName" : "", - "emailVerified" : false, "createdTimestamp" : 1701764678734, + "username" : "john", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "", + "lastName" : "", "credentials" : [ { "id" : "136b209f-3b75-45d9-b448-e1ec93dc7ea4", "type" : "password", @@ -1351,13 +1347,13 @@ "groups" : [ ] }, { "id" : "fb833c1b-3a7d-4224-9ab1-672e7203bab5", - "username" : "john.doe", - "firstName" : "John", - "lastName" : "Doe", - "emailVerified" : false, "createdTimestamp" : 1702030523698, + "username" : "john.doe", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "John", + "lastName" : "Doe", "credentials" : [ { "id" : "b8b3d5cf-4fa3-46ed-8a3e-18875acecff0", "type" : "password", @@ -1373,13 +1369,13 @@ "groups" : [ ] }, { "id" : "9045c192-428a-4fff-bf7a-b9a9ac16742f", - "username" : "paul.visitor", - "firstName" : "Paul", - "lastName" : "Visitor", - "emailVerified" : false, "createdTimestamp" : 1702030666980, + "username" : "paul.visitor", "enabled" : true, "totp" : false, + "emailVerified" : false, + "firstName" : "Paul", + "lastName" : "Visitor", "credentials" : [ { "id" : "81d4c206-c0a0-4ee6-9fcb-19f755e66150", "type" : "password", @@ -1393,18 +1389,18 @@ "realmRoles" : [ "visitor", "default-roles-basyx" ], "notBefore" : 0, "groups" : [ ] - }, { + }, { "id" : "a19abcac-34d5-46bb-a604-b07dc234e80f", - "username" : "service-account-workstation-1", - "emailVerified" : false, "createdTimestamp" : 1715582034760, + "username" : "service-account-workstation-1", "enabled" : true, "totp" : false, + "emailVerified" : false, "serviceAccountClientId" : "workstation-1", "credentials" : [ ], "disableableCredentialTypes" : [ ], "requiredActions" : [ ], - "realmRoles" : [ "basyx-reader", "basyx-deleter", "basyx-updater", "admin", "basyx-creator", "default-roles-basyx" ], + "realmRoles" : [ "basyx-reader", "basyx-deleter", "basyx-updater", "basyx-creator", "default-roles-basyx", "admin" ], "clientRoles" : { "workstation-1" : [ "uma_protection" ] }, @@ -1412,14 +1408,14 @@ "groups" : [ "/BaSyxGroup" ] }, { "id" : "77957093-d593-44b4-b4e9-bc365e840cdd", + "createdTimestamp" : 1715539640949, "username" : "test.user", + "enabled" : true, + "totp" : false, + "emailVerified" : true, "firstName" : "Test", "lastName" : "User", "email" : "test.user@gmail.com", - "emailVerified" : true, - "createdTimestamp" : 1715539640949, - "enabled" : true, - "totp" : false, "credentials" : [ { "id" : "11672ccf-38b7-421b-8d01-755e0f2197da", "type" : "password", @@ -1432,7 +1428,7 @@ "requiredActions" : [ ], "realmRoles" : [ "default-roles-basyx" ], "notBefore" : 0, - "groups" : [ "/BaSyxGroup" ] + "groups" : [ "/BaSyxGroup" ] } ], "scopeMappings" : [ { "clientScope" : "offline_access", @@ -1579,7 +1575,7 @@ "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ], "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] }, { - "id" : "615f29de-5f3d-4384-8b71-7351ff2c2a32", + "id" : "615f29de-5f3d-4384-8b71-7351ff2c2a32", "clientId" : "basyx-demo", "name" : "", "description" : "", @@ -1605,47 +1601,9 @@ "protocol" : "openid-connect", "attributes" : { "oidc.ciba.grant.enabled" : "false", - "client.secret.creation.time" : "1716897911", - "backchannel.logout.session.required" : "true", - "post.logout.redirect.uris" : "+", "oauth2.device.authorization.grant.enabled" : "false", - "backchannel.logout.revoke.offline.tokens" : "false" - }, - "authenticationFlowBindingOverrides" : { }, - "fullScopeAllowed" : true, - "nodeReRegistrationTimeout" : -1, - "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ], - "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] - }, { - "id" : "047f9c4e-3895-4562-9d8d-87cf3b1c7c05", - "clientId" : "basyx-web-ui", - "name" : "", - "description" : "", - "rootUrl" : "", - "adminUrl" : "", - "baseUrl" : "", - "surrogateAuthRequired" : false, - "enabled" : true, - "alwaysDisplayInConsole" : false, - "clientAuthenticatorType" : "client-secret", - "redirectUris" : [ "http://localhost:3000/*" ], - "webOrigins" : [ "http://localhost:3000" ], - "notBefore" : 0, - "bearerOnly" : false, - "consentRequired" : false, - "standardFlowEnabled" : true, - "implicitFlowEnabled" : true, - "directAccessGrantsEnabled" : true, - "serviceAccountsEnabled" : false, - "publicClient" : true, - "frontchannelLogout" : true, - "protocol" : "openid-connect", - "attributes" : { - "oidc.ciba.grant.enabled" : "false", + "client.secret.creation.time" : "1716897911", "backchannel.logout.session.required" : "true", - "post.logout.redirect.uris" : "http://localhost:3000/*", - "oauth2.device.authorization.grant.enabled" : "true", - "display.on.consent.screen" : "false", "backchannel.logout.revoke.offline.tokens" : "false" }, "authenticationFlowBindingOverrides" : { }, @@ -1653,7 +1611,7 @@ "nodeReRegistrationTimeout" : -1, "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ], "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] - }, { + }, { "id" : "45f21c3d-4e85-466f-984f-d7bd47392453", "clientId" : "broker", "name" : "${client_broker}", @@ -1755,7 +1713,7 @@ } ], "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ], "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] - }, { + }, { "id" : "96031210-9e6c-4252-a22e-e81a47e30d65", "clientId" : "workstation-1", "name" : "Workstation 1", @@ -1783,10 +1741,9 @@ "protocol" : "openid-connect", "attributes" : { "oidc.ciba.grant.enabled" : "false", + "oauth2.device.authorization.grant.enabled" : "false", "client.secret.creation.time" : "1715582034", "backchannel.logout.session.required" : "true", - "post.logout.redirect.uris" : "+", - "oauth2.device.authorization.grant.enabled" : "false", "backchannel.logout.revoke.offline.tokens" : "false" }, "authenticationFlowBindingOverrides" : { }, @@ -1800,7 +1757,6 @@ "consentRequired" : false, "config" : { "user.session.note" : "clientAddress", - "userinfo.token.claim" : "true", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "clientAddress", @@ -1814,7 +1770,6 @@ "consentRequired" : false, "config" : { "user.session.note" : "clientHost", - "userinfo.token.claim" : "true", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "clientHost", @@ -1828,7 +1783,6 @@ "consentRequired" : false, "config" : { "user.session.note" : "client_id", - "userinfo.token.claim" : "true", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "client_id", @@ -1836,15 +1790,7 @@ } } ], "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ], - "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ], - "authorizationSettings" : { - "allowRemoteResourceManagement" : true, - "policyEnforcementMode" : "ENFORCING", - "resources" : [ ], - "policies" : [ ], - "scopes" : [ ], - "decisionStrategy" : "UNANIMOUS" - } + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] } ], "clientScopes" : [ { "id" : "e0f355da-f9ff-4104-b305-043b0188747b", @@ -2342,7 +2288,7 @@ "subType" : "anonymous", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "saml-user-attribute-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-address-mapper", "oidc-full-name-mapper", "oidc-usermodel-property-mapper", "saml-role-list-mapper", "oidc-usermodel-attribute-mapper", "saml-user-property-mapper" ] + "allowed-protocol-mapper-types" : [ "oidc-sha256-pairwise-sub-mapper", "saml-role-list-mapper", "oidc-usermodel-property-mapper", "oidc-full-name-mapper", "oidc-address-mapper", "saml-user-attribute-mapper", "saml-user-property-mapper", "oidc-usermodel-attribute-mapper" ] } }, { "id" : "7256d195-1e91-4f63-a9c4-6bef95243a92", @@ -2379,7 +2325,7 @@ "subType" : "authenticated", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "saml-user-attribute-mapper", "oidc-usermodel-property-mapper", "oidc-address-mapper", "oidc-full-name-mapper", "saml-role-list-mapper", "oidc-usermodel-attribute-mapper", "saml-user-property-mapper", "oidc-sha256-pairwise-sub-mapper" ] + "allowed-protocol-mapper-types" : [ "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper", "saml-user-property-mapper", "oidc-usermodel-property-mapper", "saml-role-list-mapper", "oidc-full-name-mapper", "oidc-address-mapper", "saml-user-attribute-mapper" ] } }, { "id" : "face2c9e-4d23-44e2-9a09-74e1d8448bd3", @@ -2405,14 +2351,6 @@ "allow-default-scopes" : [ "true" ] } } ], - "org.keycloak.userprofile.UserProfileProvider" : [ { - "id" : "2997d5f7-8adc-453d-b672-3d4f01f833ba", - "providerId" : "declarative-user-profile", - "subComponents" : { }, - "config" : { - "kc.user.profile.config" : [ "{\"attributes\":[{\"name\":\"username\",\"displayName\":\"${username}\",\"validations\":{\"length\":{\"min\":3,\"max\":255},\"username-prohibited-characters\":{},\"up-username-not-idn-homograph\":{}},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"email\",\"displayName\":\"${email}\",\"validations\":{\"email\":{},\"length\":{\"max\":255}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"firstName\",\"displayName\":\"${firstName}\",\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"lastName\",\"displayName\":\"${lastName}\",\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false}],\"groups\":[{\"name\":\"user-metadata\",\"displayHeader\":\"User metadata\",\"displayDescription\":\"Attributes, which refer to user metadata\"}],\"unmanagedAttributePolicy\":\"ENABLED\"}" ] - } - } ], "org.keycloak.keys.KeyProvider" : [ { "id" : "9948a63f-b171-4137-bb81-beabd0c049f0", "name" : "aes-generated", @@ -2423,17 +2361,6 @@ "secret" : [ "D82qLVou0ux0UswcrMSTlw" ], "priority" : [ "100" ] } - }, { - "id" : "f594170e-f886-4653-9d9c-a70d87f66ae5", - "name" : "hmac-generated-hs512", - "providerId" : "hmac-generated", - "subComponents" : { }, - "config" : { - "kid" : [ "be9f743e-81d8-4db4-972b-914de6b73ad1" ], - "secret" : [ "LD4EsL3zKuD39yf_HXsNgFLBqiE7pmTTipK3Fg7JCm3QfmdTulIVt7o3LvXVd2Z-6fvBHIFT8DbhHpXuCMu1XY7xiB5TGJSlgs3b5kVb7Dz4xyd4QR5VajswABlcLZDF0_n2LXtpQ72nZmpGBC-DEMSavpvtbMtOb7EJvMlVUws" ], - "priority" : [ "100" ], - "algorithm" : [ "HS512" ] - } }, { "id" : "4a3be057-744a-44c4-9211-9a98d7c6303c", "name" : "rsa-enc-generated", @@ -2463,8 +2390,8 @@ "providerId" : "hmac-generated", "subComponents" : { }, "config" : { - "kid" : [ "8dc274eb-ee41-475b-b397-52fffaf3b558" ], - "secret" : [ "DHWa3cwv5TFTPhjldSbGi44H0qb5UhRJjyE2N2HGwxRptCp4sgot3_0Z8YcTD3fFzXQvsHy5UShsaUwD0i2SaxcksllwigKdUc8kgK28DM1jN-_A98ht-kn_s5mJRqfsTFHnzQ_-Ur2DSzU1d16pNn0J-Gbhb7e6ySF5_LZQwBk" ], + "kid" : [ "6c9fd13c-0afe-46e8-83a6-3fb95ba32dcf" ], + "secret" : [ "f9qfZHpSvHgZ2ti_iZjMfWEM3DyJtlxEpNEzunKXI7qbqnLzwbsMFI4n_vF9YpM4r6mlARVIBnffHgjAwPZvnw" ], "priority" : [ "100" ], "algorithm" : [ "HS256" ] } @@ -2840,6 +2767,13 @@ "priority" : 20, "autheticatorFlow" : false, "userSetupAllowed" : false + }, { + "authenticator" : "registration-profile-action", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 40, + "autheticatorFlow" : false, + "userSetupAllowed" : false }, { "authenticator" : "registration-password-action", "authenticatorFlow" : false, @@ -2984,14 +2918,6 @@ "defaultAction" : false, "priority" : 80, "config" : { } - }, { - "alias" : "delete_credential", - "name" : "Delete Credential", - "providerId" : "delete_credential", - "enabled" : true, - "defaultAction" : false, - "priority" : 100, - "config" : { } }, { "alias" : "update_user_locale", "name" : "Update User Locale", @@ -3007,7 +2933,6 @@ "resetCredentialsFlow" : "reset credentials", "clientAuthenticationFlow" : "clients", "dockerAuthenticationFlow" : "docker auth", - "firstBrokerLoginFlow" : "first broker login", "attributes" : { "cibaBackchannelTokenDeliveryMode" : "poll", "cibaExpiresIn" : "120", @@ -3022,7 +2947,7 @@ "cibaInterval" : "5", "realmReusableOtpCode" : "false" }, - "keycloakVersion" : "24.0.4", + "keycloakVersion" : "22.0.0", "userManagedAccessAllowed" : false, "clientProfiles" : { "profiles" : [ ] From 6ac4276a5114834cbb1cde286ea317165fc34e9d Mon Sep 17 00:00:00 2001 From: Mohammad Ghazanfar Ali Danish Date: Fri, 20 Sep 2024 11:18:20 +0200 Subject: [PATCH 13/20] Fixes import issues in SM Registry Signed-off-by: Mohammad Ghazanfar Ali Danish --- .../authorization/AuthorizedSubmodelRegistryStorage.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/feature/authorization/AuthorizedSubmodelRegistryStorage.java b/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/feature/authorization/AuthorizedSubmodelRegistryStorage.java index 4c65a0985..9b0efa417 100644 --- a/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/feature/authorization/AuthorizedSubmodelRegistryStorage.java +++ b/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/feature/authorization/AuthorizedSubmodelRegistryStorage.java @@ -33,9 +33,6 @@ import java.util.TreeMap; import java.util.stream.Collectors; -import org.eclipse.digitaltwin.basyx.aasregistry.feature.authorization.AasRegistryTargetInformation; -import org.eclipse.digitaltwin.basyx.aasregistry.feature.authorization.rbac.AasRegistryTargetPermissionVerifier; -import org.eclipse.digitaltwin.basyx.aasregistry.model.AssetAdministrationShellDescriptor; import org.eclipse.digitaltwin.basyx.authorization.rbac.Action; import org.eclipse.digitaltwin.basyx.authorization.rbac.RbacPermissionResolver; import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetInformation; From a1eb3c870cba83850357d4142a183c118eff260a Mon Sep 17 00:00:00 2001 From: Mohammad Ghazanfar Ali Danish Date: Fri, 20 Sep 2024 11:26:49 +0200 Subject: [PATCH 14/20] Fixes app.prop of aas env Signed-off-by: Mohammad Ghazanfar Ali Danish --- .../src/main/resources/application.properties | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/basyx.aasenvironment/basyx.aasenvironment.component/src/main/resources/application.properties b/basyx.aasenvironment/basyx.aasenvironment.component/src/main/resources/application.properties index aa38ac083..bada859f2 100644 --- a/basyx.aasenvironment/basyx.aasenvironment.component/src/main/resources/application.properties +++ b/basyx.aasenvironment/basyx.aasenvironment.component/src/main/resources/application.properties @@ -33,18 +33,18 @@ basyx.backend = InMemory #################################################################################### # Authorization #################################################################################### -basyx.feature.authorization.enabled = true -basyx.feature.authorization.type = rbac -basyx.feature.authorization.jwtBearerTokenProvider = keycloak -basyx.feature.authorization.rbac.file = classpath:rbac_rules.json -spring.security.oauth2.resourceserver.jwt.issuer-uri= http://localhost:9097/realms/BaSyx - -basyx.feature.authorization.rules.backend=Submodel -basyx.feature.authorization.rules.backend.submodel.authorization.endpoint=http://localhost:8055/submodels/N0E3MTA0QkRBQjU3RTE4NA== -basyx.feature.authorization.rules.backend.submodel.authorization.token-endpoint=http://localhost:9097/realms/BaSyx/protocol/openid-connect/token -basyx.feature.authorization.rules.backend.submodel.authorization.grant-type = CLIENT_CREDENTIALS -basyx.feature.authorization.rules.backend.submodel.authorization.client-id=workstation-1 -basyx.feature.authorization.rules.backend.submodel.authorization.client-secret=nY0mjyECF60DGzNmQUjL81XurSl8etom +#basyx.feature.authorization.enabled = true +#basyx.feature.authorization.type = rbac +#basyx.feature.authorization.jwtBearerTokenProvider = keycloak +#basyx.feature.authorization.rbac.file = classpath:rbac_rules.json +#spring.security.oauth2.resourceserver.jwt.issuer-uri= http://localhost:9097/realms/BaSyx +# +#basyx.feature.authorization.rules.backend=Submodel +#basyx.feature.authorization.rules.backend.submodel.authorization.endpoint=http://localhost:8055/submodels/N0E3MTA0QkRBQjU3RTE4NA== +#basyx.feature.authorization.rules.backend.submodel.authorization.token-endpoint=http://localhost:9097/realms/BaSyx/protocol/openid-connect/token +#basyx.feature.authorization.rules.backend.submodel.authorization.grant-type = CLIENT_CREDENTIALS +#basyx.feature.authorization.rules.backend.submodel.authorization.client-id=workstation-1 +#basyx.feature.authorization.rules.backend.submodel.authorization.client-secret=nY0mjyECF60DGzNmQUjL81XurSl8etom ## This is for preconfiguration of a secured AAS Environment # basyx.aasenvironment.authorization.preconfiguration.token-endpoint=http://localhost:9097/realms/BaSyx/protocol/openid-connect/token From 1327e885d94ecd3b78a445344a1658552c8e71a5 Mon Sep 17 00:00:00 2001 From: Mohammad Ghazanfar Ali Danish Date: Fri, 20 Sep 2024 11:34:50 +0200 Subject: [PATCH 15/20] Fixes CI Signed-off-by: Mohammad Ghazanfar Ali Danish --- ci/docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/docker-compose.yml b/ci/docker-compose.yml index 12f05bf21..10fecd1b9 100644 --- a/ci/docker-compose.yml +++ b/ci/docker-compose.yml @@ -108,7 +108,7 @@ services: BASYX_FEATURE_AUTHORIZATION_ENABLED: true BASYX_FEATURE_AUTHORIZATION_TYPE: rbac BASYX_FEATURE_AUTHORIZATION_JWTBEARERTOKENPROVIDER: keycloak - SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: http://keycloak:8080/realms/BaSyx + SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: http://keycloak-fixed:8080/realms/BaSyx BASYX_FEATURE_AUTHORIZATION_RBAC_FILE: file:/rbac/rbac_rules.json volumes: - ./keycloak/rules/rbac_rules-aas-registry.json:/rbac/rbac_rules.json:ro @@ -126,7 +126,7 @@ services: BASYX_FEATURE_AUTHORIZATION_ENABLED: true BASYX_FEATURE_AUTHORIZATION_TYPE: rbac BASYX_FEATURE_AUTHORIZATION_JWTBEARERTOKENPROVIDER: keycloak - SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: http://keycloak:8080/realms/BaSyx + SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: http://keycloak-fixed:8080/realms/BaSyx BASYX_FEATURE_AUTHORIZATION_RBAC_FILE: file:/rbac/rbac_rules.json volumes: - ./keycloak/rules/rbac_rules-sm-registry.json:/rbac/rbac_rules.json:ro From 934ae772dee77fc46c5762b5b933786307094b59 Mon Sep 17 00:00:00 2001 From: Mohammad Ghazanfar Ali Danish Date: Fri, 20 Sep 2024 11:43:23 +0200 Subject: [PATCH 16/20] Fixes AAS and SM Reg tests Signed-off-by: Mohammad Ghazanfar Ali Danish --- .../tests/integration/AuthorizedAasRegistryTestSuite.java | 2 +- .../feature/authorization/TestAuthorizedSubmodelRegistry.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/basyx.aasregistry/basyx.aasregistry-service-basetests/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/service/tests/integration/AuthorizedAasRegistryTestSuite.java b/basyx.aasregistry/basyx.aasregistry-service-basetests/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/service/tests/integration/AuthorizedAasRegistryTestSuite.java index 8403d5cec..3e825e63b 100644 --- a/basyx.aasregistry/basyx.aasregistry-service-basetests/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/service/tests/integration/AuthorizedAasRegistryTestSuite.java +++ b/basyx.aasregistry/basyx.aasregistry-service-basetests/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/service/tests/integration/AuthorizedAasRegistryTestSuite.java @@ -134,7 +134,7 @@ public void getAllAasDescriptorsWithInsufficientPermissionRole() throws IOExcept String accessToken = getAccessTokenProvider().getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword()); CloseableHttpResponse retrievalResponse = getAllElementsWithAuthorization(aasRegistryBaseUrl, accessToken); - assertEquals(HttpStatus.FORBIDDEN.value(), retrievalResponse.getCode()); + assertEquals(HttpStatus.OK.value(), retrievalResponse.getCode()); } @Test diff --git a/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/regression/feature/authorization/TestAuthorizedSubmodelRegistry.java b/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/regression/feature/authorization/TestAuthorizedSubmodelRegistry.java index f72f1345c..37f359a32 100644 --- a/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/regression/feature/authorization/TestAuthorizedSubmodelRegistry.java +++ b/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/regression/feature/authorization/TestAuthorizedSubmodelRegistry.java @@ -144,7 +144,7 @@ public void getAllSubmodelDescriptorsWithInsufficientPermissionRole() throws IOE String accessToken = tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword()); CloseableHttpResponse retrievalResponse = getAllElementsWithAuthorization(submodelRegistryBaseUrl, accessToken); - assertEquals(HttpStatus.OK.value(), retrievalResponse.getCode()); + assertEquals(HttpStatus.FORBIDDEN.value(), retrievalResponse.getCode()); } @Test From 089dc4016d1293e3cfaf9b5d42e9644da7640c7f Mon Sep 17 00:00:00 2001 From: Mohammad Ghazanfar Ali Danish Date: Fri, 20 Sep 2024 12:02:13 +0200 Subject: [PATCH 17/20] Allows swagger ui api endpoint without auth Signed-off-by: Mohammad Ghazanfar Ali Danish --- .../basyx/authorization/CommonSecurityConfiguration.java | 1 + 1 file changed, 1 insertion(+) diff --git a/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/CommonSecurityConfiguration.java b/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/CommonSecurityConfiguration.java index f255dc426..97a16afd6 100644 --- a/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/CommonSecurityConfiguration.java +++ b/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/CommonSecurityConfiguration.java @@ -49,6 +49,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .authorizeHttpRequests(authorize -> authorize .requestMatchers("/actuator/health/**").permitAll() .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() + .requestMatchers("/swagger-ui/index.html").permitAll() .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2 From 5ff95bfd279cc396f916fc39624a329c1f165acb Mon Sep 17 00:00:00 2001 From: Mohammad Ghazanfar Ali Danish Date: Fri, 20 Sep 2024 15:24:52 +0200 Subject: [PATCH 18/20] Allows swagger ui api endpoint without auth Signed-off-by: Mohammad Ghazanfar Ali Danish --- .../basyx/authorization/CommonSecurityConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/CommonSecurityConfiguration.java b/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/CommonSecurityConfiguration.java index 97a16afd6..ed7a11470 100644 --- a/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/CommonSecurityConfiguration.java +++ b/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/CommonSecurityConfiguration.java @@ -49,7 +49,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .authorizeHttpRequests(authorize -> authorize .requestMatchers("/actuator/health/**").permitAll() .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() - .requestMatchers("/swagger-ui/index.html").permitAll() + .requestMatchers("/swagger-ui/**").permitAll() .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2 From 5c7836af833c0ced2bc11c87ead2f5e6ee4083e0 Mon Sep 17 00:00:00 2001 From: Mohammad Ghazanfar Ali Danish Date: Mon, 7 Oct 2024 13:04:09 +0200 Subject: [PATCH 19/20] Allows Swagger API without authorization Signed-off-by: Mohammad Ghazanfar Ali Danish --- .../basyx/authorization/CommonSecurityConfiguration.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/CommonSecurityConfiguration.java b/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/CommonSecurityConfiguration.java index ed7a11470..b694515c1 100644 --- a/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/CommonSecurityConfiguration.java +++ b/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/CommonSecurityConfiguration.java @@ -50,6 +50,9 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .requestMatchers("/actuator/health/**").permitAll() .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() .requestMatchers("/swagger-ui/**").permitAll() + .requestMatchers("/v3/**").permitAll() + .requestMatchers("/api-docs/**").permitAll() + .requestMatchers("api-docs/swagger-config/**").permitAll() .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2 From 84f7be87803b2753617aedce8fc5b7533d8caa59 Mon Sep 17 00:00:00 2001 From: Mohammad Ghazanfar Ali Danish Date: Mon, 23 Dec 2024 11:10:38 +0100 Subject: [PATCH 20/20] Refactors classes Signed-off-by: Mohammad Ghazanfar Ali Danish --- .github/workflows/basyx_test.yml | 2 - .github/workflows/docker_test.yml | 2 - ...vironmentTargetInformationAdapterTest.java | 2 +- .../AuthorizedAasRegistryStorage.java | 30 +--- .../AasRegistryTargetInformationAdapter.java | 36 ++--- .../TestAuthorizedAasRegistry.java | 2 +- .../AuthorizedAasRegistryTestSuite.java | 2 +- .../AuthorizedAasRepository.java | 27 +--- .../submodel/AasTargetInformationAdapter.java | 33 ++--- .../TestAuthorizedAasRepository.java | 2 +- .../CommonSecurityConfiguration.java | 4 - .../rbac/RbacPermissionResolver.java | 4 - .../rbac/SimpleRbacPermissionResolver.java | 9 -- ...uthorizedConceptDescriptionRepository.java | 27 +--- .../CDTargetInformationAdapterTest.java | 26 ++-- .../TestAuthorizedCDRepository.java | 2 +- .../AuthorizedSubmodelRegistryStorage.java | 32 +---- ...modelRegistryTargetInformationAdapter.java | 49 +++---- ...lRegistryTargetInformationAdapterTest.java | 128 ++++++++---------- .../AuthorizedSubmodelRepository.java | 55 +------- .../SubmodelTargetInformationAdapter.java | 65 +++------ .../SubmodelTargetInformationAdapterTest.java | 128 ++++++++---------- .../TestAuthorizedSubmodelRepository.java | 2 +- 23 files changed, 212 insertions(+), 457 deletions(-) diff --git a/.github/workflows/basyx_test.yml b/.github/workflows/basyx_test.yml index 90143950c..1187244a2 100644 --- a/.github/workflows/basyx_test.yml +++ b/.github/workflows/basyx_test.yml @@ -1,8 +1,6 @@ name: Build and Test BaSyx on: - push: - branches: [ temp-feature/filter-dyn-rbac ] pull_request: branches: [ main ] paths-ignore: diff --git a/.github/workflows/docker_test.yml b/.github/workflows/docker_test.yml index dcd288655..d10df98fa 100644 --- a/.github/workflows/docker_test.yml +++ b/.github/workflows/docker_test.yml @@ -4,8 +4,6 @@ name: Build and Start Docker Images on: - push: - branches: [ temp-feature/filter-dyn-rbac ] pull_request: branches: [ main ] paths-ignore: diff --git a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/AasEnvironmentTargetInformationAdapterTest.java b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/AasEnvironmentTargetInformationAdapterTest.java index 298e3381e..801ac354e 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/AasEnvironmentTargetInformationAdapterTest.java +++ b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/AasEnvironmentTargetInformationAdapterTest.java @@ -46,7 +46,7 @@ import org.eclipse.digitaltwin.basyx.core.exceptions.InvalidTargetInformationException; /** - * Tests {@link AasRegistryTargetInformationAdapter} + * Tests {@link AasEnvironmentTargetInformationAdapter} * * @author danish */ diff --git a/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/feature/authorization/AuthorizedAasRegistryStorage.java b/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/feature/authorization/AuthorizedAasRegistryStorage.java index de3babf34..acacfbeb4 100644 --- a/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/feature/authorization/AuthorizedAasRegistryStorage.java +++ b/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/feature/authorization/AuthorizedAasRegistryStorage.java @@ -28,10 +28,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Objects; import java.util.Set; -import java.util.TreeMap; -import java.util.stream.Collectors; import org.eclipse.digitaltwin.basyx.aasregistry.feature.authorization.rbac.AasRegistryTargetPermissionVerifier; import org.eclipse.digitaltwin.basyx.aasregistry.model.AssetAdministrationShellDescriptor; @@ -46,11 +43,9 @@ import org.eclipse.digitaltwin.basyx.aasregistry.service.storage.DescriptorFilter; import org.eclipse.digitaltwin.basyx.authorization.rbac.Action; import org.eclipse.digitaltwin.basyx.authorization.rbac.RbacPermissionResolver; -import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetInformation; import org.eclipse.digitaltwin.basyx.core.exceptions.InsufficientPermissionException; import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult; import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; -import org.eclipse.digitaltwin.basyx.core.pagination.PaginationSupport; /** * Decorator for authorized {@link AasRegistryStorage} @@ -69,29 +64,8 @@ public AuthorizedAasRegistryStorage(AasRegistryStorage decorated, RbacPermission @Override public CursorResult> getAllAasDescriptors(PaginationInfo pRequest, DescriptorFilter filter) { - boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new AasRegistryTargetInformation(getIdAsList(AasRegistryTargetPermissionVerifier.ALL_ALLOWED_WILDCARD))); - - if (isAuthorized) - return decorated.getAllAasDescriptors(pRequest, filter); - - List targetInformations = permissionResolver.getMatchingTargetInformationInRules(Action.READ, new AasRegistryTargetInformation(getIdAsList(AasRegistryTargetPermissionVerifier.ALL_ALLOWED_WILDCARD))); - - List allIds = targetInformations.stream().map(AasRegistryTargetInformation.class::cast) - .map(AasRegistryTargetInformation::getAasIds).flatMap(List::stream).collect(Collectors.toList()); - - List aasDescriptors = allIds.stream().map(id -> { - try { - return getAasDescriptor(id); - } catch (Exception e) { - return null; - } - }).filter(Objects::nonNull).collect(Collectors.toList()); - - TreeMap aasMap = aasDescriptors.stream().collect(Collectors.toMap(AssetAdministrationShellDescriptor::getId, aas -> aas, (a, b) -> a, TreeMap::new)); - - PaginationSupport paginationSupport = new PaginationSupport<>(aasMap, AssetAdministrationShellDescriptor::getId); - - return paginationSupport.getPaged(pRequest); + assertHasPermission(Action.READ, getIdAsList(AasRegistryTargetPermissionVerifier.ALL_ALLOWED_WILDCARD)); + return decorated.getAllAasDescriptors(pRequest, filter); } @Override diff --git a/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/feature/authorization/rbac/backend/submodel/AasRegistryTargetInformationAdapter.java b/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/feature/authorization/rbac/backend/submodel/AasRegistryTargetInformationAdapter.java index 376eae387..13dab5e90 100644 --- a/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/feature/authorization/rbac/backend/submodel/AasRegistryTargetInformationAdapter.java +++ b/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/feature/authorization/rbac/backend/submodel/AasRegistryTargetInformationAdapter.java @@ -52,14 +52,12 @@ public class AasRegistryTargetInformationAdapter implements TargetInformationAda @Override public SubmodelElementCollection adapt(TargetInformation targetInformation) { - SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder() - .idShort("targetInformation").build(); + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation").build(); SubmodelElementList aasId = new DefaultSubmodelElementList.Builder().idShort("aasIds").build(); Property typeProperty = new DefaultProperty.Builder().idShort("@type").value("aas-registry").build(); - List aasIds = ((AasRegistryTargetInformation) targetInformation).getAasIds().stream() - .map(this::transform).collect(Collectors.toList()); + List aasIds = ((AasRegistryTargetInformation) targetInformation).getAasIds().stream().map(this::transform).collect(Collectors.toList()); aasId.setValue(aasIds); targetInformationSMC.setValue(Arrays.asList(aasId, typeProperty)); @@ -69,30 +67,21 @@ public SubmodelElementCollection adapt(TargetInformation targetInformation) { @Override public TargetInformation adapt(SubmodelElementCollection targetInformation) { - + String targetInformationType = getTargetInformationType(targetInformation); - + if (!targetInformationType.equals("aas-registry")) - throw new InvalidTargetInformationException( - "The TargetInformation @type: " + targetInformationType + " is not compatible with " - + getClass().getName() + "."); + throw new InvalidTargetInformationException("The TargetInformation @type: " + targetInformationType + " is not compatible with " + getClass().getName() + "."); - SubmodelElement aasIdSubmodelElement = targetInformation.getValue().stream() - .filter(sme -> sme.getIdShort().equals("aasIds")).findAny() - .orElseThrow(() -> new InvalidTargetInformationException( - "The TargetInformation defined in the SubmodelElementCollection Rule with id: " - + targetInformation.getIdShort() + " is not compatible with the " - + getClass().getName())); + SubmodelElement aasIdSubmodelElement = targetInformation.getValue().stream().filter(sme -> sme.getIdShort().equals("aasIds")).findAny().orElseThrow( + () -> new InvalidTargetInformationException("The TargetInformation defined in the SubmodelElementCollection Rule with id: " + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName())); if (!(aasIdSubmodelElement instanceof SubmodelElementList)) - throw new InvalidTargetInformationException( - "The TargetInformation defined in the SubmodelElementCollection Rule with id: " - + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName()); + throw new InvalidTargetInformationException("The TargetInformation defined in the SubmodelElementCollection Rule with id: " + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName()); SubmodelElementList aasIdList = (SubmodelElementList) aasIdSubmodelElement; - List aasIds = aasIdList.getValue().stream().map(Property.class::cast).map(Property::getValue) - .collect(Collectors.toList()); + List aasIds = aasIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).collect(Collectors.toList()); return new AasRegistryTargetInformation(aasIds); } @@ -103,11 +92,8 @@ private Property transform(String aasId) { private String getTargetInformationType(SubmodelElementCollection targetInformation) { - Property typeProperty = (Property) targetInformation.getValue().stream() - .filter(sme -> sme.getIdShort().equals("@type")).findAny() - .orElseThrow(() -> new InvalidTargetInformationException( - "The TargetInformation defined in the SubmodelElementCollection Rule with id: " - + targetInformation.getIdShort() + " does not have @type definition")); + Property typeProperty = (Property) targetInformation.getValue().stream().filter(sme -> sme.getIdShort().equals("@type")).findAny() + .orElseThrow(() -> new InvalidTargetInformationException("The TargetInformation defined in the SubmodelElementCollection Rule with id: " + targetInformation.getIdShort() + " does not have @type definition")); return typeProperty.getValue(); } diff --git a/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasregistry/regression/feature/authorization/TestAuthorizedAasRegistry.java b/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasregistry/regression/feature/authorization/TestAuthorizedAasRegistry.java index 31d3f5b02..e233ab306 100644 --- a/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasregistry/regression/feature/authorization/TestAuthorizedAasRegistry.java +++ b/basyx.aasregistry/basyx.aasregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasregistry/regression/feature/authorization/TestAuthorizedAasRegistry.java @@ -144,7 +144,7 @@ public void getAllAasDescriptorsWithInsufficientPermissionRole() throws IOExcept String accessToken = tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword()); CloseableHttpResponse retrievalResponse = getAllElementsWithAuthorization(aasRegistryBaseUrl, accessToken); - assertEquals(HttpStatus.OK.value(), retrievalResponse.getCode()); + assertEquals(HttpStatus.FORBIDDEN.value(), retrievalResponse.getCode()); } @Test diff --git a/basyx.aasregistry/basyx.aasregistry-service-basetests/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/service/tests/integration/AuthorizedAasRegistryTestSuite.java b/basyx.aasregistry/basyx.aasregistry-service-basetests/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/service/tests/integration/AuthorizedAasRegistryTestSuite.java index 3e825e63b..8403d5cec 100644 --- a/basyx.aasregistry/basyx.aasregistry-service-basetests/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/service/tests/integration/AuthorizedAasRegistryTestSuite.java +++ b/basyx.aasregistry/basyx.aasregistry-service-basetests/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/service/tests/integration/AuthorizedAasRegistryTestSuite.java @@ -134,7 +134,7 @@ public void getAllAasDescriptorsWithInsufficientPermissionRole() throws IOExcept String accessToken = getAccessTokenProvider().getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword()); CloseableHttpResponse retrievalResponse = getAllElementsWithAuthorization(aasRegistryBaseUrl, accessToken); - assertEquals(HttpStatus.OK.value(), retrievalResponse.getCode()); + assertEquals(HttpStatus.FORBIDDEN.value(), retrievalResponse.getCode()); } @Test diff --git a/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/AuthorizedAasRepository.java b/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/AuthorizedAasRepository.java index 243351917..a4f128864 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/AuthorizedAasRepository.java +++ b/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/AuthorizedAasRepository.java @@ -30,9 +30,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Objects; -import java.util.TreeMap; -import java.util.stream.Collectors; import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell; import org.eclipse.digitaltwin.aas4j.v3.model.AssetInformation; @@ -40,13 +37,11 @@ import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; import org.eclipse.digitaltwin.basyx.authorization.rbac.Action; import org.eclipse.digitaltwin.basyx.authorization.rbac.RbacPermissionResolver; -import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetInformation; import org.eclipse.digitaltwin.basyx.core.exceptions.CollidingIdentifierException; import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException; import org.eclipse.digitaltwin.basyx.core.exceptions.InsufficientPermissionException; import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult; import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; -import org.eclipse.digitaltwin.basyx.core.pagination.PaginationSupport; /** * Decorator for authorized {@link AasRepository} @@ -69,27 +64,9 @@ public AuthorizedAasRepository(AasRepository decorated, RbacPermissionResolver> getAllAas(PaginationInfo pInfo) { boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new AasTargetInformation(getIdAsList("*"))); - if (isAuthorized) - return decorated.getAllAas(pInfo); - - List targetInformations = permissionResolver.getMatchingTargetInformationInRules(Action.READ, new AasTargetInformation(getIdAsList("*"))); - - List allIds = targetInformations.stream().map(AasTargetInformation.class::cast) - .map(AasTargetInformation::getAasIds).flatMap(List::stream).collect(Collectors.toList()); - - List aasDescriptors = allIds.stream().map(id -> { - try { - return getAas(id); - } catch (Exception e) { - return null; - } - }).filter(Objects::nonNull).collect(Collectors.toList()); + throwExceptionIfInsufficientPermission(isAuthorized); - TreeMap aasMap = aasDescriptors.stream().collect(Collectors.toMap(AssetAdministrationShell::getId, aas -> aas, (a, b) -> a, TreeMap::new)); - - PaginationSupport paginationSupport = new PaginationSupport<>(aasMap, AssetAdministrationShell::getId); - - return paginationSupport.getPaged(pInfo); + return decorated.getAllAas(pInfo); } @Override diff --git a/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/rbac/backend/submodel/AasTargetInformationAdapter.java b/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/rbac/backend/submodel/AasTargetInformationAdapter.java index db29c1460..2bba6fe74 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/rbac/backend/submodel/AasTargetInformationAdapter.java +++ b/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/rbac/backend/submodel/AasTargetInformationAdapter.java @@ -52,14 +52,12 @@ public class AasTargetInformationAdapter implements TargetInformationAdapter { @Override public SubmodelElementCollection adapt(TargetInformation targetInformation) { - SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder() - .idShort("targetInformation").build(); + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation").build(); SubmodelElementList aasId = new DefaultSubmodelElementList.Builder().idShort("aasIds").build(); Property typeProperty = new DefaultProperty.Builder().idShort("@type").value("aas").build(); - List aasIds = ((AasTargetInformation) targetInformation).getAasIds().stream() - .map(this::transform).collect(Collectors.toList()); + List aasIds = ((AasTargetInformation) targetInformation).getAasIds().stream().map(this::transform).collect(Collectors.toList()); aasId.setValue(aasIds); targetInformationSMC.setValue(Arrays.asList(aasId, typeProperty)); @@ -73,25 +71,17 @@ public TargetInformation adapt(SubmodelElementCollection targetInformation) { String targetInformationType = getTargetInformationType(targetInformation); if (!targetInformationType.equals("aas")) - throw new InvalidTargetInformationException("The TargetInformation @type: " + targetInformationType - + " is not compatible with " + getClass().getName() + "."); + throw new InvalidTargetInformationException("The TargetInformation @type: " + targetInformationType + " is not compatible with " + getClass().getName() + "."); - SubmodelElement aasIdSubmodelElement = targetInformation.getValue().stream() - .filter(sme -> sme.getIdShort().equals("aasIds")).findAny() - .orElseThrow(() -> new InvalidTargetInformationException( - "The TargetInformation defined in the SubmodelElementCollection Rule with id: " - + targetInformation.getIdShort() + " is not compatible with the " - + getClass().getName())); + SubmodelElement aasIdSubmodelElement = targetInformation.getValue().stream().filter(sme -> sme.getIdShort().equals("aasIds")).findAny().orElseThrow( + () -> new InvalidTargetInformationException("The TargetInformation defined in the SubmodelElementCollection Rule with id: " + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName())); if (!(aasIdSubmodelElement instanceof SubmodelElementList)) - throw new InvalidTargetInformationException( - "The TargetInformation defined in the SubmodelElementCollection Rule with id: " - + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName()); + throw new InvalidTargetInformationException("The TargetInformation defined in the SubmodelElementCollection Rule with id: " + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName()); SubmodelElementList aasIdList = (SubmodelElementList) aasIdSubmodelElement; - List aasIds = aasIdList.getValue().stream().map(Property.class::cast).map(Property::getValue) - .collect(Collectors.toList()); + List aasIds = aasIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).collect(Collectors.toList()); return new AasTargetInformation(aasIds); } @@ -99,14 +89,11 @@ public TargetInformation adapt(SubmodelElementCollection targetInformation) { private Property transform(String aasId) { return new DefaultProperty.Builder().value(aasId).build(); } - + private String getTargetInformationType(SubmodelElementCollection targetInformation) { - Property typeProperty = (Property) targetInformation.getValue().stream() - .filter(sme -> sme.getIdShort().equals("@type")).findAny() - .orElseThrow(() -> new InvalidTargetInformationException( - "The TargetInformation defined in the SubmodelElementCollection Rule with id: " - + targetInformation.getIdShort() + " does not have @type definition")); + Property typeProperty = (Property) targetInformation.getValue().stream().filter(sme -> sme.getIdShort().equals("@type")).findAny() + .orElseThrow(() -> new InvalidTargetInformationException("The TargetInformation defined in the SubmodelElementCollection Rule with id: " + targetInformation.getIdShort() + " does not have @type definition")); return typeProperty.getValue(); } diff --git a/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/TestAuthorizedAasRepository.java b/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/TestAuthorizedAasRepository.java index 9d08021b4..83c561788 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/TestAuthorizedAasRepository.java +++ b/basyx.aasrepository/basyx.aasrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/TestAuthorizedAasRepository.java @@ -131,7 +131,7 @@ public void getAllAasWithInsufficientPermissionRole() throws IOException { String accessToken = tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword()); CloseableHttpResponse retrievalResponse = getAllAasWithAuthorization(accessToken); - assertEquals(HttpStatus.OK.value(), retrievalResponse.getCode()); + assertEquals(HttpStatus.FORBIDDEN.value(), retrievalResponse.getCode()); } @Test diff --git a/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/CommonSecurityConfiguration.java b/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/CommonSecurityConfiguration.java index b694515c1..f255dc426 100644 --- a/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/CommonSecurityConfiguration.java +++ b/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/CommonSecurityConfiguration.java @@ -49,10 +49,6 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .authorizeHttpRequests(authorize -> authorize .requestMatchers("/actuator/health/**").permitAll() .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() - .requestMatchers("/swagger-ui/**").permitAll() - .requestMatchers("/v3/**").permitAll() - .requestMatchers("/api-docs/**").permitAll() - .requestMatchers("api-docs/swagger-config/**").permitAll() .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2 diff --git a/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/rbac/RbacPermissionResolver.java b/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/rbac/RbacPermissionResolver.java index bc697415e..d8165b807 100644 --- a/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/rbac/RbacPermissionResolver.java +++ b/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/rbac/RbacPermissionResolver.java @@ -25,8 +25,6 @@ package org.eclipse.digitaltwin.basyx.authorization.rbac; -import java.util.List; - /** * An interface for resolving Rbac permissions * @@ -36,6 +34,4 @@ public interface RbacPermissionResolver { public boolean hasPermission(Action action, T targetInformation); - public List getMatchingTargetInformationInRules(Action action, T targetInformation); - } diff --git a/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/rbac/SimpleRbacPermissionResolver.java b/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/rbac/SimpleRbacPermissionResolver.java index a760a0f56..f2b4be32c 100644 --- a/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/rbac/SimpleRbacPermissionResolver.java +++ b/basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/rbac/SimpleRbacPermissionResolver.java @@ -79,15 +79,6 @@ public boolean hasPermission(final Action action, final T targetInformation) { return matchingRule.isPresent(); } - - public List getMatchingTargetInformationInRules(final Action action, final T targetInformation) { - - List roles = roleAuthenticator.getRoles(); - - List filteredRbacRulesForTargetInfos = roles.stream().map(role -> RbacRuleKeyGenerator.generateKey(role, action.toString(), targetInformation.getClass().getName())).filter(rbacStorage::exist).map(rbacStorage::getRbacRule).collect(Collectors.toList()); - - return filteredRbacRulesForTargetInfos.stream().map(rbacRule -> rbacRule.getTargetInformation()).collect(Collectors.toList()); - } private Stream getMatchingRules(final List roles, final Action action, final T targetInformation) { diff --git a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/AuthorizedConceptDescriptionRepository.java b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/AuthorizedConceptDescriptionRepository.java index 97b6fbcf3..ec181040c 100644 --- a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/AuthorizedConceptDescriptionRepository.java +++ b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/AuthorizedConceptDescriptionRepository.java @@ -28,15 +28,11 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Objects; -import java.util.TreeMap; -import java.util.stream.Collectors; import org.eclipse.digitaltwin.aas4j.v3.model.ConceptDescription; import org.eclipse.digitaltwin.aas4j.v3.model.Reference; import org.eclipse.digitaltwin.basyx.authorization.rbac.Action; import org.eclipse.digitaltwin.basyx.authorization.rbac.RbacPermissionResolver; -import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetInformation; import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.ConceptDescriptionRepository; import org.eclipse.digitaltwin.basyx.core.exceptions.CollidingIdentifierException; import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException; @@ -44,7 +40,6 @@ import org.eclipse.digitaltwin.basyx.core.exceptions.MissingIdentifierException; import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult; import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; -import org.eclipse.digitaltwin.basyx.core.pagination.PaginationSupport; /** * Decorator for authorized {@link ConceptDescriptionRepository} @@ -66,27 +61,9 @@ public AuthorizedConceptDescriptionRepository(ConceptDescriptionRepository decor public CursorResult> getAllConceptDescriptions(PaginationInfo pInfo) { boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new ConceptDescriptionTargetInformation(getIdAsList("*"))); - if (isAuthorized) - return decorated.getAllConceptDescriptions(pInfo); - - List targetInformations = permissionResolver.getMatchingTargetInformationInRules(Action.READ, new ConceptDescriptionTargetInformation(getIdAsList("*"))); - - List allIds = targetInformations.stream().map(ConceptDescriptionTargetInformation.class::cast) - .map(ConceptDescriptionTargetInformation::getConceptDescriptionIds).flatMap(List::stream).collect(Collectors.toList()); - - List conceptDesc = allIds.stream().map(id -> { - try { - return getConceptDescription(id); - } catch (Exception e) { - return null; - } - }).filter(Objects::nonNull).collect(Collectors.toList()); + throwExceptionIfInsufficientPermission(isAuthorized); - TreeMap aasMap = conceptDesc.stream().collect(Collectors.toMap(ConceptDescription::getId, aas -> aas, (a, b) -> a, TreeMap::new)); - - PaginationSupport paginationSupport = new PaginationSupport<>(aasMap, ConceptDescription::getId); - - return paginationSupport.getPaged(pInfo); + return decorated.getAllConceptDescriptions(pInfo); } @Override diff --git a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/CDTargetInformationAdapterTest.java b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/CDTargetInformationAdapterTest.java index 8b90f8b87..e75950638 100644 --- a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/CDTargetInformationAdapterTest.java +++ b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/CDTargetInformationAdapterTest.java @@ -46,7 +46,7 @@ import org.eclipse.digitaltwin.basyx.core.exceptions.InvalidTargetInformationException; /** - * Tests {@link AasRegistryTargetInformationAdapter} + * Tests {@link CDTargetInformationAdapter} * * @author danish */ @@ -72,14 +72,14 @@ public void testAdaptTargetInformationToSubmodelElementCollection() { List elements = result.getValue(); assertEquals(2, elements.size()); - SubmodelElementList aasIdList = (SubmodelElementList) elements.get(0); - assertEquals("conceptDescriptionIds", aasIdList.getIdShort()); + SubmodelElementList cdIdList = (SubmodelElementList) elements.get(0); + assertEquals("conceptDescriptionIds", cdIdList.getIdShort()); Property typeProperty = (Property) elements.get(1); assertEquals("@type", typeProperty.getIdShort()); - List actualAasIds = aasIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).map(String::valueOf).collect(Collectors.toList()); - assertEquals(conceptDescriptiornIds, actualAasIds); + List actualCDIds = cdIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).map(String::valueOf).collect(Collectors.toList()); + assertEquals(conceptDescriptiornIds, actualCDIds); String actualType = typeProperty.getValue(); assertTrue(actualType.equals("concept-description")); @@ -91,12 +91,12 @@ public void testAdaptSubmodelElementCollectionToTargetInformation() { List expectedCDIds = Arrays.asList("cdId1", "cdId2"); String type = "concept-description"; - List aasIdProperties = expectedCDIds.stream().map(aasId -> new DefaultProperty.Builder().value(aasId).build()).collect(Collectors.toList()); + List cdIdProperties = expectedCDIds.stream().map(aasId -> new DefaultProperty.Builder().value(aasId).build()).collect(Collectors.toList()); - SubmodelElementList aasIdList = new DefaultSubmodelElementList.Builder().idShort("conceptDescriptionIds").value(aasIdProperties).build(); + SubmodelElementList cdIdList = new DefaultSubmodelElementList.Builder().idShort("conceptDescriptionIds").value(cdIdProperties).build(); SubmodelElement typeProperty = createTypeProperty(type); - SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation").value(Arrays.asList(aasIdList, typeProperty)).build(); + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation").value(Arrays.asList(cdIdList, typeProperty)).build(); TargetInformation result = cdTargetInformationAdapter.adapt(targetInformationSMC); @@ -105,7 +105,7 @@ public void testAdaptSubmodelElementCollectionToTargetInformation() { } @Test - public void testAdaptTargetInformationWithEmptyAasIds() { + public void testAdaptTargetInformationWithEmptyCDIds() { List cdIds = Collections.emptyList(); TargetInformation targetInformation = new ConceptDescriptionTargetInformation(cdIds); @@ -117,18 +117,18 @@ public void testAdaptTargetInformationWithEmptyAasIds() { List elements = result.getValue(); assertEquals(2, elements.size()); - SubmodelElementList aasIdList = (SubmodelElementList) elements.get(0); - assertEquals("conceptDescriptionIds", aasIdList.getIdShort()); + SubmodelElementList cdIdList = (SubmodelElementList) elements.get(0); + assertEquals("conceptDescriptionIds", cdIdList.getIdShort()); Property typeProperty = (Property) elements.get(1); assertEquals("@type", typeProperty.getIdShort()); - List actualAasIds = aasIdList.getValue().stream() + List actualCDIds = cdIdList.getValue().stream() .map(Property.class::cast) .map(Property::getValue) .map(String::valueOf) .collect(Collectors.toList()); - assertTrue(actualAasIds.isEmpty()); + assertTrue(actualCDIds.isEmpty()); String actualType = typeProperty.getValue(); assertTrue(actualType.equals("concept-description")); diff --git a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/TestAuthorizedCDRepository.java b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/TestAuthorizedCDRepository.java index 80befd8b2..7956a08ad 100644 --- a/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/TestAuthorizedCDRepository.java +++ b/basyx.conceptdescriptionrepository/basyx.conceptdescriptionrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/conceptdescriptionrepository/feature/authorization/TestAuthorizedCDRepository.java @@ -113,7 +113,7 @@ public void getAllCDWithInsufficientPermissionRole() throws IOException { String accessToken = tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword()); CloseableHttpResponse retrievalResponse = getAllCDWithAuthorization(accessToken); - assertEquals(HttpStatus.OK.value(), retrievalResponse.getCode()); + assertEquals(HttpStatus.FORBIDDEN.value(), retrievalResponse.getCode()); } @Test diff --git a/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/feature/authorization/AuthorizedSubmodelRegistryStorage.java b/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/feature/authorization/AuthorizedSubmodelRegistryStorage.java index 9b0efa417..74887d22e 100644 --- a/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/feature/authorization/AuthorizedSubmodelRegistryStorage.java +++ b/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/feature/authorization/AuthorizedSubmodelRegistryStorage.java @@ -28,18 +28,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Objects; import java.util.Set; -import java.util.TreeMap; -import java.util.stream.Collectors; - import org.eclipse.digitaltwin.basyx.authorization.rbac.Action; import org.eclipse.digitaltwin.basyx.authorization.rbac.RbacPermissionResolver; -import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetInformation; import org.eclipse.digitaltwin.basyx.core.exceptions.InsufficientPermissionException; import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult; import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; -import org.eclipse.digitaltwin.basyx.core.pagination.PaginationSupport; import org.eclipse.digitaltwin.basyx.submodelregistry.feature.authorization.rbac.SubmodelRegistryTargetPermissionVerifier; import org.eclipse.digitaltwin.basyx.submodelregistry.model.SubmodelDescriptor; import org.eclipse.digitaltwin.basyx.submodelregistry.service.errors.SubmodelAlreadyExistsException; @@ -63,30 +57,8 @@ public AuthorizedSubmodelRegistryStorage(SubmodelRegistryStorage decorated, Rbac @Override public CursorResult> getAllSubmodelDescriptors(PaginationInfo pRequest) { - boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelRegistryTargetInformation(getIdAsList(SubmodelRegistryTargetPermissionVerifier.ALL_ALLOWED_WILDCARD))); - throwExceptionIfInsufficientPermission(isAuthorized); - - if (isAuthorized) - return decorated.getAllSubmodelDescriptors(pRequest); - - List targetInformations = permissionResolver.getMatchingTargetInformationInRules(Action.READ, new SubmodelRegistryTargetInformation(getIdAsList(SubmodelRegistryTargetPermissionVerifier.ALL_ALLOWED_WILDCARD))); - - List allIds = targetInformations.stream().map(SubmodelRegistryTargetInformation.class::cast) - .map(SubmodelRegistryTargetInformation::getSubmodelIds).flatMap(List::stream).collect(Collectors.toList()); - - List aasDescriptors = allIds.stream().map(id -> { - try { - return getSubmodelDescriptor(id); - } catch (Exception e) { - return null; - } - }).filter(Objects::nonNull).collect(Collectors.toList()); - - TreeMap aasMap = aasDescriptors.stream().collect(Collectors.toMap(SubmodelDescriptor::getId, aas -> aas, (a, b) -> a, TreeMap::new)); - - PaginationSupport paginationSupport = new PaginationSupport<>(aasMap, SubmodelDescriptor::getId); - - return paginationSupport.getPaged(pRequest); + assertHasPermission(Action.READ, getIdAsList(SubmodelRegistryTargetPermissionVerifier.ALL_ALLOWED_WILDCARD)); + return decorated.getAllSubmodelDescriptors(pRequest); } @Override diff --git a/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/feature/authorization/rbac/backend/submodel/SubmodelRegistryTargetInformationAdapter.java b/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/feature/authorization/rbac/backend/submodel/SubmodelRegistryTargetInformationAdapter.java index 0e9f68c14..10ec7e8c4 100644 --- a/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/feature/authorization/rbac/backend/submodel/SubmodelRegistryTargetInformationAdapter.java +++ b/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/feature/authorization/rbac/backend/submodel/SubmodelRegistryTargetInformationAdapter.java @@ -42,7 +42,7 @@ import org.eclipse.digitaltwin.basyx.submodelregistry.feature.authorization.SubmodelRegistryTargetInformation; /** - * An implementation of the {@link TargetInformationAdapter} to adapt with Aas + * An implementation of the {@link TargetInformationAdapter} to adapt with Submodel Registry * {@link TargetInformation} * * @author danish @@ -52,17 +52,15 @@ public class SubmodelRegistryTargetInformationAdapter implements TargetInformati @Override public SubmodelElementCollection adapt(TargetInformation targetInformation) { - SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder() - .idShort("targetInformation").build(); + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation").build(); - SubmodelElementList aasId = new DefaultSubmodelElementList.Builder().idShort("submodelIds").build(); + SubmodelElementList submodelId = new DefaultSubmodelElementList.Builder().idShort("submodelIds").build(); Property typeProperty = new DefaultProperty.Builder().idShort("@type").value("submodel-registry").build(); - List aasIds = ((SubmodelRegistryTargetInformation) targetInformation).getSubmodelIds().stream() - .map(this::transform).collect(Collectors.toList()); - aasId.setValue(aasIds); + List submodelIds = ((SubmodelRegistryTargetInformation) targetInformation).getSubmodelIds().stream().map(this::transform).collect(Collectors.toList()); + submodelId.setValue(submodelIds); - targetInformationSMC.setValue(Arrays.asList(aasId, typeProperty)); + targetInformationSMC.setValue(Arrays.asList(submodelId, typeProperty)); return targetInformationSMC; } @@ -73,40 +71,29 @@ public TargetInformation adapt(SubmodelElementCollection targetInformation) { String targetInformationType = getTargetInformationType(targetInformation); if (!targetInformationType.equals("submodel-registry")) - throw new InvalidTargetInformationException("The TargetInformation @type: " + targetInformationType - + " is not compatible with " + getClass().getName() + "."); + throw new InvalidTargetInformationException("The TargetInformation @type: " + targetInformationType + " is not compatible with " + getClass().getName() + "."); - SubmodelElement aasIdSubmodelElement = targetInformation.getValue().stream() - .filter(sme -> sme.getIdShort().equals("submodelIds")).findAny() - .orElseThrow(() -> new InvalidTargetInformationException( - "The TargetInformation defined in the SubmodelElementCollection Rule with id: " - + targetInformation.getIdShort() + " is not compatible with the " - + getClass().getName())); + SubmodelElement submodelIdSubmodelElement = targetInformation.getValue().stream().filter(sme -> sme.getIdShort().equals("submodelIds")).findAny().orElseThrow( + () -> new InvalidTargetInformationException("The TargetInformation defined in the SubmodelElementCollection Rule with id: " + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName())); - if (!(aasIdSubmodelElement instanceof SubmodelElementList)) - throw new InvalidTargetInformationException( - "The TargetInformation defined in the SubmodelElementCollection Rule with id: " - + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName()); + if (!(submodelIdSubmodelElement instanceof SubmodelElementList)) + throw new InvalidTargetInformationException("The TargetInformation defined in the SubmodelElementCollection Rule with id: " + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName()); - SubmodelElementList aasIdList = (SubmodelElementList) aasIdSubmodelElement; + SubmodelElementList submodelIdList = (SubmodelElementList) submodelIdSubmodelElement; - List aasIds = aasIdList.getValue().stream().map(Property.class::cast).map(Property::getValue) - .collect(Collectors.toList()); + List submodelIds = submodelIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).collect(Collectors.toList()); - return new SubmodelRegistryTargetInformation(aasIds); + return new SubmodelRegistryTargetInformation(submodelIds); } - private Property transform(String aasId) { - return new DefaultProperty.Builder().value(aasId).build(); + private Property transform(String submodelId) { + return new DefaultProperty.Builder().value(submodelId).build(); } private String getTargetInformationType(SubmodelElementCollection targetInformation) { - Property typeProperty = (Property) targetInformation.getValue().stream() - .filter(sme -> sme.getIdShort().equals("@type")).findAny() - .orElseThrow(() -> new InvalidTargetInformationException( - "The TargetInformation defined in the SubmodelElementCollection Rule with id: " - + targetInformation.getIdShort() + " does not have @type definition")); + Property typeProperty = (Property) targetInformation.getValue().stream().filter(sme -> sme.getIdShort().equals("@type")).findAny() + .orElseThrow(() -> new InvalidTargetInformationException("The TargetInformation defined in the SubmodelElementCollection Rule with id: " + targetInformation.getIdShort() + " does not have @type definition")); return typeProperty.getValue(); } diff --git a/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/regression/feature/authorization/SubmodelRegistryTargetInformationAdapterTest.java b/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/regression/feature/authorization/SubmodelRegistryTargetInformationAdapterTest.java index 1b3836fd6..e50b29404 100644 --- a/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/regression/feature/authorization/SubmodelRegistryTargetInformationAdapterTest.java +++ b/basyx.submodelregistry/basyx.submodelregistry-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/regression/feature/authorization/SubmodelRegistryTargetInformationAdapterTest.java @@ -47,117 +47,109 @@ import org.eclipse.digitaltwin.basyx.submodelregistry.feature.authorization.rbac.backend.submodel.SubmodelRegistryTargetInformationAdapter; /** - * Tests {@link AasRegistryTargetInformationAdapter} + * Tests {@link SubmodelRegistryTargetInformationAdapter} * * @author danish */ public class SubmodelRegistryTargetInformationAdapterTest { - private SubmodelRegistryTargetInformationAdapter aasRegistryTargetInformationAdapter; + private SubmodelRegistryTargetInformationAdapter smRegistryTargetInformationAdapter; @Before public void setUp() { - aasRegistryTargetInformationAdapter = new SubmodelRegistryTargetInformationAdapter(); + smRegistryTargetInformationAdapter = new SubmodelRegistryTargetInformationAdapter(); } @Test public void testAdaptTargetInformationToSubmodelElementCollection() { - List aasIds = Arrays.asList("aasId1", "aasId2"); - TargetInformation targetInformation = new SubmodelRegistryTargetInformation(aasIds); + List submodelIds = Arrays.asList("aasId1", "aasId2"); + TargetInformation targetInformation = new SubmodelRegistryTargetInformation(submodelIds); - SubmodelElementCollection result = aasRegistryTargetInformationAdapter.adapt(targetInformation); + SubmodelElementCollection result = smRegistryTargetInformationAdapter.adapt(targetInformation); assertEquals("targetInformation", result.getIdShort()); List elements = result.getValue(); assertEquals(2, elements.size()); - SubmodelElementList aasIdList = (SubmodelElementList) elements.get(0); - assertEquals("submodelIds", aasIdList.getIdShort()); - + SubmodelElementList submodelIdList = (SubmodelElementList) elements.get(0); + assertEquals("submodelIds", submodelIdList.getIdShort()); + Property typeProperty = (Property) elements.get(1); assertEquals("@type", typeProperty.getIdShort()); - List actualAasIds = aasIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).map(String::valueOf).collect(Collectors.toList()); - assertEquals(aasIds, actualAasIds); - + List actualSubmodelIds = submodelIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).map(String::valueOf).collect(Collectors.toList()); + assertEquals(submodelIds, actualSubmodelIds); + String actualType = typeProperty.getValue(); - assertTrue(actualType.equals("submodel-registry")); + assertTrue(actualType.equals("submodel-registry")); } @Test public void testAdaptSubmodelElementCollectionToTargetInformation() { - List expectedAasIds = Arrays.asList("aasId1", "aasId2"); + List expectedSubmodelIds = Arrays.asList("aasId1", "aasId2"); String type = "submodel-registry"; - - List aasIdProperties = expectedAasIds.stream().map(aasId -> new DefaultProperty.Builder().value(aasId).build()).collect(Collectors.toList()); - SubmodelElementList aasIdList = new DefaultSubmodelElementList.Builder().idShort("submodelIds").value(aasIdProperties).build(); + List submodelIdProperties = expectedSubmodelIds.stream().map(smId -> new DefaultProperty.Builder().value(smId).build()).collect(Collectors.toList()); + + SubmodelElementList submodelIdList = new DefaultSubmodelElementList.Builder().idShort("submodelIds").value(submodelIdProperties).build(); SubmodelElement typeProperty = createTypeProperty(type); - - SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation").value(Arrays.asList(aasIdList, typeProperty)).build(); - TargetInformation result = aasRegistryTargetInformationAdapter.adapt(targetInformationSMC); + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation").value(Arrays.asList(submodelIdList, typeProperty)).build(); + + TargetInformation result = smRegistryTargetInformationAdapter.adapt(targetInformationSMC); assertTrue(result instanceof SubmodelRegistryTargetInformation); - assertEquals(expectedAasIds, ((SubmodelRegistryTargetInformation) result).getSubmodelIds()); + assertEquals(expectedSubmodelIds, ((SubmodelRegistryTargetInformation) result).getSubmodelIds()); } - - @Test - public void testAdaptTargetInformationWithEmptyAasIds() { - - List aasIds = Collections.emptyList(); - TargetInformation targetInformation = new SubmodelRegistryTargetInformation(aasIds); - SubmodelElementCollection result = aasRegistryTargetInformationAdapter.adapt(targetInformation); + @Test + public void testAdaptTargetInformationWithEmptySubmodelIds() { + + List submodelIds = Collections.emptyList(); + TargetInformation targetInformation = new SubmodelRegistryTargetInformation(submodelIds); - assertEquals("targetInformation", result.getIdShort()); + SubmodelElementCollection result = smRegistryTargetInformationAdapter.adapt(targetInformation); - List elements = result.getValue(); - assertEquals(2, elements.size()); + assertEquals("targetInformation", result.getIdShort()); - SubmodelElementList aasIdList = (SubmodelElementList) elements.get(0); - assertEquals("submodelIds", aasIdList.getIdShort()); - - Property typeProperty = (Property) elements.get(1); + List elements = result.getValue(); + assertEquals(2, elements.size()); + + SubmodelElementList submodelIdList = (SubmodelElementList) elements.get(0); + assertEquals("submodelIds", submodelIdList.getIdShort()); + + Property typeProperty = (Property) elements.get(1); assertEquals("@type", typeProperty.getIdShort()); - List actualAasIds = aasIdList.getValue().stream() - .map(Property.class::cast) - .map(Property::getValue) - .map(String::valueOf) - .collect(Collectors.toList()); - assertTrue(actualAasIds.isEmpty()); - - String actualType = typeProperty.getValue(); - assertTrue(actualType.equals("submodel-registry")); - } - - @Test(expected = InvalidTargetInformationException.class) - public void testAdaptSubmodelElementCollectionWithInvalidStructure() { - - SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation") - .value(Collections.singletonList(new DefaultProperty.Builder().idShort("invalidElement").value("value").build())) - .build(); - - aasRegistryTargetInformationAdapter.adapt(targetInformationSMC); - } - - @Test(expected = InvalidTargetInformationException.class) - public void testAdaptSubmodelElementCollectionWithoutAasIds() { - - SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation") - .value(Collections.emptyList()) - .build(); - - aasRegistryTargetInformationAdapter.adapt(targetInformationSMC); - } - - private SubmodelElement createTypeProperty(String type) { - return new DefaultProperty.Builder().idShort("@type").value(type).build(); + List actualSubmodelIds = submodelIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).map(String::valueOf).collect(Collectors.toList()); + assertTrue(actualSubmodelIds.isEmpty()); + + String actualType = typeProperty.getValue(); + assertTrue(actualType.equals("submodel-registry")); + } + + @Test(expected = InvalidTargetInformationException.class) + public void testAdaptSubmodelElementCollectionWithInvalidStructure() { + + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation") + .value(Collections.singletonList(new DefaultProperty.Builder().idShort("invalidElement").value("value").build())).build(); + + smRegistryTargetInformationAdapter.adapt(targetInformationSMC); } + @Test(expected = InvalidTargetInformationException.class) + public void testAdaptSubmodelElementCollectionWithoutSubmodelIds() { + + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation").value(Collections.emptyList()).build(); + + smRegistryTargetInformationAdapter.adapt(targetInformationSMC); + } + + private SubmodelElement createTypeProperty(String type) { + return new DefaultProperty.Builder().idShort("@type").value(type).build(); + } } diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepository.java index b952820cc..2b94ecaac 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepository.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepository.java @@ -30,16 +30,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Objects; -import java.util.TreeMap; -import java.util.stream.Collectors; import org.eclipse.digitaltwin.aas4j.v3.model.OperationVariable; import org.eclipse.digitaltwin.aas4j.v3.model.Submodel; import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement; import org.eclipse.digitaltwin.basyx.authorization.rbac.Action; import org.eclipse.digitaltwin.basyx.authorization.rbac.RbacPermissionResolver; -import org.eclipse.digitaltwin.basyx.authorization.rbac.TargetInformation; import org.eclipse.digitaltwin.basyx.core.exceptions.CollidingIdentifierException; import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException; import org.eclipse.digitaltwin.basyx.core.exceptions.ElementNotAFileException; @@ -47,7 +43,6 @@ import org.eclipse.digitaltwin.basyx.core.exceptions.InsufficientPermissionException; import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult; import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; -import org.eclipse.digitaltwin.basyx.core.pagination.PaginationSupport; import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository; import org.eclipse.digitaltwin.basyx.submodelservice.value.SubmodelElementValue; import org.eclipse.digitaltwin.basyx.submodelservice.value.SubmodelValueOnly; @@ -73,27 +68,9 @@ public AuthorizedSubmodelRepository(SubmodelRepository decorated, RbacPermission public CursorResult> getAllSubmodels(PaginationInfo pInfo) { boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(getIdAsList(ALL_ALLOWED_WILDCARD), getIdAsList(ALL_ALLOWED_WILDCARD))); - if (isAuthorized) - return decorated.getAllSubmodels(pInfo); - - List targetInformations = permissionResolver.getMatchingTargetInformationInRules(Action.READ, new SubmodelTargetInformation(getIdAsList(ALL_ALLOWED_WILDCARD), getIdAsList(ALL_ALLOWED_WILDCARD))); - - List allIds = targetInformations.stream().map(SubmodelTargetInformation.class::cast) - .map(SubmodelTargetInformation::getSubmodelIds).flatMap(List::stream).collect(Collectors.toList()); - - List submodels = allIds.stream().map(id -> { - try { - return getSubmodel(id); - } catch (Exception e) { - return null; - } - }).filter(Objects::nonNull).collect(Collectors.toList()); - - TreeMap aasMap = submodels.stream().collect(Collectors.toMap(Submodel::getId, aas -> aas, (a, b) -> a, TreeMap::new)); - - PaginationSupport paginationSupport = new PaginationSupport<>(aasMap, Submodel::getId); - - return paginationSupport.getPaged(pInfo); + throwExceptionIfInsufficientPermission(isAuthorized); + + return decorated.getAllSubmodels(pInfo); } @Override @@ -145,29 +122,9 @@ public void deleteSubmodel(String submodelId) throws ElementDoesNotExistExceptio public CursorResult> getSubmodelElements(String submodelId, PaginationInfo pInfo) throws ElementDoesNotExistException { boolean isAuthorized = permissionResolver.hasPermission(Action.READ, new SubmodelTargetInformation(getIdAsList(submodelId), getIdAsList(ALL_ALLOWED_WILDCARD))); - if (isAuthorized) - return decorated.getSubmodelElements(submodelId, pInfo); - - getSubmodel(submodelId); - - List targetInformations = permissionResolver.getMatchingTargetInformationInRules(Action.READ, new SubmodelTargetInformation(getIdAsList(submodelId), getIdAsList(ALL_ALLOWED_WILDCARD))); - - List allIds = targetInformations.stream().map(SubmodelTargetInformation.class::cast) - .map(SubmodelTargetInformation::getSubmodelElementIdShortPaths).flatMap(List::stream).collect(Collectors.toList()); - - List smes = allIds.stream().map(id -> { - try { - return getSubmodelElement(submodelId, id); - } catch (Exception e) { - return null; - } - }).filter(Objects::nonNull).collect(Collectors.toList()); - - TreeMap aasMap = smes.stream().collect(Collectors.toMap(SubmodelElement::getIdShort, aas -> aas, (a, b) -> a, TreeMap::new)); - - PaginationSupport paginationSupport = new PaginationSupport<>(aasMap, SubmodelElement::getIdShort); - - return paginationSupport.getPaged(pInfo); + throwExceptionIfInsufficientPermission(isAuthorized); + + return decorated.getSubmodelElements(submodelId, pInfo); } @Override diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/rbac/backend/submodel/SubmodelTargetInformationAdapter.java b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/rbac/backend/submodel/SubmodelTargetInformationAdapter.java index 489da521a..0f7bc5a22 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/rbac/backend/submodel/SubmodelTargetInformationAdapter.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/rbac/backend/submodel/SubmodelTargetInformationAdapter.java @@ -42,7 +42,7 @@ import org.eclipse.digitaltwin.basyx.submodelrepository.feature.authorization.SubmodelTargetInformation; /** - * An implementation of the {@link TargetInformationAdapter} to adapt with Aas + * An implementation of the {@link TargetInformationAdapter} to adapt with Submodel * {@link TargetInformation} * * @author danish @@ -52,18 +52,14 @@ public class SubmodelTargetInformationAdapter implements TargetInformationAdapte @Override public SubmodelElementCollection adapt(TargetInformation targetInformation) { - SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder() - .idShort("targetInformation").build(); + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation").build(); SubmodelElementList submodelId = new DefaultSubmodelElementList.Builder().idShort("submodelIds").build(); - SubmodelElementList submodelElementId = new DefaultSubmodelElementList.Builder() - .idShort("submodelElementIdShortPaths").build(); + SubmodelElementList submodelElementId = new DefaultSubmodelElementList.Builder().idShort("submodelElementIdShortPaths").build(); Property typeProperty = new DefaultProperty.Builder().idShort("@type").value("submodel").build(); - List submodelIds = ((SubmodelTargetInformation) targetInformation).getSubmodelIds().stream() - .map(this::transform).collect(Collectors.toList()); - List submodelElementIds = ((SubmodelTargetInformation) targetInformation) - .getSubmodelElementIdShortPaths().stream().map(this::transform).collect(Collectors.toList()); + List submodelIds = ((SubmodelTargetInformation) targetInformation).getSubmodelIds().stream().map(this::transform).collect(Collectors.toList()); + List submodelElementIds = ((SubmodelTargetInformation) targetInformation).getSubmodelElementIdShortPaths().stream().map(this::transform).collect(Collectors.toList()); submodelId.setValue(submodelIds); submodelElementId.setValue(submodelElementIds); @@ -78,51 +74,34 @@ public TargetInformation adapt(SubmodelElementCollection targetInformation) { String targetInformationType = getTargetInformationType(targetInformation); if (!targetInformationType.equals("submodel")) - throw new InvalidTargetInformationException("The TargetInformation @type: " + targetInformationType - + " is not compatible with " + getClass().getName() + "."); - - SubmodelElement submodelIdSubmodelElement = targetInformation.getValue().stream() - .filter(sme -> sme.getIdShort().equals("submodelIds")).findAny() - .orElseThrow(() -> new InvalidTargetInformationException( - "The TargetInformation defined in the SubmodelElementCollection Rule with id: " - + targetInformation.getIdShort() + " is not compatible with the " - + getClass().getName())); - - SubmodelElement smeIdSubmodelElement = targetInformation.getValue().stream() - .filter(sme -> sme.getIdShort().equals("submodelElementIdShortPaths")).findAny() - .orElseThrow(() -> new InvalidTargetInformationException( - "The TargetInformation defined in the SubmodelElementCollection Rule with id: " - + targetInformation.getIdShort() + " is not compatible with the " - + getClass().getName())); - - if (!(submodelIdSubmodelElement instanceof SubmodelElementList) - || !(smeIdSubmodelElement instanceof SubmodelElementList)) - throw new InvalidTargetInformationException( - "The TargetInformation defined in the SubmodelElementCollection Rule with id: " - + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName()); + throw new InvalidTargetInformationException("The TargetInformation @type: " + targetInformationType + " is not compatible with " + getClass().getName() + "."); + + SubmodelElement submodelIdSubmodelElement = targetInformation.getValue().stream().filter(sme -> sme.getIdShort().equals("submodelIds")).findAny().orElseThrow( + () -> new InvalidTargetInformationException("The TargetInformation defined in the SubmodelElementCollection Rule with id: " + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName())); + + SubmodelElement smeIdSubmodelElement = targetInformation.getValue().stream().filter(sme -> sme.getIdShort().equals("submodelElementIdShortPaths")).findAny().orElseThrow( + () -> new InvalidTargetInformationException("The TargetInformation defined in the SubmodelElementCollection Rule with id: " + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName())); + + if (!(submodelIdSubmodelElement instanceof SubmodelElementList) || !(smeIdSubmodelElement instanceof SubmodelElementList)) + throw new InvalidTargetInformationException("The TargetInformation defined in the SubmodelElementCollection Rule with id: " + targetInformation.getIdShort() + " is not compatible with the " + getClass().getName()); SubmodelElementList submodelIdList = (SubmodelElementList) submodelIdSubmodelElement; SubmodelElementList smeIdList = (SubmodelElementList) smeIdSubmodelElement; - List submodelIds = submodelIdList.getValue().stream().map(Property.class::cast).map(Property::getValue) - .collect(Collectors.toList()); - List smeIds = smeIdList.getValue().stream().map(Property.class::cast).map(Property::getValue) - .collect(Collectors.toList()); + List submodelIds = submodelIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).collect(Collectors.toList()); + List smeIds = smeIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).collect(Collectors.toList()); return new SubmodelTargetInformation(submodelIds, smeIds); } - private Property transform(String aasId) { - return new DefaultProperty.Builder().value(aasId).build(); + private Property transform(String submodelId) { + return new DefaultProperty.Builder().value(submodelId).build(); } - + private String getTargetInformationType(SubmodelElementCollection targetInformation) { - Property typeProperty = (Property) targetInformation.getValue().stream() - .filter(sme -> sme.getIdShort().equals("@type")).findAny() - .orElseThrow(() -> new InvalidTargetInformationException( - "The TargetInformation defined in the SubmodelElementCollection Rule with id: " - + targetInformation.getIdShort() + " does not have @type definition")); + Property typeProperty = (Property) targetInformation.getValue().stream().filter(sme -> sme.getIdShort().equals("@type")).findAny() + .orElseThrow(() -> new InvalidTargetInformationException("The TargetInformation defined in the SubmodelElementCollection Rule with id: " + targetInformation.getIdShort() + " does not have @type definition")); return typeProperty.getValue(); } diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/SubmodelTargetInformationAdapterTest.java b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/SubmodelTargetInformationAdapterTest.java index 2d6bed23f..96f3a1507 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/SubmodelTargetInformationAdapterTest.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/SubmodelTargetInformationAdapterTest.java @@ -46,7 +46,7 @@ import org.eclipse.digitaltwin.basyx.submodelrepository.feature.authorization.rbac.backend.submodel.SubmodelTargetInformationAdapter; /** - * Tests {@link AasRegistryTargetInformationAdapter} + * Tests {@link SubmodelTargetInformationAdapter} * * @author danish */ @@ -64,7 +64,7 @@ public void testAdaptTargetInformationToSubmodelElementCollection() { List submodelIds = Arrays.asList("aasId1", "aasId2"); List smeIds = Arrays.asList("sme1.sme2", "sme4.sme5.sme6", "sme7"); - + TargetInformation targetInformation = new SubmodelTargetInformation(submodelIds, smeIds); SubmodelElementCollection result = submodelTargetInformationAdapter.adapt(targetInformation); @@ -76,10 +76,10 @@ public void testAdaptTargetInformationToSubmodelElementCollection() { SubmodelElementList submodelIdList = (SubmodelElementList) elements.get(0); assertEquals("submodelIds", submodelIdList.getIdShort()); - + SubmodelElementList smeIdList = (SubmodelElementList) elements.get(1); assertEquals("submodelElementIdShortPaths", smeIdList.getIdShort()); - + Property typeProperty = (Property) elements.get(2); assertEquals("@type", typeProperty.getIdShort()); @@ -87,9 +87,9 @@ public void testAdaptTargetInformationToSubmodelElementCollection() { List actualSmeIds = smeIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).map(String::valueOf).collect(Collectors.toList()); assertEquals(submodelIds, actualSubmodelIds); assertEquals(smeIds, actualSmeIds); - + String actualType = typeProperty.getValue(); - assertTrue(actualType.equals("submodel")); + assertTrue(actualType.equals("submodel")); } @Test @@ -98,7 +98,7 @@ public void testAdaptSubmodelElementCollectionToTargetInformation() { List expectedSubmodelIds = Arrays.asList("aasId1", "aasId2"); List expectedSmeIds = Arrays.asList("sme1.sme2", "sme4.sme5.sme6", "sme7"); String type = "submodel"; - + List submodelIdProperties = expectedSubmodelIds.stream().map(submodelId -> new DefaultProperty.Builder().value(submodelId).build()).collect(Collectors.toList()); List smeIdProperties = expectedSmeIds.stream().map(smeId -> new DefaultProperty.Builder().value(smeId).build()).collect(Collectors.toList()); @@ -114,72 +114,60 @@ public void testAdaptSubmodelElementCollectionToTargetInformation() { assertEquals(expectedSubmodelIds, ((SubmodelTargetInformation) result).getSubmodelIds()); assertEquals(expectedSmeIds, ((SubmodelTargetInformation) result).getSubmodelElementIdShortPaths()); } - - @Test - public void testAdaptTargetInformationWithEmptyAasIds() { - - List submodelIds = Collections.emptyList(); - List smeIds = Collections.emptyList(); - - TargetInformation targetInformation = new SubmodelTargetInformation(submodelIds, smeIds); - - SubmodelElementCollection result = submodelTargetInformationAdapter.adapt(targetInformation); - - assertEquals("targetInformation", result.getIdShort()); - - List elements = result.getValue(); - assertEquals(3, elements.size()); - - SubmodelElementList submodelIdList = (SubmodelElementList) elements.get(0); - assertEquals("submodelIds", submodelIdList.getIdShort()); - - SubmodelElementList smeIdList = (SubmodelElementList) elements.get(1); - assertEquals("submodelElementIdShortPaths", smeIdList.getIdShort()); - - Property typeProperty = (Property) elements.get(2); + + @Test + public void testAdaptTargetInformationWithEmptySubmodelIds() { + + List submodelIds = Collections.emptyList(); + List smeIds = Collections.emptyList(); + + TargetInformation targetInformation = new SubmodelTargetInformation(submodelIds, smeIds); + + SubmodelElementCollection result = submodelTargetInformationAdapter.adapt(targetInformation); + + assertEquals("targetInformation", result.getIdShort()); + + List elements = result.getValue(); + assertEquals(3, elements.size()); + + SubmodelElementList submodelIdList = (SubmodelElementList) elements.get(0); + assertEquals("submodelIds", submodelIdList.getIdShort()); + + SubmodelElementList smeIdList = (SubmodelElementList) elements.get(1); + assertEquals("submodelElementIdShortPaths", smeIdList.getIdShort()); + + Property typeProperty = (Property) elements.get(2); assertEquals("@type", typeProperty.getIdShort()); - List actualSubmodelIds = submodelIdList.getValue().stream() - .map(Property.class::cast) - .map(Property::getValue) - .map(String::valueOf) - .collect(Collectors.toList()); - assertTrue(actualSubmodelIds.isEmpty()); - - List actualSmeIds = smeIdList.getValue().stream() - .map(Property.class::cast) - .map(Property::getValue) - .map(String::valueOf) - .collect(Collectors.toList()); - assertTrue(actualSmeIds.isEmpty()); - - String actualType = typeProperty.getValue(); - assertTrue(actualType.equals("submodel")); - } - - @Test(expected = InvalidTargetInformationException.class) - public void testAdaptSubmodelElementCollectionWithInvalidStructure() { - - SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation") - .value(Collections.singletonList(new DefaultProperty.Builder().idShort("invalidElement").value("value").build())) - .build(); - - submodelTargetInformationAdapter.adapt(targetInformationSMC); - } - - @Test(expected = InvalidTargetInformationException.class) - public void testAdaptSubmodelElementCollectionWithoutSubmodelIds() { - - SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation") - .value(Collections.emptyList()) - .build(); - - submodelTargetInformationAdapter.adapt(targetInformationSMC); - } - - private SubmodelElement createTypeProperty(String type) { - return new DefaultProperty.Builder().idShort("@type").value(type).build(); + List actualSubmodelIds = submodelIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).map(String::valueOf).collect(Collectors.toList()); + assertTrue(actualSubmodelIds.isEmpty()); + + List actualSmeIds = smeIdList.getValue().stream().map(Property.class::cast).map(Property::getValue).map(String::valueOf).collect(Collectors.toList()); + assertTrue(actualSmeIds.isEmpty()); + + String actualType = typeProperty.getValue(); + assertTrue(actualType.equals("submodel")); } + @Test(expected = InvalidTargetInformationException.class) + public void testAdaptSubmodelElementCollectionWithInvalidStructure() { + + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation") + .value(Collections.singletonList(new DefaultProperty.Builder().idShort("invalidElement").value("value").build())).build(); + + submodelTargetInformationAdapter.adapt(targetInformationSMC); + } + + @Test(expected = InvalidTargetInformationException.class) + public void testAdaptSubmodelElementCollectionWithoutSubmodelIds() { + + SubmodelElementCollection targetInformationSMC = new DefaultSubmodelElementCollection.Builder().idShort("targetInformation").value(Collections.emptyList()).build(); + + submodelTargetInformationAdapter.adapt(targetInformationSMC); + } + + private SubmodelElement createTypeProperty(String type) { + return new DefaultProperty.Builder().idShort("@type").value(type).build(); + } } diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/TestAuthorizedSubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/TestAuthorizedSubmodelRepository.java index 085eb545a..b65f1e8ce 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/TestAuthorizedSubmodelRepository.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/TestAuthorizedSubmodelRepository.java @@ -132,7 +132,7 @@ public void getAllSubmodelsWithInsufficientPermissionRole() throws IOException { String accessToken = tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword()); CloseableHttpResponse retrievalResponse = getAllSubmodelsWithAuthorization(accessToken); - assertEquals(HttpStatus.OK.value(), retrievalResponse.getCode()); + assertEquals(HttpStatus.FORBIDDEN.value(), retrievalResponse.getCode()); } @Test