diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/pom.xml b/basyx.aasenvironment/basyx.aasenvironment-client/pom.xml
index 926097d98..4f3e5f019 100644
--- a/basyx.aasenvironment/basyx.aasenvironment-client/pom.xml
+++ b/basyx.aasenvironment/basyx.aasenvironment-client/pom.xml
@@ -75,5 +75,33 @@
mockito-core
test
+
+ org.eclipse.digitaltwin.basyx
+ basyx.aasservice-client
+ test
+ tests
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.aasservice-core
+ test
+ tests
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.aasenvironment-feature-authorization
+ test
+
+
+ org.apache.httpcomponents.client5
+ httpclient5
+ test
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.http
+ test
+ tests
+
\ No newline at end of file
diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/AuthorizedConnectedAasManager.java b/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/AuthorizedConnectedAasManager.java
new file mode 100644
index 000000000..a40cc652c
--- /dev/null
+++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/AuthorizedConnectedAasManager.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * 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.client;
+
+import org.eclipse.digitaltwin.basyx.aasenvironment.client.resolvers.EndpointResolver;
+import org.eclipse.digitaltwin.basyx.aasenvironment.client.resolvers.DescriptorResolverManager;
+import org.eclipse.digitaltwin.basyx.aasenvironment.client.resolvers.authorization.AuthorizedAasDescriptorResolver;
+import org.eclipse.digitaltwin.basyx.aasenvironment.client.resolvers.authorization.AuthorizedSubmodelDescriptorResolver;
+import org.eclipse.digitaltwin.basyx.aasregistry.client.model.AssetAdministrationShellDescriptor;
+import org.eclipse.digitaltwin.basyx.aasregistry.main.client.AuthorizedConnectedAasRegistry;
+import org.eclipse.digitaltwin.basyx.aasrepository.client.AuthorizedConnectedAasRepository;
+import org.eclipse.digitaltwin.basyx.aasservice.client.ConnectedAasService;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.TokenManager;
+import org.eclipse.digitaltwin.basyx.client.internal.resolver.DescriptorResolver;
+import org.eclipse.digitaltwin.basyx.submodelregistry.client.AuthorizedConnectedSubmodelRegistry;
+import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.SubmodelDescriptor;
+import org.eclipse.digitaltwin.basyx.submodelrepository.client.AuthorizedConnectedSubmodelRepository;
+import org.eclipse.digitaltwin.basyx.submodelservice.client.ConnectedSubmodelService;
+
+/**
+ * Authorized client component for executing consolidated Repository and Registry requests
+ *
+ * @author danish
+ *
+ */
+public class AuthorizedConnectedAasManager extends ConnectedAasManager {
+
+ public AuthorizedConnectedAasManager(AuthorizedConnectedAasRegistry authorizedAasRegistryApi, AuthorizedConnectedAasRepository authorizedAasRepository, AuthorizedConnectedSubmodelRegistry authorizedSubmodelRegistryApi, AuthorizedConnectedSubmodelRepository authorizedSubmodelRepository) {
+ super(authorizedAasRegistryApi, authorizedAasRepository, authorizedSubmodelRegistryApi, authorizedSubmodelRepository, getAuthorizedResolver(authorizedAasRepository.getTokenManager(), authorizedSubmodelRepository.getTokenManager()));
+ }
+
+ private static DescriptorResolverManager getAuthorizedResolver(TokenManager authorizedAasRepoTokenManager, TokenManager authorizedSubmodelRepoTokenManager) {
+ DescriptorResolver aasDescriptorResolver = new AuthorizedAasDescriptorResolver(new EndpointResolver(), authorizedAasRepoTokenManager);
+ DescriptorResolver smDescriptorResolver = new AuthorizedSubmodelDescriptorResolver(new EndpointResolver(), authorizedSubmodelRepoTokenManager);
+
+ return new DescriptorResolverManager(aasDescriptorResolver, smDescriptorResolver);
+ }
+
+}
\ No newline at end of file
diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/ConnectedAasManager.java b/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/ConnectedAasManager.java
index bc913e7b4..a9ab86892 100644
--- a/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/ConnectedAasManager.java
+++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/ConnectedAasManager.java
@@ -38,11 +38,14 @@
import org.eclipse.digitaltwin.basyx.aasenvironment.client.exceptions.NoValidEndpointFoundException;
import org.eclipse.digitaltwin.basyx.aasenvironment.client.exceptions.RegistryHttpRequestException;
import org.eclipse.digitaltwin.basyx.aasenvironment.client.resolvers.AasDescriptorResolver;
+import org.eclipse.digitaltwin.basyx.aasenvironment.client.resolvers.DescriptorResolverManager;
+import org.eclipse.digitaltwin.basyx.aasenvironment.client.resolvers.EndpointResolver;
import org.eclipse.digitaltwin.basyx.aasenvironment.client.resolvers.SubmodelDescriptorResolver;
import org.eclipse.digitaltwin.basyx.aasregistry.client.api.RegistryAndDiscoveryInterfaceApi;
import org.eclipse.digitaltwin.basyx.aasregistry.client.model.AssetAdministrationShellDescriptor;
import org.eclipse.digitaltwin.basyx.aasrepository.client.ConnectedAasRepository;
import org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration.AasDescriptorFactory;
+import org.eclipse.digitaltwin.basyx.client.internal.resolver.DescriptorResolver;
import org.eclipse.digitaltwin.basyx.aasservice.client.ConnectedAasService;
import org.eclipse.digitaltwin.basyx.submodelregistry.client.api.SubmodelRegistryApi;
import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.SubmodelDescriptor;
@@ -53,7 +56,7 @@
/**
* Client component for executing consolidated Repository and Registry requests
*
- * @author mateusmolina, jungjan
+ * @author mateusmolina, jungjan, danish
*
*/
public class ConnectedAasManager {
@@ -64,10 +67,10 @@ public class ConnectedAasManager {
private final RegistryAndDiscoveryInterfaceApi aasRegistryApi;
private final SubmodelRegistryApi smRegistryApi;
- private final AasDescriptorResolver aasDescriptorResolver;
+ private final DescriptorResolver aasDescriptorResolver;
private final AasDescriptorFactory aasDescriptorFactory;
- private final SubmodelDescriptorResolver smDescriptorResolver;
+ private final DescriptorResolver smDescriptorResolver;
private final SubmodelDescriptorFactory smDescriptorFactory;
/**
@@ -79,22 +82,21 @@ public class ConnectedAasManager {
* @param submodelBaseRepositoryUrl
*/
public ConnectedAasManager(String aasRegistryBaseUrl, String aasRepositoryBaseUrl, String submodelRegistryBaseUrl, String submodelBaseRepositoryUrl) {
- this(new RegistryAndDiscoveryInterfaceApi(aasRegistryBaseUrl), new ConnectedAasRepository(aasRepositoryBaseUrl), aasRepositoryBaseUrl, new SubmodelRegistryApi(submodelRegistryBaseUrl),
- new ConnectedSubmodelRepository(submodelBaseRepositoryUrl), submodelBaseRepositoryUrl);
+ this(new RegistryAndDiscoveryInterfaceApi(aasRegistryBaseUrl), new ConnectedAasRepository(aasRepositoryBaseUrl), new SubmodelRegistryApi(submodelRegistryBaseUrl),
+ new ConnectedSubmodelRepository(submodelBaseRepositoryUrl), getResolver());
}
-
- ConnectedAasManager(RegistryAndDiscoveryInterfaceApi aasRegistryApi, ConnectedAasRepository aasRepository, String aasRepositoryBaseUrl, SubmodelRegistryApi smRegistryApi, ConnectedSubmodelRepository smRepository,
- String submodelBaseRepositoryUrl) {
+
+ ConnectedAasManager(RegistryAndDiscoveryInterfaceApi aasRegistryApi, ConnectedAasRepository aasRepository, SubmodelRegistryApi smRegistryApi, ConnectedSubmodelRepository smRepository, DescriptorResolverManager resolver) {
this.aasRepository = aasRepository;
this.aasRegistryApi = aasRegistryApi;
this.smRepository = smRepository;
this.smRegistryApi = smRegistryApi;
- this.aasDescriptorResolver = ConnectedAasManagerHelper.buildAasDescriptorResolver();
- this.aasDescriptorFactory = ConnectedAasManagerHelper.buildAasDescriptorFactory(aasRepositoryBaseUrl);
- this.smDescriptorResolver = ConnectedAasManagerHelper.buildSubmodelDescriptorResolver();
- this.smDescriptorFactory = ConnectedAasManagerHelper.buildSmDescriptorFactory(submodelBaseRepositoryUrl);
+ this.aasDescriptorResolver = resolver.getAasDescriptorResolver();
+ this.aasDescriptorFactory = ConnectedAasManagerHelper.buildAasDescriptorFactory(aasRepository.getBaseUrl());
+ this.smDescriptorResolver = resolver.getSubmodelDescriptorResolver();
+ this.smDescriptorFactory = ConnectedAasManagerHelper.buildSmDescriptorFactory(smRepository.getBaseUrl());
}
-
+
/**
* Retrieves a ConnectedAasService in an AAS registry by its identifier.
*
@@ -102,7 +104,7 @@ public ConnectedAasManager(String aasRegistryBaseUrl, String aasRepositoryBaseUr
* The identifier of the AAS to retrieve.
* @return The retrieved ConnectedAasService object.
*/
- public ConnectedAasService getAas(String identifier) throws NoValidEndpointFoundException {
+ public ConnectedAasService getAasService(String identifier) throws NoValidEndpointFoundException {
AssetAdministrationShellDescriptor descriptor;
try {
@@ -110,7 +112,7 @@ public ConnectedAasService getAas(String identifier) throws NoValidEndpointFound
} catch (Exception e) {
throw new RegistryHttpRequestException(identifier, e);
}
- return aasDescriptorResolver.resolveAasDescriptor(descriptor);
+ return aasDescriptorResolver.resolveDescriptor(descriptor);
}
/**
@@ -121,7 +123,7 @@ public ConnectedAasService getAas(String identifier) throws NoValidEndpointFound
* The identifier of the submodel to retrieve.
* @return The retrieved ConnectedSubmodelService object.
*/
- public ConnectedSubmodelService getSubmodel(String identifier) {
+ public ConnectedSubmodelService getSubmodelService(String identifier) {
SubmodelDescriptor descriptor;
try {
@@ -130,7 +132,7 @@ public ConnectedSubmodelService getSubmodel(String identifier) {
throw new RegistryHttpRequestException(identifier, e);
}
- return smDescriptorResolver.resolveSubmodelDescriptor(descriptor);
+ return smDescriptorResolver.resolveDescriptor(descriptor);
}
/**
@@ -141,11 +143,11 @@ public ConnectedSubmodelService getSubmodel(String identifier) {
* @return The retrieved Submodel object.
*/
public List getAllSubmodels(String shellIdentifier) {
- AssetAdministrationShell shell = getAas(shellIdentifier).getAAS();
+ AssetAdministrationShell shell = getAasService(shellIdentifier).getAAS();
List submodelReferences = shell.getSubmodels();
return submodelReferences.parallelStream()
.map(this::extractSubmodelIdentifierFromReference)
- .map(this::getSubmodel)
+ .map(this::getSubmodelService)
.collect(Collectors.toList());
}
@@ -247,5 +249,12 @@ private Key extractSubmodelKeyFromReference(Reference submodelReference) {
return submodelReference.getKeys()
.get(0);
}
+
+ private static DescriptorResolverManager getResolver() {
+ DescriptorResolver aasDescriptorResolver = new AasDescriptorResolver(new EndpointResolver());
+ DescriptorResolver smDescriptorResolver = new SubmodelDescriptorResolver(new EndpointResolver());
+
+ return new DescriptorResolverManager(aasDescriptorResolver, smDescriptorResolver);
+ }
}
diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/ConnectedAasManagerHelper.java b/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/ConnectedAasManagerHelper.java
index 6505c8f3e..ab6044d7f 100644
--- a/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/ConnectedAasManagerHelper.java
+++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/ConnectedAasManagerHelper.java
@@ -26,9 +26,6 @@
package org.eclipse.digitaltwin.basyx.aasenvironment.client;
-import org.eclipse.digitaltwin.basyx.aasenvironment.client.resolvers.AasDescriptorResolver;
-import org.eclipse.digitaltwin.basyx.aasenvironment.client.resolvers.EndpointResolver;
-import org.eclipse.digitaltwin.basyx.aasenvironment.client.resolvers.SubmodelDescriptorResolver;
import org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration.AasDescriptorFactory;
import org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration.mapper.AttributeMapper;
import org.eclipse.digitaltwin.basyx.http.Aas4JHTTPSerializationExtension;
@@ -41,7 +38,7 @@
/**
* Provides builder methods for {@link ConnectedAasManager} dependencies
*
- * @author mateusmolina
+ * @author mateusmolina, danish
*
*/
class ConnectedAasManagerHelper {
@@ -58,14 +55,6 @@ static ObjectMapper buildObjectMapper() {
return builder.build();
}
- static AasDescriptorResolver buildAasDescriptorResolver() {
- return new AasDescriptorResolver(new EndpointResolver());
- }
-
- static SubmodelDescriptorResolver buildSubmodelDescriptorResolver() {
- return new SubmodelDescriptorResolver(new EndpointResolver());
- }
-
static AasDescriptorFactory buildAasDescriptorFactory(String aasRepositoryBaseUrl) {
AttributeMapper attributeMapper = new AttributeMapper(objectMapper);
diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/AasDescriptorResolver.java b/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/AasDescriptorResolver.java
index 927586b6c..6e743a8e1 100644
--- a/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/AasDescriptorResolver.java
+++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/AasDescriptorResolver.java
@@ -31,14 +31,15 @@
import org.eclipse.digitaltwin.basyx.aasregistry.client.model.AssetAdministrationShellDescriptor;
import org.eclipse.digitaltwin.basyx.aasregistry.client.model.Endpoint;
import org.eclipse.digitaltwin.basyx.aasservice.client.ConnectedAasService;
+import org.eclipse.digitaltwin.basyx.client.internal.resolver.DescriptorResolver;
/**
- * Resolves an AasDescriptor into an AssetAdministrationShell
+ * Resolves an AasDescriptor into a {@link ConnectedAasService}
*
- * @author mateusmolina
+ * @author mateusmolina, danish
*
*/
-public class AasDescriptorResolver {
+public class AasDescriptorResolver implements DescriptorResolver {
private final EndpointResolver endpointResolver;
@@ -50,20 +51,20 @@ public class AasDescriptorResolver {
public AasDescriptorResolver(EndpointResolver endpointResolver) {
this.endpointResolver = endpointResolver;
}
-
+
/**
* Resolves an AASDescriptor to a ConnectedAasService
*
* @param aasDescriptor
* @return
*/
- public ConnectedAasService resolveAasDescriptor(AssetAdministrationShellDescriptor aasDescriptor) {
+ public ConnectedAasService resolveDescriptor(AssetAdministrationShellDescriptor aasDescriptor) {
String endpoint = endpointResolver.resolveFirst(aasDescriptor.getEndpoints(), AasDescriptorResolver::parseEndpoint);
return new ConnectedAasService(endpoint);
}
- private static Optional parseEndpoint(Endpoint endpoint) {
+ public static Optional parseEndpoint(Endpoint endpoint) {
try {
if (endpoint == null || endpoint.getProtocolInformation() == null || endpoint.getProtocolInformation()
.getHref() == null)
diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/DescriptorResolverManager.java b/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/DescriptorResolverManager.java
new file mode 100644
index 000000000..d3e807b9b
--- /dev/null
+++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/DescriptorResolverManager.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * 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.client.resolvers;
+
+import org.eclipse.digitaltwin.basyx.aasregistry.client.model.AssetAdministrationShellDescriptor;
+import org.eclipse.digitaltwin.basyx.aasservice.client.ConnectedAasService;
+import org.eclipse.digitaltwin.basyx.client.internal.resolver.DescriptorResolver;
+import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.SubmodelDescriptor;
+import org.eclipse.digitaltwin.basyx.submodelservice.client.ConnectedSubmodelService;
+
+/**
+ * A helper class for accumulating various {@link DescriptorResolver}s
+ *
+ * @author danish
+ */
+public class DescriptorResolverManager {
+
+ private DescriptorResolver aasDescriptorResolver;
+ private DescriptorResolver submodelDescriptorResolver;
+
+ public DescriptorResolverManager(DescriptorResolver aasDescriptorResolver, DescriptorResolver submodelDescriptorResolver) {
+ super();
+ this.aasDescriptorResolver = aasDescriptorResolver;
+ this.submodelDescriptorResolver = submodelDescriptorResolver;
+ }
+
+ public DescriptorResolver getAasDescriptorResolver() {
+ return aasDescriptorResolver;
+ }
+
+ public DescriptorResolver getSubmodelDescriptorResolver() {
+ return submodelDescriptorResolver;
+ }
+
+}
diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/EndpointResolver.java b/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/EndpointResolver.java
index 62675d9f1..75a7d85df 100644
--- a/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/EndpointResolver.java
+++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/EndpointResolver.java
@@ -106,7 +106,7 @@ private boolean isURIWorking(URI uri) {
connection.setConnectTimeout(timeout);
connection.setReadTimeout(timeout);
int responseCode = connection.getResponseCode();
- return (200 <= responseCode && responseCode <= 399);
+ return (200 <= responseCode && responseCode <= 401);
} catch (Exception e) {
return false;
} finally {
diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/SubmodelDescriptorResolver.java b/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/SubmodelDescriptorResolver.java
index dc0cb6c4e..5ddc10d72 100644
--- a/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/SubmodelDescriptorResolver.java
+++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/SubmodelDescriptorResolver.java
@@ -25,6 +25,7 @@
package org.eclipse.digitaltwin.basyx.aasenvironment.client.resolvers;
+import org.eclipse.digitaltwin.basyx.client.internal.resolver.DescriptorResolver;
import java.net.URI;
import java.util.Optional;
@@ -33,12 +34,12 @@
import org.eclipse.digitaltwin.basyx.submodelservice.client.ConnectedSubmodelService;
/**
- * Resolves a SubmodelDescriptor into a Submodel
+ * Resolves a SubmodelDescriptor into a {@link ConnectedSubmodelService}
*
- * @author mateusmolina
+ * @author mateusmolina, danish
*
*/
-public class SubmodelDescriptorResolver {
+public class SubmodelDescriptorResolver implements DescriptorResolver {
private final EndpointResolver endpointResolver;
@@ -58,13 +59,13 @@ public SubmodelDescriptorResolver(EndpointResolver endpointResolver) {
* the Submodel Descriptor to be resolved
* @return the Connected submodelserver
*/
- public ConnectedSubmodelService resolveSubmodelDescriptor(SubmodelDescriptor smDescriptor) {
+ public ConnectedSubmodelService resolveDescriptor(SubmodelDescriptor smDescriptor) {
String endpoint = endpointResolver.resolveFirst(smDescriptor.getEndpoints(), SubmodelDescriptorResolver::parseEndpoint);
return new ConnectedSubmodelService(endpoint);
}
- private static Optional parseEndpoint(Endpoint endpoint) {
+ public static Optional parseEndpoint(Endpoint endpoint) {
try {
if (endpoint == null || endpoint.getProtocolInformation() == null || endpoint.getProtocolInformation()
.getHref() == null)
diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/URIParser.java b/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/URIParser.java
index c9f56434f..a8fccfa72 100644
--- a/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/URIParser.java
+++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/URIParser.java
@@ -35,6 +35,6 @@
*
*/
@FunctionalInterface
-interface URIParser {
+public interface URIParser {
public Optional parse(T object);
}
diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/authorization/AuthorizedAasDescriptorResolver.java b/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/authorization/AuthorizedAasDescriptorResolver.java
new file mode 100644
index 000000000..840dbc36b
--- /dev/null
+++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/authorization/AuthorizedAasDescriptorResolver.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * 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.client.resolvers.authorization;
+
+import org.eclipse.digitaltwin.basyx.aasenvironment.client.resolvers.AasDescriptorResolver;
+import org.eclipse.digitaltwin.basyx.aasenvironment.client.resolvers.EndpointResolver;
+import org.eclipse.digitaltwin.basyx.aasregistry.client.model.AssetAdministrationShellDescriptor;
+import org.eclipse.digitaltwin.basyx.aasservice.client.AuthorizedConnectedAasService;
+import org.eclipse.digitaltwin.basyx.aasservice.client.ConnectedAasService;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.TokenManager;
+import org.eclipse.digitaltwin.basyx.client.internal.resolver.DescriptorResolver;
+
+/**
+ * Resolves an AasDescriptor into a {@link AuthorizedConnectedAasService}
+ *
+ * @author danish
+ *
+ */
+public class AuthorizedAasDescriptorResolver implements DescriptorResolver {
+
+ private final EndpointResolver endpointResolver;
+ private final TokenManager tokenManager;
+
+ public AuthorizedAasDescriptorResolver(EndpointResolver endpointResolver, TokenManager tokenManager) {
+ this.endpointResolver = endpointResolver;
+ this.tokenManager = tokenManager;
+ }
+
+ @Override
+ public AuthorizedConnectedAasService resolveDescriptor(AssetAdministrationShellDescriptor descriptor) {
+ String endpoint = endpointResolver.resolveFirst(descriptor.getEndpoints(), AasDescriptorResolver::parseEndpoint);
+
+ return new AuthorizedConnectedAasService(endpoint, tokenManager);
+ }
+
+}
diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/authorization/AuthorizedSubmodelDescriptorResolver.java b/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/authorization/AuthorizedSubmodelDescriptorResolver.java
new file mode 100644
index 000000000..350e52c35
--- /dev/null
+++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/authorization/AuthorizedSubmodelDescriptorResolver.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * 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.client.resolvers.authorization;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.Key;
+import org.eclipse.digitaltwin.aas4j.v3.model.KeyTypes;
+import org.eclipse.digitaltwin.aas4j.v3.model.Reference;
+import org.eclipse.digitaltwin.aas4j.v3.model.ReferenceTypes;
+import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultKey;
+import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultReference;
+import org.eclipse.digitaltwin.basyx.aasenvironment.client.resolvers.EndpointResolver;
+import org.eclipse.digitaltwin.basyx.aasenvironment.client.resolvers.SubmodelDescriptorResolver;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.TokenManager;
+import org.eclipse.digitaltwin.basyx.client.internal.resolver.DescriptorResolver;
+import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.SubmodelDescriptor;
+import org.eclipse.digitaltwin.basyx.submodelservice.client.AuthorizedConnectedSubmodelService;
+import org.eclipse.digitaltwin.basyx.submodelservice.client.ConnectedSubmodelService;
+
+/**
+ * Resolves an SubmodelDescriptor into a {@link AuthorizedConnectedSubmodelService}
+ *
+ * @author danish
+ *
+ */
+public class AuthorizedSubmodelDescriptorResolver implements DescriptorResolver {
+
+ private final EndpointResolver endpointResolver;
+ private final TokenManager tokenManager;
+
+ public AuthorizedSubmodelDescriptorResolver(EndpointResolver endpointResolver, TokenManager tokenManager) {
+ this.endpointResolver = endpointResolver;
+ this.tokenManager = tokenManager;
+ }
+
+ @Override
+ public AuthorizedConnectedSubmodelService resolveDescriptor(SubmodelDescriptor descriptor) {
+ String endpoint = endpointResolver.resolveFirst(descriptor.getEndpoints(), SubmodelDescriptorResolver::parseEndpoint);
+
+ return new AuthorizedConnectedSubmodelService(endpoint, tokenManager);
+ }
+
+ public Reference deriveReferenceFromSubmodelDescriptor(SubmodelDescriptor smDescriptor) {
+ return new DefaultReference.Builder().type(ReferenceTypes.EXTERNAL_REFERENCE).keys(generateKeyFromId(smDescriptor.getId())).build();
+ }
+
+ private static Key generateKeyFromId(String smId) {
+ return new DefaultKey.Builder().type(KeyTypes.SUBMODEL).value(smId).build();
+ }
+
+}
diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/TestAuthorizedConnectedAasManager.java b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/TestAuthorizedConnectedAasManager.java
new file mode 100644
index 000000000..98ca90893
--- /dev/null
+++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/TestAuthorizedConnectedAasManager.java
@@ -0,0 +1,267 @@
+/*******************************************************************************
+ * 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.client;
+
+import static org.mockito.Mockito.spy;
+import java.io.IOException;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell;
+import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
+import org.eclipse.digitaltwin.basyx.aasregistry.client.ApiException;
+import org.eclipse.digitaltwin.basyx.aasregistry.client.api.RegistryAndDiscoveryInterfaceApi;
+import org.eclipse.digitaltwin.basyx.aasregistry.client.model.AssetAdministrationShellDescriptor;
+import org.eclipse.digitaltwin.basyx.aasregistry.main.client.AuthorizedConnectedAasRegistry;
+import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository;
+import org.eclipse.digitaltwin.basyx.aasrepository.client.AuthorizedConnectedAasRepository;
+import org.eclipse.digitaltwin.basyx.aasrepository.client.ConnectedAasRepository;
+import org.eclipse.digitaltwin.basyx.aasservice.client.TestAuthorizedConnectedAasService;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.TokenManager;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.credential.ClientCredential;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.grant.ClientCredentialAccessTokenProvider;
+import org.eclipse.digitaltwin.basyx.submodelregistry.client.AuthorizedConnectedSubmodelRegistry;
+import org.eclipse.digitaltwin.basyx.submodelregistry.client.api.SubmodelRegistryApi;
+import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.SubmodelDescriptor;
+import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository;
+import org.eclipse.digitaltwin.basyx.submodelrepository.client.AuthorizedConnectedSubmodelRepository;
+import org.eclipse.digitaltwin.basyx.submodelrepository.client.ConnectedSubmodelRepository;
+import org.junit.BeforeClass;
+import org.springframework.boot.SpringApplication;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+/**
+ * Test for {@link AuthorizedConnectedAasManager}
+ *
+ * @author danish
+ *
+ */
+public class TestAuthorizedConnectedAasManager extends TestConnectedAasManager {
+
+ private static final String PROFILE = "authorization";
+ protected final static String AAS_REGISTRY_BASE_PATH = "http://localhost:8051";
+ protected final static String SM_REGISTRY_BASE_PATH = "http://localhost:8061";
+
+ private final static TokenManager TOKEN_MANAGER = new TokenManager("http://localhost:9096/realms/BaSyx/protocol/openid-connect/token", new ClientCredentialAccessTokenProvider(new ClientCredential("workstation-1", "nY0mjyECF60DGzNmQUjL81XurSl8etom")));
+ private final static TokenManager TOKEN_MANAGER_REGISTRY = new TokenManager("http://localhost:9097/realms/BaSyx/protocol/openid-connect/token", new ClientCredentialAccessTokenProvider(new ClientCredential("workstation-1", "nY0mjyECF60DGzNmQUjL81XurSl8etom")));
+
+ private static AuthorizedConnectedAasRepository connectedAasRepository;
+ private static AuthorizedConnectedSubmodelRepository connectedSmRepository;
+ private static AuthorizedConnectedAasRegistry aasRegistryApi;
+ private static AuthorizedConnectedSubmodelRegistry smRegistryApi;
+
+ private AuthorizedConnectedAasManager aasManager;
+
+ @BeforeClass
+ public static void initApplication() {
+ SpringApplication application = new SpringApplication(DummyAasEnvironmentComponent.class);
+ application.setAdditionalProfiles(PROFILE);
+
+ appContext = application.run(new String[] {});
+
+ aasRepository = appContext.getBean(AasRepository.class);
+ smRepository = appContext.getBean(SubmodelRepository.class);
+ }
+
+ @Override
+ protected ConnectedAasManager getConnectedAasManager() {
+
+ aasManager = new AuthorizedConnectedAasManager(aasRegistryApi, connectedAasRepository, smRegistryApi, connectedSmRepository);
+
+ return aasManager;
+ }
+
+ @Override
+ protected SubmodelRegistryApi getConnectedSubmodelRegistry() {
+
+ smRegistryApi = spy(new AuthorizedConnectedSubmodelRegistry(SM_REGISTRY_BASE_PATH, TOKEN_MANAGER_REGISTRY));
+
+ return smRegistryApi;
+ }
+
+ @Override
+ protected RegistryAndDiscoveryInterfaceApi getConnectedAasRegistry() {
+
+ aasRegistryApi = spy(new AuthorizedConnectedAasRegistry(AAS_REGISTRY_BASE_PATH, TOKEN_MANAGER_REGISTRY));
+
+ return aasRegistryApi;
+ }
+
+ @Override
+ protected ConnectedSubmodelRepository getConnectedSubmodelRepo() {
+
+ connectedSmRepository = spy(new AuthorizedConnectedSubmodelRepository(SM_REPOSITORY_BASE_PATH, TOKEN_MANAGER));
+
+ return connectedSmRepository;
+ }
+
+ @Override
+ protected ConnectedAasRepository getConnectedAasRepo() {
+
+ connectedAasRepository = spy(new AuthorizedConnectedAasRepository(AAS_REPOSITORY_BASE_PATH, TOKEN_MANAGER));
+
+ return connectedAasRepository;
+ }
+
+ @Override
+ protected Submodel getSubmodelFromManager(String submodelId) {
+ return aasManager.getSubmodelService(submodelId).getSubmodel();
+ }
+
+ @Override
+ protected AssetAdministrationShellDescriptor getDescriptorFromAasRegistry(String shellId) throws ApiException {
+ return new AuthorizedConnectedAasRegistry(AAS_REGISTRY_BASE_PATH, TOKEN_MANAGER_REGISTRY).getAssetAdministrationShellDescriptorById(shellId);
+ }
+
+ @Override
+ protected AssetAdministrationShell getAasFromRepo(String shellId) {
+ try {
+ TestAuthorizedConnectedAasService.configureSecurityContext(TestAuthorizedConnectedAasService.getTokenProvider());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ AssetAdministrationShell assetAdministrationShell = aasRepository.getAas(shellId);
+
+ SecurityContextHolder.clearContext();
+
+ return assetAdministrationShell;
+ }
+
+ @Override
+ protected SubmodelDescriptor getDescriptorFromSubmodelRegistry(String submodelId) throws org.eclipse.digitaltwin.basyx.submodelregistry.client.ApiException {
+ return new AuthorizedConnectedSubmodelRegistry(SM_REGISTRY_BASE_PATH, TOKEN_MANAGER_REGISTRY).getSubmodelDescriptorById(submodelId);
+ }
+
+ @Override
+ protected Submodel getSubmodelFromRepo(String submodelId) {
+ try {
+ TestAuthorizedConnectedAasService.configureSecurityContext(TestAuthorizedConnectedAasService.getTokenProvider());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ Submodel submodel = smRepository.getSubmodel(submodelId);
+
+ SecurityContextHolder.clearContext();
+
+ return submodel;
+ }
+
+ @Override
+ protected AssetAdministrationShell getAasFromManager(String shellId) {
+ return aasManager.getAasService(shellId).getAAS();
+ }
+
+ @Override
+ protected void populateRegistries() {
+ try {
+ new AuthorizedConnectedAasRegistry(AAS_REGISTRY_BASE_PATH, TOKEN_MANAGER_REGISTRY).postAssetAdministrationShellDescriptor(FIXTURE.buildAasPre1Descriptor());
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ }
+ try {
+ new AuthorizedConnectedSubmodelRegistry(SM_REGISTRY_BASE_PATH, TOKEN_MANAGER_REGISTRY).postSubmodelDescriptor(FIXTURE.buildSmPre1Descriptor());
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ @Override
+ protected void cleanUpRegistries() {
+ try {
+ new AuthorizedConnectedAasRegistry(AAS_REGISTRY_BASE_PATH, TOKEN_MANAGER_REGISTRY).deleteAllShellDescriptors();
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ }
+ try {
+ new AuthorizedConnectedSubmodelRegistry(SM_REGISTRY_BASE_PATH, TOKEN_MANAGER_REGISTRY).deleteAllSubmodelDescriptors();
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ @Override
+ protected void populateRepositories() {
+ try {
+ TestAuthorizedConnectedAasService.configureSecurityContext(TestAuthorizedConnectedAasService.getTokenProvider());
+
+ aasRepository.createAas(FIXTURE.buildAasPre1());
+
+ SecurityContextHolder.clearContext();
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ }
+ try {
+ TestAuthorizedConnectedAasService.configureSecurityContext(TestAuthorizedConnectedAasService.getTokenProvider());
+
+ smRepository.createSubmodel(FIXTURE.buildSmPre1());
+
+ SecurityContextHolder.clearContext();
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ @Override
+ protected void cleanUpRepositories() {
+ try {
+ TestAuthorizedConnectedAasService.configureSecurityContext(TestAuthorizedConnectedAasService.getTokenProvider());
+
+ aasRepository.deleteAas(TestFixture.AAS_PRE1_ID);
+
+ SecurityContextHolder.clearContext();
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ }
+ try {
+ TestAuthorizedConnectedAasService.configureSecurityContext(TestAuthorizedConnectedAasService.getTokenProvider());
+
+ aasRepository.deleteAas(TestFixture.AAS_POS1_ID);
+
+ SecurityContextHolder.clearContext();
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ }
+ try {
+ TestAuthorizedConnectedAasService.configureSecurityContext(TestAuthorizedConnectedAasService.getTokenProvider());
+
+ smRepository.deleteSubmodel(TestFixture.SM_PRE1_ID);
+
+ SecurityContextHolder.clearContext();
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ }
+ try {
+ TestAuthorizedConnectedAasService.configureSecurityContext(TestAuthorizedConnectedAasService.getTokenProvider());
+
+ smRepository.deleteSubmodel(TestFixture.SM_POS1_ID);
+
+ SecurityContextHolder.clearContext();
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+}
diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/TestConnectedAasManager.java b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/TestConnectedAasManager.java
index 8ba010436..5263c68e6 100644
--- a/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/TestConnectedAasManager.java
+++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/TestConnectedAasManager.java
@@ -40,11 +40,17 @@
import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell;
import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
import org.eclipse.digitaltwin.basyx.aasenvironment.client.exceptions.NoValidEndpointFoundException;
+import org.eclipse.digitaltwin.basyx.aasenvironment.client.resolvers.AasDescriptorResolver;
+import org.eclipse.digitaltwin.basyx.aasenvironment.client.resolvers.DescriptorResolverManager;
+import org.eclipse.digitaltwin.basyx.aasenvironment.client.resolvers.EndpointResolver;
+import org.eclipse.digitaltwin.basyx.aasenvironment.client.resolvers.SubmodelDescriptorResolver;
import org.eclipse.digitaltwin.basyx.aasregistry.client.ApiException;
import org.eclipse.digitaltwin.basyx.aasregistry.client.api.RegistryAndDiscoveryInterfaceApi;
import org.eclipse.digitaltwin.basyx.aasregistry.client.model.AssetAdministrationShellDescriptor;
import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository;
import org.eclipse.digitaltwin.basyx.aasrepository.client.ConnectedAasRepository;
+import org.eclipse.digitaltwin.basyx.aasservice.client.ConnectedAasService;
+import org.eclipse.digitaltwin.basyx.client.internal.resolver.DescriptorResolver;
import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException;
import org.eclipse.digitaltwin.basyx.submodelregistry.client.api.SubmodelRegistryApi;
import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.SubmodelDescriptor;
@@ -67,23 +73,23 @@
*
*/
public class TestConnectedAasManager {
- private final static String AAS_REPOSITORY_BASE_PATH = "http://localhost:8081";
- private final static String SM_REPOSITORY_BASE_PATH = "http://localhost:8081";
- private final static String AAS_REGISTRY_BASE_PATH = "http://localhost:8050";
- private final static String SM_REGISTRY_BASE_PATH = "http://localhost:8060";
+ protected final static String AAS_REPOSITORY_BASE_PATH = "http://localhost:8081";
+ protected final static String SM_REPOSITORY_BASE_PATH = "http://localhost:8081";
+ protected final static String AAS_REGISTRY_BASE_PATH = "http://localhost:8050";
+ protected final static String SM_REGISTRY_BASE_PATH = "http://localhost:8060";
- private static ConfigurableApplicationContext appContext;
- private static AasRepository aasRepository;
- private static SubmodelRepository smRepository;
+ protected static ConfigurableApplicationContext appContext;
+ protected static AasRepository aasRepository;
+ protected static SubmodelRepository smRepository;
- private final static TestFixture FIXTURE = new TestFixture(AAS_REPOSITORY_BASE_PATH, SM_REPOSITORY_BASE_PATH);
+ protected final static TestFixture FIXTURE = new TestFixture(AAS_REPOSITORY_BASE_PATH, SM_REPOSITORY_BASE_PATH);
- private static ConnectedAasRepository connectedAasRepository;
- private static ConnectedSubmodelRepository connectedSmRepository;
- private static RegistryAndDiscoveryInterfaceApi aasRegistryApi;
- private static SubmodelRegistryApi smRegistryApi;
+ protected static ConnectedAasRepository connectedAasRepository;
+ protected static ConnectedSubmodelRepository connectedSmRepository;
+ protected static RegistryAndDiscoveryInterfaceApi aasRegistryApi;
+ protected static SubmodelRegistryApi smRegistryApi;
- private ConnectedAasManager aasManager;
+ protected ConnectedAasManager aasManager;
@BeforeClass
public static void initApplication() {
@@ -100,12 +106,12 @@ public static void cleanUpContext() {
@Before
public void setupRepositories() {
- connectedAasRepository = spy(new ConnectedAasRepository(AAS_REPOSITORY_BASE_PATH));
- connectedSmRepository = spy(new ConnectedSubmodelRepository(SM_REPOSITORY_BASE_PATH));
- aasRegistryApi = spy(new RegistryAndDiscoveryInterfaceApi(AAS_REGISTRY_BASE_PATH));
- smRegistryApi = spy(new SubmodelRegistryApi(SM_REGISTRY_BASE_PATH));
-
- aasManager = new ConnectedAasManager(aasRegistryApi, connectedAasRepository, AAS_REPOSITORY_BASE_PATH, smRegistryApi, connectedSmRepository, SM_REPOSITORY_BASE_PATH);
+ connectedAasRepository = getConnectedAasRepo();
+ connectedSmRepository = getConnectedSubmodelRepo();
+ aasRegistryApi = getConnectedAasRegistry();
+ smRegistryApi = getConnectedSubmodelRegistry();
+
+ aasManager = getConnectedAasManager();
cleanUpRegistries();
populateRepositories();
@@ -132,8 +138,8 @@ public void createAas() throws ApiException {
inOrder.verify(aasRegistryApi, times(1))
.postAssetAdministrationShellDescriptor(expectedDescriptor);
- assertEquals(expectedAas, aasRepository.getAas(TestFixture.AAS_POS1_ID));
- assertEquals(expectedDescriptor, new RegistryAndDiscoveryInterfaceApi(AAS_REGISTRY_BASE_PATH).getAssetAdministrationShellDescriptorById(TestFixture.AAS_POS1_ID));
+ assertEquals(expectedAas, getAasFromRepo(TestFixture.AAS_POS1_ID));
+ assertEquals(expectedDescriptor, getDescriptorFromAasRegistry(TestFixture.AAS_POS1_ID));
}
@Test
@@ -152,8 +158,8 @@ public void createSubmodelInAas() throws Exception {
inOrder.verify(connectedAasRepository, times(1))
.addSubmodelReference(eq(TestFixture.AAS_PRE1_ID), any());
- assertEquals(expectedSm, smRepository.getSubmodel(TestFixture.SM_POS1_ID));
- assertEquals(expectedDescriptor, new SubmodelRegistryApi(SM_REGISTRY_BASE_PATH).getSubmodelDescriptorById(TestFixture.SM_POS1_ID));
+ assertEquals(expectedSm, getSubmodelFromRepo(TestFixture.SM_POS1_ID));
+ assertEquals(expectedDescriptor, getDescriptorFromSubmodelRegistry(TestFixture.SM_POS1_ID));
}
@Test
@@ -167,8 +173,8 @@ public void deleteAas() throws ApiException {
inOrder.verify(connectedAasRepository, times(1))
.deleteAas(TestFixture.AAS_PRE1_ID);
- assertThrows(ElementDoesNotExistException.class, () -> aasRepository.getAas(TestFixture.AAS_PRE1_ID));
- assertThrows(Exception.class, () -> new RegistryAndDiscoveryInterfaceApi(AAS_REGISTRY_BASE_PATH).getAssetAdministrationShellDescriptorById(TestFixture.AAS_PRE1_ID));
+ assertThrows(ElementDoesNotExistException.class, () -> getAasFromRepo(TestFixture.AAS_PRE1_ID));
+ assertThrows(Exception.class, () -> getDescriptorFromAasRegistry(TestFixture.AAS_PRE1_ID));
}
@Test
@@ -184,15 +190,15 @@ public void deleteSubmodelOfAas() throws Exception {
inOrder.verify(connectedSmRepository, times(1))
.deleteSubmodel(TestFixture.SM_PRE1_ID);
- assertThrows(ElementDoesNotExistException.class, () -> smRepository.getSubmodel(TestFixture.SM_PRE1_ID));
- assertThrows(Exception.class, () -> new SubmodelRegistryApi(SM_REGISTRY_BASE_PATH).getSubmodelDescriptorById(TestFixture.SM_PRE1_ID));
+ assertThrows(ElementDoesNotExistException.class, () -> getSubmodelFromRepo(TestFixture.SM_PRE1_ID));
+ assertThrows(Exception.class, () -> getDescriptorFromSubmodelRegistry(TestFixture.SM_PRE1_ID));
}
@Test
public void getAas() throws ApiException, NoValidEndpointFoundException {
AssetAdministrationShell expectedAas = FIXTURE.buildAasPre1();
- AssetAdministrationShell actualAas = aasManager.getAas(TestFixture.AAS_PRE1_ID)
+ AssetAdministrationShell actualAas = aasManager.getAasService(TestFixture.AAS_PRE1_ID)
.getAAS();
assertEquals(expectedAas, actualAas);
@@ -201,13 +207,62 @@ public void getAas() throws ApiException, NoValidEndpointFoundException {
@Test
public void getSubmodel() throws Exception {
Submodel expectedSm = FIXTURE.buildSmPre1();
-
- Submodel actualSm = aasManager.getSubmodel(TestFixture.SM_PRE1_ID)
+
+ Submodel actualSm = aasManager.getSubmodelService(TestFixture.SM_PRE1_ID)
.getSubmodel();
assertEquals(expectedSm, actualSm);
}
+ protected Submodel getSubmodelFromManager(String submodelId) {
+ return aasManager.getSubmodelService(submodelId).getSubmodel();
+ }
+
+ protected AssetAdministrationShellDescriptor getDescriptorFromAasRegistry(String shellId) throws ApiException {
+ return new RegistryAndDiscoveryInterfaceApi(AAS_REGISTRY_BASE_PATH).getAssetAdministrationShellDescriptorById(shellId);
+ }
+
+ protected AssetAdministrationShell getAasFromRepo(String shellId) {
+ return aasRepository.getAas(shellId);
+ }
+
+ protected SubmodelDescriptor getDescriptorFromSubmodelRegistry(String submodelId) throws org.eclipse.digitaltwin.basyx.submodelregistry.client.ApiException {
+ return new SubmodelRegistryApi(SM_REGISTRY_BASE_PATH).getSubmodelDescriptorById(submodelId);
+ }
+
+ protected Submodel getSubmodelFromRepo(String submodelId) {
+ return smRepository.getSubmodel(TestFixture.SM_POS1_ID);
+ }
+
+ protected AssetAdministrationShell getAasFromManager(String shellId) {
+ return aasManager.getAasService(shellId).getAAS();
+ }
+
+ protected ConnectedAasManager getConnectedAasManager() {
+ DescriptorResolver aasDescriptorResolver = new AasDescriptorResolver(new EndpointResolver());
+ DescriptorResolver smDescriptorResolver = new SubmodelDescriptorResolver(new EndpointResolver());
+
+ DescriptorResolverManager descriptorResolverManager = new DescriptorResolverManager(aasDescriptorResolver, smDescriptorResolver);
+
+ return new ConnectedAasManager(aasRegistryApi, connectedAasRepository, smRegistryApi, connectedSmRepository, descriptorResolverManager);
+ }
+
+ protected SubmodelRegistryApi getConnectedSubmodelRegistry() {
+ return spy(new SubmodelRegistryApi(SM_REGISTRY_BASE_PATH));
+ }
+
+ protected RegistryAndDiscoveryInterfaceApi getConnectedAasRegistry() {
+ return spy(new RegistryAndDiscoveryInterfaceApi(AAS_REGISTRY_BASE_PATH));
+ }
+
+ protected ConnectedSubmodelRepository getConnectedSubmodelRepo() {
+ return spy(new ConnectedSubmodelRepository(SM_REPOSITORY_BASE_PATH));
+ }
+
+ protected ConnectedAasRepository getConnectedAasRepo() {
+ return spy(new ConnectedAasRepository(AAS_REPOSITORY_BASE_PATH));
+ }
+
@Test
public void getAllSubmodels() {
Submodel otherExpectedSubmodel = FIXTURE.buildSmPos1();
@@ -223,7 +278,7 @@ public void getAllSubmodels() {
}
- private void populateRegistries() {
+ protected void populateRegistries() {
try {
new RegistryAndDiscoveryInterfaceApi(AAS_REGISTRY_BASE_PATH).postAssetAdministrationShellDescriptor(FIXTURE.buildAasPre1Descriptor());
} catch (Exception e) {
@@ -236,7 +291,7 @@ private void populateRegistries() {
}
}
- private void cleanUpRegistries() {
+ protected void cleanUpRegistries() {
try {
new RegistryAndDiscoveryInterfaceApi(AAS_REGISTRY_BASE_PATH).deleteAllShellDescriptors();
} catch (Exception e) {
@@ -249,7 +304,7 @@ private void cleanUpRegistries() {
}
}
- private void populateRepositories() {
+ protected void populateRepositories() {
try {
aasRepository.createAas(FIXTURE.buildAasPre1());
} catch (Exception e) {
@@ -262,7 +317,7 @@ private void populateRepositories() {
}
}
- private void cleanUpRepositories() {
+ protected void cleanUpRepositories() {
try {
aasRepository.deleteAas(TestFixture.AAS_PRE1_ID);
} catch (Exception e) {
diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/RegistryDescriptorResolverTest.java b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/RegistryDescriptorResolverTest.java
index 533119693..e0666db0d 100644
--- a/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/RegistryDescriptorResolverTest.java
+++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/resolvers/RegistryDescriptorResolverTest.java
@@ -77,7 +77,8 @@ public void resolveAasDescriptor() throws ApiException {
AasDescriptorResolver resolver = new AasDescriptorResolver(new EndpointResolver());
AssetAdministrationShell expectedAas = FIXTURE.buildAasPre1();
- AssetAdministrationShell actualAas = resolver.resolveAasDescriptor(FIXTURE.buildAasPre1Descriptor()).getAAS();
+
+ AssetAdministrationShell actualAas = resolver.resolveDescriptor(FIXTURE.buildAasPre1Descriptor()).getAAS();
assertEquals(expectedAas, actualAas);
}
@@ -87,7 +88,8 @@ public void resolveSmDescriptor() throws ApiException {
SubmodelDescriptorResolver resolver = new SubmodelDescriptorResolver(new EndpointResolver());
Submodel expectedSm = FIXTURE.buildSmPre1();
- Submodel actualSm = resolver.resolveSubmodelDescriptor(FIXTURE.buildSmPre1Descriptor()).getSubmodel();
+
+ Submodel actualSm = resolver.resolveDescriptor(FIXTURE.buildSmPre1Descriptor()).getSubmodel();
assertEquals(expectedSm, actualSm);
}
diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/src/test/resources/application-authorization.properties b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/resources/application-authorization.properties
new file mode 100644
index 000000000..ce03007f1
--- /dev/null
+++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/resources/application-authorization.properties
@@ -0,0 +1,61 @@
+server.port=8081
+spring.application.name=AAS Environment
+
+basyx.backend = InMemory
+
+#basyx.backend = MongoDB
+#spring.data.mongodb.host=mongo
+# or spring.data.mongodb.host=127.0.0.1
+#spring.data.mongodb.port=27017
+#spring.data.mongodb.database=aasenvironments
+#spring.data.mongodb.authentication-database=admin
+#spring.data.mongodb.username=mongoAdmin
+#spring.data.mongodb.password=mongoPassword
+
+# basyx.aasrepository.feature.mqtt.enabled = true
+# mqtt.clientId=TestClient
+# mqtt.hostname = localhost
+# mqtt.port = 1883
+
+# basyx.cors.allowed-origins=http://localhost:3000, http://localhost:4000
+# basyx.cors.allowed-methods=GET,POST,PATCH,DELETE,PUT,OPTIONS,HEAD
+
+####################################################################################
+# Preconfiguring the Environment;
+####################################################################################
+# Comma seperated list that contains Environment files to load on startup
+# To load from Classpath (src/main/resources) use classpath:path/to/file.end
+# To load from Filesystem ( On your local machine ) use the prefix file:
+#
+# basyx.environment = classpath:testEnvironment.json,classpath:testEnvironment.xml,file:C:\\Users\\Administrator\\Documents\\01_Festo.aasx,file:/var/www/html/01_Submodel.json
+#
+
+####################################################################################
+# 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:9096/realms/BaSyx
+
+####################################################################################
+# Operation Delegation
+####################################################################################
+# This feature is enabled by default
+
+#basyx.submodelrepository.feature.operation.delegation.enabled = false
+
+####################################################################################
+# Max File Size
+####################################################################################
+# To define the maximum size of file to be uploaded in a request (default 1 MB)
+
+# spring.servlet.multipart.max-file-size=128KB
+
+####################################################################################
+# Max Request Size
+####################################################################################
+# To define the total request size for a multipart/form-data (default 10 MB)
+
+# spring.servlet.multipart.max-request-size=128KB
\ No newline at end of file
diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/src/test/resources/authorization/modulus.txt b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/resources/authorization/modulus.txt
new file mode 100644
index 000000000..9def9d5fa
--- /dev/null
+++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/resources/authorization/modulus.txt
@@ -0,0 +1 @@
+sQ4RUvlZXLjQpLizIMht46ASwpwUoPpUDTmUhxF_VV-ezHTHbTAdv_5RA1GgCyTjAQe_Ih6dLByZrJFaroyvqgIJdMRCb0MwajI1US0_NwHtVvo5dea_-GKeHGRzvYZjxVlooR_1xmskfAM_NR_NaOMUhr_TNV7n7LXEb06L55DYnqdqrUnhXLewBq1lo54GsMqxN4hlkc4nJ2uYUtWEkV4SlMyYRjXBlylpuWFO0-_FsmaqSx7CZWjNWmqKlxnvUgRrT5Vh-9ZgCAOmGtLuFYOVWzqmVjWtyiJ1pYfWjwc86XeWBFefVY1lkoNNoSYKV4AZIkeF2M_-FHNzNGhLOw
\ No newline at end of file
diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/src/test/resources/rbac_rules.json b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/resources/rbac_rules.json
new file mode 100644
index 000000000..8a91a29f8
--- /dev/null
+++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/resources/rbac_rules.json
@@ -0,0 +1,656 @@
+[
+ {
+ "role": "basyx-reader",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "*"
+ }
+ },
+ {
+ "role": "admin",
+ "action": ["CREATE", "READ", "UPDATE", "DELETE"],
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "*"
+ }
+ },
+ {
+ "role": "basyx-reader-two",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "specificAasId"
+ }
+ },
+ {
+ "role": "basyx-creator",
+ "action": "CREATE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "*"
+ }
+ },
+ {
+ "role": "basyx-updater",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "*"
+ }
+ },
+ {
+ "role": "basyx-updater-two",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "specificAasId"
+ }
+ },
+ {
+ "role": "basyx-asset-updater",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "*"
+ }
+ },
+ {
+ "role": "basyx-asset-updater-two",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "specificAasId-2"
+ }
+ },
+ {
+ "role": "basyx-deleter",
+ "action": "DELETE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "*"
+ }
+ },
+ {
+ "role": "basyx-deleter-two",
+ "action": "DELETE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "specificAasId-2"
+ }
+ },
+ {
+ "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",
+ "submodelElementIdShortPaths": "smc2.specificSubmodelElementIdShort"
+ }
+ },
+ {
+ "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"
+ }
+ },
+ {
+ "role": "basyx-reader",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "concept-description",
+ "conceptDescriptionIds": "*"
+ }
+ },
+ {
+ "role": "admin",
+ "action": ["CREATE", "READ", "UPDATE", "DELETE"],
+ "targetInformation": {
+ "@type": "concept-description",
+ "conceptDescriptionIds": "*"
+ }
+ },
+ {
+ "role": "basyx-reader-two",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "concept-description",
+ "conceptDescriptionIds": "specificConceptDescriptionId"
+ }
+ },
+ {
+ "role": "basyx-creator",
+ "action": "CREATE",
+ "targetInformation": {
+ "@type": "concept-description",
+ "conceptDescriptionIds": "*"
+ }
+ },
+ {
+ "role": "basyx-creator",
+ "action": "CREATE",
+ "targetInformation": {
+ "@type": "aas-environment",
+ "aasIds": "*",
+ "submodelIds": "*"
+ }
+ },
+ {
+ "role": "basyx-updater",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "concept-description",
+ "conceptDescriptionIds": "*"
+ }
+ },
+ {
+ "role": "basyx-updater-two",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "concept-description",
+ "conceptDescriptionIds": "specificConceptDescriptionId"
+ }
+ },
+ {
+ "role": "basyx-deleter",
+ "action": "DELETE",
+ "targetInformation": {
+ "@type": "concept-description",
+ "conceptDescriptionIds": "*"
+ }
+ },
+ {
+ "role": "basyx-deleter-two",
+ "action": "DELETE",
+ "targetInformation": {
+ "@type": "concept-description",
+ "conceptDescriptionIds": "specificConceptDescriptionId-2"
+ }
+ },
+ {
+ "role": "admin",
+ "action": ["CREATE", "READ", "UPDATE", "DELETE"],
+ "targetInformation": {
+ "@type": "aas-environment",
+ "aasIds": "*",
+ "submodelIds": "*"
+ }
+ },
+ {
+ "role": "basyx-reader",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "aas-environment",
+ "aasIds": "*",
+ "submodelIds": "*"
+ }
+ },
+ {
+ "role": "basyx-reader-two",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "aas-environment",
+ "aasIds": "*",
+ "submodelIds": "*"
+ }
+ },
+ {
+ "role": "basyx-reader-two",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "*"
+ }
+ },
+ {
+ "role": "basyx-reader-two",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "submodel",
+ "submodelIds": "*",
+ "submodelElementIdShortPaths": "*"
+ }
+ },
+ {
+ "role": "basyx-reader-two",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "concept-description",
+ "conceptDescriptionIds": "*"
+ }
+ },
+ {
+ "role": "basyx-updater",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "aas-environment",
+ "aasIds": "*",
+ "submodelIds": "*"
+ }
+ },
+ {
+ "role": "basyx-reader-serialization",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "aas-environment",
+ "aasIds": ["shell001", "shell002"],
+ "submodelIds": ["7A7104BDAB57E184", "AC69B1CB44F07935"]
+ }
+ },
+ {
+ "role": "basyx-reader-serialization",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "*"
+ }
+ },
+ {
+ "role": "basyx-reader-serialization",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "submodel",
+ "submodelIds": "*",
+ "submodelElementIdShortPaths": "*"
+ }
+ },
+ {
+ "role": "basyx-reader-serialization",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "concept-description",
+ "conceptDescriptionIds": "*"
+ }
+ },
+ {
+ "role": "basyx-reader-serialization-two",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "aas-environment",
+ "aasIds": ["shell001", "shell002"],
+ "submodelIds": ["7A7104BDAB57E184"]
+ }
+ },
+ {
+ "role": "basyx-uploader",
+ "action": "CREATE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "*"
+ }
+ },
+ {
+ "role": "basyx-uploader",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "*"
+ }
+ },
+ {
+ "role": "basyx-uploader",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "*"
+ }
+ },
+ {
+ "role": "basyx-uploader",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "submodel",
+ "submodelIds": "*",
+ "submodelElementIdShortPaths": "*"
+ }
+ },
+ {
+ "role": "basyx-uploader",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "concept-description",
+ "conceptDescriptionIds": "*"
+ }
+ },
+ {
+ "role": "basyx-uploader",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "concept-description",
+ "conceptDescriptionIds": "*"
+ }
+ },
+ {
+ "role": "basyx-uploader",
+ "action": "CREATE",
+ "targetInformation": {
+ "@type": "submodel",
+ "submodelIds": "*",
+ "submodelElementIdShortPaths": "*"
+ }
+ },
+ {
+ "role": "basyx-uploader",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "submodel",
+ "submodelIds": "*",
+ "submodelElementIdShortPaths": "*"
+ }
+ },
+ {
+ "role": "basyx-uploader",
+ "action": "CREATE",
+ "targetInformation": {
+ "@type": "concept-description",
+ "conceptDescriptionIds": "*"
+ }
+ },
+ {
+ "role": "basyx-uploader",
+ "action": "CREATE",
+ "targetInformation": {
+ "@type": "aas-environment",
+ "aasIds": "*",
+ "submodelIds": "*"
+ }
+ },
+ {
+ "role": "basyx-uploader-two",
+ "action": "CREATE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "http://customer.com/aas/9175_7013_7091_9168"
+ }
+ },
+ {
+ "role": "basyx-uploader-two",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "http://customer.com/aas/9175_7013_7091_9168"
+ }
+ },
+ {
+ "role": "basyx-uploader-two",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "http://customer.com/aas/9175_7013_7091_9168"
+ }
+ },
+ {
+ "role": "basyx-uploader-two",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "submodel",
+ "submodelIds": "*",
+ "submodelElementIdShortPaths": "*"
+ }
+ },
+ {
+ "role": "basyx-uploader-two",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "concept-description",
+ "conceptDescriptionIds": "*"
+ }
+ },
+ {
+ "role": "basyx-uploader-two",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "concept-description",
+ "conceptDescriptionIds": "*"
+ }
+ },
+ {
+ "role": "basyx-uploader-two",
+ "action": "CREATE",
+ "targetInformation": {
+ "@type": "submodel",
+ "submodelIds": "*",
+ "submodelElementIdShortPaths": "*"
+ }
+ },
+ {
+ "role": "basyx-uploader-two",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "submodel",
+ "submodelIds": "*",
+ "submodelElementIdShortPaths": "*"
+ }
+ },
+ {
+ "role": "basyx-uploader-two",
+ "action": "CREATE",
+ "targetInformation": {
+ "@type": "concept-description",
+ "conceptDescriptionIds": "*"
+ }
+ },
+ {
+ "role": "basyx-uploader-two",
+ "action": "CREATE",
+ "targetInformation": {
+ "@type": "aas-environment",
+ "aasIds": "http://customer.com/aas/9175_7013_7091_9168",
+ "submodelIds": ["http://i40.customer.com/type/1/1/7A7104BDAB57E184", "http://i40.customer.com/type/1/1/1A7B62B529F19152", "http://i40.customer.com/instance/1/1/AC69B1CB44F07935"]
+ }
+ },
+ {
+ "role": "basyx-uploader-three",
+ "action": "CREATE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "http://customer.com/aas/9175_7013_7091_9168"
+ }
+ },
+ {
+ "role": "basyx-uploader-three",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "http://customer.com/aas/9175_7013_7091_9168"
+ }
+ },
+ {
+ "role": "basyx-uploader-three",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "submodel",
+ "submodelIds": "*",
+ "submodelElementIdShortPaths": "*"
+ }
+ },
+ {
+ "role": "basyx-uploader-three",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "concept-description",
+ "conceptDescriptionIds": "*"
+ }
+ },
+ {
+ "role": "basyx-uploader-three",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "concept-description",
+ "conceptDescriptionIds": "*"
+ }
+ },
+ {
+ "role": "basyx-uploader-three",
+ "action": "CREATE",
+ "targetInformation": {
+ "@type": "submodel",
+ "submodelIds": "*",
+ "submodelElementIdShortPaths": "*"
+ }
+ },
+ {
+ "role": "basyx-uploader-three",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "submodel",
+ "submodelIds": "*",
+ "submodelElementIdShortPaths": "*"
+ }
+ },
+ {
+ "role": "basyx-uploader-three",
+ "action": "CREATE",
+ "targetInformation": {
+ "@type": "concept-description",
+ "conceptDescriptionIds": "*"
+ }
+ },
+ {
+ "role": "basyx-uploader-three",
+ "action": "CREATE",
+ "targetInformation": {
+ "@type": "aas-environment",
+ "aasIds": "AuthorizedAasID",
+ "submodelIds": ["http://i40.customer.com/type/1/1/7A7104BDAB57E184", "http://i40.customer.com/type/1/1/1A7B62B529F19152", "http://i40.customer.com/instance/1/1/AC69B1CB44F07935"]
+ }
+ }
+]
\ No newline at end of file
diff --git a/basyx.aasregistry/basyx.aasregistry-client-native/pom.xml b/basyx.aasregistry/basyx.aasregistry-client-native/pom.xml
index da0cc8bc0..eb589f472 100644
--- a/basyx.aasregistry/basyx.aasregistry-client-native/pom.xml
+++ b/basyx.aasregistry/basyx.aasregistry-client-native/pom.xml
@@ -19,7 +19,6 @@
${openapi.result.folder}/${openapi.name}
- src/generated/java
maven-clean-plugin
@@ -52,8 +51,10 @@
jsonpatch-maven-plugin
- ${project.basedir}/../${openapi.folder.name}/${openapi.base.name}
- ${project.basedir}/../${openapi.folder.name}/${patch.base-extensions.name}
+
+ ${project.basedir}/../${openapi.folder.name}/${openapi.base.name}
+
+ ${project.basedir}/../${openapi.folder.name}/${patch.base-extensions.name}
@@ -72,9 +73,12 @@
native
${openapi.result.file}
- org.eclipse.digitaltwin.basyx.aasregistry.client.api
- org.eclipse.digitaltwin.basyx.aasregistry.client
- org.eclipse.digitaltwin.basyx.aasregistry.client.model
+
+ org.eclipse.digitaltwin.basyx.aasregistry.client.api
+
+ org.eclipse.digitaltwin.basyx.aasregistry.client
+
+ org.eclipse.digitaltwin.basyx.aasregistry.client.model
true
true
false
@@ -133,6 +137,10 @@
com.google.code.findbugs
jsr305
+
+ org.eclipse.digitaltwin.basyx
+ basyx.client
+
diff --git a/basyx.aasregistry/basyx.aasregistry-client-native/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/main/client/AuthorizedConnectedAasRegistry.java b/basyx.aasregistry/basyx.aasregistry-client-native/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/main/client/AuthorizedConnectedAasRegistry.java
new file mode 100644
index 000000000..5b340f36a
--- /dev/null
+++ b/basyx.aasregistry/basyx.aasregistry-client-native/src/main/java/org/eclipse/digitaltwin/basyx/aasregistry/main/client/AuthorizedConnectedAasRegistry.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * 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.aasregistry.main.client;
+
+import java.io.IOException;
+import java.net.http.HttpRequest;
+
+import org.eclipse.digitaltwin.basyx.aasregistry.client.api.RegistryAndDiscoveryInterfaceApi;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.TokenManager;
+
+/**
+ * Provides access to an authorized Aas Registry on a remote server
+ *
+ * @author danish
+ */
+public class AuthorizedConnectedAasRegistry extends RegistryAndDiscoveryInterfaceApi {
+
+ private final TokenManager tokenManager;
+ private final String aasRegistryBasePath;
+
+ /**
+ *
+ * @param basePath
+ * the Url of the Aas Registry without the "/shell-descriptors" part
+ * @param tokenManager
+ */
+ public AuthorizedConnectedAasRegistry(String basePath, TokenManager tokenManager) {
+ super(basePath, getRequestBuilder(tokenManager));
+ this.aasRegistryBasePath = basePath;
+ this.tokenManager = tokenManager;
+ }
+
+ public String getBaseUrl() {
+ return aasRegistryBasePath;
+ }
+
+ public TokenManager getTokenManager() {
+ return tokenManager;
+ }
+
+ private static HttpRequest.Builder getRequestBuilder(TokenManager tokenManager) {
+ try {
+ return HttpRequest.newBuilder().header("Authorization", "Bearer " + tokenManager.getAccessToken());
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException("Unable to request access token");
+ }
+ }
+
+}
diff --git a/basyx.aasregistry/basyx.aasregistry-client-native/templates/libraries/native/api.mustache b/basyx.aasregistry/basyx.aasregistry-client-native/templates/libraries/native/api.mustache
index 5ec50e316..d5d40059e 100644
--- a/basyx.aasregistry/basyx.aasregistry-client-native/templates/libraries/native/api.mustache
+++ b/basyx.aasregistry/basyx.aasregistry-client-native/templates/libraries/native/api.mustache
@@ -59,17 +59,36 @@ public class {{classname}} {
private final Duration memberVarReadTimeout;
private final {{#fullJavaUtil}}java.util.function.{{/fullJavaUtil}}Consumer> memberVarResponseInterceptor;
private final {{#fullJavaUtil}}java.util.function.{{/fullJavaUtil}}Consumer> memberVarAsyncResponseInterceptor;
+ private HttpRequest.Builder httpRequestBuilder;
public {{classname}}() {
this(new ApiClient());
+ this.httpRequestBuilder = HttpRequest.newBuilder();
+ }
+
+ public {{classname}}(HttpRequest.Builder httpRequestBuilder) {
+ this(new ApiClient());
+ this.httpRequestBuilder = httpRequestBuilder;
}
public {{classname}}(String protocol, String host, int port) {
this(protocol + "://" + host + ":" + port);
+ this.httpRequestBuilder = HttpRequest.newBuilder();
+ }
+
+ public {{classname}}(String protocol, String host, int port, HttpRequest.Builder httpRequestBuilder) {
+ this(protocol + "://" + host + ":" + port);
+ this.httpRequestBuilder = httpRequestBuilder;
}
public {{classname}}(String basePath) {
this(withBaseUri(new ApiClient(), basePath));
+ this.httpRequestBuilder = HttpRequest.newBuilder();
+ }
+
+ public {{classname}}(String basePath, HttpRequest.Builder httpRequestBuilder) {
+ this(withBaseUri(new ApiClient(), basePath));
+ this.httpRequestBuilder = httpRequestBuilder;
}
private static ApiClient withBaseUri(ApiClient client, String uri) {
@@ -361,7 +380,7 @@ public class {{classname}} {
{{#hasPathParams}}{{#pathParams}}{{#vendorExtensions.x-utf8-base64-url-encoded-as-string}}String {{{paramName}}}AsBase64EncodedParam = {{{paramName}}} == null ? null : new String(java.util.Base64.getUrlEncoder().encode({{paramName}}.getBytes(java.nio.charset.StandardCharsets.UTF_8)), java.nio.charset.StandardCharsets.UTF_8);
{{/vendorExtensions.x-utf8-base64-url-encoded-as-string}}{{/pathParams}}{{/hasPathParams}}
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
{{! Switch delimiters for baseName so we can write constants like "{query}" }}
String localVarPath = "{{{path}}}"{{#pathParams}}
diff --git a/basyx.aasregistry/basyx.aasregistry-service-release-kafka-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/aasregistry/service/storage/mongodb/AuthorizedClientTest.java b/basyx.aasregistry/basyx.aasregistry-service-release-kafka-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/aasregistry/service/storage/mongodb/AuthorizedClientTest.java
new file mode 100644
index 000000000..0fb2192e1
--- /dev/null
+++ b/basyx.aasregistry/basyx.aasregistry-service-release-kafka-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/aasregistry/service/storage/mongodb/AuthorizedClientTest.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * 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.aasregistry.service.storage.mongodb;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import java.io.IOException;
+import org.eclipse.digitaltwin.basyx.aasregistry.client.ApiException;
+import org.eclipse.digitaltwin.basyx.aasregistry.client.api.RegistryAndDiscoveryInterfaceApi;
+import org.eclipse.digitaltwin.basyx.aasregistry.client.model.AssetAdministrationShellDescriptor;
+import org.eclipse.digitaltwin.basyx.aasregistry.main.client.AuthorizedConnectedAasRegistry;
+import org.eclipse.digitaltwin.basyx.aasregistry.service.storage.mongodb.KafkaEventsMongoDbStorageIntegrationTest.RegistrationEventKafkaListener;
+import org.eclipse.digitaltwin.basyx.aasregistry.service.tests.integration.BaseIntegrationTest;
+import org.eclipse.digitaltwin.basyx.aasregistry.service.tests.integration.EventQueue;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.TokenManager;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.credential.ClientCredential;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.grant.ClientCredentialAccessTokenProvider;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpStatus;
+import org.springframework.test.context.TestPropertySource;
+
+/**
+ * Tests the Authorized client
+ *
+ * @author danish
+ */
+@TestPropertySource(properties = {"spring.profiles.active=kafkaEvents,mongoDbStorage", "spring.kafka.bootstrap-servers=PLAINTEXT_HOST://localhost:9092", "spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:9096/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.data.mongodb.database=aasregistry", "spring.data.mongodb.uri=mongodb://mongoAdmin:mongoPassword@localhost:27017/"})
+public class AuthorizedClientTest extends BaseIntegrationTest {
+
+ @Autowired
+ private RegistrationEventKafkaListener listener;
+
+ @Value("${local.server.port}")
+ private int port;
+
+ @Before
+ public void awaitAssignment() throws InterruptedException {
+ listener.awaitTopicAssignment();
+ }
+
+ @Override
+ public EventQueue queue() {
+ return listener.getQueue();
+ }
+
+ @Before
+ @Override
+ public void initClient() throws ApiException {
+ api = new AuthorizedConnectedAasRegistry("http://127.0.0.1:" + port, new TokenManager("http://localhost:9096/realms/BaSyx/protocol/openid-connect/token", new ClientCredentialAccessTokenProvider(new ClientCredential("workstation-1", "nY0mjyECF60DGzNmQUjL81XurSl8etom"))));
+
+ api.deleteAllShellDescriptors();
+ queue().assertNoAdditionalMessage();
+ }
+
+ @Test
+ public void sendUnauthorizedRequest() throws IOException {
+ TokenManager mockTokenManager = Mockito.mock(TokenManager.class);
+
+ Mockito.when(mockTokenManager.getAccessToken()).thenReturn("mockedAccessToken");
+
+ RegistryAndDiscoveryInterfaceApi registryApi = new AuthorizedConnectedAasRegistry("http://127.0.0.1:" + port, mockTokenManager);
+
+ AssetAdministrationShellDescriptor descriptor = new AssetAdministrationShellDescriptor();
+ descriptor.setIdShort("shortId");
+
+ ApiException exception = assertThrows(ApiException.class, () -> {
+ registryApi.postAssetAdministrationShellDescriptor(descriptor);
+ });
+
+ assertEquals(HttpStatus.UNAUTHORIZED.value(), exception.getCode());
+ }
+
+ @Test
+ @Override
+ public void whenPostSubmodelDescriptor_LocationIsReturned() throws ApiException, IOException {
+ // TODO: It uses normal GET unauthorized request, need to override and refactor
+ }
+
+ @Test
+ @Override
+ public void whenPostShellDescriptor_LocationIsReturned() throws ApiException, IOException {
+ // TODO: It uses normal GET unauthorized request, need to override and refactor
+ }
+
+}
diff --git a/basyx.aasregistry/basyx.aasregistry-service-release-kafka-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/aasregistry/service/storage/mongodb/KafkaEventsMongoDbStorageIntegrationTest.java b/basyx.aasregistry/basyx.aasregistry-service-release-kafka-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/aasregistry/service/storage/mongodb/KafkaEventsMongoDbStorageIntegrationTest.java
index 8eb388ab6..0edaf75c5 100644
--- a/basyx.aasregistry/basyx.aasregistry-service-release-kafka-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/aasregistry/service/storage/mongodb/KafkaEventsMongoDbStorageIntegrationTest.java
+++ b/basyx.aasregistry/basyx.aasregistry-service-release-kafka-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/aasregistry/service/storage/mongodb/KafkaEventsMongoDbStorageIntegrationTest.java
@@ -63,7 +63,7 @@ public EventQueue queue() {
@KafkaListener(topics = "aas-registry", batch = "false", groupId = "kafka-test", autoStartup = "true")
@Component
- private static class RegistrationEventKafkaListener implements ConsumerSeekAware {
+ public static class RegistrationEventKafkaListener implements ConsumerSeekAware {
private final EventQueue queue;
private final CountDownLatch latch = new CountDownLatch(1);
@@ -75,6 +75,10 @@ private static class RegistrationEventKafkaListener implements ConsumerSeekAware
public RegistrationEventKafkaListener(ObjectMapper mapper) {
this.queue = new EventQueue(mapper);
}
+
+ public EventQueue getQueue() {
+ return this.queue;
+ }
@KafkaHandler
public void receiveMessage(String content) {
diff --git a/basyx.aasrepository/basyx.aasrepository-client/pom.xml b/basyx.aasrepository/basyx.aasrepository-client/pom.xml
index 52ec3a222..7a7ab42a5 100644
--- a/basyx.aasrepository/basyx.aasrepository-client/pom.xml
+++ b/basyx.aasrepository/basyx.aasrepository-client/pom.xml
@@ -1,4 +1,4 @@
-
4.0.0
@@ -21,7 +21,7 @@
org.eclipse.digitaltwin.basyx
basyx.aasrepository-core
-
+
org.eclipse.digitaltwin.basyx
basyx.aasrepository-core
@@ -35,12 +35,45 @@
test
tests
+
+ org.eclipse.digitaltwin.basyx
+ basyx.authorization
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.authorization
+ test
+ tests
+
+
+ org.apache.httpcomponents.client5
+ httpclient5
+ test
+
org.eclipse.digitaltwin.basyx
basyx.aasservice-core
tests
test
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.aasrepository-feature-authorization
+ test
+ tests
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.http
+ test
+ tests
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.aasrepository-feature-authorization
+ test
+
org.eclipse.digitaltwin.basyx
basyx.aasrepository-http
@@ -56,9 +89,20 @@
basyx.aasservice-backend-inmemory
test
+
+ org.mockito
+ mockito-core
+ test
+
org.eclipse.digitaltwin.basyx
basyx.aasservice-client
+
+ org.eclipse.digitaltwin.basyx
+ basyx.aasservice-client
+ test
+ tests
+
\ No newline at end of file
diff --git a/basyx.aasrepository/basyx.aasrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/client/AuthorizedConnectedAasRepository.java b/basyx.aasrepository/basyx.aasrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/client/AuthorizedConnectedAasRepository.java
new file mode 100644
index 000000000..516dfcd0f
--- /dev/null
+++ b/basyx.aasrepository/basyx.aasrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/client/AuthorizedConnectedAasRepository.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * 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.aasrepository.client;
+
+import java.io.IOException;
+import java.net.http.HttpRequest;
+
+import org.eclipse.digitaltwin.basyx.aasrepository.client.internal.AssetAdministrationShellRepositoryApi;
+import org.eclipse.digitaltwin.basyx.aasservice.client.ConnectedAasService;
+import org.eclipse.digitaltwin.basyx.aasservice.client.AuthorizedConnectedAasService;
+import org.eclipse.digitaltwin.basyx.client.internal.ApiException;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.TokenManager;
+
+/**
+ * Provides access to an Authorized Aas Repository on a remote server
+ *
+ * @author danish
+ */
+public class AuthorizedConnectedAasRepository extends ConnectedAasRepository {
+
+ private TokenManager tokenManager;
+
+ public AuthorizedConnectedAasRepository(String repoUrl, TokenManager tokenManager) {
+ super(repoUrl, new AssetAdministrationShellRepositoryApi(repoUrl, getRequestBuilder(tokenManager)));
+ this.tokenManager = tokenManager;
+ }
+
+ public TokenManager getTokenManager() {
+ return tokenManager;
+ }
+
+ @Override
+ public ConnectedAasService getConnectedAasService(String aasId) {
+ try {
+ getAas(aasId);
+ return new AuthorizedConnectedAasService(getAasUrl(aasId), tokenManager);
+ } catch (ApiException e) {
+ throw mapExceptionAasAccess(aasId, e);
+ }
+ }
+
+ private static HttpRequest.Builder getRequestBuilder(TokenManager tokenManager) {
+ try {
+ return HttpRequest.newBuilder().header("Authorization", "Bearer " + tokenManager.getAccessToken());
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException("Unable to request access token");
+ }
+ }
+
+}
diff --git a/basyx.aasrepository/basyx.aasrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/client/ConnectedAasRepository.java b/basyx.aasrepository/basyx.aasrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/client/ConnectedAasRepository.java
index 370631eb4..c302adc52 100644
--- a/basyx.aasrepository/basyx.aasrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/client/ConnectedAasRepository.java
+++ b/basyx.aasrepository/basyx.aasrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/client/ConnectedAasRepository.java
@@ -66,6 +66,15 @@ public ConnectedAasRepository(String repoUrl) {
this.aasRepoUrl = repoUrl;
this.repoApi = new AssetAdministrationShellRepositoryApi(repoUrl);
}
+
+ public ConnectedAasRepository(String repoUrl, AssetAdministrationShellRepositoryApi assetAdministrationShellRepositoryApi) {
+ this.aasRepoUrl = repoUrl;
+ this.repoApi = assetAdministrationShellRepositoryApi;
+ }
+
+ public String getBaseUrl() {
+ return aasRepoUrl;
+ }
@Override
public CursorResult> getAllAas(PaginationInfo pInfo) {
@@ -165,7 +174,7 @@ public void deleteThumbnail(String aasId) {
getConnectedAasService(aasId).deleteThumbnail();
}
- private String getAasUrl(String aasId) {
+ protected String getAasUrl(String aasId) {
return aasRepoUrl + "/shells/" + Base64UrlEncodedIdentifier.encodeIdentifier(aasId);
}
@@ -177,7 +186,7 @@ private RuntimeException mapExceptionAasUpdate(String aasId, ApiException e) {
return mapExceptionAasAccess(aasId, e);
}
- private RuntimeException mapExceptionAasAccess(String aasId, ApiException e) {
+ protected RuntimeException mapExceptionAasAccess(String aasId, ApiException e) {
if (e.getCode() == HttpStatus.NOT_FOUND.value()) {
return new ElementDoesNotExistException(aasId);
} else if (e.getCode() == HttpStatus.CONFLICT.value()) {
diff --git a/basyx.aasrepository/basyx.aasrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/client/internal/AssetAdministrationShellRepositoryApi.java b/basyx.aasrepository/basyx.aasrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/client/internal/AssetAdministrationShellRepositoryApi.java
index 26289eb78..da33c612e 100644
--- a/basyx.aasrepository/basyx.aasrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/client/internal/AssetAdministrationShellRepositoryApi.java
+++ b/basyx.aasrepository/basyx.aasrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/client/internal/AssetAdministrationShellRepositoryApi.java
@@ -64,19 +64,37 @@ public class AssetAdministrationShellRepositoryApi {
private final Consumer> memberVarResponseInterceptor;
private final Consumer> memberVarAsyncResponseInterceptor;
+ private HttpRequest.Builder httpRequestBuilder;
public AssetAdministrationShellRepositoryApi() {
this(new ApiClient());
+ this.httpRequestBuilder = HttpRequest.newBuilder();
+ }
+
+ public AssetAdministrationShellRepositoryApi(HttpRequest.Builder httpRequestBuilder) {
+ this();
+ this.httpRequestBuilder = httpRequestBuilder;
}
public AssetAdministrationShellRepositoryApi(ObjectMapper mapper, String baseUri) {
this(new ApiClient(HttpClient.newBuilder(), mapper, baseUri));
+ this.httpRequestBuilder = HttpRequest.newBuilder();
+ }
+
+ public AssetAdministrationShellRepositoryApi(ObjectMapper mapper, String baseUri, HttpRequest.Builder httpRequestBuilder) {
+ this(mapper, baseUri);
+ this.httpRequestBuilder = httpRequestBuilder;
}
public AssetAdministrationShellRepositoryApi(String baseUri) {
this(new ApiClient(HttpClient.newBuilder(), new JsonMapperFactory().create(new SimpleAbstractTypeResolverFactory().create()), baseUri));
+ this.httpRequestBuilder = HttpRequest.newBuilder();
+ }
+
+ public AssetAdministrationShellRepositoryApi(String baseUri, HttpRequest.Builder httpRequestBuilder) {
+ this(baseUri);
+ this.httpRequestBuilder = httpRequestBuilder;
}
-
public AssetAdministrationShellRepositoryApi(ApiClient apiClient) {
memberVarHttpClient = apiClient.getHttpClient();
@@ -173,10 +191,10 @@ private HttpRequest.Builder deleteAssetAdministrationShellByIdRequestBuilder(Str
throw new ApiException(400, "Missing the required parameter 'aasIdentifier' when calling deleteAssetAdministrationShellById");
}
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
-
String localVarPath = "/shells/{aasIdentifier}"
.replace("{aasIdentifier}", ApiClient.urlEncode(aasIdentifier.toString()));
+
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
localVarRequestBuilder.uri(URI.create(memberVarBaseUri + localVarPath));
@@ -344,9 +362,9 @@ private ApiResponse>
private HttpRequest.Builder getAllAssetAdministrationShellsRequestBuilder(List assetIds, String idShort, Integer limit, String cursor) throws ApiException {
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
-
String localVarPath = "/shells";
+
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
List localVarQueryParams = new ArrayList<>();
StringJoiner localVarQueryStringJoiner = new StringJoiner("&");
@@ -452,10 +470,10 @@ private HttpRequest.Builder getAssetAdministrationShellByIdRequestBuilder(String
throw new ApiException(400, "Missing the required parameter 'aasIdentifier' when calling getAssetAdministrationShellById");
}
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
-
String localVarPath = "/shells/{aasIdentifier}"
.replace("{aasIdentifier}", ApiClient.urlEncode(aasIdentifier.toString()));
+
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
localVarRequestBuilder.uri(URI.create(memberVarBaseUri + localVarPath));
@@ -713,9 +731,9 @@ private HttpRequest.Builder postAssetAdministrationShellRequestBuilder(AssetAdmi
throw new ApiException(400, "Missing the required parameter 'assetAdministrationShell' when calling postAssetAdministrationShell");
}
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
-
String localVarPath = "/shells";
+
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
localVarRequestBuilder.uri(URI.create(memberVarBaseUri + localVarPath));
@@ -916,10 +934,10 @@ private HttpRequest.Builder putAssetAdministrationShellByIdRequestBuilder(String
throw new ApiException(400, "Missing the required parameter 'assetAdministrationShell' when calling putAssetAdministrationShellById");
}
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
-
String localVarPath = "/shells/{aasIdentifier}"
.replace("{aasIdentifier}", ApiClient.urlEncode(aasIdentifier.toString()));
+
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
localVarRequestBuilder.uri(URI.create(memberVarBaseUri + localVarPath));
diff --git a/basyx.aasrepository/basyx.aasrepository-client/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/client/TestAuthorizedConnectedAasRepository.java b/basyx.aasrepository/basyx.aasrepository-client/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/client/TestAuthorizedConnectedAasRepository.java
new file mode 100644
index 000000000..aa6903e89
--- /dev/null
+++ b/basyx.aasrepository/basyx.aasrepository-client/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/client/TestAuthorizedConnectedAasRepository.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * 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.aasrepository.client;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell;
+import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository;
+import org.eclipse.digitaltwin.basyx.aasrepository.AasRepositorySuite;
+import org.eclipse.digitaltwin.basyx.aasrepository.DummyAasFactory;
+import org.eclipse.digitaltwin.basyx.aasrepository.feature.authorization.DummyAuthorizedAasRepositoryComponent;
+import org.eclipse.digitaltwin.basyx.aasservice.client.TestAuthorizedConnectedAasService;
+import org.eclipse.digitaltwin.basyx.client.internal.ApiException;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.TokenManager;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.credential.ClientCredential;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.grant.ClientCredentialAccessTokenProvider;
+import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.springframework.boot.SpringApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+/**
+ * Tests for {@link AuthorizedConnectedAasRepository}
+ *
+ * @author danish
+ */
+public class TestAuthorizedConnectedAasRepository extends AasRepositorySuite {
+
+ private static ConfigurableApplicationContext appContext;
+ private static final String PROFILE = "authorization";
+
+ @BeforeClass
+ public static void startAASRepo() throws Exception {
+ SpringApplication application = new SpringApplication(DummyAuthorizedAasRepositoryComponent.class);
+ application.setAdditionalProfiles(PROFILE);
+
+ appContext = application.run(new String[] {});
+ }
+
+ @After
+ public void removeAasFromRepo() throws FileNotFoundException, IOException {
+ TestAuthorizedConnectedAasService.configureSecurityContext(TestAuthorizedConnectedAasService.getTokenProvider());
+
+ AasRepository repo = appContext.getBean(AasRepository.class);
+ repo.getAllAas(PaginationInfo.NO_LIMIT).getResult().stream().map(s -> s.getId()).forEach(repo::deleteAas);
+
+ SecurityContextHolder.clearContext();
+ }
+
+ @AfterClass
+ public static void shutdownAASRepo() {
+ appContext.close();
+ }
+
+ @Test
+ public void sendUnauthorizedRequest() throws IOException {
+ TokenManager mockTokenManager = Mockito.mock(TokenManager.class);
+
+ Mockito.when(mockTokenManager.getAccessToken()).thenReturn("mockedAccessToken");
+
+ AasRepository aasRepository = new AuthorizedConnectedAasRepository("http://localhost:8081", mockTokenManager);
+
+ AssetAdministrationShell expected = DummyAasFactory.createAasWithSubmodelReference();
+
+ ApiException exception = assertThrows(ApiException.class, () -> {
+ aasRepository.createAas(expected);
+ });
+
+ assertEquals(HttpStatus.UNAUTHORIZED.value(), exception.getCode());
+ }
+
+ @Override
+ protected AasRepository getAasRepository() {
+ return new AuthorizedConnectedAasRepository("http://localhost:8081", new TokenManager("http://localhost:9096/realms/BaSyx/protocol/openid-connect/token", new ClientCredentialAccessTokenProvider(new ClientCredential("workstation-1", "nY0mjyECF60DGzNmQUjL81XurSl8etom"))));
+ }
+}
diff --git a/basyx.aasrepository/basyx.aasrepository-client/src/test/resources/application-authorization.properties b/basyx.aasrepository/basyx.aasrepository-client/src/test/resources/application-authorization.properties
new file mode 100644
index 000000000..ebd881a6f
--- /dev/null
+++ b/basyx.aasrepository/basyx.aasrepository-client/src/test/resources/application-authorization.properties
@@ -0,0 +1,42 @@
+server.port=8081
+server.error.path=/error
+
+spring.application.name=AAS Repository
+basyx.aasrepo.name=aas-repo
+
+basyx.backend = InMemory
+
+#basyx.aasrepository.feature.registryintegration=http://host.docker.internal:8050/api/v3.0
+#basyx.externalurl=http://localhost:8081
+
+#basyx.backend = MongoDB
+#spring.data.mongodb.host=127.0.0.1
+#spring.data.mongodb.port=27017
+#spring.data.mongodb.database=aas
+#spring.data.mongodb.authentication-database=admin
+#spring.data.mongodb.username=mongoAdmin
+#spring.data.mongodb.password=mongoPassword
+
+
+# basyx.aasrepository.feature.mqtt.enabled = true
+# mqtt.clientId=TestClient
+# mqtt.hostname = localhost
+# mqtt.port = 1883
+
+# basyx.cors.allowed-origins=http://localhost:3000, http://localhost:4000
+
+####################################################################################
+# 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:9096/realms/BaSyx
+
+####################################################################################
+# Operation Delegation
+####################################################################################
+# This feature is enabled by default
+
+#basyx.submodelrepository.feature.operation.delegation.enabled = false
\ No newline at end of file
diff --git a/basyx.aasrepository/basyx.aasrepository-client/src/test/resources/application.properties b/basyx.aasrepository/basyx.aasrepository-client/src/test/resources/application.properties
new file mode 100644
index 000000000..1ffdef76b
--- /dev/null
+++ b/basyx.aasrepository/basyx.aasrepository-client/src/test/resources/application.properties
@@ -0,0 +1,42 @@
+server.port=8080
+server.error.path=/error
+
+spring.application.name=AAS Repository
+basyx.aasrepo.name=aas-repo
+
+basyx.backend = InMemory
+
+#basyx.aasrepository.feature.registryintegration=http://host.docker.internal:8050/api/v3.0
+#basyx.externalurl=http://localhost:8081
+
+#basyx.backend = MongoDB
+#spring.data.mongodb.host=127.0.0.1
+#spring.data.mongodb.port=27017
+#spring.data.mongodb.database=aas
+#spring.data.mongodb.authentication-database=admin
+#spring.data.mongodb.username=mongoAdmin
+#spring.data.mongodb.password=mongoPassword
+
+
+# basyx.aasrepository.feature.mqtt.enabled = true
+# mqtt.clientId=TestClient
+# mqtt.hostname = localhost
+# mqtt.port = 1883
+
+# basyx.cors.allowed-origins=http://localhost:3000, http://localhost:4000
+
+####################################################################################
+# 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:9096/realms/BaSyx
+
+####################################################################################
+# Operation Delegation
+####################################################################################
+# This feature is enabled by default
+
+#basyx.submodelrepository.feature.operation.delegation.enabled = false
\ No newline at end of file
diff --git a/basyx.aasrepository/basyx.aasrepository-client/src/test/resources/rbac_rules.json b/basyx.aasrepository/basyx.aasrepository-client/src/test/resources/rbac_rules.json
new file mode 100644
index 000000000..6bf880c8d
--- /dev/null
+++ b/basyx.aasrepository/basyx.aasrepository-client/src/test/resources/rbac_rules.json
@@ -0,0 +1,82 @@
+[
+ {
+ "role": "basyx-reader",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "*"
+ }
+ },
+ {
+ "role": "admin",
+ "action": ["CREATE", "READ", "UPDATE", "DELETE"],
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "*"
+ }
+ },
+ {
+ "role": "basyx-reader-two",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": ["testAasId1", "specificAasId", "testAasId2"]
+ }
+ },
+ {
+ "role": "basyx-creator",
+ "action": "CREATE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "*"
+ }
+ },
+ {
+ "role": "basyx-updater",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "*"
+ }
+ },
+ {
+ "role": "basyx-updater-two",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "specificAasId"
+ }
+ },
+ {
+ "role": "basyx-asset-updater",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "*"
+ }
+ },
+ {
+ "role": "basyx-asset-updater-two",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "specificAasId-2"
+ }
+ },
+ {
+ "role": "basyx-deleter",
+ "action": "DELETE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "*"
+ }
+ },
+ {
+ "role": "basyx-deleter-two",
+ "action": "DELETE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "specificAasId-2"
+ }
+ }
+]
\ No newline at end of file
diff --git a/basyx.aasservice/basyx.aasservice-client/pom.xml b/basyx.aasservice/basyx.aasservice-client/pom.xml
index 3ed0c61ee..6e61a88b4 100644
--- a/basyx.aasservice/basyx.aasservice-client/pom.xml
+++ b/basyx.aasservice/basyx.aasservice-client/pom.xml
@@ -1,16 +1,18 @@
-
- 4.0.0
-
- org.eclipse.digitaltwin.basyx
- basyx.aasservice
- ${revision}
-
- basyx.aasservice-client
- BaSyx AasServiceClient
+
+ 4.0.0
+
+ org.eclipse.digitaltwin.basyx
+ basyx.aasservice
+ ${revision}
+
+ basyx.aasservice-client
+ BaSyx AasServiceClient
BaSyx AasServiceClient enabling interactions with a
Aas Service
-
-
+
+
org.eclipse.digitaltwin.aas4j
aas4j-model
@@ -44,18 +46,59 @@
tests
test
-
+
+ org.eclipse.digitaltwin.basyx
+ basyx.aasrepository-feature-authorization
+ test
+ tests
+
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.aasrepository-feature-authorization
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
org.eclipse.digitaltwin.basyx
basyx.aasrepository-http
test
+
+ org.apache.httpcomponents.client5
+ httpclient5
+ test
+
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.http
+ test
+ tests
+
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.authorization
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.authorization
+ test
+ tests
+
+
org.eclipse.digitaltwin.basyx
basyx.client
-
+
junit
junit
diff --git a/basyx.aasservice/basyx.aasservice-client/src/main/java/org/eclipse/digitaltwin/basyx/aasservice/client/AuthorizedConnectedAasService.java b/basyx.aasservice/basyx.aasservice-client/src/main/java/org/eclipse/digitaltwin/basyx/aasservice/client/AuthorizedConnectedAasService.java
new file mode 100644
index 000000000..270f8d1b4
--- /dev/null
+++ b/basyx.aasservice/basyx.aasservice-client/src/main/java/org/eclipse/digitaltwin/basyx/aasservice/client/AuthorizedConnectedAasService.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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.aasservice.client;
+
+import java.io.IOException;
+import java.net.http.HttpRequest;
+
+import org.eclipse.digitaltwin.basyx.aasservice.client.internal.AssetAdministrationShellServiceApi;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.TokenManager;
+
+/**
+ * Provides access to an Authorized Aas Service on a remote server - regardless if it is
+ * hosted on a Aas Repository or standalone
+ *
+ * @author danish
+ */
+public class AuthorizedConnectedAasService extends ConnectedAasService {
+
+ /**
+ *
+ * @param repoUrl
+ * the Url of the AAS Repository without the "/shells" part
+ * @param tokenManager
+ */
+ public AuthorizedConnectedAasService(String repoUrl, TokenManager tokenManager) {
+ super(new AssetAdministrationShellServiceApi(repoUrl, getRequestBuilder(tokenManager)));
+ }
+
+ private static HttpRequest.Builder getRequestBuilder(TokenManager tokenManager) {
+ try {
+ return HttpRequest.newBuilder()
+ .header("Authorization", "Bearer " + tokenManager.getAccessToken());
+ } catch (IOException e) {
+ throw new RuntimeException("Unable to request access token");
+ }
+ }
+
+}
diff --git a/basyx.aasservice/basyx.aasservice-client/src/main/java/org/eclipse/digitaltwin/basyx/aasservice/client/ConnectedAasService.java b/basyx.aasservice/basyx.aasservice-client/src/main/java/org/eclipse/digitaltwin/basyx/aasservice/client/ConnectedAasService.java
index 7f74f404e..b2ad3aa4a 100644
--- a/basyx.aasservice/basyx.aasservice-client/src/main/java/org/eclipse/digitaltwin/basyx/aasservice/client/ConnectedAasService.java
+++ b/basyx.aasservice/basyx.aasservice-client/src/main/java/org/eclipse/digitaltwin/basyx/aasservice/client/ConnectedAasService.java
@@ -57,6 +57,10 @@ public class ConnectedAasService implements AasService {
public ConnectedAasService(String aasServiceUrl) {
this.serviceApi = new AssetAdministrationShellServiceApi(aasServiceUrl);
}
+
+ public ConnectedAasService(AssetAdministrationShellServiceApi assetAdministrationShellServiceApi) {
+ this.serviceApi = assetAdministrationShellServiceApi;
+ }
@Override
public AssetAdministrationShell getAAS() throws ElementDoesNotExistException {
diff --git a/basyx.aasservice/basyx.aasservice-client/src/main/java/org/eclipse/digitaltwin/basyx/aasservice/client/internal/AssetAdministrationShellServiceApi.java b/basyx.aasservice/basyx.aasservice-client/src/main/java/org/eclipse/digitaltwin/basyx/aasservice/client/internal/AssetAdministrationShellServiceApi.java
index 31b1dcd28..4b444cc5b 100644
--- a/basyx.aasservice/basyx.aasservice-client/src/main/java/org/eclipse/digitaltwin/basyx/aasservice/client/internal/AssetAdministrationShellServiceApi.java
+++ b/basyx.aasservice/basyx.aasservice-client/src/main/java/org/eclipse/digitaltwin/basyx/aasservice/client/internal/AssetAdministrationShellServiceApi.java
@@ -73,17 +73,36 @@ public class AssetAdministrationShellServiceApi {
private final Consumer memberVarInterceptor;
private final Duration memberVarReadTimeout;
private final Consumer> memberVarResponseInterceptor;
+ private HttpRequest.Builder httpRequestBuilder;
public AssetAdministrationShellServiceApi() {
this(new ApiClient());
+ this.httpRequestBuilder = HttpRequest.newBuilder();
}
+
+ public AssetAdministrationShellServiceApi(HttpRequest.Builder httpRequestBuilder) {
+ this();
+ this.httpRequestBuilder = httpRequestBuilder;
+}
public AssetAdministrationShellServiceApi(ObjectMapper mapper, String baseUri) {
this(new ApiClient(HttpClient.newBuilder(), mapper, baseUri));
+ this.httpRequestBuilder = HttpRequest.newBuilder();
}
+ public AssetAdministrationShellServiceApi(ObjectMapper mapper, String baseUri, HttpRequest.Builder httpRequestBuilder) {
+ this(mapper, baseUri);
+ this.httpRequestBuilder = httpRequestBuilder;
+}
+
public AssetAdministrationShellServiceApi(String baseUri) {
this(new ApiClient(HttpClient.newBuilder(), new JsonMapperFactory().create(new SimpleAbstractTypeResolverFactory().create()), baseUri));
+ this.httpRequestBuilder = HttpRequest.newBuilder();
+ }
+
+ public AssetAdministrationShellServiceApi(String baseUri, HttpRequest.Builder httpRequestBuilder) {
+ this(baseUri);
+ this.httpRequestBuilder = httpRequestBuilder;
}
@@ -182,9 +201,9 @@ private HttpRequest.Builder deleteSubmodelReferenceByIdRequestBuilder(String sub
throw new ApiException(400, "Missing the required parameter 'submodelIdentifier' when calling deleteSubmodelReferenceById");
}
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
-
String localVarPath = "/submodel-refs/{submodelIdentifier}".replace("{submodelIdentifier}", ApiClient.urlEncode(submodelIdentifier.toString()));
+
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
localVarRequestBuilder.uri(URI.create(memberVarBaseUri + localVarPath));
@@ -265,9 +284,9 @@ public ApiResponse deleteThumbnailWithHttpInfoNoUrlEncoding() throws ApiEx
private HttpRequest.Builder deleteThumbnailRequestBuilder() throws ApiException {
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
-
String localVarPath = "/asset-information/thumbnail";
+
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
localVarRequestBuilder.uri(URI.create(memberVarBaseUri + localVarPath));
@@ -329,9 +348,9 @@ private ApiResponse>> getAllSubmode
private HttpRequest.Builder getAllSubmodelReferencesRequestBuilder(Integer limit, String cursor) throws ApiException {
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
-
String localVarPath = "/submodel-refs";
+
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
List localVarQueryParams = new ArrayList<>();
StringJoiner localVarQueryStringJoiner = new StringJoiner("&");
@@ -421,8 +440,8 @@ public ApiResponse getAssetAdministrationShellWithHttp
private HttpRequest.Builder getAssetAdministrationShellRequestBuilder() throws ApiException {
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
-
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
+
localVarRequestBuilder.uri(URI.create(memberVarBaseUri));
localVarRequestBuilder.header("Accept", "application/json");
@@ -575,9 +594,9 @@ public ApiResponse getAssetInformationWithHttpInfoNoUrlEncodin
private HttpRequest.Builder getAssetInformationRequestBuilder() throws ApiException {
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
-
String localVarPath = "/asset-information";
+
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
localVarRequestBuilder.uri(URI.create(memberVarBaseUri + localVarPath));
@@ -729,9 +748,9 @@ public ApiResponse getThumbnailWithHttpInfoNoUrlEncoding() throws ApiExcep
private HttpRequest.Builder getThumbnailRequestBuilder() throws ApiException {
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
-
String localVarPath = "/asset-information/thumbnail";
+
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
localVarRequestBuilder.uri(URI.create(memberVarBaseUri + localVarPath));
@@ -814,9 +833,9 @@ private HttpRequest.Builder postSubmodelReferenceRequestBuilder(Reference refere
throw new ApiException(400, "Missing the required parameter 'reference' when calling postSubmodelReference");
}
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
-
String localVarPath = "/submodel-refs";
+
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
localVarRequestBuilder.uri(URI.create(memberVarBaseUri + localVarPath));
@@ -1002,9 +1021,9 @@ private HttpRequest.Builder putAssetInformationRequestBuilder(AssetInformation a
throw new ApiException(400, "Missing the required parameter 'assetInformation' when calling putAssetInformation");
}
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
-
String localVarPath = "/asset-information";
+
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
localVarRequestBuilder.uri(URI.create(memberVarBaseUri + localVarPath));
@@ -1068,8 +1087,11 @@ private ApiResponse putThumbnailWithHttpInfoNoUrlEncoding(String fileName,
}
private HttpRequest.Builder putThumbnailRequestBuilder(String fileName, ContentType contentType, InputStream inputStream) throws ApiException {
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
+
String localVarPath = "/asset-information/thumbnail";
+
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
+
localVarRequestBuilder.uri(URI.create(memberVarBaseUri + localVarPath));
localVarRequestBuilder.header("Accept", "application/json");
diff --git a/basyx.aasservice/basyx.aasservice-client/src/test/java/org/eclipse/digitaltwin/basyx/aasservice/client/TestAuthorizedConnectedAasService.java b/basyx.aasservice/basyx.aasservice-client/src/test/java/org/eclipse/digitaltwin/basyx/aasservice/client/TestAuthorizedConnectedAasService.java
new file mode 100644
index 000000000..57a4d6180
--- /dev/null
+++ b/basyx.aasservice/basyx.aasservice-client/src/test/java/org/eclipse/digitaltwin/basyx/aasservice/client/TestAuthorizedConnectedAasService.java
@@ -0,0 +1,156 @@
+/*******************************************************************************
+ * 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.aasservice.client;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.security.interfaces.RSAPublicKey;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell;
+import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository;
+import org.eclipse.digitaltwin.basyx.aasrepository.DummyAasFactory;
+import org.eclipse.digitaltwin.basyx.aasrepository.feature.authorization.DummyAuthorizedAasRepositoryComponent;
+import org.eclipse.digitaltwin.basyx.aasservice.AasService;
+import org.eclipse.digitaltwin.basyx.aasservice.AasServiceSuite;
+import org.eclipse.digitaltwin.basyx.authorization.AccessTokenProvider;
+import org.eclipse.digitaltwin.basyx.authorization.DummyCredential;
+import org.eclipse.digitaltwin.basyx.authorization.DummyCredentialStore;
+import org.eclipse.digitaltwin.basyx.authorization.jwt.JwtTokenDecoder;
+import org.eclipse.digitaltwin.basyx.authorization.jwt.PublicKeyUtils;
+import org.eclipse.digitaltwin.basyx.client.internal.ApiException;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.TokenManager;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.credential.ClientCredential;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.grant.ClientCredentialAccessTokenProvider;
+import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo;
+import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier;
+import org.eclipse.digitaltwin.basyx.http.serialization.BaSyxHttpTestUtils;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.springframework.boot.SpringApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
+
+/**
+ * Tests for authorized ConnectedAasService
+ *
+ * @author danish
+ */
+public class TestAuthorizedConnectedAasService extends AasServiceSuite {
+
+ private static final String AAS_REPO_URL = "http://localhost:8081/shells/";
+ private static ConfigurableApplicationContext appContext;
+ private static final String PROFILE = "authorization";
+ public static String authenticaltionServerTokenEndpoint = "http://localhost:9096/realms/BaSyx/protocol/openid-connect/token";
+ public static String clientId = "basyx-client-api";
+
+ @BeforeClass
+ public static void startAASRepo() throws Exception {
+ SpringApplication application = new SpringApplication(DummyAuthorizedAasRepositoryComponent.class);
+ application.setAdditionalProfiles(PROFILE);
+
+ appContext = application.run(new String[] {});
+ }
+
+ @After
+ public void removeAasFromRepo() {
+ AasRepository repo = appContext.getBean(AasRepository.class);
+ repo.getAllAas(PaginationInfo.NO_LIMIT).getResult().stream().map(s -> s.getId()).forEach(repo::deleteAas);
+ }
+
+ @AfterClass
+ public static void shutdownSubmodelService() {
+ appContext.close();
+ }
+
+ @Test
+ public void sendUnauthorizedRequest() throws IOException {
+ TokenManager mockTokenManager = Mockito.mock(TokenManager.class);
+
+ Mockito.when(mockTokenManager.getAccessToken()).thenReturn("mockedAccessToken");
+
+ createDummyShellOnRepo("dummyAasId");
+
+ AasService aasService = new AuthorizedConnectedAasService(AAS_REPO_URL + "dummyAasId", mockTokenManager);
+
+ ApiException exception = assertThrows(ApiException.class, () -> {
+ aasService.getAAS();
+ });
+
+ assertEquals(HttpStatus.UNAUTHORIZED.value(), exception.getCode());
+ }
+
+ private void createDummyShellOnRepo(String shellId) throws FileNotFoundException, IOException {
+ configureSecurityContext(getTokenProvider());
+
+ AasRepository repo = appContext.getBean(AasRepository.class);
+
+ AssetAdministrationShell expected1 = DummyAasFactory.createAasWithSubmodelReference();
+ expected1.setId(shellId);
+
+ repo.createAas(expected1);
+ }
+
+ @Override
+ protected AasService getAasService(AssetAdministrationShell shell) {
+ AasRepository repo = appContext.getBean(AasRepository.class);
+ repo.createAas(shell);
+ String base64UrlEncodedId = Base64UrlEncodedIdentifier.encodeIdentifier(shell.getId());
+ return new AuthorizedConnectedAasService(AAS_REPO_URL + base64UrlEncodedId, new TokenManager("http://localhost:9096/realms/BaSyx/protocol/openid-connect/token", new ClientCredentialAccessTokenProvider(new ClientCredential("workstation-1", "nY0mjyECF60DGzNmQUjL81XurSl8etom"))));
+ }
+
+ public static void configureSecurityContext(AccessTokenProvider accessTokenProvider) throws FileNotFoundException, IOException {
+ String adminToken = getAdminAccessToken(accessTokenProvider);
+
+ String modulus = BaSyxHttpTestUtils.readJSONStringFromClasspath("authorization/modulus.txt");
+
+ String exponent = "AQAB";
+
+ RSAPublicKey rsaPublicKey = PublicKeyUtils.buildPublicKey(modulus, exponent);
+
+ Jwt jwt = JwtTokenDecoder.decodeJwt(adminToken, rsaPublicKey);
+
+ SecurityContextHolder.getContext().setAuthentication(new JwtAuthenticationToken(jwt));
+ }
+
+ public static String getAdminAccessToken(AccessTokenProvider tokenProvider) {
+ DummyCredential dummyCredential = DummyCredentialStore.ADMIN_CREDENTIAL;
+
+ return tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword());
+ }
+
+ public static AccessTokenProvider getTokenProvider() {
+ return new AccessTokenProvider(authenticaltionServerTokenEndpoint, clientId);
+ }
+}
diff --git a/basyx.aasservice/basyx.aasservice-client/src/test/resources/application-authorization.properties b/basyx.aasservice/basyx.aasservice-client/src/test/resources/application-authorization.properties
new file mode 100644
index 000000000..ebd881a6f
--- /dev/null
+++ b/basyx.aasservice/basyx.aasservice-client/src/test/resources/application-authorization.properties
@@ -0,0 +1,42 @@
+server.port=8081
+server.error.path=/error
+
+spring.application.name=AAS Repository
+basyx.aasrepo.name=aas-repo
+
+basyx.backend = InMemory
+
+#basyx.aasrepository.feature.registryintegration=http://host.docker.internal:8050/api/v3.0
+#basyx.externalurl=http://localhost:8081
+
+#basyx.backend = MongoDB
+#spring.data.mongodb.host=127.0.0.1
+#spring.data.mongodb.port=27017
+#spring.data.mongodb.database=aas
+#spring.data.mongodb.authentication-database=admin
+#spring.data.mongodb.username=mongoAdmin
+#spring.data.mongodb.password=mongoPassword
+
+
+# basyx.aasrepository.feature.mqtt.enabled = true
+# mqtt.clientId=TestClient
+# mqtt.hostname = localhost
+# mqtt.port = 1883
+
+# basyx.cors.allowed-origins=http://localhost:3000, http://localhost:4000
+
+####################################################################################
+# 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:9096/realms/BaSyx
+
+####################################################################################
+# Operation Delegation
+####################################################################################
+# This feature is enabled by default
+
+#basyx.submodelrepository.feature.operation.delegation.enabled = false
\ No newline at end of file
diff --git a/basyx.aasservice/basyx.aasservice-client/src/test/resources/application.properties b/basyx.aasservice/basyx.aasservice-client/src/test/resources/application.properties
new file mode 100644
index 000000000..1ffdef76b
--- /dev/null
+++ b/basyx.aasservice/basyx.aasservice-client/src/test/resources/application.properties
@@ -0,0 +1,42 @@
+server.port=8080
+server.error.path=/error
+
+spring.application.name=AAS Repository
+basyx.aasrepo.name=aas-repo
+
+basyx.backend = InMemory
+
+#basyx.aasrepository.feature.registryintegration=http://host.docker.internal:8050/api/v3.0
+#basyx.externalurl=http://localhost:8081
+
+#basyx.backend = MongoDB
+#spring.data.mongodb.host=127.0.0.1
+#spring.data.mongodb.port=27017
+#spring.data.mongodb.database=aas
+#spring.data.mongodb.authentication-database=admin
+#spring.data.mongodb.username=mongoAdmin
+#spring.data.mongodb.password=mongoPassword
+
+
+# basyx.aasrepository.feature.mqtt.enabled = true
+# mqtt.clientId=TestClient
+# mqtt.hostname = localhost
+# mqtt.port = 1883
+
+# basyx.cors.allowed-origins=http://localhost:3000, http://localhost:4000
+
+####################################################################################
+# 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:9096/realms/BaSyx
+
+####################################################################################
+# Operation Delegation
+####################################################################################
+# This feature is enabled by default
+
+#basyx.submodelrepository.feature.operation.delegation.enabled = false
\ No newline at end of file
diff --git a/basyx.aasservice/basyx.aasservice-client/src/test/resources/rbac_rules.json b/basyx.aasservice/basyx.aasservice-client/src/test/resources/rbac_rules.json
new file mode 100644
index 000000000..6bf880c8d
--- /dev/null
+++ b/basyx.aasservice/basyx.aasservice-client/src/test/resources/rbac_rules.json
@@ -0,0 +1,82 @@
+[
+ {
+ "role": "basyx-reader",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "*"
+ }
+ },
+ {
+ "role": "admin",
+ "action": ["CREATE", "READ", "UPDATE", "DELETE"],
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "*"
+ }
+ },
+ {
+ "role": "basyx-reader-two",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": ["testAasId1", "specificAasId", "testAasId2"]
+ }
+ },
+ {
+ "role": "basyx-creator",
+ "action": "CREATE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "*"
+ }
+ },
+ {
+ "role": "basyx-updater",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "*"
+ }
+ },
+ {
+ "role": "basyx-updater-two",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "specificAasId"
+ }
+ },
+ {
+ "role": "basyx-asset-updater",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "*"
+ }
+ },
+ {
+ "role": "basyx-asset-updater-two",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "specificAasId-2"
+ }
+ },
+ {
+ "role": "basyx-deleter",
+ "action": "DELETE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "*"
+ }
+ },
+ {
+ "role": "basyx-deleter-two",
+ "action": "DELETE",
+ "targetInformation": {
+ "@type": "aas",
+ "aasIds": "specificAasId-2"
+ }
+ }
+]
\ No newline at end of file
diff --git a/basyx.common/basyx.client/pom.xml b/basyx.common/basyx.client/pom.xml
index de0da1421..b751f6a23 100644
--- a/basyx.common/basyx.client/pom.xml
+++ b/basyx.common/basyx.client/pom.xml
@@ -1,9 +1,11 @@
-
- 4.0.0
-
- org.eclipse.digitaltwin.basyx
- basyx.common
- ${revision}
+
+ 4.0.0
+
+ org.eclipse.digitaltwin.basyx
+ basyx.common
+ ${revision}
basyx.client
@@ -11,11 +13,11 @@
BaSyx Client Base
-
- org.eclipse.digitaltwin.basyx
- basyx.http
-
-
+
+ org.eclipse.digitaltwin.basyx
+ basyx.http
+
+
com.fasterxml.jackson.core
jackson-core
@@ -49,6 +51,21 @@
jakarta.annotation-api
provided
+
+
+
+ org.json
+ json
+ 20240303
+
+
+
+
+ com.nimbusds
+ oauth2-oidc-sdk
+ 11.12
+
+
\ No newline at end of file
diff --git a/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/TokenManager.java b/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/TokenManager.java
new file mode 100644
index 000000000..b1a36a0ad
--- /dev/null
+++ b/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/TokenManager.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * 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.client.internal.authorization;
+
+import java.io.IOException;
+
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.grant.AccessTokenProvider;
+import org.eclipse.digitaltwin.basyx.core.exceptions.AccessTokenRetrievalException;
+
+import com.nimbusds.oauth2.sdk.AccessTokenResponse;
+import com.nimbusds.oauth2.sdk.token.AccessToken;
+import com.nimbusds.oauth2.sdk.token.RefreshToken;
+
+/**
+ * Requests and manages the Access Tokens and Refresh Tokens.
+ *
+ * @author danish
+ */
+public class TokenManager {
+
+ private String tokenEndpoint;
+ private AccessTokenProvider accessTokenProvider;
+ private String accessToken;
+ private String refreshToken;
+ private long accessTokenExpiryTime;
+ private long refreshTokenExpiryTime;
+
+ public TokenManager(String tokenEndpoint, AccessTokenProvider accessTokenProvider) {
+ super();
+ this.tokenEndpoint = tokenEndpoint;
+ this.accessTokenProvider = accessTokenProvider;
+ }
+
+ public String getTokenEndpoint() {
+ return tokenEndpoint;
+ }
+
+ public AccessTokenProvider getAccessTokenProvider() {
+ return this.accessTokenProvider;
+ }
+
+ /**
+ * Provides access token
+ *
+ * @return accessToken
+ * @throws IOException
+ */
+ public synchronized String getAccessToken() throws IOException {
+
+ if (accessToken != null && System.currentTimeMillis() < accessTokenExpiryTime)
+ return accessToken;
+
+ if (refreshToken != null && System.currentTimeMillis() < refreshTokenExpiryTime) {
+ try {
+ return requestAccessToken(accessTokenProvider.getAccessTokenResponse(tokenEndpoint, refreshToken));
+ } catch (IOException e) {
+ throw new AccessTokenRetrievalException("Error occurred while retrieving access token" + e.getMessage());
+ }
+ }
+
+ try {
+ return requestAccessToken(accessTokenProvider.getAccessTokenResponse(tokenEndpoint));
+ } catch (IOException e) {
+ throw new AccessTokenRetrievalException("Error occurred while retrieving access token" + e.getMessage());
+ }
+ }
+
+ private String requestAccessToken(AccessTokenResponse accessTokenResponse) throws IOException {
+ AccessToken accessTokenObj = accessTokenResponse.getTokens().getAccessToken();
+ accessToken = accessTokenObj.getValue();
+ accessTokenExpiryTime = accessTokenObj.getLifetime();
+
+ RefreshToken refreshTokenObj = accessTokenResponse.getTokens().getRefreshToken();
+
+ if (refreshTokenObj != null) {
+ refreshToken = refreshTokenObj.getValue();
+ refreshTokenExpiryTime = System.currentTimeMillis() + (30L * 24L * 60L * 60L * 1000L);
+ }
+
+ return accessToken;
+ }
+
+}
diff --git a/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/credential/ClientCredential.java b/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/credential/ClientCredential.java
new file mode 100644
index 000000000..519dcd793
--- /dev/null
+++ b/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/credential/ClientCredential.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * 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.client.internal.authorization.credential;
+
+/**
+ * A representational class for Client credential
+ *
+ * @author danish
+ */
+public class ClientCredential {
+
+ private String clientId;
+ private String clientSecret;
+
+ public ClientCredential(String clientId) {
+ super();
+ this.clientId = clientId;
+ }
+
+ public ClientCredential(String clientId, String clientSecret) {
+ this(clientId);
+ this.clientSecret = clientSecret;
+ }
+
+ public String getClientId() {
+ return clientId;
+ }
+
+ public String getClientSecret() {
+ return clientSecret;
+ }
+
+}
diff --git a/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/credential/PasswordCredential.java b/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/credential/PasswordCredential.java
new file mode 100644
index 000000000..c16634f09
--- /dev/null
+++ b/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/credential/PasswordCredential.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * 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.client.internal.authorization.credential;
+
+/**
+ * A representational class for Password credential
+ *
+ * @author danish
+ */
+public class PasswordCredential {
+
+ private String username;
+ private String password;
+
+ public PasswordCredential(String username, String password) {
+ super();
+ this.username = username;
+ this.password = password;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+}
diff --git a/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/grant/AccessTokenProvider.java b/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/grant/AccessTokenProvider.java
new file mode 100644
index 000000000..dac4a669f
--- /dev/null
+++ b/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/grant/AccessTokenProvider.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * 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.client.internal.authorization.grant;
+
+import com.nimbusds.oauth2.sdk.AccessTokenResponse;
+
+/**
+ * An interface for access token providers with different flows.
+ *
+ * @author danish
+ */
+public interface AccessTokenProvider {
+
+ /**
+ * Provides an access token response
+ *
+ * @param tokenEndpoint
+ * @return accessTokenResponse
+ */
+ AccessTokenResponse getAccessTokenResponse(String tokenEndpoint);
+
+ /**
+ * Provides an access token response
+ *
+ * @param tokenEndpoint
+ * @param refreshToken
+ * @return accessTokenResponse
+ */
+ AccessTokenResponse getAccessTokenResponse(String tokenEndpoint, String refreshToken);
+
+}
diff --git a/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/grant/ClientCredentialAccessTokenProvider.java b/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/grant/ClientCredentialAccessTokenProvider.java
new file mode 100644
index 000000000..6e8c7954e
--- /dev/null
+++ b/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/grant/ClientCredentialAccessTokenProvider.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * 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.client.internal.authorization.grant;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collection;
+
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.credential.ClientCredential;
+import org.eclipse.digitaltwin.basyx.core.exceptions.AccessTokenRetrievalException;
+
+import com.nimbusds.oauth2.sdk.AccessTokenResponse;
+import com.nimbusds.oauth2.sdk.Scope;
+import com.nimbusds.oauth2.sdk.TokenRequest;
+import com.nimbusds.oauth2.sdk.TokenResponse;
+import com.nimbusds.oauth2.sdk.auth.Secret;
+import com.nimbusds.oauth2.sdk.id.ClientID;
+import com.nimbusds.oauth2.sdk.token.RefreshToken;
+import com.nimbusds.oauth2.sdk.AuthorizationGrant;
+import com.nimbusds.oauth2.sdk.ClientCredentialsGrant;
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.oauth2.sdk.RefreshTokenGrant;
+import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic;
+import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
+
+/**
+ * Access token provider for the Client Credentials flow
+ *
+ * @author danish
+ */
+public class ClientCredentialAccessTokenProvider implements AccessTokenProvider {
+
+ private final ClientCredential clientCredential;
+ private Collection scopes;
+
+ public ClientCredentialAccessTokenProvider(ClientCredential clientCredential) {
+ this.clientCredential = clientCredential;
+ }
+
+ public ClientCredentialAccessTokenProvider(ClientCredential clientCredential, Collection scopes) {
+ this(clientCredential);
+ this.scopes = scopes;
+ }
+
+ @Override
+ public AccessTokenResponse getAccessTokenResponse(String tokenEndpoint) {
+
+ if (isPublicClient())
+ throw new RuntimeException("The client cannnot be public client with Password Grant");
+
+ AuthorizationGrant clientGrant = new ClientCredentialsGrant();
+
+ ClientAuthentication clientAuth = new ClientSecretBasic(new ClientID(clientCredential.getClientId()), new Secret(clientCredential.getClientSecret()));
+
+ URI tokenEndpointUri = getTokenEndpointUri(tokenEndpoint);
+
+ TokenRequest request = new TokenRequest(tokenEndpointUri, clientAuth, clientGrant, Scope.parse(scopes));
+
+ return getTokenResponse(request);
+ }
+
+ @Override
+ public AccessTokenResponse getAccessTokenResponse(String tokenEndpoint, String refreshToken) {
+
+ if (isPublicClient())
+ throw new RuntimeException("The client cannnot be public client with Password Grant");
+
+ RefreshToken refreshTokenObj = new RefreshToken(refreshToken);
+
+ AuthorizationGrant refreshTokenGrant = new RefreshTokenGrant(refreshTokenObj);
+
+ ClientAuthentication clientAuth = new ClientSecretBasic(new ClientID(clientCredential.getClientId()), new Secret(clientCredential.getClientSecret()));
+
+ URI tokenEndpointUri = getTokenEndpointUri(tokenEndpoint);
+
+ TokenRequest request = new TokenRequest(tokenEndpointUri, clientAuth, refreshTokenGrant);
+
+ return getTokenResponse(request);
+ }
+
+ private AccessTokenResponse getTokenResponse(TokenRequest request) {
+ TokenResponse response;
+
+ try {
+ response = TokenResponse.parse(request.toHTTPRequest().send());
+ } catch (ParseException | IOException e) {
+ throw new AccessTokenRetrievalException("Error occurred while retrieving access token" + e.getMessage());
+ }
+
+ if (!response.indicatesSuccess())
+ throw new AccessTokenRetrievalException("Error occurred while retrieving access token" + response.toErrorResponse().toString());
+
+ return response.toSuccessResponse();
+ }
+
+ private boolean isPublicClient() {
+ return clientCredential.getClientSecret().isBlank();
+ }
+
+ private URI getTokenEndpointUri(String tokenEndpoint) {
+ try {
+ return new URI(tokenEndpoint);
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+
+}
diff --git a/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/grant/PasswordCredentialAccessTokenProvider.java b/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/grant/PasswordCredentialAccessTokenProvider.java
new file mode 100644
index 000000000..61fd00a90
--- /dev/null
+++ b/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/grant/PasswordCredentialAccessTokenProvider.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * 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.client.internal.authorization.grant;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collection;
+
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.credential.ClientCredential;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.credential.PasswordCredential;
+import org.eclipse.digitaltwin.basyx.core.exceptions.AccessTokenRetrievalException;
+
+import com.nimbusds.oauth2.sdk.AccessTokenResponse;
+import com.nimbusds.oauth2.sdk.Scope;
+import com.nimbusds.oauth2.sdk.TokenRequest;
+import com.nimbusds.oauth2.sdk.TokenResponse;
+import com.nimbusds.oauth2.sdk.auth.Secret;
+import com.nimbusds.oauth2.sdk.id.ClientID;
+import com.nimbusds.oauth2.sdk.token.RefreshToken;
+import com.nimbusds.oauth2.sdk.ResourceOwnerPasswordCredentialsGrant;
+import com.nimbusds.oauth2.sdk.AuthorizationGrant;
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.oauth2.sdk.RefreshTokenGrant;
+import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic;
+import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
+
+/**
+ * Access token provider for the Username Password Credentials flow
+ *
+ * @author danish
+ */
+public class PasswordCredentialAccessTokenProvider implements AccessTokenProvider {
+
+ private final PasswordCredential passwordCredential;
+ private final ClientCredential clientCredential;
+ private Collection scopes;
+
+ public PasswordCredentialAccessTokenProvider(PasswordCredential passwordCredential, ClientCredential clientCredential) {
+ this.passwordCredential = passwordCredential;
+ this.clientCredential = clientCredential;
+ }
+
+ public PasswordCredentialAccessTokenProvider(PasswordCredential passwordCredential, ClientCredential clientCredential, Collection scopes) {
+ this(passwordCredential, clientCredential);
+ this.scopes = scopes;
+ }
+
+ @Override
+ public AccessTokenResponse getAccessTokenResponse(String tokenEndpoint) {
+
+ AuthorizationGrant passwordGrant = new ResourceOwnerPasswordCredentialsGrant(passwordCredential.getUsername(), new Secret(passwordCredential.getPassword()));
+
+ ClientAuthentication clientAuth = new ClientSecretBasic(new ClientID(clientCredential.getClientId()), new Secret(clientCredential.getClientSecret()));
+
+ URI tokenEndpointUri = getTokenEndpointUri(tokenEndpoint);
+
+ TokenRequest request = new TokenRequest(tokenEndpointUri, clientAuth, passwordGrant, Scope.parse(scopes));
+
+ return getTokenResponse(request);
+ }
+
+ @Override
+ public AccessTokenResponse getAccessTokenResponse(String tokenEndpoint, String refreshToken) {
+
+ RefreshToken refreshTokenObj = new RefreshToken(refreshToken);
+
+ AuthorizationGrant refreshTokenGrant = new RefreshTokenGrant(refreshTokenObj);
+
+ ClientAuthentication clientAuth = new ClientSecretBasic(new ClientID(clientCredential.getClientId()), new Secret(clientCredential.getClientSecret()));
+
+ URI tokenEndpointUri = getTokenEndpointUri(tokenEndpoint);
+
+ TokenRequest request = new TokenRequest(tokenEndpointUri, clientAuth, refreshTokenGrant);
+
+ return getTokenResponse(request);
+ }
+
+ private AccessTokenResponse getTokenResponse(TokenRequest request) {
+ TokenResponse response;
+
+ try {
+ response = TokenResponse.parse(request.toHTTPRequest().send());
+ } catch (ParseException | IOException e) {
+ throw new AccessTokenRetrievalException("Error occurred while retrieving access token" + e.getMessage());
+ }
+
+ if (!response.indicatesSuccess())
+ throw new AccessTokenRetrievalException("Error occurred while retrieving access token" + response.toErrorResponse().toString());
+
+ return response.toSuccessResponse();
+ }
+
+ private URI getTokenEndpointUri(String tokenEndpoint) {
+ try {
+ return new URI(tokenEndpoint);
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+
+}
diff --git a/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/resolver/DescriptorResolver.java b/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/resolver/DescriptorResolver.java
new file mode 100644
index 000000000..a9f8d0bdc
--- /dev/null
+++ b/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/resolver/DescriptorResolver.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * 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.client.internal.resolver;
+
+/**
+ * A generic interface for different kinds of resolvers for Descriptor.
+ *
+ * @author danish
+ */
+public interface DescriptorResolver {
+
+ O resolveDescriptor(I descriptor);
+
+}
diff --git a/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/AccessTokenRetrievalException.java b/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/AccessTokenRetrievalException.java
new file mode 100644
index 000000000..b4e6ff1bc
--- /dev/null
+++ b/basyx.common/basyx.core/src/main/java/org/eclipse/digitaltwin/basyx/core/exceptions/AccessTokenRetrievalException.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * 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.core.exceptions;
+
+/**
+ * Indicates failure while retrieving the access token
+ *
+ * @author danish
+ *
+ */
+@SuppressWarnings("serial")
+public class AccessTokenRetrievalException extends RuntimeException {
+
+ public AccessTokenRetrievalException() {
+ super();
+ }
+
+ public AccessTokenRetrievalException(String message) {
+ super(message);
+ }
+
+}
diff --git a/basyx.submodelregistry/basyx.submodelregistry-client-native/pom.xml b/basyx.submodelregistry/basyx.submodelregistry-client-native/pom.xml
index cf3b06bd6..d59bdbe0b 100644
--- a/basyx.submodelregistry/basyx.submodelregistry-client-native/pom.xml
+++ b/basyx.submodelregistry/basyx.submodelregistry-client-native/pom.xml
@@ -20,7 +20,6 @@
${openapi.result.folder}/${openapi.name}
- src/generated/java
maven-clean-plugin
@@ -130,6 +129,10 @@
jakarta.annotation
jakarta.annotation-api
+
+ org.eclipse.digitaltwin.basyx
+ basyx.client
+
diff --git a/basyx.submodelregistry/basyx.submodelregistry-client-native/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/client/AuthorizedConnectedSubmodelRegistry.java b/basyx.submodelregistry/basyx.submodelregistry-client-native/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/client/AuthorizedConnectedSubmodelRegistry.java
new file mode 100644
index 000000000..5ea01059e
--- /dev/null
+++ b/basyx.submodelregistry/basyx.submodelregistry-client-native/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/client/AuthorizedConnectedSubmodelRegistry.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * 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.client;
+
+import java.io.IOException;
+import java.net.http.HttpRequest;
+
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.TokenManager;
+import org.eclipse.digitaltwin.basyx.submodelregistry.client.api.SubmodelRegistryApi;
+
+/**
+ * Provides access to an authorized Submodel Registry on a remote server
+ *
+ * @author danish
+ */
+public class AuthorizedConnectedSubmodelRegistry extends SubmodelRegistryApi {
+
+ private final TokenManager tokenManager;
+ private final String submodelRegistryBasePath;
+
+ public AuthorizedConnectedSubmodelRegistry(String basePath, TokenManager tokenManager) {
+ super(basePath, getRequestBuilder(tokenManager));
+ this.submodelRegistryBasePath = basePath;
+ this.tokenManager = tokenManager;
+ }
+
+ public String getBaseUrl() {
+ return submodelRegistryBasePath;
+ }
+
+ public TokenManager getTokenManager() {
+ return tokenManager;
+ }
+
+ private static HttpRequest.Builder getRequestBuilder(TokenManager tokenManager) {
+ try {
+ return HttpRequest.newBuilder().header("Authorization", "Bearer " + tokenManager.getAccessToken());
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException("Unable to request access token");
+ }
+ }
+
+}
diff --git a/basyx.submodelregistry/basyx.submodelregistry-client-native/templates/libraries/native/api.mustache b/basyx.submodelregistry/basyx.submodelregistry-client-native/templates/libraries/native/api.mustache
index 5ec50e316..d5d40059e 100644
--- a/basyx.submodelregistry/basyx.submodelregistry-client-native/templates/libraries/native/api.mustache
+++ b/basyx.submodelregistry/basyx.submodelregistry-client-native/templates/libraries/native/api.mustache
@@ -59,17 +59,36 @@ public class {{classname}} {
private final Duration memberVarReadTimeout;
private final {{#fullJavaUtil}}java.util.function.{{/fullJavaUtil}}Consumer> memberVarResponseInterceptor;
private final {{#fullJavaUtil}}java.util.function.{{/fullJavaUtil}}Consumer> memberVarAsyncResponseInterceptor;
+ private HttpRequest.Builder httpRequestBuilder;
public {{classname}}() {
this(new ApiClient());
+ this.httpRequestBuilder = HttpRequest.newBuilder();
+ }
+
+ public {{classname}}(HttpRequest.Builder httpRequestBuilder) {
+ this(new ApiClient());
+ this.httpRequestBuilder = httpRequestBuilder;
}
public {{classname}}(String protocol, String host, int port) {
this(protocol + "://" + host + ":" + port);
+ this.httpRequestBuilder = HttpRequest.newBuilder();
+ }
+
+ public {{classname}}(String protocol, String host, int port, HttpRequest.Builder httpRequestBuilder) {
+ this(protocol + "://" + host + ":" + port);
+ this.httpRequestBuilder = httpRequestBuilder;
}
public {{classname}}(String basePath) {
this(withBaseUri(new ApiClient(), basePath));
+ this.httpRequestBuilder = HttpRequest.newBuilder();
+ }
+
+ public {{classname}}(String basePath, HttpRequest.Builder httpRequestBuilder) {
+ this(withBaseUri(new ApiClient(), basePath));
+ this.httpRequestBuilder = httpRequestBuilder;
}
private static ApiClient withBaseUri(ApiClient client, String uri) {
@@ -361,7 +380,7 @@ public class {{classname}} {
{{#hasPathParams}}{{#pathParams}}{{#vendorExtensions.x-utf8-base64-url-encoded-as-string}}String {{{paramName}}}AsBase64EncodedParam = {{{paramName}}} == null ? null : new String(java.util.Base64.getUrlEncoder().encode({{paramName}}.getBytes(java.nio.charset.StandardCharsets.UTF_8)), java.nio.charset.StandardCharsets.UTF_8);
{{/vendorExtensions.x-utf8-base64-url-encoded-as-string}}{{/pathParams}}{{/hasPathParams}}
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
{{! Switch delimiters for baseName so we can write constants like "{query}" }}
String localVarPath = "{{{path}}}"{{#pathParams}}
diff --git a/basyx.submodelregistry/basyx.submodelregistry-service-release-kafka-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/service/storage/mongodb/AuthorizedClientTest.java b/basyx.submodelregistry/basyx.submodelregistry-service-release-kafka-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/service/storage/mongodb/AuthorizedClientTest.java
new file mode 100644
index 000000000..a904ca815
--- /dev/null
+++ b/basyx.submodelregistry/basyx.submodelregistry-service-release-kafka-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/service/storage/mongodb/AuthorizedClientTest.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * 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.service.storage.mongodb;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import java.io.IOException;
+
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.TokenManager;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.credential.ClientCredential;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.grant.ClientCredentialAccessTokenProvider;
+import org.eclipse.digitaltwin.basyx.submodelregistry.client.ApiException;
+import org.eclipse.digitaltwin.basyx.submodelregistry.client.AuthorizedConnectedSubmodelRegistry;
+import org.eclipse.digitaltwin.basyx.submodelregistry.client.api.SubmodelRegistryApi;
+import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.SubmodelDescriptor;
+import org.eclipse.digitaltwin.basyx.submodelregistry.service.storage.mongodb.KafkaEventsMongoDbStorageIntegrationTest.RegistrationEventKafkaListener;
+import org.eclipse.digitaltwin.basyx.submodelregistry.service.tests.integration.BaseIntegrationTest;
+import org.eclipse.digitaltwin.basyx.submodelregistry.service.tests.integration.EventQueue;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpStatus;
+import org.springframework.test.context.TestPropertySource;
+
+/**
+ * Tests the Authorized client
+ *
+ * @author danish
+ */
+@TestPropertySource(properties = {"spring.profiles.active=kafkaEvents,mongoDbStorage", "spring.kafka.bootstrap-servers=PLAINTEXT_HOST://localhost:9092", "spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:9096/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.data.mongodb.database=submodelregistry", "spring.data.mongodb.uri=mongodb://mongoAdmin:mongoPassword@localhost:27017/"})
+public class AuthorizedClientTest extends BaseIntegrationTest {
+
+ @Autowired
+ private RegistrationEventKafkaListener listener;
+
+ @Value("${local.server.port}")
+ private int port;
+
+ @Before
+ public void awaitAssignment() throws InterruptedException {
+ listener.awaitTopicAssignment();
+ }
+
+ @Override
+ public EventQueue queue() {
+ return listener.getQueue();
+ }
+
+ @Before
+ @Override
+ public void initClient() throws ApiException {
+ api = new AuthorizedConnectedSubmodelRegistry("http://127.0.0.1:" + port, new TokenManager("http://localhost:9096/realms/BaSyx/protocol/openid-connect/token", new ClientCredentialAccessTokenProvider(new ClientCredential("workstation-1", "nY0mjyECF60DGzNmQUjL81XurSl8etom"))));
+ api.deleteAllSubmodelDescriptors();
+ queue().assertNoAdditionalMessage();
+ }
+
+ @Test
+ public void sendUnauthorizedRequest() throws IOException {
+ TokenManager mockTokenManager = Mockito.mock(TokenManager.class);
+
+ Mockito.when(mockTokenManager.getAccessToken()).thenReturn("mockedAccessToken");
+
+ SubmodelRegistryApi registryApi = new AuthorizedConnectedSubmodelRegistry("http://127.0.0.1:" + port, mockTokenManager);
+
+ SubmodelDescriptor descriptor = new SubmodelDescriptor();
+ descriptor.setIdShort("shortId");
+
+ ApiException exception = assertThrows(ApiException.class, () -> {
+ registryApi.postSubmodelDescriptor(descriptor);
+ });
+
+ assertEquals(HttpStatus.UNAUTHORIZED.value(), exception.getCode());
+ }
+
+ @Test
+ @Override
+ public void whenPostSubmodelDescriptor_LocationIsReturned() throws ApiException, IOException {
+ // TODO: It uses normal GET unauthorized request, need to override and refactor
+ }
+
+}
diff --git a/basyx.submodelregistry/basyx.submodelregistry-service-release-kafka-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/service/storage/mongodb/KafkaEventsMongoDbStorageIntegrationTest.java b/basyx.submodelregistry/basyx.submodelregistry-service-release-kafka-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/service/storage/mongodb/KafkaEventsMongoDbStorageIntegrationTest.java
index 201815e49..0bb275662 100644
--- a/basyx.submodelregistry/basyx.submodelregistry-service-release-kafka-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/service/storage/mongodb/KafkaEventsMongoDbStorageIntegrationTest.java
+++ b/basyx.submodelregistry/basyx.submodelregistry-service-release-kafka-mongodb/src/test/java/org/eclipse/digitaltwin/basyx/submodelregistry/service/storage/mongodb/KafkaEventsMongoDbStorageIntegrationTest.java
@@ -64,7 +64,7 @@ public EventQueue queue() {
@KafkaListener(topics = "submodel-registry", batch = "false", groupId = "kafka-test", autoStartup = "true" )
@Component
- private static class RegistrationEventKafkaListener implements ConsumerSeekAware {
+ public static class RegistrationEventKafkaListener implements ConsumerSeekAware {
private final EventQueue queue;
private final CountDownLatch latch = new CountDownLatch(1);
@@ -81,6 +81,10 @@ public RegistrationEventKafkaListener(ObjectMapper mapper) {
public void receiveMessage(String content) {
queue.offer(content);
}
+
+ public EventQueue getQueue() {
+ return this.queue;
+ }
@Override
public void onPartitionsAssigned(Map assignments,
diff --git a/basyx.submodelrepository/basyx.submodelrepository-client/pom.xml b/basyx.submodelrepository/basyx.submodelrepository-client/pom.xml
index aa48feed0..e4001b476 100644
--- a/basyx.submodelrepository/basyx.submodelrepository-client/pom.xml
+++ b/basyx.submodelrepository/basyx.submodelrepository-client/pom.xml
@@ -25,6 +25,11 @@
org.eclipse.digitaltwin.basyx
basyx.submodelrepository-core
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.client
+
org.eclipse.digitaltwin.basyx
@@ -69,6 +74,12 @@
org.eclipse.digitaltwin.basyx
basyx.submodelservice-client
+
+ org.eclipse.digitaltwin.basyx
+ basyx.submodelservice-client
+ test
+ tests
+
junit
@@ -85,5 +96,44 @@
basyx.submodelservice-backend-inmemory
test
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.authorization
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.authorization
+ test
+ tests
+
+
+ org.apache.httpcomponents.client5
+ httpclient5
+ test
+
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.submodelrepository-feature-authorization
+ test
+ tests
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.http
+ test
+ tests
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.submodelrepository-feature-authorization
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
\ No newline at end of file
diff --git a/basyx.submodelrepository/basyx.submodelrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/AuthorizedConnectedSubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/AuthorizedConnectedSubmodelRepository.java
new file mode 100644
index 000000000..65cd7c83c
--- /dev/null
+++ b/basyx.submodelrepository/basyx.submodelrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/AuthorizedConnectedSubmodelRepository.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * 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.client;
+
+import java.io.IOException;
+import java.net.http.HttpRequest;
+
+import org.eclipse.digitaltwin.basyx.client.internal.ApiException;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.TokenManager;
+import org.eclipse.digitaltwin.basyx.submodelrepository.client.internal.SubmodelRepositoryApi;
+import org.eclipse.digitaltwin.basyx.submodelservice.client.AuthorizedConnectedSubmodelService;
+import org.eclipse.digitaltwin.basyx.submodelservice.client.ConnectedSubmodelService;
+
+/**
+ * Provides access to an Authorized Submodel Repository on a remote server
+ *
+ * @author danish
+ */
+public class AuthorizedConnectedSubmodelRepository extends ConnectedSubmodelRepository {
+
+ private TokenManager tokenManager;
+
+ public AuthorizedConnectedSubmodelRepository(String repoUrl, TokenManager tokenManager) {
+ super(repoUrl, new SubmodelRepositoryApi(repoUrl, getRequestBuilder(tokenManager)));
+ this.tokenManager = tokenManager;
+ }
+
+ public TokenManager getTokenManager() {
+ return tokenManager;
+ }
+
+ @Override
+ public ConnectedSubmodelService getConnectedSubmodelService(String submodelId) {
+ try {
+ getSubmodel(submodelId);
+ return new AuthorizedConnectedSubmodelService(getSubmodelUrl(submodelId), tokenManager);
+ } catch (ApiException e) {
+ throw mapExceptionSubmodelAccess(getSubmodelUrl(submodelId), e);
+ }
+ }
+
+ private static HttpRequest.Builder getRequestBuilder(TokenManager tokenManager) {
+ try {
+ return HttpRequest.newBuilder()
+ .header("Authorization", "Bearer " + tokenManager.getAccessToken());
+ } catch (IOException e) {
+ throw new RuntimeException("Unable to request access token");
+ }
+ }
+
+}
diff --git a/basyx.submodelrepository/basyx.submodelrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/ConnectedSubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/ConnectedSubmodelRepository.java
index e7589bf51..a4d10f14f 100644
--- a/basyx.submodelrepository/basyx.submodelrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/ConnectedSubmodelRepository.java
+++ b/basyx.submodelrepository/basyx.submodelrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/ConnectedSubmodelRepository.java
@@ -71,6 +71,15 @@ public ConnectedSubmodelRepository(String submodelRepoUrl) {
this.repoApi = new SubmodelRepositoryApi(submodelRepoUrl);
this.submodelRepoUrl = submodelRepoUrl;
}
+
+ public ConnectedSubmodelRepository(String submodelRepoUrl, SubmodelRepositoryApi submodelRepositoryApi) {
+ this.submodelRepoUrl = submodelRepoUrl;
+ this.repoApi = submodelRepositoryApi;
+ }
+
+ public String getBaseUrl() {
+ return submodelRepoUrl;
+ }
/**
* Retrieves the Submodel with the specific id
@@ -216,11 +225,11 @@ public void deleteFileValue(String submodelId, String idShortPath) throws Elemen
getConnectedSubmodelService(submodelId).deleteFileValue(idShortPath);
}
- private String getSubmodelUrl(String submodelId) {
+ protected String getSubmodelUrl(String submodelId) {
return submodelRepoUrl + "/submodels/" + Base64UrlEncodedIdentifier.encodeIdentifier(submodelId);
}
- private RuntimeException mapExceptionSubmodelAccess(String submodelId, ApiException e) {
+ protected RuntimeException mapExceptionSubmodelAccess(String submodelId, ApiException e) {
if (e.getCode() == HttpStatus.NOT_FOUND.value()) {
return new ElementDoesNotExistException(submodelId);
} else if (e.getCode() == HttpStatus.CONFLICT.value()) {
diff --git a/basyx.submodelrepository/basyx.submodelrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/internal/SubmodelRepositoryApi.java b/basyx.submodelrepository/basyx.submodelrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/internal/SubmodelRepositoryApi.java
index fa0f78180..88e0e9140 100644
--- a/basyx.submodelrepository/basyx.submodelrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/internal/SubmodelRepositoryApi.java
+++ b/basyx.submodelrepository/basyx.submodelrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/internal/SubmodelRepositoryApi.java
@@ -43,6 +43,7 @@
import org.eclipse.digitaltwin.basyx.client.internal.ApiException;
import org.eclipse.digitaltwin.basyx.client.internal.ApiResponse;
import org.eclipse.digitaltwin.basyx.client.internal.Pair;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.TokenManager;
import org.eclipse.digitaltwin.basyx.core.pagination.CursorResult;
import org.eclipse.digitaltwin.basyx.http.pagination.Base64UrlEncodedCursorResult;
@@ -61,17 +62,35 @@ public class SubmodelRepositoryApi {
private final Duration memberVarReadTimeout;
private final Consumer> memberVarResponseInterceptor;
private final Consumer> memberVarAsyncResponseInterceptor;
+ private HttpRequest.Builder httpRequestBuilder;
public SubmodelRepositoryApi() {
this(new ApiClient());
}
+
+ public SubmodelRepositoryApi(HttpRequest.Builder httpRequestBuilder) {
+ this();
+ this.httpRequestBuilder = httpRequestBuilder;
+ }
public SubmodelRepositoryApi(ObjectMapper mapper, String baseUri) {
this(new ApiClient(HttpClient.newBuilder(), mapper, baseUri));
+ this.httpRequestBuilder = HttpRequest.newBuilder();
+ }
+
+ public SubmodelRepositoryApi(ObjectMapper mapper, String baseUri, HttpRequest.Builder httpRequestBuilder) {
+ this(mapper, baseUri);
+ this.httpRequestBuilder = httpRequestBuilder;
}
public SubmodelRepositoryApi(String baseUri) {
this(new ApiClient(HttpClient.newBuilder(), new JsonMapperFactory().create(new SimpleAbstractTypeResolverFactory().create()), baseUri));
+ this.httpRequestBuilder = HttpRequest.newBuilder();
+ }
+
+ public SubmodelRepositoryApi(String baseUri, HttpRequest.Builder httpRequestBuilder) {
+ this(baseUri);
+ this.httpRequestBuilder = httpRequestBuilder;
}
public SubmodelRepositoryApi(ApiClient apiClient) {
@@ -183,7 +202,7 @@ private HttpRequest.Builder getSubmodelByIdRequestBuilder(String submodelIdentif
throw new ApiException(400, "Missing the required parameter 'submodelIdentifier' when calling getSubmodelById");
}
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
String localVarPath = "/submodels/{submodelIdentifier}".replace("{submodelIdentifier}", ApiClient.urlEncode(submodelIdentifier.toString()));
@@ -287,7 +306,7 @@ private HttpRequest.Builder postSubmodelRequestBuilder(Submodel submodel) throws
throw new ApiException(400, "Missing the required parameter 'submodel' when calling postSubmodel");
}
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
String localVarPath = "/submodels";
@@ -391,7 +410,7 @@ private HttpRequest.Builder putSubmodelByIdRequestBuilder(String submodelIdentif
throw new ApiException(400, "Missing the required parameter 'submodel' when calling putSubmodelById");
}
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
String localVarPath = "/submodels/{submodelIdentifier}".replace("{submodelIdentifier}", ApiClient.urlEncode(submodelIdentifier.toString()));
@@ -485,7 +504,7 @@ private HttpRequest.Builder deleteSubmodelByIdRequestBuilder(String submodelIden
throw new ApiException(400, "Missing the required parameter 'submodelIdentifier' when calling deleteSubmodelById");
}
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
String localVarPath = "/submodels/{submodelIdentifier}".replace("{submodelIdentifier}", ApiClient.urlEncode(submodelIdentifier.toString()));
@@ -560,7 +579,7 @@ private ApiResponse>> getAllSubmodel
private HttpRequest.Builder getAllSubmodelsRequestBuilder(String semanticId, String idShort, Integer limit, String cursor, String level, String extent) throws ApiException {
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
String localVarPath = "/submodels";
diff --git a/basyx.submodelrepository/basyx.submodelrepository-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/TestAuthorizedConnectedSubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/TestAuthorizedConnectedSubmodelRepository.java
new file mode 100644
index 000000000..a6392f36f
--- /dev/null
+++ b/basyx.submodelrepository/basyx.submodelrepository-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/TestAuthorizedConnectedSubmodelRepository.java
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ * 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.client;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Collection;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
+import org.eclipse.digitaltwin.basyx.client.internal.ApiException;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.TokenManager;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.credential.ClientCredential;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.grant.ClientCredentialAccessTokenProvider;
+import org.eclipse.digitaltwin.basyx.core.exceptions.MissingIdentifierException;
+import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo;
+import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository;
+import org.eclipse.digitaltwin.basyx.submodelrepository.core.SubmodelRepositorySuite;
+import org.eclipse.digitaltwin.basyx.submodelrepository.feature.authorization.DummyAuthorizedSubmodelRepositoryComponent;
+import org.eclipse.digitaltwin.basyx.submodelservice.DummySubmodelFactory;
+import org.eclipse.digitaltwin.basyx.submodelservice.client.TestAuthorizedConnectedSubmodelService;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.springframework.boot.SpringApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+/**
+ * Tests for {@link AuthorizedConnectedSubmodelRepository}
+ *
+ * @author danish
+ */
+public class TestAuthorizedConnectedSubmodelRepository extends SubmodelRepositorySuite {
+
+ private static final String SUBMODEL_REPO_URL = "http://localhost:8081";
+ private static ConfigurableApplicationContext appContext;
+ private static final String PROFILE = "authorization";
+
+ @BeforeClass
+ public static void startAASRepo() throws Exception {
+ SpringApplication application = new SpringApplication(DummyAuthorizedSubmodelRepositoryComponent.class);
+ application.setAdditionalProfiles(PROFILE);
+
+ appContext = application.run(new String[] {});
+ }
+
+ @After
+ public void removeSubmodelFromRepo() throws FileNotFoundException, IOException {
+ TestAuthorizedConnectedSubmodelService.configureSecurityContext(TestAuthorizedConnectedSubmodelService.getTokenProvider());
+
+ SubmodelRepository repo = appContext.getBean(SubmodelRepository.class);
+ repo.getAllSubmodels(PaginationInfo.NO_LIMIT).getResult().stream().map(s -> s.getId()).forEach(repo::deleteSubmodel);
+
+ SecurityContextHolder.clearContext();
+ }
+
+ @AfterClass
+ public static void shutdownAASRepo() {
+ appContext.close();
+ }
+
+ @Test
+ public void sendUnauthorizedRequest() throws IOException {
+ TokenManager mockTokenManager = Mockito.mock(TokenManager.class);
+
+ Mockito.when(mockTokenManager.getAccessToken()).thenReturn("mockedAccessToken");
+
+ SubmodelRepository submodelRepository = new AuthorizedConnectedSubmodelRepository(SUBMODEL_REPO_URL, mockTokenManager);
+
+ Submodel expected = DummySubmodelFactory.createSimpleDataSubmodel();
+
+ ApiException exception = assertThrows(ApiException.class, () -> {
+ submodelRepository.createSubmodel(expected);
+ });
+
+ assertEquals(HttpStatus.UNAUTHORIZED.value(), exception.getCode());
+ }
+
+ @Override
+ protected ConnectedSubmodelRepository getSubmodelRepository() {
+ return new AuthorizedConnectedSubmodelRepository(SUBMODEL_REPO_URL, new TokenManager("http://localhost:9096/realms/BaSyx/protocol/openid-connect/token", new ClientCredentialAccessTokenProvider(new ClientCredential("workstation-1", "nY0mjyECF60DGzNmQUjL81XurSl8etom"))));
+ }
+
+ @Override
+ protected ConnectedSubmodelRepository getSubmodelRepository(Collection submodels) {
+ try {
+ TestAuthorizedConnectedSubmodelService.configureSecurityContext(TestAuthorizedConnectedSubmodelService.getTokenProvider());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ SubmodelRepository repo = appContext.getBean(SubmodelRepository.class);
+ submodels.forEach(repo::createSubmodel);
+
+ SecurityContextHolder.clearContext();
+
+ return getSubmodelRepository();
+ }
+
+ @Override
+ @Test(expected = MissingIdentifierException.class)
+ public void updateExistingSubmodelWithMismatchId() {
+ // TODO There should be a way to differentiate between both exceptions through
+ // the Http response
+ super.updateExistingSubmodelWithMismatchId();
+ }
+
+ @Override
+ protected boolean fileExistsInStorage(String fileValue) {
+ java.io.File file = new java.io.File(fileValue);
+
+ return file.exists();
+ }
+}
diff --git a/basyx.submodelrepository/basyx.submodelrepository-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/TestConnectedSubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/TestConnectedSubmodelRepository.java
index 4ee14b0b7..f1f4065ef 100644
--- a/basyx.submodelrepository/basyx.submodelrepository-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/TestConnectedSubmodelRepository.java
+++ b/basyx.submodelrepository/basyx.submodelrepository-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/TestConnectedSubmodelRepository.java
@@ -77,7 +77,7 @@ public void getConnectedSubmodelServiceNonExistingSubmodel() {
@Override
protected ConnectedSubmodelRepository getSubmodelRepository() {
- return new ConnectedSubmodelRepository("http://localhost:8080");
+ return new ConnectedSubmodelRepository("http://localhost:8081");
}
@Override
diff --git a/basyx.submodelrepository/basyx.submodelrepository-client/src/test/resources/application-authorization.properties b/basyx.submodelrepository/basyx.submodelrepository-client/src/test/resources/application-authorization.properties
new file mode 100644
index 000000000..7e32a7c95
--- /dev/null
+++ b/basyx.submodelrepository/basyx.submodelrepository-client/src/test/resources/application-authorization.properties
@@ -0,0 +1,33 @@
+server.port=8081
+
+spring.application.name=Submodel Repository
+basyx.smrepo.name = sm-repo
+basyx.backend = InMemory
+
+# basyx.submodelrepository.feature.registryintegration=http://localhost:8060/api/v3.0
+# basyx.externalurl=http://localhost:8081
+
+#basyx.backend = MongoDB
+#spring.data.mongodb.host=mongo
+# or spring.data.mongodb.host=127.0.0.1
+#spring.data.mongodb.port=27017
+#spring.data.mongodb.database=submodels
+#spring.data.mongodb.authentication-database=admin
+#spring.data.mongodb.username=mongoAdmin
+#spring.data.mongodb.password=mongoPassword
+
+# basyx.submodelrepository.feature.mqtt.enabled = true
+# mqtt.clientId=TestClient
+# mqtt.hostname = localhost
+# mqtt.port = 1883
+
+# basyx.cors.allowed-origins=http://localhost:3000, http://localhost:4000
+
+####################################################################################
+# 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:9096/realms/BaSyx
\ No newline at end of file
diff --git a/basyx.submodelrepository/basyx.submodelrepository-client/src/test/resources/application.properties b/basyx.submodelrepository/basyx.submodelrepository-client/src/test/resources/application.properties
new file mode 100644
index 000000000..fdc1f0c47
--- /dev/null
+++ b/basyx.submodelrepository/basyx.submodelrepository-client/src/test/resources/application.properties
@@ -0,0 +1,33 @@
+server.port=8081
+
+spring.application.name=Submodel Repository
+basyx.smrepo.name = sm-repo
+basyx.backend = InMemory
+
+# basyx.submodelrepository.feature.registryintegration=http://localhost:8060/api/v3.0
+# basyx.externalurl=http://localhost:8081
+
+#basyx.backend = MongoDB
+#spring.data.mongodb.host=mongo
+# or spring.data.mongodb.host=127.0.0.1
+#spring.data.mongodb.port=27017
+#spring.data.mongodb.database=submodels
+#spring.data.mongodb.authentication-database=admin
+#spring.data.mongodb.username=mongoAdmin
+#spring.data.mongodb.password=mongoPassword
+
+# basyx.submodelrepository.feature.mqtt.enabled = true
+# mqtt.clientId=TestClient
+# mqtt.hostname = localhost
+# mqtt.port = 1883
+
+# basyx.cors.allowed-origins=http://localhost:3000, http://localhost:4000
+
+####################################################################################
+# 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:9096/realms/BaSyx
\ No newline at end of file
diff --git a/basyx.submodelrepository/basyx.submodelrepository-client/src/test/resources/rbac_rules.json b/basyx.submodelrepository/basyx.submodelrepository-client/src/test/resources/rbac_rules.json
new file mode 100644
index 000000000..ef11e18f6
--- /dev/null
+++ b/basyx.submodelrepository/basyx.submodelrepository-client/src/test/resources/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/basyx.submodelservice/basyx.submodelservice-client/pom.xml b/basyx.submodelservice/basyx.submodelservice-client/pom.xml
index c13b742ec..65ec41d68 100644
--- a/basyx.submodelservice/basyx.submodelservice-client/pom.xml
+++ b/basyx.submodelservice/basyx.submodelservice-client/pom.xml
@@ -55,5 +55,48 @@
basyx.submodelservice-backend-inmemory
test
+
+
+ org.apache.httpcomponents.client5
+ httpclient5
+ test
+
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.http
+ test
+ tests
+
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.authorization
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.authorization
+ test
+ tests
+
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.submodelrepository-feature-authorization
+ test
+ tests
+
+
+
+ org.eclipse.digitaltwin.basyx
+ basyx.submodelrepository-feature-authorization
+ test
+
+
+
+ org.mockito
+ mockito-core
+ test
+
\ No newline at end of file
diff --git a/basyx.submodelservice/basyx.submodelservice-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/client/AuthorizedConnectedSubmodelService.java b/basyx.submodelservice/basyx.submodelservice-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/client/AuthorizedConnectedSubmodelService.java
new file mode 100644
index 000000000..549d02881
--- /dev/null
+++ b/basyx.submodelservice/basyx.submodelservice-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/client/AuthorizedConnectedSubmodelService.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * 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.submodelservice.client;
+
+import java.io.IOException;
+import java.net.http.HttpRequest;
+
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.TokenManager;
+import org.eclipse.digitaltwin.basyx.submodelservice.client.internal.SubmodelServiceApi;
+
+/**
+ * Provides access to an authorized Submodel Service on a remote server - regardless if it
+ * is hosted on a Submodel Repository or standalone
+ *
+ * @author danish
+ */
+public class AuthorizedConnectedSubmodelService extends ConnectedSubmodelService {
+
+ /**
+ *
+ * @param repoUrl
+ * the Url of the Submodel Repository without the "/submodels" part
+ * @param tokenManager
+ */
+ public AuthorizedConnectedSubmodelService(String repoUrl, TokenManager tokenManager) {
+ super(new SubmodelServiceApi(repoUrl, getRequestBuilder(tokenManager)));
+ }
+
+ private static HttpRequest.Builder getRequestBuilder(TokenManager tokenManager) {
+ try {
+ return HttpRequest.newBuilder()
+ .header("Authorization", "Bearer " + tokenManager.getAccessToken());
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException("Unable to request access token");
+ }
+ }
+
+}
diff --git a/basyx.submodelservice/basyx.submodelservice-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/client/ConnectedSubmodelService.java b/basyx.submodelservice/basyx.submodelservice-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/client/ConnectedSubmodelService.java
index 3fb627832..df7baf690 100644
--- a/basyx.submodelservice/basyx.submodelservice-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/client/ConnectedSubmodelService.java
+++ b/basyx.submodelservice/basyx.submodelservice-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/client/ConnectedSubmodelService.java
@@ -73,6 +73,11 @@ public ConnectedSubmodelService(String submodelServiceUrl) {
this.serviceApi = new SubmodelServiceApi(submodelServiceUrl);
this.submodelElementValueMapperFactory = new SubmodelElementValueMapperFactory();
}
+
+ public ConnectedSubmodelService(SubmodelServiceApi submodelServiceApi) {
+ this.serviceApi = submodelServiceApi;
+ this.submodelElementValueMapperFactory = new SubmodelElementValueMapperFactory();
+ }
@Override
public Submodel getSubmodel() {
diff --git a/basyx.submodelservice/basyx.submodelservice-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/client/internal/SubmodelServiceApi.java b/basyx.submodelservice/basyx.submodelservice-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/client/internal/SubmodelServiceApi.java
index fc5280e0c..73eb5af24 100644
--- a/basyx.submodelservice/basyx.submodelservice-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/client/internal/SubmodelServiceApi.java
+++ b/basyx.submodelservice/basyx.submodelservice-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/client/internal/SubmodelServiceApi.java
@@ -74,18 +74,37 @@ public class SubmodelServiceApi {
private final Duration memberVarReadTimeout;
private final Consumer> memberVarResponseInterceptor;
private final Consumer> memberVarAsyncResponseInterceptor;
+ private HttpRequest.Builder httpRequestBuilder;
public SubmodelServiceApi() {
this(new ApiClient());
-}
+ this.httpRequestBuilder = HttpRequest.newBuilder();
+ }
+
+ public SubmodelServiceApi(HttpRequest.Builder httpRequestBuilder) {
+ this();
+ this.httpRequestBuilder = httpRequestBuilder;
+ }
public SubmodelServiceApi(ObjectMapper mapper, String baseUri) {
this(new ApiClient(HttpClient.newBuilder(), mapper, baseUri));
-}
+ this.httpRequestBuilder = HttpRequest.newBuilder();
+ }
+
+ public SubmodelServiceApi(ObjectMapper mapper, String baseUri, HttpRequest.Builder httpRequestBuilder) {
+ this(mapper, baseUri);
+ this.httpRequestBuilder = httpRequestBuilder;
+ }
-public SubmodelServiceApi(String baseUri) {
- this(new ApiClient(HttpClient.newBuilder(), new SubmodelSpecificJsonMapperFactory().create(), baseUri));
-}
+ public SubmodelServiceApi(String baseUri) {
+ this(new ApiClient(HttpClient.newBuilder(), new SubmodelSpecificJsonMapperFactory().create(), baseUri));
+ this.httpRequestBuilder = HttpRequest.newBuilder();
+ }
+
+ public SubmodelServiceApi(String baseUri, HttpRequest.Builder httpRequestBuilder) {
+ this(baseUri);
+ this.httpRequestBuilder = httpRequestBuilder;
+ }
@@ -189,7 +208,7 @@ public ApiResponse getSubmodelWithHttpInfoNoUrlEncoding(String level,
private HttpRequest.Builder getSubmodelRequestBuilder(String level, String extent) throws ApiException {
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
String localVarPath = "";
@@ -311,7 +330,7 @@ private HttpRequest.Builder getSubmodelElementByPathRequestBuilder(String idShor
throw new ApiException(400, "Missing the required parameter 'idShortPath' when calling getSubmodelElementByPath");
}
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
String localVarPath = "/submodel-elements/{idShortPath}".replace("{idShortPath}", ApiClient.urlEncode(idShortPath.toString()));
@@ -436,7 +455,7 @@ private HttpRequest.Builder getSubmodelElementByPathValueOnlyRequestBuilder(Stri
throw new ApiException(400, "Missing the required parameter 'idShortPath' when calling getSubmodelElementByPathValueOnly");
}
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
String localVarPath = "/submodel-elements/{idShortPath}/$value".replace("{idShortPath}", ApiClient.urlEncode(idShortPath.toString()));
@@ -577,7 +596,7 @@ private HttpRequest.Builder patchSubmodelElementByPathValueOnlyRequestBuilder(St
throw new ApiException(400, "Missing the required parameter 'getSubmodelElementsValueResult' when calling patchSubmodelElementByPathValueOnly");
}
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
String localVarPath = "/submodel-elements/{idShortPath}/$value".replace("{idShortPath}", ApiClient.urlEncode(idShortPath.toString()));
@@ -688,7 +707,7 @@ private HttpRequest.Builder postSubmodelElementRequestBuilder(SubmodelElement su
throw new ApiException(400, "Missing the required parameter 'submodelElement' when calling postSubmodelElement");
}
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
String localVarPath = "/submodel-elements";
@@ -793,7 +812,7 @@ private HttpRequest.Builder postSubmodelElementByPathRequestBuilder(String idSho
throw new ApiException(400, "Missing the required parameter 'submodelElement' when calling postSubmodelElementByPath");
}
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
String localVarPath = "/submodel-elements/{idShortPath}".replace("{idShortPath}", ApiClient.urlEncode(idShortPath.toString()));
@@ -908,7 +927,7 @@ private HttpRequest.Builder putSubmodelElementByPathRequestBuilder(String idShor
throw new ApiException(400, "Missing the required parameter 'submodelElement' when calling putSubmodelElementByPath");
}
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
String localVarPath = "/submodel-elements/{idShortPath}".replace("{idShortPath}", ApiClient.urlEncode(idShortPath.toString()));
@@ -1019,7 +1038,7 @@ private HttpRequest.Builder deleteSubmodelElementByPathRequestBuilder(String idS
throw new ApiException(400, "Missing the required parameter 'idShortPath' when calling deleteSubmodelElementByPath");
}
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
String localVarPath = "/submodel-elements/{idShortPath}".replace("{idShortPath}", ApiClient.urlEncode(idShortPath.toString()));
@@ -1090,7 +1109,7 @@ private ApiResponse>> getAllS
private HttpRequest.Builder getAllSubmodelElementsRequestBuilder(Integer limit, String cursor, String level, String extent) throws ApiException {
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
String localVarPath = "/submodel-elements";
@@ -1179,7 +1198,7 @@ private HttpRequest.Builder putFileByPathRequestBuilder(String idShortPath, Stri
if (inputStream == null)
throw new ApiException(400, "Missing the required parameter 'inputStream' when calling putFileByPath");
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
String localVarPath = "/submodel-elements/{idShortPath}/attachment".replace("{idShortPath}", ApiClient.urlEncode(idShortPath));
@@ -1288,7 +1307,7 @@ private HttpRequest.Builder deleteFileByPathRequestBuilder(String idShortPath) t
throw new ApiException(400, "Missing the required parameter 'idShortPath' when calling deleteFileByPath");
}
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
String localVarPath = "/submodel-elements/{idShortPath}/attachment".replace("{idShortPath}", ApiClient.urlEncode(idShortPath.toString()));
@@ -1355,7 +1374,7 @@ private HttpRequest.Builder getFileByPathRequestBuilder(String idShortPath) thro
throw new ApiException(400, "Missing the required parameter 'idShortPath' when calling getFileByPath");
}
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
String localVarPath = "/submodel-elements/{idShortPath}/attachment".replace("{idShortPath}", ApiClient.urlEncode(idShortPath.toString()));
@@ -1452,7 +1471,7 @@ private HttpRequest.Builder invokeOperationRequestBuilder(String idShortPath, Op
throw new ApiException(400, "Missing the required parameter 'operationRequest' when calling invokeOperation");
}
- HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();
+ HttpRequest.Builder localVarRequestBuilder = this.httpRequestBuilder.copy();
String localVarPath = "/submodel-elements/{idShortPath}/invoke".replace("{idShortPath}", ApiClient.urlEncode(idShortPath.toString()));
diff --git a/basyx.submodelservice/basyx.submodelservice-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/client/TestAuthorizedConnectedSubmodelService.java b/basyx.submodelservice/basyx.submodelservice-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/client/TestAuthorizedConnectedSubmodelService.java
new file mode 100644
index 000000000..02e3487d2
--- /dev/null
+++ b/basyx.submodelservice/basyx.submodelservice-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/client/TestAuthorizedConnectedSubmodelService.java
@@ -0,0 +1,164 @@
+/*******************************************************************************
+ * 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.submodelservice.client;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.security.interfaces.RSAPublicKey;
+
+import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
+import org.eclipse.digitaltwin.basyx.authorization.AccessTokenProvider;
+import org.eclipse.digitaltwin.basyx.authorization.DummyCredential;
+import org.eclipse.digitaltwin.basyx.authorization.DummyCredentialStore;
+import org.eclipse.digitaltwin.basyx.authorization.jwt.JwtTokenDecoder;
+import org.eclipse.digitaltwin.basyx.authorization.jwt.PublicKeyUtils;
+import org.eclipse.digitaltwin.basyx.client.internal.ApiException;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.TokenManager;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.credential.ClientCredential;
+import org.eclipse.digitaltwin.basyx.client.internal.authorization.grant.ClientCredentialAccessTokenProvider;
+import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo;
+import org.eclipse.digitaltwin.basyx.http.Base64UrlEncodedIdentifier;
+import org.eclipse.digitaltwin.basyx.http.serialization.BaSyxHttpTestUtils;
+import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository;
+import org.eclipse.digitaltwin.basyx.submodelrepository.feature.authorization.DummyAuthorizedSubmodelRepositoryComponent;
+import org.eclipse.digitaltwin.basyx.submodelservice.DummySubmodelFactory;
+import org.eclipse.digitaltwin.basyx.submodelservice.SubmodelService;
+import org.eclipse.digitaltwin.basyx.submodelservice.SubmodelServiceSuite;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.springframework.boot.SpringApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
+
+/**
+ * Tests the authorized Connected Submodel Service
+ *
+ * @author danish
+ */
+public class TestAuthorizedConnectedSubmodelService extends SubmodelServiceSuite {
+
+ private static final String SUBMODEL_REPO_URL = "http://localhost:8081/submodels/";
+ private static ConfigurableApplicationContext appContext;
+ private static final String PROFILE = "authorization";
+ public static String authenticaltionServerTokenEndpoint = "http://localhost:9096/realms/BaSyx/protocol/openid-connect/token";
+ public static String clientId = "basyx-client-api";
+
+ @BeforeClass
+ public static void startAASRepo() throws Exception {
+ SpringApplication application = new SpringApplication(DummyAuthorizedSubmodelRepositoryComponent.class);
+ application.setAdditionalProfiles(PROFILE);
+
+ appContext = application.run(new String[] {});
+ }
+
+ @After
+ public void removeAasFromRepo() {
+ SubmodelRepository repo = appContext.getBean(SubmodelRepository.class);
+ repo.getAllSubmodels(PaginationInfo.NO_LIMIT).getResult().stream().map(s -> s.getId()).forEach(repo::deleteSubmodel);
+ }
+
+ @AfterClass
+ public static void shutdownSubmodelService() {
+ appContext.close();
+ }
+
+ @Test
+ public void sendUnauthorizedRequest() throws IOException {
+ TokenManager mockTokenManager = Mockito.mock(TokenManager.class);
+
+ Mockito.when(mockTokenManager.getAccessToken()).thenReturn("mockedAccessToken");
+
+ createDummyShellOnRepo("dummySubmodelId");
+
+ SubmodelService submodelService = new AuthorizedConnectedSubmodelService(SUBMODEL_REPO_URL + "dummySubmodelId", mockTokenManager);
+
+ ApiException exception = assertThrows(ApiException.class, () -> {
+ submodelService.getSubmodel();
+ });
+
+ assertEquals(HttpStatus.UNAUTHORIZED.value(), exception.getCode());
+ }
+
+ private void createDummyShellOnRepo(String shellId) throws FileNotFoundException, IOException {
+ configureSecurityContext(getTokenProvider());
+
+ SubmodelRepository repo = appContext.getBean(SubmodelRepository.class);
+
+ Submodel expected1 = DummySubmodelFactory.createSimpleDataSubmodel();
+ expected1.setId(shellId);
+
+ repo.createSubmodel(expected1);
+ }
+
+ @Override
+ protected SubmodelService getSubmodelService(Submodel submodel) {
+ SubmodelRepository repo = appContext.getBean(SubmodelRepository.class);
+ repo.createSubmodel(submodel);
+ String base64UrlEncodedId = Base64UrlEncodedIdentifier.encodeIdentifier(submodel.getId());
+ return new AuthorizedConnectedSubmodelService(SUBMODEL_REPO_URL + base64UrlEncodedId, new TokenManager("http://localhost:9096/realms/BaSyx/protocol/openid-connect/token", new ClientCredentialAccessTokenProvider(new ClientCredential("workstation-1", "nY0mjyECF60DGzNmQUjL81XurSl8etom"))));
+ }
+
+ @Override
+ protected boolean fileExistsInStorage(String fileValue) {
+ java.io.File file = new java.io.File(fileValue);
+
+ return file.exists();
+ }
+
+ public static void configureSecurityContext(AccessTokenProvider accessTokenProvider) throws FileNotFoundException, IOException {
+ String adminToken = getAdminAccessToken(accessTokenProvider);
+
+ String modulus = BaSyxHttpTestUtils.readJSONStringFromClasspath("authorization/modulus.txt");
+
+ String exponent = "AQAB";
+
+ RSAPublicKey rsaPublicKey = PublicKeyUtils.buildPublicKey(modulus, exponent);
+
+ Jwt jwt = JwtTokenDecoder.decodeJwt(adminToken, rsaPublicKey);
+
+ SecurityContextHolder.getContext().setAuthentication(new JwtAuthenticationToken(jwt));
+ }
+
+ public static String getAdminAccessToken(AccessTokenProvider tokenProvider) {
+ DummyCredential dummyCredential = DummyCredentialStore.ADMIN_CREDENTIAL;
+
+ return tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword());
+ }
+
+ public static AccessTokenProvider getTokenProvider() {
+ return new AccessTokenProvider(authenticaltionServerTokenEndpoint, clientId);
+ }
+
+}
diff --git a/basyx.submodelservice/basyx.submodelservice-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/client/TestConnectedSubmodelElements.java b/basyx.submodelservice/basyx.submodelservice-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/client/TestConnectedSubmodelElements.java
index d3d4f5648..1860d833f 100644
--- a/basyx.submodelservice/basyx.submodelservice-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/client/TestConnectedSubmodelElements.java
+++ b/basyx.submodelservice/basyx.submodelservice-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/client/TestConnectedSubmodelElements.java
@@ -104,7 +104,7 @@ public class TestConnectedSubmodelElements {
private static final String EXPECTED_STRING = "This is a test";
private static final String SUBMODEL_ID_SHORT = "submodelIdShort";
private static final String SUBMODEL_ID = "submodelId";
- private static final String ACCESS_URL = "http://localhost:8080/submodels/";
+ private static final String ACCESS_URL = "http://localhost:8081/submodels/";
private static ConfigurableApplicationContext appContext;
@BeforeClass
diff --git a/basyx.submodelservice/basyx.submodelservice-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/client/TestConnectedSubmodelService.java b/basyx.submodelservice/basyx.submodelservice-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/client/TestConnectedSubmodelService.java
index d14c32b2a..fa997b7ce 100644
--- a/basyx.submodelservice/basyx.submodelservice-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/client/TestConnectedSubmodelService.java
+++ b/basyx.submodelservice/basyx.submodelservice-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/client/TestConnectedSubmodelService.java
@@ -71,7 +71,7 @@ protected SubmodelService getSubmodelService(Submodel submodel) {
SubmodelRepository repo = appContext.getBean(SubmodelRepository.class);
repo.createSubmodel(submodel);
String base64UrlEncodedId = Base64UrlEncodedIdentifier.encodeIdentifier(submodel.getId());
- return new ConnectedSubmodelService("http://localhost:8080/submodels/" + base64UrlEncodedId);
+ return new ConnectedSubmodelService("http://localhost:8081/submodels/" + base64UrlEncodedId);
}
@Override
diff --git a/basyx.submodelservice/basyx.submodelservice-client/src/test/resources/application-authorization.properties b/basyx.submodelservice/basyx.submodelservice-client/src/test/resources/application-authorization.properties
new file mode 100644
index 000000000..7e32a7c95
--- /dev/null
+++ b/basyx.submodelservice/basyx.submodelservice-client/src/test/resources/application-authorization.properties
@@ -0,0 +1,33 @@
+server.port=8081
+
+spring.application.name=Submodel Repository
+basyx.smrepo.name = sm-repo
+basyx.backend = InMemory
+
+# basyx.submodelrepository.feature.registryintegration=http://localhost:8060/api/v3.0
+# basyx.externalurl=http://localhost:8081
+
+#basyx.backend = MongoDB
+#spring.data.mongodb.host=mongo
+# or spring.data.mongodb.host=127.0.0.1
+#spring.data.mongodb.port=27017
+#spring.data.mongodb.database=submodels
+#spring.data.mongodb.authentication-database=admin
+#spring.data.mongodb.username=mongoAdmin
+#spring.data.mongodb.password=mongoPassword
+
+# basyx.submodelrepository.feature.mqtt.enabled = true
+# mqtt.clientId=TestClient
+# mqtt.hostname = localhost
+# mqtt.port = 1883
+
+# basyx.cors.allowed-origins=http://localhost:3000, http://localhost:4000
+
+####################################################################################
+# 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:9096/realms/BaSyx
\ No newline at end of file
diff --git a/basyx.submodelservice/basyx.submodelservice-client/src/test/resources/application.properties b/basyx.submodelservice/basyx.submodelservice-client/src/test/resources/application.properties
new file mode 100644
index 000000000..fdc1f0c47
--- /dev/null
+++ b/basyx.submodelservice/basyx.submodelservice-client/src/test/resources/application.properties
@@ -0,0 +1,33 @@
+server.port=8081
+
+spring.application.name=Submodel Repository
+basyx.smrepo.name = sm-repo
+basyx.backend = InMemory
+
+# basyx.submodelrepository.feature.registryintegration=http://localhost:8060/api/v3.0
+# basyx.externalurl=http://localhost:8081
+
+#basyx.backend = MongoDB
+#spring.data.mongodb.host=mongo
+# or spring.data.mongodb.host=127.0.0.1
+#spring.data.mongodb.port=27017
+#spring.data.mongodb.database=submodels
+#spring.data.mongodb.authentication-database=admin
+#spring.data.mongodb.username=mongoAdmin
+#spring.data.mongodb.password=mongoPassword
+
+# basyx.submodelrepository.feature.mqtt.enabled = true
+# mqtt.clientId=TestClient
+# mqtt.hostname = localhost
+# mqtt.port = 1883
+
+# basyx.cors.allowed-origins=http://localhost:3000, http://localhost:4000
+
+####################################################################################
+# 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:9096/realms/BaSyx
\ No newline at end of file
diff --git a/basyx.submodelservice/basyx.submodelservice-client/src/test/resources/authorization/modulus.txt b/basyx.submodelservice/basyx.submodelservice-client/src/test/resources/authorization/modulus.txt
new file mode 100644
index 000000000..9def9d5fa
--- /dev/null
+++ b/basyx.submodelservice/basyx.submodelservice-client/src/test/resources/authorization/modulus.txt
@@ -0,0 +1 @@
+sQ4RUvlZXLjQpLizIMht46ASwpwUoPpUDTmUhxF_VV-ezHTHbTAdv_5RA1GgCyTjAQe_Ih6dLByZrJFaroyvqgIJdMRCb0MwajI1US0_NwHtVvo5dea_-GKeHGRzvYZjxVlooR_1xmskfAM_NR_NaOMUhr_TNV7n7LXEb06L55DYnqdqrUnhXLewBq1lo54GsMqxN4hlkc4nJ2uYUtWEkV4SlMyYRjXBlylpuWFO0-_FsmaqSx7CZWjNWmqKlxnvUgRrT5Vh-9ZgCAOmGtLuFYOVWzqmVjWtyiJ1pYfWjwc86XeWBFefVY1lkoNNoSYKV4AZIkeF2M_-FHNzNGhLOw
\ No newline at end of file
diff --git a/basyx.submodelservice/basyx.submodelservice-client/src/test/resources/rbac_rules.json b/basyx.submodelservice/basyx.submodelservice-client/src/test/resources/rbac_rules.json
new file mode 100644
index 000000000..ef11e18f6
--- /dev/null
+++ b/basyx.submodelservice/basyx.submodelservice-client/src/test/resources/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/.env b/ci/.env
new file mode 100644
index 000000000..5e316bdf3
--- /dev/null
+++ b/ci/.env
@@ -0,0 +1,3 @@
+BASYX_VERSION=2.0.0-SNAPSHOT
+AAS_WEBUI_VERSION=v2-240125
+KEYCLOAK_VERSION=24.0.4
\ No newline at end of file
diff --git a/ci/docker-compose.yml b/ci/docker-compose.yml
index 4cc177c8f..ff216c98d 100644
--- a/ci/docker-compose.yml
+++ b/ci/docker-compose.yml
@@ -75,7 +75,7 @@ services:
- basyx-java-server-sdk
aas-registry-log-mem:
- image: eclipsebasyx/aas-registry-log-mem:2.0.0-SNAPSHOT
+ image: eclipsebasyx/aas-registry-log-mem:$BASYX_VERSION
container_name: aas-registry-log-mem
ports:
- "8050:8080"
@@ -86,7 +86,7 @@ services:
- basyx-java-server-sdk
sm-registry-log-mem:
- image: eclipsebasyx/submodel-registry-log-mem:2.0.0-SNAPSHOT
+ image: eclipsebasyx/submodel-registry-log-mem:$BASYX_VERSION
container_name: sm-registry-log-mem
environment:
SERVER_SERVLET_CONTEXT_PATH: /
@@ -96,11 +96,51 @@ services:
networks:
- basyx-java-server-sdk
+ secured-aas-registry-log-mem:
+ image: eclipsebasyx/aas-registry-log-mem:$BASYX_VERSION
+ container_name: secured-aas-registry-log-mem
+ ports:
+ - "8051:8080"
+ 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-aas-registry.json:/rbac/rbac_rules.json:ro
+ restart: always
+ networks:
+ - basyx-java-server-sdk
+
+ secured-sm-registry-log-mem:
+ image: eclipsebasyx/submodel-registry-log-mem:$BASYX_VERSION
+ container_name: secured-sm-registry-log-mem
+ 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-sm-registry.json:/rbac/rbac_rules.json:ro
+ ports:
+ - "8061:8080"
+ restart: always
+ networks:
+ - basyx-java-server-sdk
+
keycloak:
build:
- context: ./../examples/BaSyxSecured/keycloak
+ context: ./keycloak
volumes:
- - ./:/opt/jboss/keycloak/imports
+ - ./keycloak/realm:/opt/jboss/keycloak/imports
ports:
- 9096:8080
environment:
@@ -109,6 +149,25 @@ services:
networks:
- basyx-java-server-sdk
+ keycloak-fixed-uri:
+ image: eclipsebasyx/keycloak:0.0.1
+ build:
+ context: ./keycloak
+ dockerfile: Dockerfile.keycloak
+ container_name: keycloak-fixed-uri
+ environment:
+ KC_HOSTNAME: localhost
+ KC_SPI_INITIALIZER_ISSUER_BASE_URI: http://keycloak: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
+ networks:
+ - basyx-java-server-sdk
+
networks:
basyx-java-server-sdk:
name: basyx-java-server-sdk
diff --git a/ci/keycloak/Dockerfile b/ci/keycloak/Dockerfile
new file mode 100644
index 000000000..54bc49c28
--- /dev/null
+++ b/ci/keycloak/Dockerfile
@@ -0,0 +1,14 @@
+FROM quay.io/keycloak/keycloak:22.0.0
+
+# Make the realm configuration available for import
+COPY /realm/BaSyx-realm.json /opt/keycloak_import/
+
+# Import the realm and user
+RUN /opt/keycloak/bin/kc.sh import --file /opt/keycloak_import/BaSyx-realm.json
+
+# The Keycloak server is configured to listen on port 8080
+EXPOSE 8080
+EXPOSE 8443
+
+# Import the realm on start-up
+CMD ["start-dev"]
\ No newline at end of file
diff --git a/ci/keycloak/Dockerfile.keycloak b/ci/keycloak/Dockerfile.keycloak
new file mode 100644
index 000000000..f7f0af270
--- /dev/null
+++ b/ci/keycloak/Dockerfile.keycloak
@@ -0,0 +1,13 @@
+# syntax=docker/dockerfile:1
+FROM maven:3-eclipse-temurin-17-alpine as build
+WORKDIR /workspace
+COPY ./initializer/pom.xml /workspace/pom.xml
+COPY ./initializer/src /workspace/src
+COPY ./realm/BaSyx-realm.json /workspace/BaSyx-realm.json
+RUN mvn install
+
+FROM keycloak/keycloak:24.0.4
+COPY --from=build /workspace/target/org.eclipse.digitaltwin.basyx.v3.clients-keycloak-issuer-initializer.jar /opt/keycloak/providers/issuer-initializer.jar
+COPY --from=build /workspace/BaSyx-realm.json /opt/keycloak/data/import/BaSyx-realm.json
+
+RUN /opt/keycloak/bin/kc.sh import --file /opt/keycloak/data/import/BaSyx-realm.json
\ No newline at end of file
diff --git a/ci/keycloak/initializer/pom.xml b/ci/keycloak/initializer/pom.xml
new file mode 100644
index 000000000..62d5c64b2
--- /dev/null
+++ b/ci/keycloak/initializer/pom.xml
@@ -0,0 +1,126 @@
+
+
+ 4.0.0
+ org.eclipse.digitaltwin.basyx.v3.clients
+ keycloak-issuer-initializer
+ 1.0-SNAPSHOT
+
+
+ 24.0.3
+ UTF-8
+ 1.14.11
+ 2.0.13
+
+
+
+
+ org.keycloak
+ keycloak-core
+ ${keycloak.version}
+
+
+ org.keycloak
+ keycloak-server-spi
+ ${keycloak.version}
+
+
+ org.keycloak
+ keycloak-server-spi-private
+ ${keycloak.version}
+
+
+ org.keycloak
+ keycloak-services
+ ${keycloak.version}
+
+
+
+ com.google.auto.service
+ auto-service
+
+ 1.1.1
+
+
+ org.slf4j
+ slf4j-api
+ ${slf4j.version}
+
+
+ net.bytebuddy
+ byte-buddy
+ 1.14.15
+
+
+ net.bytebuddy
+ byte-buddy-agent
+ 1.14.15
+
+
+ com.cronutils
+ cron-utils
+ 9.2.1
+
+
+ org.slf4j
+ slf4j-reload4j
+ ${slf4j.version}
+
+
+
+
+ ${project.groupId}-${project.artifactId}
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+
+
+ package
+
+ shade
+
+
+
+
+
+ org.keycloak:*
+
+ **/*
+
+
+
+ io.smallrye.common:smallrye-common-annotation
+
+ **/*
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ci/keycloak/initializer/src/main/java/org/eclipse/digitaltwin/basyx/keycloak/initializer/InitializerProviderFactory.java b/ci/keycloak/initializer/src/main/java/org/eclipse/digitaltwin/basyx/keycloak/initializer/InitializerProviderFactory.java
new file mode 100644
index 000000000..ccb4be066
--- /dev/null
+++ b/ci/keycloak/initializer/src/main/java/org/eclipse/digitaltwin/basyx/keycloak/initializer/InitializerProviderFactory.java
@@ -0,0 +1,22 @@
+package org.eclipse.digitaltwin.basyx.keycloak.initializer;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+
+public interface InitializerProviderFactory extends ProviderFactory, Provider {
+
+ @Override
+ default Provider create(KeycloakSession session) {
+ return null;
+ }
+
+ @Override
+ default void init(Config.Scope config) {
+ }
+
+ @Override
+ default void close() {
+ }
+}
diff --git a/ci/keycloak/initializer/src/main/java/org/eclipse/digitaltwin/basyx/keycloak/initializer/InitializerSpi.java b/ci/keycloak/initializer/src/main/java/org/eclipse/digitaltwin/basyx/keycloak/initializer/InitializerSpi.java
new file mode 100644
index 000000000..67da7d22c
--- /dev/null
+++ b/ci/keycloak/initializer/src/main/java/org/eclipse/digitaltwin/basyx/keycloak/initializer/InitializerSpi.java
@@ -0,0 +1,29 @@
+package org.eclipse.digitaltwin.basyx.keycloak.initializer;
+
+import com.google.auto.service.AutoService;
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+@AutoService(Spi.class)
+public class InitializerSpi implements Spi {
+ @Override
+ public boolean isInternal() {
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return "initializer";
+ }
+
+ @Override
+ public Class extends Provider> getProviderClass() {
+ return Provider.class;
+ }
+
+ @Override
+ public Class extends ProviderFactory extends Provider>> getProviderFactoryClass() {
+ return InitializerProviderFactory.class;
+ }
+}
diff --git a/ci/keycloak/initializer/src/main/java/org/eclipse/digitaltwin/basyx/keycloak/initializer/realm/IssuerInitializerProvider.java b/ci/keycloak/initializer/src/main/java/org/eclipse/digitaltwin/basyx/keycloak/initializer/realm/IssuerInitializerProvider.java
new file mode 100644
index 000000000..8ad4f8edf
--- /dev/null
+++ b/ci/keycloak/initializer/src/main/java/org/eclipse/digitaltwin/basyx/keycloak/initializer/realm/IssuerInitializerProvider.java
@@ -0,0 +1,83 @@
+package org.eclipse.digitaltwin.basyx.keycloak.initializer.realm;
+
+import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy;
+import static net.bytebuddy.matcher.ElementMatchers.named;
+import static net.bytebuddy.matcher.ElementMatchers.returns;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.eclipse.digitaltwin.basyx.keycloak.initializer.InitializerProviderFactory;
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.provider.ProviderConfigProperty;
+import org.keycloak.provider.ProviderConfigurationBuilder;
+import org.keycloak.services.Urls;
+import org.keycloak.services.validation.Validation;
+
+import com.google.auto.service.AutoService;
+
+import net.bytebuddy.ByteBuddy;
+import net.bytebuddy.agent.ByteBuddyAgent;
+import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
+import net.bytebuddy.implementation.MethodDelegation;
+
+@AutoService(InitializerProviderFactory.class)
+public class IssuerInitializerProvider implements InitializerProviderFactory {
+
+ public static final String PROVIDER_ID = "issuer";
+
+ private static final String CONFIG_ATTR_BASE_URI = "base-uri";
+
+ private static String issuerBaseUri;
+
+ private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(IssuerInitializerProvider.class);
+
+ @Override
+ public String getId() {
+ return PROVIDER_ID;
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ issuerBaseUri = config.get(CONFIG_ATTR_BASE_URI);
+ if (!Validation.isBlank(issuerBaseUri)) {
+ log.info("Issuer BaseURI fixed value: {}", issuerBaseUri);
+ } else {
+ log.info("Issuer BaseURI is blank");
+ }
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+ ByteBuddyAgent.install();
+ new ByteBuddy()
+ .redefine(Urls.class)
+ .method(named("realmIssuer").and(isDeclaredBy(Urls.class).and(returns(String.class))))
+ .intercept(MethodDelegation.to(this.getClass()))
+ .make()
+ .load(Urls.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
+ }
+
+ public static String realmIssuer(URI baseUri, String realmName) {
+ try {
+ baseUri = new URI(issuerBaseUri);
+ } catch (URISyntaxException | NullPointerException ignored) {
+ }
+ return Urls.realmBase(baseUri).path("{realm}").build(realmName).toString();
+ }
+
+ @Override
+ public List getConfigMetadata() {
+ return ProviderConfigurationBuilder.create()
+ .property()
+ .name(CONFIG_ATTR_BASE_URI)
+ .type(ProviderConfigProperty.STRING_TYPE)
+ .helpText("The baseUri to use for the issuer of this server. Keep empty, if the regular hostname settings should be used.")
+ .add()
+ .build();
+ }
+}
+
+
diff --git a/ci/keycloak/initializer/src/main/resources/log4j.properties b/ci/keycloak/initializer/src/main/resources/log4j.properties
new file mode 100644
index 000000000..05fb2da9e
--- /dev/null
+++ b/ci/keycloak/initializer/src/main/resources/log4j.properties
@@ -0,0 +1,8 @@
+# Root Logger
+log4j.rootLogger=INFO, stdout
+
+# Direct log messages to stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
\ No newline at end of file
diff --git a/ci/keycloak/realm/BaSyx-realm.json b/ci/keycloak/realm/BaSyx-realm.json
new file mode 100644
index 000000000..36ee74513
--- /dev/null
+++ b/ci/keycloak/realm/BaSyx-realm.json
@@ -0,0 +1,2958 @@
+{
+ "id" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "realm" : "BaSyx",
+ "notBefore" : 0,
+ "defaultSignatureAlgorithm" : "RS256",
+ "revokeRefreshToken" : false,
+ "refreshTokenMaxReuse" : 0,
+ "accessTokenLifespan" : 300,
+ "accessTokenLifespanForImplicitFlow" : 900,
+ "ssoSessionIdleTimeout" : 1800,
+ "ssoSessionMaxLifespan" : 36000,
+ "ssoSessionIdleTimeoutRememberMe" : 0,
+ "ssoSessionMaxLifespanRememberMe" : 0,
+ "offlineSessionIdleTimeout" : 2592000,
+ "offlineSessionMaxLifespanEnabled" : false,
+ "offlineSessionMaxLifespan" : 5184000,
+ "clientSessionIdleTimeout" : 0,
+ "clientSessionMaxLifespan" : 0,
+ "clientOfflineSessionIdleTimeout" : 0,
+ "clientOfflineSessionMaxLifespan" : 0,
+ "accessCodeLifespan" : 60,
+ "accessCodeLifespanUserAction" : 300,
+ "accessCodeLifespanLogin" : 1800,
+ "actionTokenGeneratedByAdminLifespan" : 43200,
+ "actionTokenGeneratedByUserLifespan" : 300,
+ "oauth2DeviceCodeLifespan" : 600,
+ "oauth2DevicePollingInterval" : 5,
+ "enabled" : true,
+ "sslRequired" : "external",
+ "registrationAllowed" : false,
+ "registrationEmailAsUsername" : false,
+ "rememberMe" : false,
+ "verifyEmail" : false,
+ "loginWithEmailAllowed" : true,
+ "duplicateEmailsAllowed" : false,
+ "resetPasswordAllowed" : false,
+ "editUsernameAllowed" : false,
+ "bruteForceProtected" : false,
+ "permanentLockout" : false,
+ "maxFailureWaitSeconds" : 900,
+ "minimumQuickLoginWaitSeconds" : 60,
+ "waitIncrementSeconds" : 60,
+ "quickLoginCheckMilliSeconds" : 1000,
+ "maxDeltaTimeSeconds" : 43200,
+ "failureFactor" : 30,
+ "roles" : {
+ "realm" : [ {
+ "id" : "efe8c80d-bcd5-4a3c-91a0-a397a80d1d52",
+ "name" : "basyx-updater-two",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "9b70ce9b-1b39-4f5a-893d-9f8956cf5dad",
+ "name" : "basyx-reader-serialization-two",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "4bbc59ce-901e-49b9-adeb-0511469595df",
+ "name" : "basyx-aas-discoverer",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "52e7db01-dd27-4589-a530-ec8491bd2026",
+ "name" : "basyx-assetid-deleter",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "20c8f106-d2fb-422d-9045-22b28151f792",
+ "name" : "basyx-sme-reader-two",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "14dd6864-bcbd-46c3-b9b6-269ce036badc",
+ "name" : "basyx-uploader-three",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "502bc902-9de6-4552-98b0-55187b847272",
+ "name" : "user",
+ "description" : "",
+ "composite" : true,
+ "composites" : {
+ "client" : {
+ "basyx-client-api" : [ "basyx-user" ]
+ }
+ },
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "d3323aef-0e1f-4ec0-ba54-e0b3f9a897eb",
+ "name" : "basyx-assetid-discoverer",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "43a09e41-bcfb-429b-8675-eaf116ad4f1f",
+ "name" : "basyx-updater",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "4028f02d-3ee1-4c18-9b6a-a22c8bda51de",
+ "name" : "basyx-sme-updater",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "6ce3248b-7c14-42b4-9cbc-e1237851d778",
+ "name" : "basyx-creator",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "5702a4dd-4ccb-44b1-805d-fd9b1c333492",
+ "name" : "basyx-sme-updater-two",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "5ad9c765-2075-4cc4-b41e-c1b11cd544c4",
+ "name" : "uma_authorization",
+ "description" : "${role_uma_authorization}",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "797d2956-a895-4171-ab44-2fc9dbcf7f4c",
+ "name" : "default-roles-basyx",
+ "description" : "${role_default-roles}",
+ "composite" : true,
+ "composites" : {
+ "realm" : [ "offline_access", "uma_authorization" ],
+ "client" : {
+ "account" : [ "view-profile", "manage-account" ]
+ }
+ },
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "94394113-64a8-4cd1-9212-5a0cd955187b",
+ "name" : "basyx-asset-updater",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "f9df352e-269d-4a5d-a263-105d8ab3ae52",
+ "name" : "basyx-reader",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "469ec431-9a4f-4d87-80fe-cf2c7bbd5d37",
+ "name" : "basyx-reader-serialization",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "b70c22a9-e17e-4914-ae43-2752bafe356a",
+ "name" : "basyx-asset-updater-two",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "a537120f-8ccc-47a8-a1a7-9229d72561e5",
+ "name" : "READ",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "ebf827ce-862a-413b-afb3-5ad410ddf4ac",
+ "name" : "admin",
+ "description" : "",
+ "composite" : true,
+ "composites" : {
+ "client" : {
+ "basyx-client-api" : [ "basyx-admin" ]
+ }
+ },
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "32b591a9-55ed-4940-a7ad-efb3c40c3d38",
+ "name" : "basyx-executor-two",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "7b698a18-f272-4178-a6a2-d09e714c488e",
+ "name" : "basyx-uploader-two",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "09fa63ab-86ae-40bb-9497-56ee46070200",
+ "name" : "basyx-sme-reader",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "7065a5d2-3ab5-471a-be8c-cda64b6ce319",
+ "name" : "basyx-uploader",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "012af7ea-5eb7-4156-929a-acbae548e105",
+ "name" : "basyx-deleter",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "53212b19-655b-4e13-ad31-ec8c7d43d35d",
+ "name" : "basyx-deleter-two",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "2d2873a1-e636-46b2-bc89-5d8ca3fcde9e",
+ "name" : "basyx-executor",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "7a2111c1-7d1f-4b41-a0de-bfe314b73b72",
+ "name" : "offline_access",
+ "description" : "${role_offline-access}",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "05885c8f-e81f-47fa-bf47-c07153fc7b1b",
+ "name" : "basyx-file-sme-reader",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "5b5d0f1e-777f-4342-8128-b9eff69aed17",
+ "name" : "maintainer",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "afb72d5e-0841-452d-b3e0-5268dcba4c2a",
+ "name" : "visitor",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "b007c30e-c4bc-46ad-b72f-8ce67ec129fd",
+ "name" : "basyx-assetid-creator",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "50fb06f4-fe2d-46d8-b02c-5f5c409e4ce5",
+ "name" : "basyx-file-sme-updater",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "905eadf9-8b63-4503-9022-2f33daaa3372",
+ "name" : "basyx-reader-two",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ }, {
+ "id" : "a0dfe40a-8ec0-492c-a2c4-fa0ff9275918",
+ "name" : "basyx-sme-updater-three",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570",
+ "attributes" : { }
+ } ],
+ "client" : {
+ "realm-management" : [ {
+ "id" : "1752f599-6520-4588-9a85-75049a5f4ea7",
+ "name" : "query-clients",
+ "description" : "${role_query-clients}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "205e3c12-0af6-4d19-8eb4-d660d854ee43",
+ "attributes" : { }
+ }, {
+ "id" : "33fd6cb3-2c41-4d41-87c7-56dece25892c",
+ "name" : "create-client",
+ "description" : "${role_create-client}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "205e3c12-0af6-4d19-8eb4-d660d854ee43",
+ "attributes" : { }
+ }, {
+ "id" : "c083167e-2b27-4860-9117-07d01eaf9d28",
+ "name" : "view-clients",
+ "description" : "${role_view-clients}",
+ "composite" : true,
+ "composites" : {
+ "client" : {
+ "realm-management" : [ "query-clients" ]
+ }
+ },
+ "clientRole" : true,
+ "containerId" : "205e3c12-0af6-4d19-8eb4-d660d854ee43",
+ "attributes" : { }
+ }, {
+ "id" : "88458e07-82e7-4e8e-a262-4c2e271dfe9c",
+ "name" : "manage-identity-providers",
+ "description" : "${role_manage-identity-providers}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "205e3c12-0af6-4d19-8eb4-d660d854ee43",
+ "attributes" : { }
+ }, {
+ "id" : "940a767e-8370-4ef4-aec4-c616393b3ff5",
+ "name" : "manage-realm",
+ "description" : "${role_manage-realm}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "205e3c12-0af6-4d19-8eb4-d660d854ee43",
+ "attributes" : { }
+ }, {
+ "id" : "33c113a8-2aa6-4f0a-a0fe-80f6c74e691d",
+ "name" : "manage-authorization",
+ "description" : "${role_manage-authorization}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "205e3c12-0af6-4d19-8eb4-d660d854ee43",
+ "attributes" : { }
+ }, {
+ "id" : "8db1ccc7-4484-4d98-a32e-1125487bbfa7",
+ "name" : "view-realm",
+ "description" : "${role_view-realm}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "205e3c12-0af6-4d19-8eb4-d660d854ee43",
+ "attributes" : { }
+ }, {
+ "id" : "70a4bece-8b1e-4f3e-86b9-56f56851880f",
+ "name" : "query-users",
+ "description" : "${role_query-users}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "205e3c12-0af6-4d19-8eb4-d660d854ee43",
+ "attributes" : { }
+ }, {
+ "id" : "f4acf86a-1877-4c8d-ab97-d30941ab1952",
+ "name" : "impersonation",
+ "description" : "${role_impersonation}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "205e3c12-0af6-4d19-8eb4-d660d854ee43",
+ "attributes" : { }
+ }, {
+ "id" : "46693e69-db27-4e94-a4a4-8e4e14cc3cd4",
+ "name" : "view-authorization",
+ "description" : "${role_view-authorization}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "205e3c12-0af6-4d19-8eb4-d660d854ee43",
+ "attributes" : { }
+ }, {
+ "id" : "206be815-72c8-40ab-a4cd-7020d1f72942",
+ "name" : "manage-events",
+ "description" : "${role_manage-events}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "205e3c12-0af6-4d19-8eb4-d660d854ee43",
+ "attributes" : { }
+ }, {
+ "id" : "e1cf18c5-6635-4f1d-8efa-d86c609515d8",
+ "name" : "query-groups",
+ "description" : "${role_query-groups}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "205e3c12-0af6-4d19-8eb4-d660d854ee43",
+ "attributes" : { }
+ }, {
+ "id" : "3ec28e2e-89f8-46af-8655-fde414a9bd28",
+ "name" : "manage-users",
+ "description" : "${role_manage-users}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "205e3c12-0af6-4d19-8eb4-d660d854ee43",
+ "attributes" : { }
+ }, {
+ "id" : "44e2e3d4-1501-4490-adbf-5376e0d8ce17",
+ "name" : "query-realms",
+ "description" : "${role_query-realms}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "205e3c12-0af6-4d19-8eb4-d660d854ee43",
+ "attributes" : { }
+ }, {
+ "id" : "add14c5f-311c-42f3-9595-2fe7c36d6c2b",
+ "name" : "view-events",
+ "description" : "${role_view-events}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "205e3c12-0af6-4d19-8eb4-d660d854ee43",
+ "attributes" : { }
+ }, {
+ "id" : "7ba6fa14-a7a0-4072-9a7c-247c5df3e60f",
+ "name" : "realm-admin",
+ "description" : "${role_realm-admin}",
+ "composite" : true,
+ "composites" : {
+ "client" : {
+ "realm-management" : [ "query-clients", "create-client", "view-clients", "manage-identity-providers", "manage-realm", "manage-authorization", "view-realm", "query-users", "impersonation", "view-authorization", "manage-events", "query-groups", "manage-users", "query-realms", "view-events", "view-identity-providers", "manage-clients", "view-users" ]
+ }
+ },
+ "clientRole" : true,
+ "containerId" : "205e3c12-0af6-4d19-8eb4-d660d854ee43",
+ "attributes" : { }
+ }, {
+ "id" : "978756e4-01e3-4fee-b0aa-b291e7a4d8d8",
+ "name" : "view-identity-providers",
+ "description" : "${role_view-identity-providers}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "205e3c12-0af6-4d19-8eb4-d660d854ee43",
+ "attributes" : { }
+ }, {
+ "id" : "0ccfa96c-7e90-43b3-ba58-cb70b42456ca",
+ "name" : "manage-clients",
+ "description" : "${role_manage-clients}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "205e3c12-0af6-4d19-8eb4-d660d854ee43",
+ "attributes" : { }
+ }, {
+ "id" : "440c9f2a-5b40-4971-a9e6-044a118561ba",
+ "name" : "view-users",
+ "description" : "${role_view-users}",
+ "composite" : true,
+ "composites" : {
+ "client" : {
+ "realm-management" : [ "query-groups", "query-users" ]
+ }
+ },
+ "clientRole" : true,
+ "containerId" : "205e3c12-0af6-4d19-8eb4-d660d854ee43",
+ "attributes" : { }
+ } ],
+ "basyx-client-api" : [ {
+ "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" : "",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "3fb3e5e5-dbd8-4d51-b964-746c5b2181a4",
+ "attributes" : { }
+ }, {
+ "id" : "05ca5b90-4eda-4a58-a724-bfc61d1c4a05",
+ "name" : "basyx-user",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "3fb3e5e5-dbd8-4d51-b964-746c5b2181a4",
+ "attributes" : { }
+ } ],
+ "security-admin-console" : [ ],
+ "admin-cli" : [ ],
+ "account-console" : [ ],
+ "broker" : [ {
+ "id" : "30909060-d910-4a45-8bcc-059768731492",
+ "name" : "read-token",
+ "description" : "${role_read-token}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "45f21c3d-4e85-466f-984f-d7bd47392453",
+ "attributes" : { }
+ } ],
+ "account" : [ {
+ "id" : "5b81c2c9-2460-4b8a-abd8-a685292eb7ce",
+ "name" : "manage-account-links",
+ "description" : "${role_manage-account-links}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "049e1323-6efb-4543-bc52-566cd292732a",
+ "attributes" : { }
+ }, {
+ "id" : "74daac96-8775-4666-8e13-070049c6d8e7",
+ "name" : "view-profile",
+ "description" : "${role_view-profile}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "049e1323-6efb-4543-bc52-566cd292732a",
+ "attributes" : { }
+ }, {
+ "id" : "d9580b03-2736-46cc-97ab-d2f62301df1d",
+ "name" : "view-groups",
+ "description" : "${role_view-groups}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "049e1323-6efb-4543-bc52-566cd292732a",
+ "attributes" : { }
+ }, {
+ "id" : "bf835a6b-c6f6-47a2-9e2b-c082cbba801c",
+ "name" : "delete-account",
+ "description" : "${role_delete-account}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "049e1323-6efb-4543-bc52-566cd292732a",
+ "attributes" : { }
+ }, {
+ "id" : "d862864d-24af-4dac-b35d-b27e4c5bd081",
+ "name" : "manage-account",
+ "description" : "${role_manage-account}",
+ "composite" : true,
+ "composites" : {
+ "client" : {
+ "account" : [ "manage-account-links" ]
+ }
+ },
+ "clientRole" : true,
+ "containerId" : "049e1323-6efb-4543-bc52-566cd292732a",
+ "attributes" : { }
+ }, {
+ "id" : "7ccd9a2b-458b-4981-ad5f-543701dbace0",
+ "name" : "manage-consent",
+ "description" : "${role_manage-consent}",
+ "composite" : true,
+ "composites" : {
+ "client" : {
+ "account" : [ "view-consent" ]
+ }
+ },
+ "clientRole" : true,
+ "containerId" : "049e1323-6efb-4543-bc52-566cd292732a",
+ "attributes" : { }
+ }, {
+ "id" : "7e16ae1b-8db3-44ea-8bfd-879e6d8ac53c",
+ "name" : "view-consent",
+ "description" : "${role_view-consent}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "049e1323-6efb-4543-bc52-566cd292732a",
+ "attributes" : { }
+ }, {
+ "id" : "8c63a071-2ca5-4991-8a37-bccf7ef696b0",
+ "name" : "view-applications",
+ "description" : "${role_view-applications}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "049e1323-6efb-4543-bc52-566cd292732a",
+ "attributes" : { }
+ } ],
+ "basyx-demo" : [ ],
+ "workstation-1" : [ {
+ "id" : "914a18c6-4f14-418f-99e0-bfdcf604ac01",
+ "name" : "uma_protection",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "96031210-9e6c-4252-a22e-e81a47e30d65",
+ "attributes" : { }
+ } ]
+ }
+ },
+ "groups" : [ {
+ "id" : "606a14f2-6114-4fd3-9ca6-4a53514fffb9",
+ "name" : "BaSyxGroup",
+ "path" : "/BaSyxGroup",
+ "attributes" : { },
+ "realmRoles" : [ "basyx-deleter", "basyx-creator", "basyx-asset-updater" ],
+ "clientRoles" : { },
+ "subGroups" : [ ]
+ } ],
+ "defaultRole" : {
+ "id" : "797d2956-a895-4171-ab44-2fc9dbcf7f4c",
+ "name" : "default-roles-basyx",
+ "description" : "${role_default-roles}",
+ "composite" : true,
+ "clientRole" : false,
+ "containerId" : "bcb69552-bf11-4249-a3eb-d0c3ab54a570"
+ },
+ "requiredCredentials" : [ "password" ],
+ "otpPolicyType" : "totp",
+ "otpPolicyAlgorithm" : "HmacSHA1",
+ "otpPolicyInitialCounter" : 0,
+ "otpPolicyDigits" : 6,
+ "otpPolicyLookAheadWindow" : 1,
+ "otpPolicyPeriod" : 30,
+ "otpPolicyCodeReusable" : false,
+ "otpSupportedApplications" : [ "totpAppMicrosoftAuthenticatorName", "totpAppGoogleName", "totpAppFreeOTPName" ],
+ "webAuthnPolicyRpEntityName" : "keycloak",
+ "webAuthnPolicySignatureAlgorithms" : [ "ES256" ],
+ "webAuthnPolicyRpId" : "",
+ "webAuthnPolicyAttestationConveyancePreference" : "not specified",
+ "webAuthnPolicyAuthenticatorAttachment" : "not specified",
+ "webAuthnPolicyRequireResidentKey" : "not specified",
+ "webAuthnPolicyUserVerificationRequirement" : "not specified",
+ "webAuthnPolicyCreateTimeout" : 0,
+ "webAuthnPolicyAvoidSameAuthenticatorRegister" : false,
+ "webAuthnPolicyAcceptableAaguids" : [ ],
+ "webAuthnPolicyPasswordlessRpEntityName" : "keycloak",
+ "webAuthnPolicyPasswordlessSignatureAlgorithms" : [ "ES256" ],
+ "webAuthnPolicyPasswordlessRpId" : "",
+ "webAuthnPolicyPasswordlessAttestationConveyancePreference" : "not specified",
+ "webAuthnPolicyPasswordlessAuthenticatorAttachment" : "not specified",
+ "webAuthnPolicyPasswordlessRequireResidentKey" : "not specified",
+ "webAuthnPolicyPasswordlessUserVerificationRequirement" : "not specified",
+ "webAuthnPolicyPasswordlessCreateTimeout" : 0,
+ "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister" : false,
+ "webAuthnPolicyPasswordlessAcceptableAaguids" : [ ],
+ "users" : [ {
+ "id" : "856b093b-ef9f-4bd0-92ca-662f680c73cc",
+ "createdTimestamp" : 1713968958846,
+ "username" : "basyx.aas.discoverer",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "bc4944be-c8e2-4c91-81cc-9478a0795906",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1713968969072,
+ "secretData" : "{\"value\":\"V1xFTsjw4G4nJY+ftBt4CavQs1d8zf0ybVtUhxQR0yg=\",\"salt\":\"XjYZbMDK9iyDdsZe2WXb6A==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "basyx-aas-discoverer", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "aef2b331-f694-4503-b0da-1412c77842ba",
+ "createdTimestamp" : 1702179260496,
+ "username" : "basyx.asset.updater",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "24db17af-1244-4cea-8af8-1047adcd0753",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1702179278395,
+ "secretData" : "{\"value\":\"YRTadONVBjE5bQjGmz2mtnr3X4IlU+4xxbmq2aBRuIo=\",\"salt\":\"wfqiItiwdM2FBYJPENkQ2A==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "default-roles-basyx", "basyx-asset-updater" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "4fc75aa9-4745-4bec-846e-de5dbd665b7c",
+ "createdTimestamp" : 1702179349503,
+ "username" : "basyx.asset.updater.2",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "db64f531-b8ab-4e33-b7b9-e3ba0e3677d4",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1702179363013,
+ "secretData" : "{\"value\":\"6HRJw+wSlbSrf8wcAGjnn5/PnD8ARlZOnJK7p87+VOc=\",\"salt\":\"VDu0YOIlhG3WHofMj2hSeg==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "basyx-asset-updater-two", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "bcbd95b2-2ec4-42a6-9b1d-39dabf0454c3",
+ "createdTimestamp" : 1713969008326,
+ "username" : "basyx.assetid.creator",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "81b26fa2-4eda-45d7-a8be-4b6d64b3d813",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1713971859329,
+ "secretData" : "{\"value\":\"yNEw5ey4rT1a0dONFUpVe7YaNfIrobNkCdL6DZjBj1A=\",\"salt\":\"JLMeQM7gjHenb5JhfzhWQA==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "basyx-assetid-creator", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "2def798c-7547-42fe-8915-be493d740005",
+ "createdTimestamp" : 1713969038565,
+ "username" : "basyx.assetid.deleter",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "fa285b2c-9a2d-4400-a524-508b0a16f4c3",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1713969048116,
+ "secretData" : "{\"value\":\"28gYVMfDZofypgu67nmV5Kv2KrPGEFshT5tYvj+LPf8=\",\"salt\":\"ZZI9X8eR7pl7YqmgC06FYw==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "basyx-assetid-deleter", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "3527f121-116c-429b-bf0d-78bf4a8b5abe",
+ "createdTimestamp" : 1713968905507,
+ "username" : "basyx.assetid.discoverer",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "4f3427f8-9da2-4372-a54b-c1e54dc8da68",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1713968917922,
+ "secretData" : "{\"value\":\"uUOChDW0wpWybeiWSqsyqKSfLN0CWANylCjzNavInB4=\",\"salt\":\"TsyGowE4zc8gsp4wRREOAQ==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "basyx-assetid-discoverer", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "f48119f5-7dff-46af-b9db-3ee96cd52550",
+ "createdTimestamp" : 1702032555719,
+ "username" : "basyx.creator",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "BaSyx",
+ "lastName" : "creator",
+ "credentials" : [ {
+ "id" : "393aa50c-70ed-4676-b5d0-b4c6cf930272",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1702032714401,
+ "secretData" : "{\"value\":\"/TOCJJp9SrRtZnB+QxjdzJCKONo5IDy/C/H5GyhsRm8=\",\"salt\":\"2hB/91M1PTQmtp9w8ULRMQ==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "basyx-creator", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "40858874-f6e7-48c8-9667-d585d7c27b57",
+ "createdTimestamp" : 1702032602188,
+ "username" : "basyx.deleter",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "BaSyx",
+ "lastName" : "Deleter",
+ "credentials" : [ {
+ "id" : "ee9224dd-6277-4c3d-8d88-b595d66f25c7",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1702032741081,
+ "secretData" : "{\"value\":\"eQ18xAGwRazaHAdd6F+WnhIDKlFn9uTUvgrYOY8sfUg=\",\"salt\":\"0CwKlmVofsusehhhfJZExA==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "basyx-deleter", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "fcf7ea95-875e-42ef-afe1-f9b6008cbaf9",
+ "createdTimestamp" : 1702161686852,
+ "username" : "basyx.deleter.2",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "1b8d0b8c-1f65-43e0-9178-785533746684",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1702161714301,
+ "secretData" : "{\"value\":\"/iZkSX4POJyi8GuL3+fMGlPVcLkssWMTA5KWt48IeXc=\",\"salt\":\"/5sjiGBNXC4xzAHjWFJr+g==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "basyx-deleter-two", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "8e820205-13fa-4d61-9513-99f717c15f73",
+ "createdTimestamp" : 1705326903328,
+ "username" : "basyx.executor",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "3e891de4-74e6-46cb-840f-6681af0ce397",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1705326918687,
+ "secretData" : "{\"value\":\"ytSpEUoTvStjF057boJpFgx59agjUC9pJiM/mdrtc7Y=\",\"salt\":\"U30NyBIzFGYhoqC+DCuMZQ==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "basyx-executor", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "3d645000-277e-46f5-b421-85f1fdb064b5",
+ "createdTimestamp" : 1705326932748,
+ "username" : "basyx.executor.2",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "d21c920f-5dd0-4ce7-ad2e-737a841bb1f8",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1705326945517,
+ "secretData" : "{\"value\":\"uAi1tN7YY/byDS0vay77q3nlJsb9gkC5lGiB8SROcIw=\",\"salt\":\"/zhdEnnDDFvuWMT9ZzLMSA==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "basyx-executor-two", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "c86047c6-3ab8-4f47-86ba-b26ea80d1986",
+ "createdTimestamp" : 1705398014689,
+ "username" : "basyx.file.sme.reader",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "bd374dc3-4fec-4232-be91-a3f5608e373e",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1705398026388,
+ "secretData" : "{\"value\":\"3p7BdFuY8JBRDaoD0Lv2i3N2XCvuM7mt5tUhdyJ+sRA=\",\"salt\":\"3+GFcXL0EAlfjmP3CwtpLg==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "basyx-file-sme-reader", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "da7acf36-1574-4c0a-aa5b-65829a512b60",
+ "createdTimestamp" : 1705398962710,
+ "username" : "basyx.file.sme.updater",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "e5fda50d-9f52-4f60-a917-31a85fc275a7",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1705398974508,
+ "secretData" : "{\"value\":\"sQsBVFdIJj7whmjzlFHkrh+ZCtrj8oXaOuK4V1q+95I=\",\"salt\":\"G1xU/pxUi5Moj3V7dRhfOg==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "basyx-file-sme-updater", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "abfaa545-3c2f-4ac6-9f41-7c166613ea35",
+ "createdTimestamp" : 1702032528855,
+ "username" : "basyx.reader",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "BaSyx",
+ "lastName" : "Reader",
+ "credentials" : [ {
+ "id" : "eea587f5-439d-487d-85d0-587b226f0683",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1702032760779,
+ "secretData" : "{\"value\":\"fNd3UFdT3clPTDFTMgdFzpNN6R34wu0R23S2vV6fOgI=\",\"salt\":\"eMifD6Sp0urGRzhEhSDc4w==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "basyx-reader", "default-roles-basyx" ],
+ "clientRoles" : {
+ "basyx-client-api" : [ "basyx-user" ]
+ },
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "37e1ba01-3b9c-428c-b0fe-be85970bf1d9",
+ "createdTimestamp" : 1702158534562,
+ "username" : "basyx.reader.2",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "3237f89d-f9f0-4b70-9a08-d31d995c6948",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1702158617052,
+ "secretData" : "{\"value\":\"5aTlmhGaKIHjdeAWoC6+ei2WGOBE62okIiVm6h0/Ur4=\",\"salt\":\"qkVSxkETPCSxTqAERBjcDw==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "basyx-reader-two", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "2d7a7bdb-c8d3-4684-8c06-8873aabd9968",
+ "createdTimestamp" : 1707983800892,
+ "username" : "basyx.reader.serialization",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "c35c2724-8ae9-4193-bd25-e5245df77f4d",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1707983812752,
+ "secretData" : "{\"value\":\"2PxseHKNw94KIO+vkxn+jLwrfRWFF3Eh6cTOviSyKTI=\",\"salt\":\"E8GDPR/T+enGFszv/XvlQA==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "basyx-reader-serialization", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "113691d0-fc03-4607-8b64-97b646bded1f",
+ "createdTimestamp" : 1707983824403,
+ "username" : "basyx.reader.serialization.2",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "86bd4fdc-077c-4546-b80a-f51788f15b56",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1707983834216,
+ "secretData" : "{\"value\":\"LMmwk+0uIx2VOOD/Llx19E6oUW6Z+fye0dD6fft8JlI=\",\"salt\":\"mWFD3gAs2jncjP6tBu9C3g==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "basyx-reader-serialization-two", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "3988486a-51eb-447b-bc87-354e5b724c76",
+ "createdTimestamp" : 1705306854299,
+ "username" : "basyx.sme.reader",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "d70787d1-9834-43d6-a10d-bca854847fec",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1705306868293,
+ "secretData" : "{\"value\":\"DaO6udzDHUKnoqpl/7bdprLMKfwqV7MjKme1/NnEQtA=\",\"salt\":\"SzPSSVEQd1ZZoOGLuWGInA==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "default-roles-basyx", "basyx-sme-reader" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "ed921577-8b07-439e-af66-cc3579a276ec",
+ "createdTimestamp" : 1705310445991,
+ "username" : "basyx.sme.reader.2",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "62446675-7b7e-40e7-bf6c-b375d8542a12",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1705310456309,
+ "secretData" : "{\"value\":\"QQkWFYjV952eOqGdkcDqqF4OdgtMCmUSKxzPF5I1LrU=\",\"salt\":\"ZUxajrFJ/cFxxH28cz8ibQ==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "basyx-sme-reader-two", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "60f02977-d033-43ed-8171-8ac44d14c62f",
+ "createdTimestamp" : 1705312271325,
+ "username" : "basyx.sme.updater",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "d224c0bc-7c5d-426d-8e96-9f4dfb141eab",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1705312282450,
+ "secretData" : "{\"value\":\"q+x0JQZAGRyDLlVAWPzLmVCJ/PPpb07xW+bAv1QFw9s=\",\"salt\":\"QC5VcH0pvVpnVPC/EnTAuA==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "basyx-sme-updater", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "9fd79b98-a230-43c8-a7ff-f88ee381c58c",
+ "createdTimestamp" : 1705315980352,
+ "username" : "basyx.sme.updater.2",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "eca8536c-fbf7-49dd-a370-405b00336965",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1705315990230,
+ "secretData" : "{\"value\":\"KpVPW6pBjilR3xUTV5X/Y8g2javOsmkY7/9TLwix8Lk=\",\"salt\":\"xmj+BOrdA7DQks9wwxV42w==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "basyx-sme-updater-two", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "20336dcf-0c1f-4344-b31b-f26048fc7faa",
+ "createdTimestamp" : 1705324670982,
+ "username" : "basyx.sme.updater.3",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "d1652462-350a-42e0-9a3e-e04cfa258237",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1705324695639,
+ "secretData" : "{\"value\":\"/qEHkHQIlm0poWXk13LWf3TpXq/ffOzflkIbretgeBw=\",\"salt\":\"arEuG45sRa+2aOZiY9PqVg==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "basyx-sme-updater-three", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "44f291ad-a0de-4035-9938-a092faf810b5",
+ "createdTimestamp" : 1702032579778,
+ "username" : "basyx.updater",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "BaSyx",
+ "lastName" : "Updater",
+ "credentials" : [ {
+ "id" : "28e62a3a-790d-477f-b139-b0ea943abfd7",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1702032778389,
+ "secretData" : "{\"value\":\"ZfuHUQ78g18C9k/zjHn/QBKsD8e9+xhxomRaBI00oSQ=\",\"salt\":\"IIhszlFoc5V3AKG0sDowkg==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "basyx-updater", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "9bf6b5c8-194f-4672-9313-9f3823cb3019",
+ "createdTimestamp" : 1702161564532,
+ "username" : "basyx.updater.2",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "59ef29d6-6dcf-42e6-b946-33d50335e0d1",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1702161581051,
+ "secretData" : "{\"value\":\"IWJZlX5XJT4IoSH1npG+APct5iL931bC54+RtjRgKAw=\",\"salt\":\"YZW26Yurco6D2uhzjVcQjA==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "basyx-updater-two", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "cb7df854-827d-4d34-a01f-33cdf07f5cea",
+ "createdTimestamp" : 1708702290219,
+ "username" : "basyx.uploader",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "7df6170e-1631-434d-8380-62f750c563cf",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1708702304670,
+ "secretData" : "{\"value\":\"M7YGdklaJzphwjjWGLfb990lR4NY4rbLTQ1LAPptEuc=\",\"salt\":\"MEYVvEIkg54+jjXQS47dbA==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "default-roles-basyx", "basyx-uploader" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "3c94e1e0-caac-48c3-a31c-c9f555233a46",
+ "createdTimestamp" : 1708932762171,
+ "username" : "basyx.uploader.2",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "0d4049cb-293b-4f76-b82b-5ba9ee4530f2",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1708932779332,
+ "secretData" : "{\"value\":\"jNTZeVbTOOtuokGeXGhYd5Aa+G9TkCS1RFikWtULN/w=\",\"salt\":\"9St8VUxP3iiO2jZYM7whww==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "default-roles-basyx", "basyx-uploader-two" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "c1a592bd-536f-4a3e-8193-c17d479814f3",
+ "createdTimestamp" : 1708934121191,
+ "username" : "basyx.uploader.3",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "7cd8fd3f-8c83-43b9-9fb7-6203e7c7376c",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1708934138452,
+ "secretData" : "{\"value\":\"BenBZDPAZWMjSh21uER8mw7PSEdXw8xRh7YPlWsNUno=\",\"salt\":\"ioGE5CqXT8XyCWkcbi9ETA==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "basyx-uploader-three", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "f3ec1793-3d62-41c3-ad34-b7b29ac88528",
+ "createdTimestamp" : 1702030619322,
+ "username" : "bob.maintainer",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "Bob",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "a45e5184-e7f3-41bc-be25-c97197852301",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1702030635868,
+ "secretData" : "{\"value\":\"qMAW5hUeZYBsbUjNqSHcRFIn05OKnS/whcapkCvLL3c=\",\"salt\":\"BsbKzzXVRR6sazSpRimCeA==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "maintainer", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "e75ac9e8-7093-4898-a203-d9839f854944",
+ "createdTimestamp" : 1702030567684,
+ "username" : "jane.doe",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "Jane",
+ "lastName" : "Doe",
+ "credentials" : [ {
+ "id" : "d1e97a9f-42b6-43d0-a070-1216d03a64b7",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1702030585530,
+ "secretData" : "{\"value\":\"S/d8o0wllcaXTOdtd/DGSIY9K6irGF0eMn9QJxZ+FSk=\",\"salt\":\"jIVz5vxpb6RpVHxkqi7jSw==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "user", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "caf7499e-4f3d-45fa-9246-99ea8f8b5c94",
+ "createdTimestamp" : 1701764678734,
+ "username" : "john",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "",
+ "lastName" : "",
+ "credentials" : [ {
+ "id" : "136b209f-3b75-45d9-b448-e1ec93dc7ea4",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1701764705215,
+ "secretData" : "{\"value\":\"wl3q7M/vsTL2T2vVXnZQG8eRzktKT5WRqDP+d1sW2tE=\",\"salt\":\"Yx270JeMPiFH36ycimpziA==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "READ", "admin", "user", "default-roles-basyx" ],
+ "clientRoles" : {
+ "basyx-client-api" : [ "basyx-admin" ]
+ },
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "fb833c1b-3a7d-4224-9ab1-672e7203bab5",
+ "createdTimestamp" : 1702030523698,
+ "username" : "john.doe",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "John",
+ "lastName" : "Doe",
+ "credentials" : [ {
+ "id" : "b8b3d5cf-4fa3-46ed-8a3e-18875acecff0",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1702030540643,
+ "secretData" : "{\"value\":\"PAA5qfnQC9ImTZslJXw6GSW6k7rhYHl7XCdlk+9yb5A=\",\"salt\":\"jtFqUjRq7RtxXNYrKSJgFA==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "admin", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "9045c192-428a-4fff-bf7a-b9a9ac16742f",
+ "createdTimestamp" : 1702030666980,
+ "username" : "paul.visitor",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "firstName" : "Paul",
+ "lastName" : "Visitor",
+ "credentials" : [ {
+ "id" : "81d4c206-c0a0-4ee6-9fcb-19f755e66150",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1702030682803,
+ "secretData" : "{\"value\":\"MedIFuT+Rl3PrjV7Nn/DJxJRiQD0Ucl6Ms2fc3wXbtU=\",\"salt\":\"12rxIShfyTRisjU8t7Smhw==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "visitor", "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "a19abcac-34d5-46bb-a604-b07dc234e80f",
+ "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", "basyx-creator", "default-roles-basyx" ],
+ "clientRoles" : {
+ "workstation-1" : [ "uma_protection" ]
+ },
+ "notBefore" : 0,
+ "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",
+ "credentials" : [ {
+ "id" : "11672ccf-38b7-421b-8d01-755e0f2197da",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1715539657887,
+ "secretData" : "{\"value\":\"7YbCeFyuYDpwu/06UbMT7OObo29RU7cG1XWrqcNwZLg=\",\"salt\":\"nAQ9KKDYyg5GGuSUyfG+kw==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ "/BaSyxGroup" ]
+ } ],
+ "scopeMappings" : [ {
+ "clientScope" : "offline_access",
+ "roles" : [ "offline_access" ]
+ } ],
+ "clientScopeMappings" : {
+ "account" : [ {
+ "client" : "account-console",
+ "roles" : [ "manage-account", "view-groups" ]
+ } ]
+ },
+ "clients" : [ {
+ "id" : "049e1323-6efb-4543-bc52-566cd292732a",
+ "clientId" : "account",
+ "name" : "${client_account}",
+ "rootUrl" : "${authBaseUrl}",
+ "baseUrl" : "/realms/BaSyx/account/",
+ "surrogateAuthRequired" : false,
+ "enabled" : true,
+ "alwaysDisplayInConsole" : false,
+ "clientAuthenticatorType" : "client-secret",
+ "redirectUris" : [ "/realms/BaSyx/account/*" ],
+ "webOrigins" : [ ],
+ "notBefore" : 0,
+ "bearerOnly" : false,
+ "consentRequired" : false,
+ "standardFlowEnabled" : true,
+ "implicitFlowEnabled" : false,
+ "directAccessGrantsEnabled" : false,
+ "serviceAccountsEnabled" : false,
+ "publicClient" : true,
+ "frontchannelLogout" : false,
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "post.logout.redirect.uris" : "+"
+ },
+ "authenticationFlowBindingOverrides" : { },
+ "fullScopeAllowed" : false,
+ "nodeReRegistrationTimeout" : 0,
+ "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ],
+ "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
+ }, {
+ "id" : "7d00b9a7-d212-4132-91f0-06e0719c7b43",
+ "clientId" : "account-console",
+ "name" : "${client_account-console}",
+ "rootUrl" : "${authBaseUrl}",
+ "baseUrl" : "/realms/BaSyx/account/",
+ "surrogateAuthRequired" : false,
+ "enabled" : true,
+ "alwaysDisplayInConsole" : false,
+ "clientAuthenticatorType" : "client-secret",
+ "redirectUris" : [ "/realms/BaSyx/account/*" ],
+ "webOrigins" : [ ],
+ "notBefore" : 0,
+ "bearerOnly" : false,
+ "consentRequired" : false,
+ "standardFlowEnabled" : true,
+ "implicitFlowEnabled" : false,
+ "directAccessGrantsEnabled" : false,
+ "serviceAccountsEnabled" : false,
+ "publicClient" : true,
+ "frontchannelLogout" : false,
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "post.logout.redirect.uris" : "+",
+ "pkce.code.challenge.method" : "S256"
+ },
+ "authenticationFlowBindingOverrides" : { },
+ "fullScopeAllowed" : false,
+ "nodeReRegistrationTimeout" : 0,
+ "protocolMappers" : [ {
+ "id" : "f5343ae7-2d59-45ff-8c56-7fcc152d90f5",
+ "name" : "audience resolve",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-audience-resolve-mapper",
+ "consentRequired" : false,
+ "config" : { }
+ } ],
+ "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ],
+ "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
+ }, {
+ "id" : "00ebf255-9c7c-444d-92b5-ff146d9147b9",
+ "clientId" : "admin-cli",
+ "name" : "${client_admin-cli}",
+ "surrogateAuthRequired" : false,
+ "enabled" : true,
+ "alwaysDisplayInConsole" : false,
+ "clientAuthenticatorType" : "client-secret",
+ "redirectUris" : [ ],
+ "webOrigins" : [ ],
+ "notBefore" : 0,
+ "bearerOnly" : false,
+ "consentRequired" : false,
+ "standardFlowEnabled" : false,
+ "implicitFlowEnabled" : false,
+ "directAccessGrantsEnabled" : true,
+ "serviceAccountsEnabled" : false,
+ "publicClient" : true,
+ "frontchannelLogout" : false,
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "post.logout.redirect.uris" : "+"
+ },
+ "authenticationFlowBindingOverrides" : { },
+ "fullScopeAllowed" : false,
+ "nodeReRegistrationTimeout" : 0,
+ "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ],
+ "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
+ }, {
+ "id" : "3fb3e5e5-dbd8-4d51-b964-746c5b2181a4",
+ "clientId" : "basyx-client-api",
+ "name" : "BaSyx Client Api",
+ "description" : "",
+ "rootUrl" : "http://localhost:8081",
+ "adminUrl" : "http://localhost:8081",
+ "baseUrl" : "http://localhost:8081",
+ "surrogateAuthRequired" : false,
+ "enabled" : true,
+ "alwaysDisplayInConsole" : false,
+ "clientAuthenticatorType" : "client-secret",
+ "redirectUris" : [ "http://localhost:8081/*" ],
+ "webOrigins" : [ "http://localhost:8081" ],
+ "notBefore" : 0,
+ "bearerOnly" : false,
+ "consentRequired" : false,
+ "standardFlowEnabled" : true,
+ "implicitFlowEnabled" : false,
+ "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:8081",
+ "oauth2.device.authorization.grant.enabled" : "false",
+ "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" : "615f29de-5f3d-4384-8b71-7351ff2c2a32",
+ "clientId" : "basyx-demo",
+ "name" : "",
+ "description" : "",
+ "rootUrl" : "",
+ "adminUrl" : "",
+ "baseUrl" : "",
+ "surrogateAuthRequired" : false,
+ "enabled" : true,
+ "alwaysDisplayInConsole" : false,
+ "clientAuthenticatorType" : "client-secret",
+ "secret" : "41f9RznNmtHaWKT7i8IDYTjl2VaLHw0q",
+ "redirectUris" : [ "/*" ],
+ "webOrigins" : [ "/*" ],
+ "notBefore" : 0,
+ "bearerOnly" : false,
+ "consentRequired" : false,
+ "standardFlowEnabled" : true,
+ "implicitFlowEnabled" : false,
+ "directAccessGrantsEnabled" : true,
+ "serviceAccountsEnabled" : false,
+ "publicClient" : false,
+ "frontchannelLogout" : true,
+ "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",
+ "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}",
+ "surrogateAuthRequired" : false,
+ "enabled" : true,
+ "alwaysDisplayInConsole" : false,
+ "clientAuthenticatorType" : "client-secret",
+ "redirectUris" : [ ],
+ "webOrigins" : [ ],
+ "notBefore" : 0,
+ "bearerOnly" : true,
+ "consentRequired" : false,
+ "standardFlowEnabled" : true,
+ "implicitFlowEnabled" : false,
+ "directAccessGrantsEnabled" : false,
+ "serviceAccountsEnabled" : false,
+ "publicClient" : false,
+ "frontchannelLogout" : false,
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "post.logout.redirect.uris" : "+"
+ },
+ "authenticationFlowBindingOverrides" : { },
+ "fullScopeAllowed" : false,
+ "nodeReRegistrationTimeout" : 0,
+ "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ],
+ "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
+ }, {
+ "id" : "205e3c12-0af6-4d19-8eb4-d660d854ee43",
+ "clientId" : "realm-management",
+ "name" : "${client_realm-management}",
+ "surrogateAuthRequired" : false,
+ "enabled" : true,
+ "alwaysDisplayInConsole" : false,
+ "clientAuthenticatorType" : "client-secret",
+ "redirectUris" : [ ],
+ "webOrigins" : [ ],
+ "notBefore" : 0,
+ "bearerOnly" : true,
+ "consentRequired" : false,
+ "standardFlowEnabled" : true,
+ "implicitFlowEnabled" : false,
+ "directAccessGrantsEnabled" : false,
+ "serviceAccountsEnabled" : false,
+ "publicClient" : false,
+ "frontchannelLogout" : false,
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "post.logout.redirect.uris" : "+"
+ },
+ "authenticationFlowBindingOverrides" : { },
+ "fullScopeAllowed" : false,
+ "nodeReRegistrationTimeout" : 0,
+ "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ],
+ "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
+ }, {
+ "id" : "2f43139f-46dc-442a-84b6-1eef146c0b16",
+ "clientId" : "security-admin-console",
+ "name" : "${client_security-admin-console}",
+ "rootUrl" : "${authAdminUrl}",
+ "baseUrl" : "/admin/BaSyx/console/",
+ "surrogateAuthRequired" : false,
+ "enabled" : true,
+ "alwaysDisplayInConsole" : false,
+ "clientAuthenticatorType" : "client-secret",
+ "redirectUris" : [ "/admin/BaSyx/console/*" ],
+ "webOrigins" : [ "+" ],
+ "notBefore" : 0,
+ "bearerOnly" : false,
+ "consentRequired" : false,
+ "standardFlowEnabled" : true,
+ "implicitFlowEnabled" : false,
+ "directAccessGrantsEnabled" : false,
+ "serviceAccountsEnabled" : false,
+ "publicClient" : true,
+ "frontchannelLogout" : false,
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "post.logout.redirect.uris" : "+",
+ "pkce.code.challenge.method" : "S256"
+ },
+ "authenticationFlowBindingOverrides" : { },
+ "fullScopeAllowed" : false,
+ "nodeReRegistrationTimeout" : 0,
+ "protocolMappers" : [ {
+ "id" : "70e02310-8910-4ad2-b02e-3091643fca8c",
+ "name" : "locale",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "locale",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "locale",
+ "jsonType.label" : "String"
+ }
+ } ],
+ "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",
+ "description" : "",
+ "rootUrl" : "",
+ "adminUrl" : "",
+ "baseUrl" : "",
+ "surrogateAuthRequired" : false,
+ "enabled" : true,
+ "alwaysDisplayInConsole" : true,
+ "clientAuthenticatorType" : "client-secret",
+ "secret" : "nY0mjyECF60DGzNmQUjL81XurSl8etom",
+ "redirectUris" : [ "/*" ],
+ "webOrigins" : [ "/*" ],
+ "notBefore" : 0,
+ "bearerOnly" : false,
+ "consentRequired" : false,
+ "standardFlowEnabled" : true,
+ "implicitFlowEnabled" : false,
+ "directAccessGrantsEnabled" : true,
+ "serviceAccountsEnabled" : true,
+ "authorizationServicesEnabled" : true,
+ "publicClient" : false,
+ "frontchannelLogout" : true,
+ "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",
+ "backchannel.logout.revoke.offline.tokens" : "false"
+ },
+ "authenticationFlowBindingOverrides" : { },
+ "fullScopeAllowed" : true,
+ "nodeReRegistrationTimeout" : -1,
+ "protocolMappers" : [ {
+ "id" : "1f332577-69b1-48cf-b2d1-95ccd6159fdf",
+ "name" : "Client IP Address",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usersessionmodel-note-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "user.session.note" : "clientAddress",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "clientAddress",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "05688508-b82b-46c5-85de-f0bacf03d6e7",
+ "name" : "Client Host",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usersessionmodel-note-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "user.session.note" : "clientHost",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "clientHost",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "a1c32e51-4369-4460-a015-98867bb101fd",
+ "name" : "Client ID",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usersessionmodel-note-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "user.session.note" : "client_id",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "client_id",
+ "jsonType.label" : "String"
+ }
+ } ],
+ "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ],
+ "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
+ } ],
+ "clientScopes" : [ {
+ "id" : "e0f355da-f9ff-4104-b305-043b0188747b",
+ "name" : "offline_access",
+ "description" : "OpenID Connect built-in scope: offline_access",
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "consent.screen.text" : "${offlineAccessScopeConsentText}",
+ "display.on.consent.screen" : "true"
+ }
+ }, {
+ "id" : "a194eeae-0c0b-4300-b613-9c7b5a281ba0",
+ "name" : "web-origins",
+ "description" : "OpenID Connect scope for add allowed web origins to the access token",
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "include.in.token.scope" : "false",
+ "display.on.consent.screen" : "false",
+ "consent.screen.text" : ""
+ },
+ "protocolMappers" : [ {
+ "id" : "c3c7617c-2d46-4cf7-893c-776b7a14797a",
+ "name" : "allowed web origins",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-allowed-origins-mapper",
+ "consentRequired" : false,
+ "config" : { }
+ } ]
+ }, {
+ "id" : "69675a1a-f2a5-4316-915b-e1a0cc02e0fe",
+ "name" : "role_list",
+ "description" : "SAML role list",
+ "protocol" : "saml",
+ "attributes" : {
+ "consent.screen.text" : "${samlRoleListScopeConsentText}",
+ "display.on.consent.screen" : "true"
+ },
+ "protocolMappers" : [ {
+ "id" : "e9883191-5f1c-4eed-90da-51806ba954cc",
+ "name" : "role list",
+ "protocol" : "saml",
+ "protocolMapper" : "saml-role-list-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "single" : "false",
+ "attribute.nameformat" : "Basic",
+ "attribute.name" : "Role"
+ }
+ } ]
+ }, {
+ "id" : "95a35d30-fb91-4a8c-a208-5c7b1644b7fa",
+ "name" : "acr",
+ "description" : "OpenID Connect scope for add acr (authentication context class reference) to the token",
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "include.in.token.scope" : "false",
+ "display.on.consent.screen" : "false"
+ },
+ "protocolMappers" : [ {
+ "id" : "51873e8d-85db-4c1f-be02-bef88d435e89",
+ "name" : "acr loa level",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-acr-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "userinfo.token.claim" : "true"
+ }
+ } ]
+ }, {
+ "id" : "d937e76f-71f3-4260-bbc6-1feffc3a655e",
+ "name" : "roles",
+ "description" : "OpenID Connect scope for add user roles to the access token",
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "include.in.token.scope" : "true",
+ "display.on.consent.screen" : "true",
+ "gui.order" : "",
+ "consent.screen.text" : "${rolesScopeConsentText}"
+ },
+ "protocolMappers" : [ {
+ "id" : "7dfd8525-9c12-49a1-a6ec-4d4d1bb74471",
+ "name" : "realm roles",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-realm-role-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "multivalued" : "true",
+ "userinfo.token.claim" : "false",
+ "user.attribute" : "foo",
+ "id.token.claim" : "false",
+ "access.token.claim" : "true",
+ "claim.name" : "realm_access.roles",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "2fe9cc2c-3f61-446e-9cf4-f34fe1964a1d",
+ "name" : "client roles",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-client-role-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "multivalued" : "true",
+ "userinfo.token.claim" : "false",
+ "user.attribute" : "foo",
+ "id.token.claim" : "false",
+ "access.token.claim" : "true",
+ "claim.name" : "resource_access.${client_id}.roles",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "4071dcc6-b7d3-42b1-93c7-e14d0a17d103",
+ "name" : "audience resolve",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-audience-resolve-mapper",
+ "consentRequired" : false,
+ "config" : { }
+ } ]
+ }, {
+ "id" : "c22bdca5-b67a-4249-a8c7-9bbe8fc16559",
+ "name" : "phone",
+ "description" : "OpenID Connect built-in scope: phone",
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "include.in.token.scope" : "true",
+ "display.on.consent.screen" : "true",
+ "consent.screen.text" : "${phoneScopeConsentText}"
+ },
+ "protocolMappers" : [ {
+ "id" : "d62a405a-d99e-4d6a-bed4-48c052abc559",
+ "name" : "phone number",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "phoneNumber",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "phone_number",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "531eab62-3750-4ed9-b101-9f1c2e709c51",
+ "name" : "phone number verified",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "phoneNumberVerified",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "phone_number_verified",
+ "jsonType.label" : "boolean"
+ }
+ } ]
+ }, {
+ "id" : "9ddb9d40-7d9e-48de-8069-dd4e49e781dd",
+ "name" : "email",
+ "description" : "OpenID Connect built-in scope: email",
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "include.in.token.scope" : "true",
+ "display.on.consent.screen" : "true",
+ "consent.screen.text" : "${emailScopeConsentText}"
+ },
+ "protocolMappers" : [ {
+ "id" : "ae918ce8-12e6-4cb3-be0d-243cbf083fcb",
+ "name" : "email verified",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-property-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "emailVerified",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "email_verified",
+ "jsonType.label" : "boolean"
+ }
+ }, {
+ "id" : "59542d0c-b9b4-4913-ba99-416cf5c4a725",
+ "name" : "email",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-property-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "email",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "email",
+ "jsonType.label" : "String"
+ }
+ } ]
+ }, {
+ "id" : "2359cb5f-9de5-410c-9e87-38f1a4db0eee",
+ "name" : "microprofile-jwt",
+ "description" : "Microprofile - JWT built-in scope",
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "include.in.token.scope" : "true",
+ "display.on.consent.screen" : "false"
+ },
+ "protocolMappers" : [ {
+ "id" : "dc0f74c2-135c-4e61-b74e-22836524b496",
+ "name" : "groups",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-realm-role-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "multivalued" : "true",
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "foo",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "groups",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "a715f65e-3a94-4bca-94df-49e1e53e5aae",
+ "name" : "upn",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-property-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "username",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "upn",
+ "jsonType.label" : "String"
+ }
+ } ]
+ }, {
+ "id" : "11a8d21b-ed5d-4567-af88-91101e132553",
+ "name" : "profile",
+ "description" : "OpenID Connect built-in scope: profile",
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "include.in.token.scope" : "true",
+ "display.on.consent.screen" : "true",
+ "consent.screen.text" : "${profileScopeConsentText}"
+ },
+ "protocolMappers" : [ {
+ "id" : "c6ae1222-2561-491e-8c33-2e2eef183f6a",
+ "name" : "nickname",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "nickname",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "nickname",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "aba683c1-ee38-4f0a-980b-9c7c31a189ad",
+ "name" : "family name",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-property-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "lastName",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "family_name",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "b5d111f6-91be-4735-9dbf-848bcf86c1d3",
+ "name" : "birthdate",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "birthdate",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "birthdate",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "8e0ca67f-030e-4ea9-af3c-8e7815442957",
+ "name" : "updated at",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "updatedAt",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "updated_at",
+ "jsonType.label" : "long"
+ }
+ }, {
+ "id" : "3d9431ca-a590-457b-a0e3-412f63f07923",
+ "name" : "zoneinfo",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "zoneinfo",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "zoneinfo",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "b54a324a-b8ee-4c66-b780-50f9b8e3e275",
+ "name" : "locale",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "locale",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "locale",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "6ffd147e-fac7-4299-ab9f-b7bef677d0ae",
+ "name" : "middle name",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "middleName",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "middle_name",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "9637b112-a2e8-4787-be6d-45f8d0804555",
+ "name" : "profile",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "profile",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "profile",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "36e9530e-da81-477e-907e-a335efff8df8",
+ "name" : "given name",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-property-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "firstName",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "given_name",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "119fb217-5662-40db-a33c-442846a58b71",
+ "name" : "full name",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-full-name-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "userinfo.token.claim" : "true"
+ }
+ }, {
+ "id" : "b607712e-db7c-42d3-ab9c-99acf62bac40",
+ "name" : "username",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-property-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "username",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "preferred_username",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "c73bc111-8b12-46ac-866f-1e690c8fa21a",
+ "name" : "website",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "website",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "website",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "8de0aa23-d84c-4f77-9ada-52d6bcd71593",
+ "name" : "picture",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "picture",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "picture",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "85b4a69b-2b52-44e0-b4f7-4ff75feba1b4",
+ "name" : "gender",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "gender",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "gender",
+ "jsonType.label" : "String"
+ }
+ } ]
+ }, {
+ "id" : "691dbb7d-ed16-4283-b737-f02676e56e82",
+ "name" : "address",
+ "description" : "OpenID Connect built-in scope: address",
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "include.in.token.scope" : "true",
+ "display.on.consent.screen" : "true",
+ "consent.screen.text" : "${addressScopeConsentText}"
+ },
+ "protocolMappers" : [ {
+ "id" : "46d9b7a5-0776-496d-9a50-e5584709677b",
+ "name" : "address",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-address-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "user.attribute.formatted" : "formatted",
+ "user.attribute.country" : "country",
+ "user.attribute.postal_code" : "postal_code",
+ "userinfo.token.claim" : "true",
+ "user.attribute.street" : "street",
+ "id.token.claim" : "true",
+ "user.attribute.region" : "region",
+ "access.token.claim" : "true",
+ "user.attribute.locality" : "locality"
+ }
+ } ]
+ } ],
+ "defaultDefaultClientScopes" : [ "role_list", "profile", "email", "web-origins", "acr", "roles" ],
+ "defaultOptionalClientScopes" : [ "offline_access", "address", "phone", "microprofile-jwt" ],
+ "browserSecurityHeaders" : {
+ "contentSecurityPolicyReportOnly" : "",
+ "xContentTypeOptions" : "nosniff",
+ "referrerPolicy" : "no-referrer",
+ "xRobotsTag" : "none",
+ "xFrameOptions" : "SAMEORIGIN",
+ "contentSecurityPolicy" : "frame-src 'self'; frame-ancestors 'self'; object-src 'none';",
+ "xXSSProtection" : "1; mode=block",
+ "strictTransportSecurity" : "max-age=31536000; includeSubDomains"
+ },
+ "smtpServer" : { },
+ "eventsEnabled" : false,
+ "eventsListeners" : [ "jboss-logging" ],
+ "enabledEventTypes" : [ ],
+ "adminEventsEnabled" : false,
+ "adminEventsDetailsEnabled" : false,
+ "identityProviders" : [ ],
+ "identityProviderMappers" : [ ],
+ "components" : {
+ "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" : [ {
+ "id" : "f44d4d8f-cc39-4467-bb75-889f8d4c9b90",
+ "name" : "Allowed Protocol Mapper Types",
+ "providerId" : "allowed-protocol-mappers",
+ "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" ]
+ }
+ }, {
+ "id" : "7256d195-1e91-4f63-a9c4-6bef95243a92",
+ "name" : "Max Clients Limit",
+ "providerId" : "max-clients",
+ "subType" : "anonymous",
+ "subComponents" : { },
+ "config" : {
+ "max-clients" : [ "200" ]
+ }
+ }, {
+ "id" : "f3d9ee71-6796-41bb-b89f-c4b2ad108b3a",
+ "name" : "Trusted Hosts",
+ "providerId" : "trusted-hosts",
+ "subType" : "anonymous",
+ "subComponents" : { },
+ "config" : {
+ "host-sending-registration-request-must-match" : [ "true" ],
+ "client-uris-must-match" : [ "true" ]
+ }
+ }, {
+ "id" : "340f74d5-41a0-45cc-8ccb-65a0a4c49ed4",
+ "name" : "Allowed Client Scopes",
+ "providerId" : "allowed-client-templates",
+ "subType" : "anonymous",
+ "subComponents" : { },
+ "config" : {
+ "allow-default-scopes" : [ "true" ]
+ }
+ }, {
+ "id" : "fb2bea0a-dca5-4784-822d-cf10518f41c6",
+ "name" : "Allowed Protocol Mapper Types",
+ "providerId" : "allowed-protocol-mappers",
+ "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" ]
+ }
+ }, {
+ "id" : "face2c9e-4d23-44e2-9a09-74e1d8448bd3",
+ "name" : "Consent Required",
+ "providerId" : "consent-required",
+ "subType" : "anonymous",
+ "subComponents" : { },
+ "config" : { }
+ }, {
+ "id" : "6d38ba87-78ee-4ca3-aecd-0164922a08e2",
+ "name" : "Full Scope Disabled",
+ "providerId" : "scope",
+ "subType" : "anonymous",
+ "subComponents" : { },
+ "config" : { }
+ }, {
+ "id" : "d9bdd722-325e-41ff-bb88-df8772c9415b",
+ "name" : "Allowed Client Scopes",
+ "providerId" : "allowed-client-templates",
+ "subType" : "authenticated",
+ "subComponents" : { },
+ "config" : {
+ "allow-default-scopes" : [ "true" ]
+ }
+ } ],
+ "org.keycloak.keys.KeyProvider" : [ {
+ "id" : "9948a63f-b171-4137-bb81-beabd0c049f0",
+ "name" : "aes-generated",
+ "providerId" : "aes-generated",
+ "subComponents" : { },
+ "config" : {
+ "kid" : [ "59f8ad80-8417-45dd-8196-c2f37ddaa309" ],
+ "secret" : [ "D82qLVou0ux0UswcrMSTlw" ],
+ "priority" : [ "100" ]
+ }
+ }, {
+ "id" : "4a3be057-744a-44c4-9211-9a98d7c6303c",
+ "name" : "rsa-enc-generated",
+ "providerId" : "rsa-enc-generated",
+ "subComponents" : { },
+ "config" : {
+ "privateKey" : [ "MIIEogIBAAKCAQEA9KETJD3hVFueAg6Dk39nNIhTNDGtEbOfz15JumFd23CNfceSgZB2fQn6/kwzxom+i2Za8NrcJh21sDbeXb0VQNnOWsjiOzDuC6VvRFzeg3eIr6BqQIEggHwk2YRqd/lD225Myk5s3G1PA/cLr0ZO+/IUwI9Z6rKayzSO0IrpHKe4cJUa6YGMeVeprN575/jcbbC+B8IXJdmcJ1+n5MUzHxpcvDFKLBOaukGTENji4qMoHxQ2YkGO5nOTDlq6Nx5J+VUMrUfP3Z7fJPvil+ma6jZtre4T3r7DoKv6jzQRbDUig/aFyx+qqStP+nPiRJ7sx6KzyJSekXPPY4bDkVdVXwIDAQABAoIBAAut4J6Tb72ANETpd+GwxiogmhK89NDXPRMaIs+WeqcrGMfZB+C0sQ60mi2a6lvmID+o7tpevWT6eFd5GL7r880chcWy0o48S0xccezKUq5/JGD4k5B2oCkfBDL4WsCImSZ4Sr7b5Xmh15E0y1nrM7X2orc0AN+u9quRKLWPJ3Fmy21NNOS6FVcAUwSJViDzG+heH8Ny91OnQs3SSh/0Ubxiv/bl54FVAoq1vrTl5xHBu8T+jXU978P6n8O8tXLaCEReAAcwrSAw98nNIuPOzqyZS5VLpxQCaVoIotorysPifrOQJFVHYK9az+jdkT1enxMdhmEewWW6KlQdUNeA+XUCgYEA+wzLLNAixryRgdlWvmmg8hVUAgXPczlDOfSOajSX7UcykTz/m0qjZ2idwUyMQvy+/ukqdJieSMegTWSq9kDyYW5THSI0GlIaX+2BMQml+enrGkDWp2Hl1G5EK9z98aw/fdVodn7U7XQKm3v1toF02Lx7sit6KFPCupg2lr248NMCgYEA+XPfFq2TuDCKhm9+yLQm5tyTzbDOfKqWAKYCYIO0iPMzeOwc09W0UOT6iW2WhFpus3jtp8lPmzfJ7MmIQ9RwhwporMGRPbV/tD7TEvCgOUOEsJv6eHgLqA1zCLI9kx1uezQglF1seb77ouOD6gTG/kK5HfwEd8GLV53TtYjYEcUCgYBerrOOAi2rgIDsVRjnFZVy1+JoJOLZlRYqrHZtzcoi3kBPEI9idSLtpEIjHgikVwh9wViWwtynnEp0BeyIlXQUlPRZv4WhC6gQ19VqtjXX7IYTz7JlTIHOvYuOc3l/BTSo86zDTBBoQeSiQX0pSOfVujh4uPcIcJa3oyKrdYBjqwKBgBfGKU7aACgfyDQD0EuEj+iUwSlrXmKXR3CMYdGc+8nJk/BEYIL37RWAnTgXz66Rh9dnAQ9qqkGa0Y3Vrzz3tDnKjitYz2TYNXGSQz+c20hZ3P2QABEXL9U2Yu5DPocU2QC4+RnqxXnc79KYaGwXRHfbGBV5fVuEgKk7C4BTqHQZAoGAUur0G3Qdu7PDFXDYYYuAk78ilVhLIp+r4nv61C8ydO4PKQPjRC+ab/UH0OvlXmGKAqeLj/cbohtOuprcHmaqIEwzxxXwQrdkBCWo7LxrV0/Ke731V5XM59disjA+F8aR78nZPBLHz5JLl0sagkKA8V3t9EwQMJ//M+ZbAqoT/GM=" ],
+ "keyUse" : [ "ENC" ],
+ "certificate" : [ "MIICmTCCAYECBgGMOOsFujANBgkqhkiG9w0BAQsFADAQMQ4wDAYDVQQDDAVCYVN5eDAeFw0yMzEyMDUwNzM3NTVaFw0zMzEyMDUwNzM5MzVaMBAxDjAMBgNVBAMMBUJhU3l4MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9KETJD3hVFueAg6Dk39nNIhTNDGtEbOfz15JumFd23CNfceSgZB2fQn6/kwzxom+i2Za8NrcJh21sDbeXb0VQNnOWsjiOzDuC6VvRFzeg3eIr6BqQIEggHwk2YRqd/lD225Myk5s3G1PA/cLr0ZO+/IUwI9Z6rKayzSO0IrpHKe4cJUa6YGMeVeprN575/jcbbC+B8IXJdmcJ1+n5MUzHxpcvDFKLBOaukGTENji4qMoHxQ2YkGO5nOTDlq6Nx5J+VUMrUfP3Z7fJPvil+ma6jZtre4T3r7DoKv6jzQRbDUig/aFyx+qqStP+nPiRJ7sx6KzyJSekXPPY4bDkVdVXwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQDtGhf0x8ll3OWG0ZPBeq75OtrxGsLeF+Ic0LPMjlZvhJ6YOj79NKuILhBfECGoOeyp2xhrWXZzrNtgl/1LFULYjeBMwmUt9Hlcj/iNSQslOl3bwUxKtVZfGfEDmC4uRqVnVC9NdQlJ8s1Wea675OrFBQevtqZMApkTvQHzSpfhK+bNGyMpBW6zYleUzvE8jDu9AtlLhjAw2/HWJHrOI2DwMN16tNth4vUlKMRfSgr/0kmxuQbqfd3H+kPgd78EgkGob7bCL27d7rRHnCBT79bWBushpTY9TnqQvfwQthGKFICEhsXOreXwRuxgJfG70Q60iwfXexknZFeaqmLPIY8k" ],
+ "priority" : [ "100" ],
+ "algorithm" : [ "RSA-OAEP" ]
+ }
+ }, {
+ "id" : "f7fcd439-e566-4b8d-8078-f300b494f90a",
+ "name" : "rsa-generated",
+ "providerId" : "rsa-generated",
+ "subComponents" : { },
+ "config" : {
+ "privateKey" : [ "MIIEowIBAAKCAQEAsQ4RUvlZXLjQpLizIMht46ASwpwUoPpUDTmUhxF/VV+ezHTHbTAdv/5RA1GgCyTjAQe/Ih6dLByZrJFaroyvqgIJdMRCb0MwajI1US0/NwHtVvo5dea/+GKeHGRzvYZjxVlooR/1xmskfAM/NR/NaOMUhr/TNV7n7LXEb06L55DYnqdqrUnhXLewBq1lo54GsMqxN4hlkc4nJ2uYUtWEkV4SlMyYRjXBlylpuWFO0+/FsmaqSx7CZWjNWmqKlxnvUgRrT5Vh+9ZgCAOmGtLuFYOVWzqmVjWtyiJ1pYfWjwc86XeWBFefVY1lkoNNoSYKV4AZIkeF2M/+FHNzNGhLOwIDAQABAoIBABCuWR5+pJcySGIEjsfbalMETVAtgVoLS6j1UFOLZ/pEqILty64zVtI0ZTCRW6lBH+wEbVMLHFbAGRBjn24Lji9PwtFP/kxaQbD3qwNLZoXJtjE4IPrFMzf1Hp1hL3BfPX4l5tArYS4BAS535dgc927W2iO55e3E08f+9hNgjJjT6fpFvKqsGsHkPVEQBr0p1vZ1JkO3FrhCP4s5HTRSHoiaFxQb2yceN/mSmQHTcRJv6yxHAuhaKL44r7KEeI8fyANvXhx+BZhu9aX9Esb72poRFOyV5Wu1QoVFpKIUNhELTJ0NOjSTiqThDXKK0EOgkooN5dhyeNRcQ4qwufUF4n0CgYEA56YwPasUGWF7ZqiqfSgXOHrMb4E/T9Y194tVM4J94eEVqNeUrqcphLsBG1g9MAmMo6rXjYnmYyVZmrmaqeaSUKpibg2Bg0yfe8p/Z0UjwHebKXAYOQDUQazGgcACV4zHPhQFUzgJ/WS3X2EJUXb8M5OBlThD94xXlT0szrjKFLUCgYEAw6q3sQDEuGm6kVSrH35CflhRcEyGYzAfG5XXUGr1BuPqzsVeQMXm5fzIvFDpSqP0E1LS1LjiQQJuRVbcEGLmpG38hGKD5LgFxx2/leu8a9+i7v1yZ6DaQB+XXrtiCoOSab8q8AeU/5AJm9JQI2jr08u//xnIy1tUh3t9wjMxRi8CgYEAvmZnolh6pb3tOt0JfOO16lNss33tdwafxv78IeFw7HcgYW4IpGF7i8BVUY9+g3xl36StlYWyGu35L2a9DEcbHjhdvQ1W1X/mWk5/13cJwsnMfAvJrRjUXcLQSpdylVl97rVwBw25kE/3NOtCSHZfJ1lnminsG41784ubx7I1Tz0CgYAahJ6UnJgMrjeczq7Ke+AjI3EWGSj1dGYi5PHjcjt3DZibIWCewrOCY/oIm6aieQnxPH5aWhw/10Z+m0ED74N2sXlRr9BURSRzUfLPLL3CmPKKFUtWBBTQL/fh3N9ysVY8gq4dDcoBwNGsjppR0jxz3d9NgX8XIG+aVQA8O5fn/QKBgBVXVKXNeEiNPSoayzDXNuCU14DintvN4+jq2kL4zmOOc1s+NHdqAP/FZfBaQwl6SDZer3Tx9oXCZatZV9y8IZEC6GXqQO7H/RPbMDcuT8B6KHPqf0SZ0F7XYL9FGwjVQ5k1ouOvWbeNPxvHfxcs0nJTpou4gklF3yO98ATFEvv1" ],
+ "keyUse" : [ "SIG" ],
+ "certificate" : [ "MIICmTCCAYECBgGMOOsFGDANBgkqhkiG9w0BAQsFADAQMQ4wDAYDVQQDDAVCYVN5eDAeFw0yMzEyMDUwNzM3NTVaFw0zMzEyMDUwNzM5MzVaMBAxDjAMBgNVBAMMBUJhU3l4MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsQ4RUvlZXLjQpLizIMht46ASwpwUoPpUDTmUhxF/VV+ezHTHbTAdv/5RA1GgCyTjAQe/Ih6dLByZrJFaroyvqgIJdMRCb0MwajI1US0/NwHtVvo5dea/+GKeHGRzvYZjxVlooR/1xmskfAM/NR/NaOMUhr/TNV7n7LXEb06L55DYnqdqrUnhXLewBq1lo54GsMqxN4hlkc4nJ2uYUtWEkV4SlMyYRjXBlylpuWFO0+/FsmaqSx7CZWjNWmqKlxnvUgRrT5Vh+9ZgCAOmGtLuFYOVWzqmVjWtyiJ1pYfWjwc86XeWBFefVY1lkoNNoSYKV4AZIkeF2M/+FHNzNGhLOwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCd6bCxZ0JvQ9/Wh1iY17lDbmbIfcm/DapqN3Q8q3VLTwvsg4mwL0lKinTztGbh2KpSmRlLTFsvED63k8hIt09FLvbPy8n7U/DddaUhxS3/MMcQ2u+Rg5zXNhIPMdqyvXD9+R9ToXToP2BfD23f8HgGXJ2y5D3u48PZ96g2SlbAbNTnESABSB3r7bcHMemT+Ud/7auSA45lLoJWmlQjJ7gbFk1Dwrv/AdYTBFM+LWflei9MQUBqP95960VbFmX9sHIBxTqQ5GxB8JNdVPSEdfJe0xP2Bh4I0+yPGDvoadncQYySnBvlmdpKy60+UWWrH9R1ritc/F///hFB77hDu2l/" ],
+ "priority" : [ "100" ]
+ }
+ }, {
+ "id" : "c6b34d4a-f4f6-4864-8c39-86b0b6762bb7",
+ "name" : "hmac-generated",
+ "providerId" : "hmac-generated",
+ "subComponents" : { },
+ "config" : {
+ "kid" : [ "6c9fd13c-0afe-46e8-83a6-3fb95ba32dcf" ],
+ "secret" : [ "f9qfZHpSvHgZ2ti_iZjMfWEM3DyJtlxEpNEzunKXI7qbqnLzwbsMFI4n_vF9YpM4r6mlARVIBnffHgjAwPZvnw" ],
+ "priority" : [ "100" ],
+ "algorithm" : [ "HS256" ]
+ }
+ } ]
+ },
+ "internationalizationEnabled" : false,
+ "supportedLocales" : [ ],
+ "authenticationFlows" : [ {
+ "id" : "76ff1526-2405-40a7-9051-977dfba08add",
+ "alias" : "Account verification options",
+ "description" : "Method with which to verity the existing account",
+ "providerId" : "basic-flow",
+ "topLevel" : false,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "idp-email-verification",
+ "authenticatorFlow" : false,
+ "requirement" : "ALTERNATIVE",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticatorFlow" : true,
+ "requirement" : "ALTERNATIVE",
+ "priority" : 20,
+ "autheticatorFlow" : true,
+ "flowAlias" : "Verify Existing Account by Re-authentication",
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "64d21769-b1ff-4012-8021-b724df8e759f",
+ "alias" : "Browser - Conditional OTP",
+ "description" : "Flow to determine if the OTP is required for the authentication",
+ "providerId" : "basic-flow",
+ "topLevel" : false,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "conditional-user-configured",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "auth-otp-form",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 20,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "e07a9d36-3a69-431d-ae89-3ac51675ea16",
+ "alias" : "Direct Grant - Conditional OTP",
+ "description" : "Flow to determine if the OTP is required for the authentication",
+ "providerId" : "basic-flow",
+ "topLevel" : false,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "conditional-user-configured",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "direct-grant-validate-otp",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 20,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "db117f4e-e2f2-40b0-9d74-c3be8f6aacaf",
+ "alias" : "First broker login - Conditional OTP",
+ "description" : "Flow to determine if the OTP is required for the authentication",
+ "providerId" : "basic-flow",
+ "topLevel" : false,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "conditional-user-configured",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "auth-otp-form",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 20,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "7a3723a1-f43f-44b4-aa75-d6d7f3c60fac",
+ "alias" : "Handle Existing Account",
+ "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider",
+ "providerId" : "basic-flow",
+ "topLevel" : false,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "idp-confirm-link",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticatorFlow" : true,
+ "requirement" : "REQUIRED",
+ "priority" : 20,
+ "autheticatorFlow" : true,
+ "flowAlias" : "Account verification options",
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "04b87ec9-fbc0-434d-ae6c-3b144d4c4fb3",
+ "alias" : "Reset - Conditional OTP",
+ "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.",
+ "providerId" : "basic-flow",
+ "topLevel" : false,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "conditional-user-configured",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "reset-otp",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 20,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "12d1fb6a-2691-4616-921f-537b2930763a",
+ "alias" : "User creation or linking",
+ "description" : "Flow for the existing/non-existing user alternatives",
+ "providerId" : "basic-flow",
+ "topLevel" : false,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticatorConfig" : "create unique user config",
+ "authenticator" : "idp-create-user-if-unique",
+ "authenticatorFlow" : false,
+ "requirement" : "ALTERNATIVE",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticatorFlow" : true,
+ "requirement" : "ALTERNATIVE",
+ "priority" : 20,
+ "autheticatorFlow" : true,
+ "flowAlias" : "Handle Existing Account",
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "72dd5217-9d7a-4b9a-8e81-c98390a05938",
+ "alias" : "Verify Existing Account by Re-authentication",
+ "description" : "Reauthentication of existing account",
+ "providerId" : "basic-flow",
+ "topLevel" : false,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "idp-username-password-form",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticatorFlow" : true,
+ "requirement" : "CONDITIONAL",
+ "priority" : 20,
+ "autheticatorFlow" : true,
+ "flowAlias" : "First broker login - Conditional OTP",
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "652e9b64-0250-41aa-ab19-b6742aa2ee4f",
+ "alias" : "browser",
+ "description" : "browser based authentication",
+ "providerId" : "basic-flow",
+ "topLevel" : true,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "auth-cookie",
+ "authenticatorFlow" : false,
+ "requirement" : "ALTERNATIVE",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "auth-spnego",
+ "authenticatorFlow" : false,
+ "requirement" : "DISABLED",
+ "priority" : 20,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "identity-provider-redirector",
+ "authenticatorFlow" : false,
+ "requirement" : "ALTERNATIVE",
+ "priority" : 25,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticatorFlow" : true,
+ "requirement" : "ALTERNATIVE",
+ "priority" : 30,
+ "autheticatorFlow" : true,
+ "flowAlias" : "forms",
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "2707b9b1-9a49-44b9-bbd3-4333d6b16a98",
+ "alias" : "clients",
+ "description" : "Base authentication for clients",
+ "providerId" : "client-flow",
+ "topLevel" : true,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "client-secret",
+ "authenticatorFlow" : false,
+ "requirement" : "ALTERNATIVE",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "client-jwt",
+ "authenticatorFlow" : false,
+ "requirement" : "ALTERNATIVE",
+ "priority" : 20,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "client-secret-jwt",
+ "authenticatorFlow" : false,
+ "requirement" : "ALTERNATIVE",
+ "priority" : 30,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "client-x509",
+ "authenticatorFlow" : false,
+ "requirement" : "ALTERNATIVE",
+ "priority" : 40,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "1aee26cc-cca5-4eef-8fb9-afa36240b518",
+ "alias" : "direct grant",
+ "description" : "OpenID Connect Resource Owner Grant",
+ "providerId" : "basic-flow",
+ "topLevel" : true,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "direct-grant-validate-username",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "direct-grant-validate-password",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 20,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticatorFlow" : true,
+ "requirement" : "CONDITIONAL",
+ "priority" : 30,
+ "autheticatorFlow" : true,
+ "flowAlias" : "Direct Grant - Conditional OTP",
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "09bb06f3-ff4d-4b65-b609-fea90f93044f",
+ "alias" : "docker auth",
+ "description" : "Used by Docker clients to authenticate against the IDP",
+ "providerId" : "basic-flow",
+ "topLevel" : true,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "docker-http-basic-authenticator",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "10fe11ec-61e9-49a6-85a8-2cd4c24da4bf",
+ "alias" : "first broker login",
+ "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account",
+ "providerId" : "basic-flow",
+ "topLevel" : true,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticatorConfig" : "review profile config",
+ "authenticator" : "idp-review-profile",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticatorFlow" : true,
+ "requirement" : "REQUIRED",
+ "priority" : 20,
+ "autheticatorFlow" : true,
+ "flowAlias" : "User creation or linking",
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "67281328-09bb-4140-9cdb-5ea1bfcf827b",
+ "alias" : "forms",
+ "description" : "Username, password, otp and other auth forms.",
+ "providerId" : "basic-flow",
+ "topLevel" : false,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "auth-username-password-form",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticatorFlow" : true,
+ "requirement" : "CONDITIONAL",
+ "priority" : 20,
+ "autheticatorFlow" : true,
+ "flowAlias" : "Browser - Conditional OTP",
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "e4d8833f-6b90-47a1-9d73-4fbf3c41e910",
+ "alias" : "registration",
+ "description" : "registration flow",
+ "providerId" : "basic-flow",
+ "topLevel" : true,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "registration-page-form",
+ "authenticatorFlow" : true,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : true,
+ "flowAlias" : "registration form",
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "41e56e9f-589e-4857-b28e-f6b8754ae400",
+ "alias" : "registration form",
+ "description" : "registration form",
+ "providerId" : "form-flow",
+ "topLevel" : false,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "registration-user-creation",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "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,
+ "requirement" : "REQUIRED",
+ "priority" : 50,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "registration-recaptcha-action",
+ "authenticatorFlow" : false,
+ "requirement" : "DISABLED",
+ "priority" : 60,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "412c0d35-ba19-4ee0-92de-4b00fd52e3fa",
+ "alias" : "reset credentials",
+ "description" : "Reset credentials for a user if they forgot their password or something",
+ "providerId" : "basic-flow",
+ "topLevel" : true,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "reset-credentials-choose-user",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "reset-credential-email",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 20,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "reset-password",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 30,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticatorFlow" : true,
+ "requirement" : "CONDITIONAL",
+ "priority" : 40,
+ "autheticatorFlow" : true,
+ "flowAlias" : "Reset - Conditional OTP",
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "eede5156-ace5-41f3-b86d-441278f5b337",
+ "alias" : "saml ecp",
+ "description" : "SAML ECP Profile Authentication Flow",
+ "providerId" : "basic-flow",
+ "topLevel" : true,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "http-basic-authenticator",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ } ]
+ } ],
+ "authenticatorConfig" : [ {
+ "id" : "2caebb6c-1b1b-42ec-ac41-1f1dabd609ad",
+ "alias" : "create unique user config",
+ "config" : {
+ "require.password.update.after.registration" : "false"
+ }
+ }, {
+ "id" : "8ef60759-3395-4d42-ba53-390f72df09d5",
+ "alias" : "review profile config",
+ "config" : {
+ "update.profile.on.first.login" : "missing"
+ }
+ } ],
+ "requiredActions" : [ {
+ "alias" : "CONFIGURE_TOTP",
+ "name" : "Configure OTP",
+ "providerId" : "CONFIGURE_TOTP",
+ "enabled" : true,
+ "defaultAction" : false,
+ "priority" : 10,
+ "config" : { }
+ }, {
+ "alias" : "TERMS_AND_CONDITIONS",
+ "name" : "Terms and Conditions",
+ "providerId" : "TERMS_AND_CONDITIONS",
+ "enabled" : false,
+ "defaultAction" : false,
+ "priority" : 20,
+ "config" : { }
+ }, {
+ "alias" : "UPDATE_PASSWORD",
+ "name" : "Update Password",
+ "providerId" : "UPDATE_PASSWORD",
+ "enabled" : true,
+ "defaultAction" : false,
+ "priority" : 30,
+ "config" : { }
+ }, {
+ "alias" : "UPDATE_PROFILE",
+ "name" : "Update Profile",
+ "providerId" : "UPDATE_PROFILE",
+ "enabled" : true,
+ "defaultAction" : false,
+ "priority" : 40,
+ "config" : { }
+ }, {
+ "alias" : "VERIFY_EMAIL",
+ "name" : "Verify Email",
+ "providerId" : "VERIFY_EMAIL",
+ "enabled" : true,
+ "defaultAction" : false,
+ "priority" : 50,
+ "config" : { }
+ }, {
+ "alias" : "delete_account",
+ "name" : "Delete Account",
+ "providerId" : "delete_account",
+ "enabled" : false,
+ "defaultAction" : false,
+ "priority" : 60,
+ "config" : { }
+ }, {
+ "alias" : "webauthn-register",
+ "name" : "Webauthn Register",
+ "providerId" : "webauthn-register",
+ "enabled" : true,
+ "defaultAction" : false,
+ "priority" : 70,
+ "config" : { }
+ }, {
+ "alias" : "webauthn-register-passwordless",
+ "name" : "Webauthn Register Passwordless",
+ "providerId" : "webauthn-register-passwordless",
+ "enabled" : true,
+ "defaultAction" : false,
+ "priority" : 80,
+ "config" : { }
+ }, {
+ "alias" : "update_user_locale",
+ "name" : "Update User Locale",
+ "providerId" : "update_user_locale",
+ "enabled" : true,
+ "defaultAction" : false,
+ "priority" : 1000,
+ "config" : { }
+ } ],
+ "browserFlow" : "browser",
+ "registrationFlow" : "registration",
+ "directGrantFlow" : "direct grant",
+ "resetCredentialsFlow" : "reset credentials",
+ "clientAuthenticationFlow" : "clients",
+ "dockerAuthenticationFlow" : "docker auth",
+ "attributes" : {
+ "cibaBackchannelTokenDeliveryMode" : "poll",
+ "cibaExpiresIn" : "120",
+ "cibaAuthRequestedUserHint" : "login_hint",
+ "oauth2DeviceCodeLifespan" : "600",
+ "clientOfflineSessionMaxLifespan" : "0",
+ "oauth2DevicePollingInterval" : "5",
+ "clientSessionIdleTimeout" : "0",
+ "parRequestUriLifespan" : "60",
+ "clientSessionMaxLifespan" : "0",
+ "clientOfflineSessionIdleTimeout" : "0",
+ "cibaInterval" : "5",
+ "realmReusableOtpCode" : "false"
+ },
+ "keycloakVersion" : "22.0.0",
+ "userManagedAccessAllowed" : false,
+ "clientProfiles" : {
+ "profiles" : [ ]
+ },
+ "clientPolicies" : {
+ "policies" : [ ]
+ }
+}
\ No newline at end of file
diff --git a/ci/keycloak/rules/rbac_rules-aas-registry.json b/ci/keycloak/rules/rbac_rules-aas-registry.json
new file mode 100644
index 000000000..d0925041e
--- /dev/null
+++ b/ci/keycloak/rules/rbac_rules-aas-registry.json
@@ -0,0 +1,82 @@
+[
+ {
+ "role": "basyx-reader",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "aas-registry",
+ "aasId": "*"
+ }
+ },
+ {
+ "role": "admin",
+ "action": ["CREATE", "READ", "UPDATE", "DELETE"],
+ "targetInformation": {
+ "@type": "aas-registry",
+ "aasId": "*"
+ }
+ },
+ {
+ "role": "basyx-reader-two",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "aas-registry",
+ "aasId": "dummyShellId_3"
+ }
+ },
+ {
+ "role": "basyx-creator",
+ "action": "CREATE",
+ "targetInformation": {
+ "@type": "aas-registry",
+ "aasId": "*"
+ }
+ },
+ {
+ "role": "basyx-updater",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "aas-registry",
+ "aasId": "*"
+ }
+ },
+ {
+ "role": "basyx-updater-two",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "aas-registry",
+ "aasId": "dummyShellId_3"
+ }
+ },
+ {
+ "role": "basyx-asset-updater",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "aas-registry",
+ "aasId": "*"
+ }
+ },
+ {
+ "role": "basyx-asset-updater-two",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "aas-registry",
+ "aasId": "specificAasId-2"
+ }
+ },
+ {
+ "role": "basyx-deleter",
+ "action": "DELETE",
+ "targetInformation": {
+ "@type": "aas-registry",
+ "aasId": "*"
+ }
+ },
+ {
+ "role": "basyx-deleter-two",
+ "action": "DELETE",
+ "targetInformation": {
+ "@type": "aas-registry",
+ "aasId": "specificAasId-2"
+ }
+ }
+]
\ No newline at end of file
diff --git a/ci/keycloak/rules/rbac_rules-sm-registry.json b/ci/keycloak/rules/rbac_rules-sm-registry.json
new file mode 100644
index 000000000..9fcf84488
--- /dev/null
+++ b/ci/keycloak/rules/rbac_rules-sm-registry.json
@@ -0,0 +1,82 @@
+[
+ {
+ "role": "basyx-reader",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "submodel-registry",
+ "submodelId": "*"
+ }
+ },
+ {
+ "role": "admin",
+ "action": ["CREATE", "READ", "UPDATE", "DELETE"],
+ "targetInformation": {
+ "@type": "submodel-registry",
+ "submodelId": "*"
+ }
+ },
+ {
+ "role": "basyx-reader-two",
+ "action": "READ",
+ "targetInformation": {
+ "@type": "submodel-registry",
+ "submodelId": "dummySubmodelId_3"
+ }
+ },
+ {
+ "role": "basyx-creator",
+ "action": "CREATE",
+ "targetInformation": {
+ "@type": "submodel-registry",
+ "submodelId": "*"
+ }
+ },
+ {
+ "role": "basyx-updater",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "submodel-registry",
+ "submodelId": "*"
+ }
+ },
+ {
+ "role": "basyx-updater-two",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "submodel-registry",
+ "submodelId": "dummySubmodelId_3"
+ }
+ },
+ {
+ "role": "basyx-asset-updater",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "submodel-registry",
+ "submodelId": "*"
+ }
+ },
+ {
+ "role": "basyx-asset-updater-two",
+ "action": "UPDATE",
+ "targetInformation": {
+ "@type": "submodel-registry",
+ "submodelId": "specificSubmodelId-2"
+ }
+ },
+ {
+ "role": "basyx-deleter",
+ "action": "DELETE",
+ "targetInformation": {
+ "@type": "submodel-registry",
+ "submodelId": "*"
+ }
+ },
+ {
+ "role": "basyx-deleter-two",
+ "action": "DELETE",
+ "targetInformation": {
+ "@type": "submodel-registry",
+ "submodelId": "specificSubmodelId-2"
+ }
+ }
+]
\ No newline at end of file
diff --git a/examples/BaSyxSecured/keycloak/BaSyx-realm.json b/examples/BaSyxSecured/keycloak/BaSyx-realm.json
index c96d45aad..36ee74513 100644
--- a/examples/BaSyxSecured/keycloak/BaSyx-realm.json
+++ b/examples/BaSyxSecured/keycloak/BaSyx-realm.json
@@ -511,6 +511,14 @@
"attributes" : { }
} ],
"basyx-client-api" : [ {
+ "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" : "",
@@ -613,10 +621,27 @@
"clientRole" : true,
"containerId" : "049e1323-6efb-4543-bc52-566cd292732a",
"attributes" : { }
+ } ],
+ "basyx-demo" : [ ],
+ "workstation-1" : [ {
+ "id" : "914a18c6-4f14-418f-99e0-bfdcf604ac01",
+ "name" : "uma_protection",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "96031210-9e6c-4252-a22e-e81a47e30d65",
+ "attributes" : { }
} ]
}
},
- "groups" : [ ],
+ "groups" : [ {
+ "id" : "606a14f2-6114-4fd3-9ca6-4a53514fffb9",
+ "name" : "BaSyxGroup",
+ "path" : "/BaSyxGroup",
+ "attributes" : { },
+ "realmRoles" : [ "basyx-deleter", "basyx-creator", "basyx-asset-updater" ],
+ "clientRoles" : { },
+ "subGroups" : [ ]
+ } ],
"defaultRole" : {
"id" : "797d2956-a895-4171-ab44-2fc9dbcf7f4c",
"name" : "default-roles-basyx",
@@ -1364,6 +1389,46 @@
"realmRoles" : [ "visitor", "default-roles-basyx" ],
"notBefore" : 0,
"groups" : [ ]
+ }, {
+ "id" : "a19abcac-34d5-46bb-a604-b07dc234e80f",
+ "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", "basyx-creator", "default-roles-basyx" ],
+ "clientRoles" : {
+ "workstation-1" : [ "uma_protection" ]
+ },
+ "notBefore" : 0,
+ "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",
+ "credentials" : [ {
+ "id" : "11672ccf-38b7-421b-8d01-755e0f2197da",
+ "type" : "password",
+ "userLabel" : "My password",
+ "createdDate" : 1715539657887,
+ "secretData" : "{\"value\":\"7YbCeFyuYDpwu/06UbMT7OObo29RU7cG1XWrqcNwZLg=\",\"salt\":\"nAQ9KKDYyg5GGuSUyfG+kw==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "default-roles-basyx" ],
+ "notBefore" : 0,
+ "groups" : [ "/BaSyxGroup" ]
} ],
"scopeMappings" : [ {
"clientScope" : "offline_access",
@@ -1510,6 +1575,43 @@
"defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ],
"optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
}, {
+ "id" : "615f29de-5f3d-4384-8b71-7351ff2c2a32",
+ "clientId" : "basyx-demo",
+ "name" : "",
+ "description" : "",
+ "rootUrl" : "",
+ "adminUrl" : "",
+ "baseUrl" : "",
+ "surrogateAuthRequired" : false,
+ "enabled" : true,
+ "alwaysDisplayInConsole" : false,
+ "clientAuthenticatorType" : "client-secret",
+ "secret" : "41f9RznNmtHaWKT7i8IDYTjl2VaLHw0q",
+ "redirectUris" : [ "/*" ],
+ "webOrigins" : [ "/*" ],
+ "notBefore" : 0,
+ "bearerOnly" : false,
+ "consentRequired" : false,
+ "standardFlowEnabled" : true,
+ "implicitFlowEnabled" : false,
+ "directAccessGrantsEnabled" : true,
+ "serviceAccountsEnabled" : false,
+ "publicClient" : false,
+ "frontchannelLogout" : true,
+ "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",
+ "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}",
@@ -1611,6 +1713,84 @@
} ],
"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",
+ "description" : "",
+ "rootUrl" : "",
+ "adminUrl" : "",
+ "baseUrl" : "",
+ "surrogateAuthRequired" : false,
+ "enabled" : true,
+ "alwaysDisplayInConsole" : true,
+ "clientAuthenticatorType" : "client-secret",
+ "secret" : "nY0mjyECF60DGzNmQUjL81XurSl8etom",
+ "redirectUris" : [ "/*" ],
+ "webOrigins" : [ "/*" ],
+ "notBefore" : 0,
+ "bearerOnly" : false,
+ "consentRequired" : false,
+ "standardFlowEnabled" : true,
+ "implicitFlowEnabled" : false,
+ "directAccessGrantsEnabled" : true,
+ "serviceAccountsEnabled" : true,
+ "authorizationServicesEnabled" : true,
+ "publicClient" : false,
+ "frontchannelLogout" : true,
+ "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",
+ "backchannel.logout.revoke.offline.tokens" : "false"
+ },
+ "authenticationFlowBindingOverrides" : { },
+ "fullScopeAllowed" : true,
+ "nodeReRegistrationTimeout" : -1,
+ "protocolMappers" : [ {
+ "id" : "1f332577-69b1-48cf-b2d1-95ccd6159fdf",
+ "name" : "Client IP Address",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usersessionmodel-note-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "user.session.note" : "clientAddress",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "clientAddress",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "05688508-b82b-46c5-85de-f0bacf03d6e7",
+ "name" : "Client Host",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usersessionmodel-note-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "user.session.note" : "clientHost",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "clientHost",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "a1c32e51-4369-4460-a015-98867bb101fd",
+ "name" : "Client ID",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usersessionmodel-note-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "user.session.note" : "client_id",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "client_id",
+ "jsonType.label" : "String"
+ }
+ } ],
+ "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "email" ],
+ "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
} ],
"clientScopes" : [ {
"id" : "e0f355da-f9ff-4104-b305-043b0188747b",
diff --git a/pom.xml b/pom.xml
index 255501885..40d84d01d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -488,6 +488,11 @@
basyx.submodelservice-http
${revision}
+
+ org.eclipse.digitaltwin.basyx
+ basyx.submodelservice-client
+ ${revision}
+
org.eclipse.digitaltwin.basyx
@@ -566,6 +571,11 @@
basyx.aasservice-feature-mqtt
${revision}
+
+ org.eclipse.digitaltwin.basyx
+ basyx.aasservice-client
+ ${revision}
+
org.eclipse.digitaltwin.basyx
@@ -831,6 +841,12 @@
${revision}
tests
+
+ org.eclipse.digitaltwin.basyx
+ basyx.submodelservice-client
+ ${revision}
+ tests
+
org.eclipse.digitaltwin.basyx
@@ -918,6 +934,12 @@
${revision}
tests
+
+ org.eclipse.digitaltwin.basyx
+ basyx.aasservice-client
+ ${revision}
+ tests
+
org.eclipse.digitaltwin.basyx