diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/pom.xml b/basyx.aasenvironment/basyx.aasenvironment-client/pom.xml index 4f3e5f019..f0ceddf4f 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-client/pom.xml +++ b/basyx.aasenvironment/basyx.aasenvironment-client/pom.xml @@ -92,6 +92,10 @@ basyx.aasenvironment-feature-authorization test + + org.eclipse.digitaltwin.basyx + basyx.aasenvironment-feature-authorization + org.apache.httpcomponents.client5 httpclient5 diff --git a/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/DummyAasEnvironmentConfiguration.java b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/DummyAasEnvironmentConfiguration.java index b8bdf7e4a..0a7a07f43 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/DummyAasEnvironmentConfiguration.java +++ b/basyx.aasenvironment/basyx.aasenvironment-client/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/client/DummyAasEnvironmentConfiguration.java @@ -31,9 +31,11 @@ import org.eclipse.digitaltwin.basyx.aasenvironment.AasEnvironmentFactory; import org.eclipse.digitaltwin.basyx.aasenvironment.feature.AasEnvironmentFeature; import org.eclipse.digitaltwin.basyx.aasenvironment.feature.DecoratedAasEnvironmentFactory; +import org.eclipse.digitaltwin.basyx.aasenvironment.preconfiguration.AasEnvironmentPreconfigurationLoader; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ResourceLoader; /** * Dummy configuration for testing the {@link AasEnvironment} authorization @@ -51,4 +53,10 @@ public static AasEnvironment getAasEnvironment(AasEnvironmentFactory aasEnvironm return new DecoratedAasEnvironmentFactory(aasEnvironmentFactory, features).create(); } + @Bean + @ConditionalOnMissingBean + public static AasEnvironmentPreconfigurationLoader getPreconfigurationLoaderForAasEnvironment(ResourceLoader resourceLoader, List pathsToLoad) { + return new AasEnvironmentPreconfigurationLoader(resourceLoader, pathsToLoad); + } + } diff --git a/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/preconfiguration/AasEnvironmentPreconfigurationLoader.java b/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/preconfiguration/AasEnvironmentPreconfigurationLoader.java index 559612d0d..0a19055c2 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/preconfiguration/AasEnvironmentPreconfigurationLoader.java +++ b/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/preconfiguration/AasEnvironmentPreconfigurationLoader.java @@ -26,8 +26,13 @@ package org.eclipse.digitaltwin.basyx.aasenvironment.preconfiguration; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.security.interfaces.RSAPublicKey; import java.util.ArrayList; + import java.util.List; import java.util.stream.Collectors; @@ -36,10 +41,13 @@ import org.eclipse.digitaltwin.basyx.aasenvironment.AasEnvironment; import org.eclipse.digitaltwin.basyx.aasenvironment.environmentloader.CompleteEnvironment; import org.eclipse.digitaltwin.basyx.aasenvironment.environmentloader.CompleteEnvironment.EnvironmentType; +import org.eclipse.digitaltwin.basyx.authorization.CommonAuthorizationProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.context.annotation.Primary; import org.springframework.core.io.ResourceLoader; import org.springframework.integration.file.RecursiveDirectoryScanner; import org.springframework.stereotype.Component; @@ -50,17 +58,15 @@ * @author fried, mateusmolina, despen, witt, jungjan, danish * */ -@Component public class AasEnvironmentPreconfigurationLoader { - + private Logger logger = LoggerFactory.getLogger(AasEnvironmentPreconfigurationLoader.class); @Value("${basyx.environment:#{null}}") private List pathsToLoad; private ResourceLoader resourceLoader; - - @Autowired + public AasEnvironmentPreconfigurationLoader(ResourceLoader resourceLoader, List pathsToLoad) { this.resourceLoader = resourceLoader; this.pathsToLoad = pathsToLoad; @@ -134,4 +140,5 @@ private void logLoadingProcess(int current, int overall, String filename) { logger.info("Loading AAS Environment ({}/{}) from file '{}'", current, overall, filename); } + } \ No newline at end of file diff --git a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/Readme.md b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/Readme.md index 760078f1d..4ebbbfabf 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/Readme.md +++ b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/Readme.md @@ -11,6 +11,17 @@ basyx.feature.authorization.rbac.file = ``` +If you want to use a preconfigured environment with authorization, you need to set the following options as well: +``` +basyx.aasenvironment.authorization.preconfiguration.token-endpoint = +basyx.aasenvironment.authorization.preconfiguration.grant-type = +basyx.aasenvironment.authorization.preconfiguration.client-id = +basyx.aasenvironment.authorization.preconfiguration.client-secret= +basyx.aasenvironment.authorization.preconfiguration.username = +basyx.aasenvironment.authorization.preconfiguration.password = +basyx.aasenvironment.authorization.preconfiguration.scopes = +``` + Note: Only Role Based Access Control (RBAC) is supported as authorization type as of now, also Keycloak is the only Jwt token provider supported now and it is also a default provider. To know more about the RBAC, please refer [Authorization Services Guide](https://www.keycloak.org/docs/latest/authorization_services/index.html) @@ -26,6 +37,15 @@ basyx.feature.authorization.rbac.file = classpath:rbac_rules.json spring.security.oauth2.resourceserver.jwt.issuer-uri= http://localhost:9096/realms/BaSyx ``` +And for preconfiguration (Grant Type would be CLIENT_CREDENTIALS in this case): + +``` +basyx.aasenvironment.authorization.preconfiguration.token-endpoint = http://localhost:9096/realms/BaSyx/protocol/openid-connect/token +basyx.aasenvironment.authorization.preconfiguration.grant-type = CLIENT_CREDENTIALS +basyx.aasenvironment.authorization.preconfiguration.client-id = workstation-1 +basyx.aasenvironment.authorization.preconfiguration.client-secret = nY0mjyECF60DGzNmQUjL81XurSl8etom +``` + ## RBAC rule configuration For configuring RBAC rules, all the rbac rules should be configured inside a json file, the rules are defined as below: diff --git a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/pom.xml b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/pom.xml index 9da9bb635..714cac31f 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/pom.xml +++ b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/pom.xml @@ -35,6 +35,10 @@ org.eclipse.digitaltwin.basyx basyx.http + + org.eclipse.digitaltwin.basyx + basyx.client + org.eclipse.digitaltwin.basyx basyx.authorization diff --git a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/AuthorizedAASEnvironmentPreconfigurationLoader.java b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/AuthorizedAASEnvironmentPreconfigurationLoader.java new file mode 100644 index 000000000..32ea360d3 --- /dev/null +++ b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/AuthorizedAASEnvironmentPreconfigurationLoader.java @@ -0,0 +1,128 @@ +/******************************************************************************* + * Copyright (C) 2024 the Eclipse BaSyx Authors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + ******************************************************************************/ + +package org.eclipse.digitaltwin.basyx.aasenvironment.feature.authorization; + +import org.apache.commons.io.IOUtils; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.DeserializationException; +import org.eclipse.digitaltwin.basyx.aasenvironment.AasEnvironment; +import org.eclipse.digitaltwin.basyx.aasenvironment.preconfiguration.AasEnvironmentPreconfigurationLoader; +import org.eclipse.digitaltwin.basyx.authorization.jwt.JwtTokenDecoder; +import org.eclipse.digitaltwin.basyx.authorization.jwt.PublicKeyUtils; +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.core.io.ClassPathResource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.security.interfaces.RSAPublicKey; +import java.util.Collection; +import java.util.List; + +public class AuthorizedAASEnvironmentPreconfigurationLoader extends AasEnvironmentPreconfigurationLoader { + + @Value("${basyx.aasenvironment.authorization.preconfiguration.token-endpoint:#{null}}") + private String authenticationServerTokenEndpoint; + + @Value("${basyx.aasenvironment.authorization.preconfiguration.client-id:#{null}}") + private String clientId; + + @Value("${basyx.aasenvironment.authorization.preconfiguration.client-secret:#{null}}") + private String clientSecret; + + @Value("${basyx.aasenvironment.authorization.preconfiguration.username:#{null}}") + private String username; + + @Value("${basyx.aasenvironment.authorization.preconfiguration.password:#{null}}") + private String password; + + @Value("${basyx.aasenvironment.authorization.preconfiguration.grant-type:#{null}}") + private String grantType; + + @Value("${basyx.aasenvironment.authorization.preconfiguration.scopes:#{null}}") + private Collection scopes; + + @Value("${basyx.environment:#{null}}") + private String basyxEnvironment; + + private AccessTokenProvider tokenProvider; + + public AuthorizedAASEnvironmentPreconfigurationLoader(ResourceLoader resourceLoader, List pathsToLoad) { + super(resourceLoader, pathsToLoad); + } + + @Override + public void loadPreconfiguredEnvironments(AasEnvironment aasEnvironment) throws IOException, InvalidFormatException, DeserializationException { + if(isEnvironmentSet()) { + setUpTokenProvider(); + configureSecurityContext(); + } + super.loadPreconfiguredEnvironments(aasEnvironment); + SecurityContextHolder.clearContext(); + } + + + private void setUpTokenProvider() { + AccessTokenProviderFactory factory = new AccessTokenProviderFactory(GrantType.valueOf(grantType),scopes); + factory.setClientCredentials(clientId, clientSecret); + factory.setPasswordCredentials(username, password); + this.tokenProvider = factory.create(); + } + + private void configureSecurityContext() throws FileNotFoundException, IOException { + TokenManager tokenManager = new TokenManager(authenticationServerTokenEndpoint, tokenProvider); + String adminToken = tokenManager.getAccessToken(); + + String modulus = getStringFromFile("authorization/modulus.txt"); + String exponent = "AQAB"; + + RSAPublicKey rsaPublicKey = PublicKeyUtils.buildPublicKey(modulus, exponent); + + Jwt jwt = JwtTokenDecoder.decodeJwt(adminToken, rsaPublicKey); + + SecurityContextHolder.getContext().setAuthentication(new JwtAuthenticationToken(jwt)); + } + + + private String getStringFromFile(String fileName) throws FileNotFoundException, IOException { + ClassPathResource classPathResource = new ClassPathResource(fileName); + InputStream in = classPathResource.getInputStream(); + return IOUtils.toString(in, StandardCharsets.UTF_8.name()); + } + + private boolean isEnvironmentSet() { + return basyxEnvironment != null; + } +} diff --git a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/AuthorizedAasEnvironmentConfiguration.java b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/AuthorizedAasEnvironmentConfiguration.java index d59defa8c..79475434a 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/AuthorizedAasEnvironmentConfiguration.java +++ b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/AuthorizedAasEnvironmentConfiguration.java @@ -27,6 +27,7 @@ import org.eclipse.digitaltwin.basyx.aasenvironment.AasEnvironment; import org.eclipse.digitaltwin.basyx.aasenvironment.feature.authorization.rbac.AasEnvironmentTargetPermissionVerifier; +import org.eclipse.digitaltwin.basyx.aasenvironment.preconfiguration.AasEnvironmentPreconfigurationLoader; import org.eclipse.digitaltwin.basyx.authorization.CommonAuthorizationProperties; import org.eclipse.digitaltwin.basyx.authorization.rbac.RbacPermissionResolver; import org.eclipse.digitaltwin.basyx.authorization.rbac.RbacStorage; @@ -36,6 +37,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.core.io.ResourceLoader; + +import java.util.List; /** * Configuration for authorized {@link AasEnvironment} @@ -57,4 +62,10 @@ public RbacPermissionResolver getAasEnvironment return new SimpleRbacPermissionResolver<>(rbacStorage, roleProvider, targetPermissionVerifier); } + @Bean + @Primary + public static AasEnvironmentPreconfigurationLoader getAuthorizedAasEnvironmentPreconfigurationLoader(ResourceLoader resourceLoader, List pathsToLoad) { + return new AuthorizedAASEnvironmentPreconfigurationLoader(resourceLoader, pathsToLoad); + } + } diff --git a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/TestAuthorizedAasEnvironmentSerialization.java b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/TestAuthorizedAasEnvironmentSerialization.java index 8ca6519c1..8f056bd58 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/TestAuthorizedAasEnvironmentSerialization.java +++ b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/TestAuthorizedAasEnvironmentSerialization.java @@ -97,17 +97,6 @@ public static void tearDown() { appContext.close(); } - @Before - public void initializeRepositories() throws FileNotFoundException, IOException { - configureSecurityContext(); - - createDummyShellsOnRepository(TestAASEnvironmentSerialization.createDummyShells(), aasRepo); - createDummySubmodelsOnRepository(TestAASEnvironmentSerialization.createDummySubmodels(), submodelRepo); - createDummyConceptDescriptionsOnRepository(TestAASEnvironmentSerialization.createDummyConceptDescriptions(), conceptDescriptionRepo); - - clearSecurityContext(); - } - @After public void reset() throws FileNotFoundException, IOException { configureSecurityContext(); @@ -119,7 +108,12 @@ public void reset() throws FileNotFoundException, IOException { assetAdministrationShells.stream().forEach(aas -> aasRepo.deleteAas(aas.getId())); submodels.stream().forEach(sm -> submodelRepo.deleteSubmodel(sm.getId())); conceptDescriptions.stream().forEach(cd -> conceptDescriptionRepo.deleteConceptDescription(cd.getId())); - + + + createDummyShellsOnRepository(TestAASEnvironmentSerialization.createDummyShells(), aasRepo); + createDummySubmodelsOnRepository(TestAASEnvironmentSerialization.createDummySubmodels(), submodelRepo); + createDummyConceptDescriptionsOnRepository(TestAASEnvironmentSerialization.createDummyConceptDescriptions(), conceptDescriptionRepo); + clearSecurityContext(); } diff --git a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/TestAuthorizedAasEnvironmentUpload.java b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/TestAuthorizedAasEnvironmentUpload.java index 4069daa3a..785ae35ad 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/TestAuthorizedAasEnvironmentUpload.java +++ b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/feature/authorization/TestAuthorizedAasEnvironmentUpload.java @@ -51,9 +51,7 @@ import org.eclipse.digitaltwin.basyx.core.pagination.PaginationInfo; import org.eclipse.digitaltwin.basyx.http.serialization.BaSyxHttpTestUtils; import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.*; import org.springframework.boot.SpringApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.HttpStatus; @@ -80,8 +78,8 @@ public class TestAuthorizedAasEnvironmentUpload { private static String healthEndpointUrl = "http://127.0.0.1:8081/actuator/health"; - @Before - public void setUp() throws FileNotFoundException, IOException { + @BeforeClass + public static void setUp() throws FileNotFoundException, IOException { tokenProvider = new AccessTokenProvider(authenticaltionServerTokenEndpoint, clientId); appContext = new SpringApplication(DummyAasEnvironmentComponent.class).run(new String[] {}); @@ -91,7 +89,7 @@ public void setUp() throws FileNotFoundException, IOException { conceptDescriptionRepo = appContext.getBean(ConceptDescriptionRepository.class); } - @After + @Before public void reset() throws FileNotFoundException, IOException { configureSecurityContext(); @@ -105,7 +103,10 @@ public void reset() throws FileNotFoundException, IOException { conceptDescriptions.stream().forEach(cd -> conceptDescriptionRepo.deleteConceptDescription(cd.getId())); clearSecurityContext(); - + } + + @AfterClass + public static void shutDown(){ appContext.close(); } diff --git a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/resources/application.properties b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/resources/application.properties index fd56412cd..c2d23e91a 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/resources/application.properties +++ b/basyx.aasenvironment/basyx.aasenvironment-feature-authorization/src/test/resources/application.properties @@ -27,7 +27,7 @@ basyx.backend = InMemory # 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 +basyx.environment = classpath:testEnvironment.aasx # #################################################################################### @@ -39,6 +39,10 @@ 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 +basyx.aasenvironment.authorization.preconfiguration.token-endpoint=http://localhost:9096/realms/BaSyx/protocol/openid-connect/token +basyx.aasenvironment.authorization.preconfiguration.grant-type = CLIENT_CREDENTIALS +basyx.aasenvironment.authorization.preconfiguration.client-id=workstation-1 +basyx.aasenvironment.authorization.preconfiguration.client-secret=nY0mjyECF60DGzNmQUjL81XurSl8etom #################################################################################### # Operation Delegation #################################################################################### diff --git a/basyx.aasenvironment/basyx.aasenvironment-http/src/test/java/org/eclipse/basyx/digitaltwin/aasenvironment/http/DummyAASEnvironmentComponent.java b/basyx.aasenvironment/basyx.aasenvironment-http/src/test/java/org/eclipse/basyx/digitaltwin/aasenvironment/http/DummyAASEnvironmentComponent.java index 2ced71b57..ec50e8040 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-http/src/test/java/org/eclipse/basyx/digitaltwin/aasenvironment/http/DummyAASEnvironmentComponent.java +++ b/basyx.aasenvironment/basyx.aasenvironment-http/src/test/java/org/eclipse/basyx/digitaltwin/aasenvironment/http/DummyAASEnvironmentComponent.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.List; import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell; import org.eclipse.digitaltwin.aas4j.v3.model.AssetKind; @@ -37,6 +38,7 @@ import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultConceptDescription; import org.eclipse.digitaltwin.basyx.aasenvironment.AasEnvironment; import org.eclipse.digitaltwin.basyx.aasenvironment.base.DefaultAASEnvironment; +import org.eclipse.digitaltwin.basyx.aasenvironment.preconfiguration.AasEnvironmentPreconfigurationLoader; import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.ConceptDescriptionRepository; import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository; @@ -44,6 +46,7 @@ import org.eclipse.digitaltwin.basyx.submodelservice.SubmodelServiceHelper; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; +import org.springframework.core.io.ResourceLoader; @SpringBootApplication(scanBasePackages = "org.eclipse.digitaltwin.basyx") public class DummyAASEnvironmentComponent { @@ -59,6 +62,11 @@ public AasEnvironment createAasEnvironmentSerialization(AasRepository aasReposit return new DefaultAASEnvironment(aasRepository, submodelRepository, conceptDescriptionRepository); } + @Bean + public AasEnvironmentPreconfigurationLoader createAasEnvironmentPreconfigurationLoader(ResourceLoader resourceLoader, List pathsToLoad) { + return new AasEnvironmentPreconfigurationLoader(resourceLoader, pathsToLoad); + } + public void initRepositories(AasRepository aasRepository, SubmodelRepository submodelRepository, ConceptDescriptionRepository conceptDescriptionRepository) { createDummySubmodels().forEach(submodelRepository::createSubmodel); createDummyShells().forEach(aasRepository::createAas); diff --git a/basyx.aasenvironment/basyx.aasenvironment.component/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/component/AasEnvironmentConfiguration.java b/basyx.aasenvironment/basyx.aasenvironment.component/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/component/AasEnvironmentConfiguration.java index 547adea41..550d87430 100644 --- a/basyx.aasenvironment/basyx.aasenvironment.component/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/component/AasEnvironmentConfiguration.java +++ b/basyx.aasenvironment/basyx.aasenvironment.component/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/component/AasEnvironmentConfiguration.java @@ -31,9 +31,11 @@ import org.eclipse.digitaltwin.basyx.aasenvironment.AasEnvironmentFactory; import org.eclipse.digitaltwin.basyx.aasenvironment.feature.AasEnvironmentFeature; import org.eclipse.digitaltwin.basyx.aasenvironment.feature.DecoratedAasEnvironmentFactory; +import org.eclipse.digitaltwin.basyx.aasenvironment.preconfiguration.AasEnvironmentPreconfigurationLoader; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ResourceLoader; /** * Configuration for aas environment for dependency injection @@ -43,11 +45,17 @@ */ @Configuration public class AasEnvironmentConfiguration { - + @Bean @ConditionalOnMissingBean public static AasEnvironment getAasEnvironment(AasEnvironmentFactory aasEnvironmentFactory, List features) { return new DecoratedAasEnvironmentFactory(aasEnvironmentFactory, features).create(); } + @Bean + @ConditionalOnMissingBean + public static AasEnvironmentPreconfigurationLoader getAasEnvironmentPreconfigurationLoader(ResourceLoader resourceLoader, List pathsToLoad) { + return new AasEnvironmentPreconfigurationLoader(resourceLoader, pathsToLoad); + } + } diff --git a/basyx.aasenvironment/basyx.aasenvironment.component/src/main/resources/aas.aasx b/basyx.aasenvironment/basyx.aasenvironment.component/src/main/resources/aas.aasx new file mode 100644 index 000000000..e1003d3ed Binary files /dev/null and b/basyx.aasenvironment/basyx.aasenvironment.component/src/main/resources/aas.aasx differ diff --git a/basyx.aasenvironment/basyx.aasenvironment.component/src/main/resources/application.properties b/basyx.aasenvironment/basyx.aasenvironment.component/src/main/resources/application.properties index a0531b2ad..aff37727d 100644 --- a/basyx.aasenvironment/basyx.aasenvironment.component/src/main/resources/application.properties +++ b/basyx.aasenvironment/basyx.aasenvironment.component/src/main/resources/application.properties @@ -4,13 +4,13 @@ spring.application.name=AAS Environment basyx.backend = InMemory #basyx.backend = MongoDB -#spring.data.mongodb.host=mongo +# 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 +# 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 @@ -27,18 +27,26 @@ basyx.backend = InMemory # 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 +#basyx.environment = classpath:aas.aasx # #################################################################################### # 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 +# basyx.feature.authorization.enabled = true +# basyx.feature.authorization.type = rbac +# basyx.feature.authorization.jwtBearerTokenProvider = keycloak +# basyx.feature.authorization.rbac.file = classpath:rbac_rules.json +# spring.security.oauth2.resourceserver.jwt.issuer-uri= http://localhost:9097/realms/BaSyx +## This is for preconfiguration of a secured AAS Environment +# basyx.aasenvironment.authorization.preconfiguration.token-endpoint=http://localhost:9097/realms/BaSyx/protocol/openid-connect/token +# basyx.aasenvironment.authorization.preconfiguration.grant-type = CLIENT_CREDENTIALS +# basyx.aasenvironment.authorization.preconfiguration.client-id=workstation-1 +# basyx.aasenvironment.authorization.preconfiguration.client-secret=nY0mjyECF60DGzNmQUjL81XurSl8etom +# basyx.aasenvironment.authorization.preconfiguration.username=username +# basyx.aasenvironment.authorization.preconfiguration.password=password +# basyx.aasenvironment.authorization.preconfiguration.scopes= #################################################################################### # Operation Delegation #################################################################################### diff --git a/basyx.aasenvironment/basyx.aasenvironment.component/src/main/resources/authorization/modulus.txt b/basyx.aasenvironment/basyx.aasenvironment.component/src/main/resources/authorization/modulus.txt new file mode 100644 index 000000000..9def9d5fa --- /dev/null +++ b/basyx.aasenvironment/basyx.aasenvironment.component/src/main/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.common/basyx.client/src/test/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/TestAccessTokenProviderFactory.java b/basyx.common/basyx.client/src/test/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/TestAccessTokenProviderFactory.java new file mode 100644 index 000000000..3a821a505 --- /dev/null +++ b/basyx.common/basyx.client/src/test/java/org/eclipse/digitaltwin/basyx/client/internal/authorization/TestAccessTokenProviderFactory.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * 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.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 org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collection; + +import static org.junit.Assert.*; + +/** + * Test for the{@link AccessTokenProviderFactory} + * + * @author fried, zielstor + */ +public class TestAccessTokenProviderFactory { + + private AccessTokenProviderFactory factory; + private Collection scopes; + + @Before + public void setup() { + scopes = Arrays.asList("scope1", "scope2"); + } + + @Test + public void testCreateWithClientCredentials() { + factory = new AccessTokenProviderFactory(GrantType.CLIENT_CREDENTIALS, scopes); + factory.setClientCredentials("clientId", "clientSecret"); + + AccessTokenProvider provider = factory.create(); + + assertNotNull(provider); + assertTrue(provider instanceof ClientCredentialAccessTokenProvider); + } + + @Test + public void testCreateWithPasswordCredentials() { + factory = new AccessTokenProviderFactory(GrantType.PASSWORD, scopes); + factory.setClientCredentials("clientId", "clientSecret"); + factory.setPasswordCredentials("username", "password"); + + AccessTokenProvider provider = factory.create(); + + assertNotNull(provider); + assertTrue(provider instanceof PasswordCredentialAccessTokenProvider); + } + + @Test + public void testCreateWithMissingClientCredentials() { + factory = new AccessTokenProviderFactory(GrantType.CLIENT_CREDENTIALS, scopes); + + assertThrows(IllegalArgumentException.class, () -> factory.create()); + } + + @Test + public void testCreateWithMissingPasswordCredentials() { + factory = new AccessTokenProviderFactory(GrantType.PASSWORD, scopes); + factory.setClientCredentials("clientId", "clientSecret"); + + assertThrows(IllegalArgumentException.class, () -> factory.create()); + } +} \ No newline at end of file diff --git a/ci/keycloak/realm/BaSyx-realm.json b/ci/keycloak/realm/BaSyx-realm.json index 36ee74513..b893decf7 100644 --- a/ci/keycloak/realm/BaSyx-realm.json +++ b/ci/keycloak/realm/BaSyx-realm.json @@ -1400,7 +1400,7 @@ "credentials" : [ ], "disableableCredentialTypes" : [ ], "requiredActions" : [ ], - "realmRoles" : [ "basyx-reader", "basyx-deleter", "basyx-updater", "basyx-creator", "default-roles-basyx" ], + "realmRoles" : [ "basyx-reader", "basyx-deleter", "basyx-updater", "basyx-creator", "default-roles-basyx", "admin" ], "clientRoles" : { "workstation-1" : [ "uma_protection" ] },