diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/Readme.md b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/Readme.md index e19eba5e2..7a39bd2c2 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/Readme.md +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/Readme.md @@ -17,3 +17,18 @@ An example valid configuration: basyx.aasrepository.feature.registryintegration = http://localhost:8050 basyx.externalurl = http://localhost:8081 ``` + +## AAS Repository Integration with Authorized AAS Registry + +If the target AAS Registry has authorization enabled, then the following properties needs to be configured in order to successfully integrate the Descriptor: + +``` +basyx.aasrepository.feature.registryintegration.authorization.enabled=true +basyx.aasrepository.feature.registryintegration.authorization.token-endpoint=http://localhost:9096/realms/BaSyx/protocol/openid-connect/token +basyx.aasrepository.feature.registryintegration.authorization.grant-type = or +basyx.aasrepository.feature.registryintegration.authorization.client-id = +basyx.aasrepository.feature.registryintegration.authorization.client-secret = +basyx.aasrepository.feature.registryintegration.authorization.username=test +basyx.aasrepository.feature.registryintegration.authorization.password=test +basyx.aasrepository.feature.registryintegration.authorization.scopes=[] +``` \ No newline at end of file diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/pom.xml b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/pom.xml index e40490069..770f8f924 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/pom.xml +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/pom.xml @@ -38,6 +38,26 @@ tests test + + org.eclipse.digitaltwin.basyx + basyx.filerepository-backend-inmemory + test + + + org.eclipse.digitaltwin.basyx + basyx.aasrepository-http + test + + + org.eclipse.digitaltwin.basyx + basyx.aasservice-backend-inmemory + test + + + org.eclipse.digitaltwin.basyx + basyx.aasrepository-backend-inmemory + test + org.apache.httpcomponents.client5 httpclient5 diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationAasRepositoryConfiguration.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationAasRepositoryConfiguration.java index c4700498b..a3e78e837 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationAasRepositoryConfiguration.java +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/RegistryIntegrationAasRepositoryConfiguration.java @@ -23,12 +23,18 @@ * SPDX-License-Identifier: MIT ******************************************************************************/ - package org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration; +import java.util.Collection; + import org.eclipse.digitaltwin.basyx.aasregistry.client.api.RegistryAndDiscoveryInterfaceApi; +import org.eclipse.digitaltwin.basyx.aasregistry.main.client.AuthorizedConnectedAasRegistry; import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; import org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration.mapper.AttributeMapper; +import org.eclipse.digitaltwin.basyx.client.internal.authorization.AccessTokenProviderFactory; +import org.eclipse.digitaltwin.basyx.client.internal.authorization.TokenManager; +import org.eclipse.digitaltwin.basyx.client.internal.authorization.grant.AccessTokenProvider; +import org.eclipse.digitaltwin.basyx.client.internal.authorization.grant.GrantType; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -46,18 +52,62 @@ @ConditionalOnExpression("!T(org.springframework.util.StringUtils).isEmpty('${basyx.aasrepository.feature.registryintegration:}') && !T(org.springframework.util.StringUtils).isEmpty('${basyx.externalurl:}')") public class RegistryIntegrationAasRepositoryConfiguration { + @Value("${basyx.aasrepository.feature.registryintegration:#{null}}") + private String registryBasePath; + + @Value("${basyx.externalurl:#{null}}") + private String aasRepositoryBaseURL; + + @Value("${basyx.aasrepository.feature.registryintegration.authorization.enabled:false}") + private boolean isAuthorizationEnabledOnRegistry; + + @Value("${basyx.aasrepository.feature.registryintegration.authorization.token-endpoint:#{null}}") + private String authenticationServerTokenEndpoint; + + @Value("${basyx.aasrepository.feature.registryintegration.authorization.grant-type:#{null}}") + private String grantType; + + @Value("${basyx.aasrepository.feature.registryintegration.authorization.client-id:#{null}}") + private String clientId; + + @Value("${basyx.aasrepository.feature.registryintegration.authorization.client-secret:#{null}}") + private String clientSecret; + + @Value("${basyx.aasrepository.feature.registryintegration.authorization.username:#{null}}") + private String username; + + @Value("${basyx.aasrepository.feature.registryintegration.authorization.password:#{null}}") + private String password; + + @Value("${basyx.aasrepository.feature.registryintegration.authorization.scopes:#{null}}") + private Collection scopes; + @Bean @ConditionalOnMissingBean - public AasRepositoryRegistryLink getAasRepositoryRegistryLink(@Value("${basyx.aasrepository.feature.registryintegration}") String registryBasePath, @Value("${basyx.externalurl}") String aasRepositoryBaseURL) { - - return new AasRepositoryRegistryLink(new RegistryAndDiscoveryInterfaceApi(registryBasePath), aasRepositoryBaseURL); + public AasRepositoryRegistryLink getAasRepositoryRegistryLink() { + + if (!isAuthorizationEnabledOnRegistry) + return new AasRepositoryRegistryLink(new RegistryAndDiscoveryInterfaceApi(registryBasePath), aasRepositoryBaseURL); + + TokenManager tokenManager = new TokenManager(authenticationServerTokenEndpoint, createAccessTokenProvider()); + + return new AasRepositoryRegistryLink(new AuthorizedConnectedAasRegistry(registryBasePath, tokenManager), aasRepositoryBaseURL); } - + @Bean @ConditionalOnMissingBean public AttributeMapper getAasAttributeMapper(ObjectMapper objectMapper) { - + return new AttributeMapper(objectMapper); } + private AccessTokenProvider createAccessTokenProvider() { + + AccessTokenProviderFactory factory = new AccessTokenProviderFactory(GrantType.valueOf(grantType), scopes); + factory.setClientCredentials(clientId, clientSecret); + factory.setPasswordCredentials(username, password); + + return factory.create(); + } + } diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryTestLinkApplication.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLinkTest.java similarity index 50% rename from basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryTestLinkApplication.java rename to basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLinkTest.java index 7dbd91dec..4c563eb38 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryTestLinkApplication.java +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLinkTest.java @@ -22,51 +22,56 @@ * * SPDX-License-Identifier: MIT ******************************************************************************/ + package org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration; -import org.junit.internal.TextListener; -import org.junit.runner.JUnitCore; -import org.junit.runner.Result; +import java.io.FileNotFoundException; +import java.io.IOException; + +import org.eclipse.digitaltwin.basyx.aasregistry.client.api.RegistryAndDiscoveryInterfaceApi; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.springframework.boot.SpringApplication; +import org.springframework.context.ConfigurableApplicationContext; /** - * Application for testing the {@link RegistryIntegrationAasRepository} feature. - * The first argument is the AAS Repository Base URL, the second argument is the AAS - * Registry URL. + * Integration test for {@link RegistryIntegrationAasRepository} feature * - * @author schnicke, danish - * + * @author danish */ -public class AasRepositoryRegistryTestLinkApplication { - - public static void main(String[] args) throws Exception { - String aasRepoBaseUrl = getAasRepositoryBaseUrl(args); - String aasRegUrl = getAasRegistryUrl(args); - - Result result = runTests(aasRepoBaseUrl, aasRegUrl); +public class AasRepositoryRegistryLinkTest extends AasRepositoryRegistryLinkTestSuite { - printResults(result); + private static final String AAS_REPO_URL = "http://localhost:8081"; + private static final String AAS_REGISTRY_BASE_URL = "http://localhost:8050"; + private static ConfigurableApplicationContext appContext; + private static AasRepositoryRegistryLink aasRepositoryRegistryLink; + + @BeforeClass + public static void setUp() throws FileNotFoundException, IOException { + appContext = new SpringApplication(DummyAasRepositoryIntegrationComponent.class).run(new String[] {}); + + aasRepositoryRegistryLink = appContext.getBean(AasRepositoryRegistryLink.class); } - - private static void printResults(Result result) { - System.out.println("Finished. Result: Failures: " + result.getFailureCount() + ". Ignored: " + result.getIgnoreCount() + ". Tests run: " + result.getRunCount() + ". Time: " + result.getRunTime() + "ms."); + + @AfterClass + public static void tearDown() { + appContext.close(); } - private static Result runTests(String aasRepoBaseUrl, String aasRegUrl) { - AasRepositoryRegistryTestLink.aasRepoBaseUrl = aasRepoBaseUrl; - AasRepositoryRegistryTestLink.aasRegistryUrl = aasRegUrl; - - JUnitCore junit = new JUnitCore(); - junit.addListener(new TextListener(System.out)); - - return junit.run(AasRepositoryRegistryTestLink.class); + @Override + protected String getAasRepoBaseUrl() { + return AAS_REPO_URL; } - private static String getAasRepositoryBaseUrl(String[] args) { - return args[0]; + @Override + protected String getAasRegistryUrl() { + return AAS_REGISTRY_BASE_URL; } - private static String getAasRegistryUrl(String[] args) { - return args[1]; + @Override + protected RegistryAndDiscoveryInterfaceApi getAasRegistryApi() { + + return aasRepositoryRegistryLink.getRegistryApi(); } } diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryTestLink.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLinkTestSuite.java similarity index 86% rename from basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryTestLink.java rename to basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLinkTestSuite.java index 1762f7bc0..a289abfa0 100644 --- a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryTestLink.java +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AasRepositoryRegistryLinkTestSuite.java @@ -45,21 +45,22 @@ import org.springframework.http.HttpStatus; /** - * Integration test for {@link RegistryIntegrationAasRepository} feature + * Test suite for {@link RegistryIntegrationAasRepository} feature * * @author danish */ -public class AasRepositoryRegistryTestLink { +public abstract class AasRepositoryRegistryLinkTestSuite { private static final String AAS_REPOSITORY_PATH = "/shells"; private static final String DUMMY_GLOBAL_ASSETID = "globalAssetId"; private static final String DUMMY_IDSHORT = "ExampleMotor"; private static final String DUMMY_AAS_ID = "customIdentifier"; - public static String aasRepoBaseUrl = "http://localhost:8081"; - public static String aasRegistryUrl = "http://localhost:8050"; + protected abstract String getAasRepoBaseUrl(); + protected abstract String getAasRegistryUrl(); + protected abstract RegistryAndDiscoveryInterfaceApi getAasRegistryApi(); - private static final AssetAdministrationShellDescriptor DUMMY_DESCRIPTOR = DummyAasDescriptorFactory.createDummyDescriptor(DUMMY_AAS_ID, DUMMY_IDSHORT, DUMMY_GLOBAL_ASSETID, aasRepoBaseUrl); + private final AssetAdministrationShellDescriptor DUMMY_DESCRIPTOR = DummyAasDescriptorFactory.createDummyDescriptor(DUMMY_AAS_ID, DUMMY_IDSHORT, DUMMY_GLOBAL_ASSETID, getAasRepoBaseUrl()); @Test public void createAas() throws FileNotFoundException, IOException, ApiException { @@ -89,7 +90,7 @@ public void deleteAas() throws FileNotFoundException, IOException, ApiException } private AssetAdministrationShellDescriptor retrieveDescriptorFromRegistry() throws ApiException { - RegistryAndDiscoveryInterfaceApi api = new RegistryAndDiscoveryInterfaceApi(aasRegistryUrl); + RegistryAndDiscoveryInterfaceApi api = getAasRegistryApi(); return api.getAssetAdministrationShellDescriptorById(DUMMY_AAS_ID); } @@ -105,7 +106,7 @@ private CloseableHttpResponse deleteAasFromRepo(String shellId) throws IOExcepti } private void assertDescriptionDeletionAtRegistry() throws ApiException { - RegistryAndDiscoveryInterfaceApi api = new RegistryAndDiscoveryInterfaceApi(aasRegistryUrl); + RegistryAndDiscoveryInterfaceApi api = getAasRegistryApi(); GetAssetAdministrationShellDescriptorsResult result = api.getAllAssetAdministrationShellDescriptors(null, null, null, null); @@ -119,11 +120,11 @@ private String getAas1JSONString() throws FileNotFoundException, IOException { } private CloseableHttpResponse createAasOnRepo(String aasJsonContent) throws IOException { - return BaSyxHttpTestUtils.executePostOnURL(createAasRepositoryUrl(aasRepoBaseUrl), aasJsonContent); + return BaSyxHttpTestUtils.executePostOnURL(createAasRepositoryUrl(getAasRepoBaseUrl()), aasJsonContent); } private String getSpecificAasAccessURL(String aasId) { - return createAasRepositoryUrl(aasRepoBaseUrl) + "/" + Base64UrlEncodedIdentifier.encodeIdentifier(aasId); + return createAasRepositoryUrl(getAasRepoBaseUrl()) + "/" + Base64UrlEncodedIdentifier.encodeIdentifier(aasId); } private static String createAasRepositoryUrl(String aasRepositoryBaseURL) { diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AuthorizedAasRepositoryRegistryLinkTest.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AuthorizedAasRepositoryRegistryLinkTest.java new file mode 100644 index 000000000..1d9d8bc57 --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/AuthorizedAasRepositoryRegistryLinkTest.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * 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.feature.registry.integration; + +import java.io.FileNotFoundException; +import java.io.IOException; +import org.eclipse.digitaltwin.basyx.aasregistry.client.api.RegistryAndDiscoveryInterfaceApi; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.springframework.boot.SpringApplication; +import org.springframework.context.ConfigurableApplicationContext; + +/** + * Integration test for Authorized Registry + * + * @author danish + */ +public class AuthorizedAasRepositoryRegistryLinkTest extends AasRepositoryRegistryLinkTestSuite { + + private static final String AAS_REPO_URL = "http://localhost:8081"; + private static final String AAS_REGISTRY_BASE_URL = "http://localhost:8051"; + private static ConfigurableApplicationContext appContext; + private static AasRepositoryRegistryLink aasRepositoryRegistryLink; + + @BeforeClass + public static void setUp() throws FileNotFoundException, IOException { + SpringApplication application = new SpringApplication(DummyAasRepositoryIntegrationComponent.class); + application.setAdditionalProfiles("authregistry"); + + appContext = application.run(new String[] {}); + + aasRepositoryRegistryLink = appContext.getBean(AasRepositoryRegistryLink.class); + } + + @AfterClass + public static void tearDown() { + appContext.close(); + } + + @Override + protected String getAasRepoBaseUrl() { + return AAS_REPO_URL; + } + + @Override + protected String getAasRegistryUrl() { + return AAS_REGISTRY_BASE_URL; + } + + @Override + protected RegistryAndDiscoveryInterfaceApi getAasRegistryApi() { + + return aasRepositoryRegistryLink.getRegistryApi(); + } + +} diff --git a/basyx.submodelrepository/basyx.submodelrepository.component/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/component/SubmodelRepositoryRegistryLinkIT.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/DummyAasRepositoryIntegrationComponent.java similarity index 70% rename from basyx.submodelrepository/basyx.submodelrepository.component/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/component/SubmodelRepositoryRegistryLinkIT.java rename to basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/DummyAasRepositoryIntegrationComponent.java index 4dc3f12cb..2b806e31c 100644 --- a/basyx.submodelrepository/basyx.submodelrepository.component/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/component/SubmodelRepositoryRegistryLinkIT.java +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/DummyAasRepositoryIntegrationComponent.java @@ -1,6 +1,6 @@ /******************************************************************************* - * Copyright (C) 2023 the Eclipse BaSyx Authors - * + * 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 @@ -19,19 +19,26 @@ * 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.component; +package org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration; -import org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration.SubmodelRepositoryRegistryTestLink; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; /** - * Integration test for the Submodel Repository integration with Submodel Registry + * Spring application configured for tests. * * @author danish * */ -public class SubmodelRepositoryRegistryLinkIT extends SubmodelRepositoryRegistryTestLink { + +@SpringBootApplication(scanBasePackages = "org.eclipse.digitaltwin.basyx") +public class DummyAasRepositoryIntegrationComponent { + + public static void main(String[] args) { + SpringApplication.run(DummyAasRepositoryIntegrationComponent.class, args); + } } diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/DummyAasRepositoryIntegrationConfiguration.java b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/DummyAasRepositoryIntegrationConfiguration.java new file mode 100644 index 000000000..94a3e7d6d --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/registry/integration/DummyAasRepositoryIntegrationConfiguration.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * 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.feature.registry.integration; + +import java.util.List; + +import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; +import org.eclipse.digitaltwin.basyx.aasrepository.AasRepositoryFactory; +import org.eclipse.digitaltwin.basyx.aasrepository.feature.AasRepositoryFeature; +import org.eclipse.digitaltwin.basyx.aasrepository.feature.DecoratedAasRepositoryFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +/** + * Configuration for tests + * + * @author danish + * + */ +@Configuration +public class DummyAasRepositoryIntegrationConfiguration { + + @Bean + @ConditionalOnMissingBean + public static AasRepository getAasRepository(AasRepositoryFactory aasRepositoryFactory, List features) { + return new DecoratedAasRepositoryFactory(aasRepositoryFactory, features).create(); + } +} diff --git a/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/resources/application-authregistry.properties b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/resources/application-authregistry.properties new file mode 100644 index 000000000..7a3407bfd --- /dev/null +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/resources/application-authregistry.properties @@ -0,0 +1,45 @@ +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://localhost:8051 +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 + +# Authorized registry integration +basyx.aasrepository.feature.registryintegration.authorization.enabled=true +basyx.aasrepository.feature.registryintegration.authorization.token-endpoint=http://localhost:9096/realms/BaSyx/protocol/openid-connect/token +basyx.aasrepository.feature.registryintegration.authorization.grant-type = CLIENT_CREDENTIALS +basyx.aasrepository.feature.registryintegration.authorization.client-id=workstation-1 +basyx.aasrepository.feature.registryintegration.authorization.client-secret=nY0mjyECF60DGzNmQUjL81XurSl8etom +# basyx.aasrepository.feature.registryintegration.authorization.username=test +# basyx.aasrepository.feature.registryintegration.authorization.password=test +# basyx.aasrepository.feature.registryintegration.authorization.scopes=[] diff --git a/basyx.aasrepository/basyx.aasrepository.component/src/main/resources/application-integration.properties b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/resources/application.properties similarity index 93% rename from basyx.aasrepository/basyx.aasrepository.component/src/main/resources/application-integration.properties rename to basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/resources/application.properties index 15c017584..4c42a98bc 100644 --- a/basyx.aasrepository/basyx.aasrepository.component/src/main/resources/application-integration.properties +++ b/basyx.aasrepository/basyx.aasrepository-feature-registry-integration/src/test/resources/application.properties @@ -6,7 +6,7 @@ basyx.aasrepo.name=aas-repo basyx.backend = InMemory -basyx.aasrepository.feature.registryintegration=http://aas-registry-log-mem:8080 +basyx.aasrepository.feature.registryintegration=http://localhost:8050 basyx.externalurl=http://localhost:8081 #basyx.backend = MongoDB diff --git a/basyx.aasrepository/basyx.aasrepository.component/pom.xml b/basyx.aasrepository/basyx.aasrepository.component/pom.xml index 3beec64ed..f9ff96aef 100644 --- a/basyx.aasrepository/basyx.aasrepository.component/pom.xml +++ b/basyx.aasrepository/basyx.aasrepository.component/pom.xml @@ -163,9 +163,6 @@ ${docker.container.waitForEndpoint} - - integration - diff --git a/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/AccessTokenProviderFactory.java b/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/AccessTokenProviderFactory.java new file mode 100644 index 000000000..50b76bfa3 --- /dev/null +++ b/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/AccessTokenProviderFactory.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * 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 org.apache.commons.lang3.NotImplementedException; +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.client.internal.authorization.grant.AccessTokenProvider; +import org.eclipse.digitaltwin.basyx.client.internal.authorization.grant.ClientCredentialAccessTokenProvider; +import org.eclipse.digitaltwin.basyx.client.internal.authorization.grant.GrantType; +import org.eclipse.digitaltwin.basyx.client.internal.authorization.grant.PasswordCredentialAccessTokenProvider; + +import java.util.ArrayList; +import java.util.Collection; + +public class AccessTokenProviderFactory { + + private final GrantType grantType; + private Collection scopes; + private String clientId; + private String clientSecret; + private String username; + private String password; + + public AccessTokenProviderFactory(GrantType grantType, Collection scopes){ + this.grantType = grantType; + this.scopes = scopes; + } + + public void setClientCredentials(String clientId, String clientSecret){ + this.clientId = clientId; + this.clientSecret = clientSecret; + } + + public void setPasswordCredentials(String username, String password){ + this.username = username; + this.password = password; + } + + public AccessTokenProvider create(){ + + if(scopes == null) + scopes = new ArrayList<>(); + + switch(grantType){ + case CLIENT_CREDENTIALS: + if(clientId == null || clientSecret == null) + throw new IllegalArgumentException("Client credentials credentials not set"); + + return new ClientCredentialAccessTokenProvider(new ClientCredential(clientId, clientSecret),scopes); + case PASSWORD: + if(clientId == null) + throw new IllegalArgumentException("ClientId is not set"); + + if(clientSecret == null) + clientSecret = ""; + + if(username == null || password == null) + throw new IllegalArgumentException("Password credentials not set"); + + return new PasswordCredentialAccessTokenProvider(new PasswordCredential(username,password),new ClientCredential(clientId, clientSecret),scopes); + default: + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/grant/GrantType.java b/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/grant/GrantType.java new file mode 100644 index 000000000..511207545 --- /dev/null +++ b/basyx.common/basyx.client/src/main/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/grant/GrantType.java @@ -0,0 +1,16 @@ +package org.eclipse.digitaltwin.basyx.client.internal.authorization.grant; + +public enum GrantType { + CLIENT_CREDENTIALS("client_credentials"), + PASSWORD("password"); + + private final String grantType; + + GrantType(String grantType) { + this.grantType = grantType; + } + + public String getGrantType() { + return this.grantType; + } +} \ No newline at end of file diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/Readme.md b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/Readme.md index 631b96f4a..52b783bb8 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/Readme.md +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/Readme.md @@ -17,3 +17,18 @@ An example valid configuration: basyx.submodelrepository.feature.registryintegration = http://localhost:8060 basyx.externalurl = http://localhost:8081 ``` + +## Submodel Repository Integration with Authorized Submodel Registry + +If the target Submodel Registry has authorization enabled, then the following properties needs to be configured in order to successfully integrate the Descriptor: + +``` +basyx.submodelrepository.feature.registryintegration.authorization.enabled=true +basyx.submodelrepository.feature.registryintegration.authorization.token-endpoint=http://localhost:9096/realms/BaSyx/protocol/openid-connect/token +basyx.submodelrepository.feature.registryintegration.authorization.grant-type = or +basyx.submodelrepository.feature.registryintegration.authorization.client-id = +basyx.submodelrepository.feature.registryintegration.authorization.client-secret = +basyx.submodelrepository.feature.registryintegration.authorization.username=test +basyx.submodelrepository.feature.registryintegration.authorization.password=test +basyx.submodelrepository.feature.registryintegration.authorization.scopes=[] +``` \ No newline at end of file diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/pom.xml b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/pom.xml index c0afbcd7e..15afdd30e 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/pom.xml +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/pom.xml @@ -44,5 +44,25 @@ httpclient5 test + + org.eclipse.digitaltwin.basyx + basyx.filerepository-backend-inmemory + test + + + org.eclipse.digitaltwin.basyx + basyx.submodelrepository-http + test + + + org.eclipse.digitaltwin.basyx + basyx.submodelservice-backend-inmemory + test + + + org.eclipse.digitaltwin.basyx + basyx.submodelrepository-backend-inmemory + test + diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationSubmodelRepositoryConfiguration.java b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationSubmodelRepositoryConfiguration.java index abb3341e2..b0ff9e81d 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationSubmodelRepositoryConfiguration.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationSubmodelRepositoryConfiguration.java @@ -25,6 +25,13 @@ package org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration; +import java.util.Collection; + +import org.eclipse.digitaltwin.basyx.client.internal.authorization.AccessTokenProviderFactory; +import org.eclipse.digitaltwin.basyx.client.internal.authorization.TokenManager; +import org.eclipse.digitaltwin.basyx.client.internal.authorization.grant.AccessTokenProvider; +import org.eclipse.digitaltwin.basyx.client.internal.authorization.grant.GrantType; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.AuthorizedConnectedSubmodelRegistry; import org.eclipse.digitaltwin.basyx.submodelregistry.client.api.SubmodelRegistryApi; import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository; import org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration.mapper.AttributeMapper; @@ -44,12 +51,47 @@ @Configuration @ConditionalOnExpression("!T(org.springframework.util.StringUtils).isEmpty('${basyx.submodelrepository.feature.registryintegration:}') && !T(org.springframework.util.StringUtils).isEmpty('${basyx.externalurl:}')") public class RegistryIntegrationSubmodelRepositoryConfiguration { + + @Value("${basyx.submodelrepository.feature.registryintegration:#{null}}") + private String registryBasePath; + + @Value("${basyx.externalurl:#{null}}") + private String submodelRepositoryBaseURL; + + @Value("${basyx.submodelrepository.feature.registryintegration.authorization.enabled:false}") + private boolean isAuthorizationEnabledOnRegistry; + + @Value("${basyx.submodelrepository.feature.registryintegration.authorization.token-endpoint:#{null}}") + private String authenticationServerTokenEndpoint; + + @Value("${basyx.submodelrepository.feature.registryintegration.authorization.grant-type:#{null}}") + private String grantType; + + @Value("${basyx.submodelrepository.feature.registryintegration.authorization.client-id:#{null}}") + private String clientId; + + @Value("${basyx.submodelrepository.feature.registryintegration.authorization.client-secret:#{null}}") + private String clientSecret; + + @Value("${basyx.submodelrepository.feature.registryintegration.authorization.username:#{null}}") + private String username; + + @Value("${basyx.submodelrepository.feature.registryintegration.authorization.password:#{null}}") + private String password; + + @Value("${basyx.submodelrepository.feature.registryintegration.authorization.scopes:#{null}}") + private Collection scopes; @Bean @ConditionalOnMissingBean public SubmodelRepositoryRegistryLink getSubmodelRepositoryRegistryLink(@Value("${basyx.submodelrepository.feature.registryintegration}") String registryBasePath, @Value("${basyx.externalurl}") String submodelRepositoryBaseURL) { - return new SubmodelRepositoryRegistryLink(new SubmodelRegistryApi(registryBasePath), submodelRepositoryBaseURL); + if (!isAuthorizationEnabledOnRegistry) + return new SubmodelRepositoryRegistryLink(new SubmodelRegistryApi(registryBasePath), submodelRepositoryBaseURL); + + TokenManager tokenManager = new TokenManager(authenticationServerTokenEndpoint, createAccessTokenProvider()); + + return new SubmodelRepositoryRegistryLink(new AuthorizedConnectedSubmodelRegistry(registryBasePath, tokenManager), submodelRepositoryBaseURL); } @Bean @@ -58,5 +100,14 @@ public AttributeMapper getSubmodelAttributeMapper(ObjectMapper objectMapper) { return new AttributeMapper(objectMapper); } + + private AccessTokenProvider createAccessTokenProvider() { + + AccessTokenProviderFactory factory = new AccessTokenProviderFactory(GrantType.valueOf(grantType), scopes); + factory.setClientCredentials(clientId, clientSecret); + factory.setPasswordCredentials(username, password); + + return factory.create(); + } } diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/AuthorizedSubmodelRepositoryRegistryLinkTest.java b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/AuthorizedSubmodelRepositoryRegistryLinkTest.java new file mode 100644 index 000000000..8a37e602a --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/AuthorizedSubmodelRepositoryRegistryLinkTest.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (C) 2024 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration; + +import java.io.FileNotFoundException; +import java.io.IOException; + +import org.eclipse.digitaltwin.basyx.submodelregistry.client.api.SubmodelRegistryApi; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.springframework.boot.SpringApplication; +import org.springframework.context.ConfigurableApplicationContext; + +/** + * Integration test for Authorized Registry + * + * @author danish + */ +public class AuthorizedSubmodelRepositoryRegistryLinkTest extends SubmodelRepositoryRegistryLinkTestSuite { + + private static final String SUBMODEL_REPO_URL = "http://localhost:8081"; + private static final String SUBMODEL_REGISTRY_BASE_URL = "http://localhost:8061"; + private static ConfigurableApplicationContext appContext; + private static SubmodelRepositoryRegistryLink submodelRepositoryRegistryLink; + + @BeforeClass + public static void setUp() throws FileNotFoundException, IOException { + SpringApplication application = new SpringApplication(DummySubmodelRepositoryIntegrationComponent.class); + application.setAdditionalProfiles("authregistry"); + + appContext = application.run(new String[] {}); + + submodelRepositoryRegistryLink = appContext.getBean(SubmodelRepositoryRegistryLink.class); + } + + @AfterClass + public static void tearDown() { + appContext.close(); + } + + @Override + protected String getSubmodelRepoBaseUrl() { + return SUBMODEL_REPO_URL; + } + + @Override + protected String getSubmodelRegistryUrl() { + return SUBMODEL_REGISTRY_BASE_URL; + } + + @Override + protected SubmodelRegistryApi getSubmodelRegistryApi() { + + return submodelRepositoryRegistryLink.getRegistryApi(); + } + + +} diff --git a/basyx.aasrepository/basyx.aasrepository.component/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/component/AASRepositoryRegistryLinkIT.java b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/DummySubmodelRepositoryIntegrationComponent.java similarity index 69% rename from basyx.aasrepository/basyx.aasrepository.component/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/component/AASRepositoryRegistryLinkIT.java rename to basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/DummySubmodelRepositoryIntegrationComponent.java index d8ecb45c5..cc48ba509 100644 --- a/basyx.aasrepository/basyx.aasrepository.component/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/component/AASRepositoryRegistryLinkIT.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/DummySubmodelRepositoryIntegrationComponent.java @@ -1,6 +1,6 @@ /******************************************************************************* - * Copyright (C) 2023 the Eclipse BaSyx Authors - * + * 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 @@ -19,19 +19,26 @@ * 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.component; +package org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration; -import org.eclipse.digitaltwin.basyx.aasrepository.feature.registry.integration.AasRepositoryRegistryTestLink; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; /** - * Integration test for the AAS Repository integration with AAS Registry + * Spring application configured for tests. * * @author danish * */ -public class AASRepositoryRegistryLinkIT extends AasRepositoryRegistryTestLink { + +@SpringBootApplication(scanBasePackages = "org.eclipse.digitaltwin.basyx") +public class DummySubmodelRepositoryIntegrationComponent { + + public static void main(String[] args) { + SpringApplication.run(DummySubmodelRepositoryIntegrationComponent.class, args); + } } diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/DummySubmodelRepositoryIntegrationConfiguration.java b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/DummySubmodelRepositoryIntegrationConfiguration.java new file mode 100644 index 000000000..2b4baea1a --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/DummySubmodelRepositoryIntegrationConfiguration.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (C) 2024 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration; + +import java.util.List; + +import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository; +import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepositoryFactory; +import org.eclipse.digitaltwin.basyx.submodelrepository.feature.DecoratedSubmodelRepositoryFactory; +import org.eclipse.digitaltwin.basyx.submodelrepository.feature.SubmodelRepositoryFeature; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +/** + * Configuration for tests + * + * @author danish + * + */ +@Configuration +public class DummySubmodelRepositoryIntegrationConfiguration { + + @Bean + @ConditionalOnMissingBean + public static SubmodelRepository getSubmodelRepository(SubmodelRepositoryFactory submodelRepositoryFactory, List features) { + return new DecoratedSubmodelRepositoryFactory(submodelRepositoryFactory, features).create(); + } +} diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryLinkTest.java b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryLinkTest.java new file mode 100644 index 000000000..8b08db14d --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryLinkTest.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (C) 2023 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration; + +import java.io.FileNotFoundException; +import java.io.IOException; +import org.eclipse.digitaltwin.basyx.submodelregistry.client.api.SubmodelRegistryApi; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.springframework.boot.SpringApplication; +import org.springframework.context.ConfigurableApplicationContext; + +/** + * Integration test for {@link RegistryIntegrationSubmodelRepository} feature + * + * @author danish + */ +public class SubmodelRepositoryRegistryLinkTest extends SubmodelRepositoryRegistryLinkTestSuite { + + private static final String SUBMODEL_REPO_URL = "http://localhost:8081"; + private static final String SUBMODEL_REGISTRY_BASE_URL = "http://localhost:8060"; + private static ConfigurableApplicationContext appContext; + private static SubmodelRepositoryRegistryLink submodelRepositoryRegistryLink; + + @BeforeClass + public static void setUp() throws FileNotFoundException, IOException { + appContext = new SpringApplication(DummySubmodelRepositoryIntegrationComponent.class).run(new String[] {}); + + submodelRepositoryRegistryLink = appContext.getBean(SubmodelRepositoryRegistryLink.class); + } + + @AfterClass + public static void tearDown() { + appContext.close(); + } + + @Override + protected String getSubmodelRepoBaseUrl() { + return SUBMODEL_REPO_URL; + } + @Override + protected String getSubmodelRegistryUrl() { + return SUBMODEL_REGISTRY_BASE_URL; + } + @Override + protected SubmodelRegistryApi getSubmodelRegistryApi() { + + return submodelRepositoryRegistryLink.getRegistryApi(); + } + + +} diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryTestLink.java b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryLinkTestSuite.java similarity index 86% rename from basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryTestLink.java rename to basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryLinkTestSuite.java index e0db76372..54b704301 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryTestLink.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryLinkTestSuite.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2023 the Eclipse BaSyx Authors + * 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 @@ -46,21 +46,22 @@ import org.springframework.http.HttpStatus; /** - * Integration test for {@link RegistryIntegrationSubmodelRepository} feature + * Test suite for {@link RegistryIntegrationSubmodelRepository} feature * * @author danish */ -public class SubmodelRepositoryRegistryTestLink { +public abstract class SubmodelRepositoryRegistryLinkTestSuite { private static final String SUBMODEL_REPOSITORY_PATH = "/submodels"; private static final String DUMMY_SUBMODEL_IDSHORT = "TechnicalData"; private static final String DUMMY_SUBMODEL_ID = "7A7104BDAB57E184"; + + protected abstract String getSubmodelRepoBaseUrl(); + protected abstract String getSubmodelRegistryUrl(); + protected abstract SubmodelRegistryApi getSubmodelRegistryApi(); - public static String submodelRepoBaseUrl = "http://localhost:8081"; - public static String submodelRegistryUrl = "http://localhost:8060"; - - private static final SubmodelDescriptor DUMMY_DESCRIPTOR = DummySubmodelDescriptorFactory.createDummyDescriptor(DUMMY_SUBMODEL_ID, DUMMY_SUBMODEL_IDSHORT, submodelRepoBaseUrl, DummySubmodelDescriptorFactory.createSemanticId()); + private final SubmodelDescriptor DUMMY_DESCRIPTOR = DummySubmodelDescriptorFactory.createDummyDescriptor(DUMMY_SUBMODEL_ID, DUMMY_SUBMODEL_IDSHORT, getSubmodelRepoBaseUrl(), DummySubmodelDescriptorFactory.createSemanticId()); @Test public void createSubmodel() throws FileNotFoundException, IOException, ApiException { @@ -84,7 +85,7 @@ public void createSubmodelElementInSubmodelElementCollection() throws FileNotFou CloseableHttpResponse creationResponse = createSubmodelOnRepo(submodelJsonContent); assertEquals(HttpStatus.CREATED.value(), creationResponse.getCode()); - CloseableHttpResponse submodelElementCreationResponse = createSubmodelElementOnRepo(submodelElementJsonContent); + createSubmodelElementOnRepo(submodelElementJsonContent); CloseableHttpResponse getResponse = BaSyxHttpTestUtils.executeGetOnURL(getSpecificSubmodelAccessURL(DUMMY_SUBMODEL_ID)); BaSyxHttpTestUtils.assertSameJSONContent(getExpectedSubmodel(), BaSyxHttpTestUtils.getResponseAsString(getResponse)); @@ -106,7 +107,7 @@ public void deleteSubmodel() throws FileNotFoundException, IOException, ApiExcep } private SubmodelDescriptor retrieveDescriptorFromRegistry() throws ApiException { - SubmodelRegistryApi api = new SubmodelRegistryApi(submodelRegistryUrl); + SubmodelRegistryApi api = getSubmodelRegistryApi(); return api.getSubmodelDescriptorById(DUMMY_SUBMODEL_ID); } @@ -122,7 +123,7 @@ private CloseableHttpResponse deleteSubmodelFromRepo(String shellId) throws IOEx } private void assertDescriptionDeletionAtRegistry() throws ApiException { - SubmodelRegistryApi api = new SubmodelRegistryApi(submodelRegistryUrl); + SubmodelRegistryApi api = getSubmodelRegistryApi(); GetSubmodelDescriptorsResult result = api.getAllSubmodelDescriptors(null, null); @@ -144,7 +145,7 @@ private String getExpectedSubmodel() throws FileNotFoundException, IOException { } private CloseableHttpResponse createSubmodelOnRepo(String submodelJsonContent) throws IOException { - return BaSyxHttpTestUtils.executePostOnURL(createSubmodelRepositoryUrl(submodelRepoBaseUrl), submodelJsonContent); + return BaSyxHttpTestUtils.executePostOnURL(createSubmodelRepositoryUrl(getSubmodelRepoBaseUrl()), submodelJsonContent); } private CloseableHttpResponse createSubmodelElementOnRepo(String submodelElementJsonContent) throws IOException { @@ -154,7 +155,7 @@ private CloseableHttpResponse createSubmodelElementOnRepo(String submodelElement } private String getSpecificSubmodelAccessURL(String submodelId) { - return createSubmodelRepositoryUrl(submodelRepoBaseUrl) + "/" + Base64UrlEncodedIdentifier.encodeIdentifier(submodelId); + return createSubmodelRepositoryUrl(getSubmodelRepoBaseUrl()) + "/" + Base64UrlEncodedIdentifier.encodeIdentifier(submodelId); } private static String createSubmodelRepositoryUrl(String smRepositoryBaseURL) { diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryTestLinkApplication.java b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryTestLinkApplication.java deleted file mode 100644 index 6b1cd2687..000000000 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/SubmodelRepositoryRegistryTestLinkApplication.java +++ /dev/null @@ -1,72 +0,0 @@ -/******************************************************************************* - * Copyright (C) 2023 the Eclipse BaSyx Authors - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * SPDX-License-Identifier: MIT - ******************************************************************************/ -package org.eclipse.digitaltwin.basyx.submodelrepository.feature.registry.integration; - -import org.junit.internal.TextListener; -import org.junit.runner.JUnitCore; -import org.junit.runner.Result; - -/** - * Application for testing the {@link RegistryIntegrationSubmodelRepository} - * feature. The first argument is the Submodel Repository Base URL, the second - * argument is the Submodel Registry URL. - * - * @author schnicke, danish - * - */ -public class SubmodelRepositoryRegistryTestLinkApplication { - - public static void main(String[] args) throws Exception { - String submodelRepoBaseUrl = getSubmodelRepositoryBaseUrl(args); - String submodelRegUrl = getSubmodelRegistryUrl(args); - - Result result = runTests(submodelRepoBaseUrl, submodelRegUrl); - - printResults(result); - } - - private static void printResults(Result result) { - System.out.println("Finished. Result: Failures: " + result.getFailureCount() + ". Ignored: " + result.getIgnoreCount() + ". Tests run: " + result.getRunCount() + ". Time: " + result.getRunTime() + "ms."); - } - - private static Result runTests(String submodelRepoBaseUrl, String submodelRegUrl) { - SubmodelRepositoryRegistryTestLink.submodelRepoBaseUrl = submodelRepoBaseUrl; - SubmodelRepositoryRegistryTestLink.submodelRegistryUrl = submodelRegUrl; - - JUnitCore junit = new JUnitCore(); - junit.addListener(new TextListener(System.out)); - - return junit.run(SubmodelRepositoryRegistryTestLink.class); - } - - private static String getSubmodelRepositoryBaseUrl(String[] args) { - return args[0]; - } - - private static String getSubmodelRegistryUrl(String[] args) { - return args[1]; - } - -} diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/resources/application-authregistry.properties b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/resources/application-authregistry.properties new file mode 100644 index 000000000..0b9e4b2e5 --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/resources/application-authregistry.properties @@ -0,0 +1,45 @@ +server.port=8081 +server.error.path=/error + +spring.application.name=AAS Repository +basyx.smrepo.name = sm-repo + +basyx.backend = InMemory + +basyx.submodelrepository.feature.registryintegration=http://localhost:8061 +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 + +# Authorized registry integration +basyx.submodelrepository.feature.registryintegration.authorization.enabled=true +basyx.submodelrepository.feature.registryintegration.authorization.token-endpoint=http://localhost:9096/realms/BaSyx/protocol/openid-connect/token +basyx.submodelrepository.feature.registryintegration.authorization.grant-type = CLIENT_CREDENTIALS +basyx.submodelrepository.feature.registryintegration.authorization.client-id=workstation-1 +basyx.submodelrepository.feature.registryintegration.authorization.client-secret=nY0mjyECF60DGzNmQUjL81XurSl8etom +# basyx.submodelrepository.feature.registryintegration.authorization.username=test +# basyx.submodelrepository.feature.registryintegration.authorization.password=test +# basyx.submodelrepository.feature.registryintegration.authorization.scopes=[] diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/resources/application.properties b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/resources/application.properties new file mode 100644 index 000000000..61ad940e9 --- /dev/null +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/test/resources/application.properties @@ -0,0 +1,34 @@ +server.port=8081 +server.error.path=/error + +spring.application.name=AAS Repository +basyx.smrepo.name = sm-repo + +basyx.backend = InMemory + +basyx.submodelrepository.feature.registryintegration=http://localhost:8060 +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 diff --git a/basyx.submodelrepository/basyx.submodelrepository.component/pom.xml b/basyx.submodelrepository/basyx.submodelrepository.component/pom.xml index a67695d6c..66cc6d53c 100644 --- a/basyx.submodelrepository/basyx.submodelrepository.component/pom.xml +++ b/basyx.submodelrepository/basyx.submodelrepository.component/pom.xml @@ -181,9 +181,6 @@ ${docker.container.waitForEndpoint} - - integration -