From 238cafecae60b80d51e318b34b24ada9fbe6f71c Mon Sep 17 00:00:00 2001 From: Istvan Zoltan Nagy Date: Tue, 13 Feb 2024 17:35:05 +0100 Subject: [PATCH] Implement new DTR validation API (for submodel request) - Removes unnecessary access control service method - Implements new REST endpoint and service method for submodel endpoint address access control handling - Adds new repository method for fetching shells by endpoint address - Adds new tests --- .../api/AccessControlRuleService.java | 3 - .../accesscontrol/api/model/AccessRule.java | 29 -- .../api/model/AccessRuleTest.java | 59 --- .../SqlBackedAccessControlRuleService.java | 10 +- ...SqlBackedAccessControlRuleServiceTest.java | 23 +- ...delDescriptorAuthorizationApiDelegate.java | 48 ++ .../registry/repository/ShellRepository.java | 12 + .../security/AuthorizationEvaluator.java | 143 +++--- .../security/OAuthSecurityConfig.java | 15 +- .../service/GranularShellAccessHandler.java | 12 +- .../registry/service/ShellService.java | 11 +- .../static/aas-registry-openapi.yaml | 60 ++- ...setAdministrationShellApiSecurityTest.java | 164 ++++++ .../semantics/registry/JwtTokenFactory.java | 270 +++++----- .../tractusx/semantics/registry/TestUtil.java | 471 +++++++++--------- 15 files changed, 766 insertions(+), 564 deletions(-) delete mode 100644 access-control-service-interface/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/api/model/AccessRule.java delete mode 100644 access-control-service-interface/src/test/java/org/eclipse/tractusx/semantics/accesscontrol/api/model/AccessRuleTest.java create mode 100644 backend/src/main/java/org/eclipse/tractusx/semantics/registry/controller/SubmodelDescriptorAuthorizationApiDelegate.java diff --git a/access-control-service-interface/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/api/AccessControlRuleService.java b/access-control-service-interface/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/api/AccessControlRuleService.java index e90f69b8..c7ca3860 100644 --- a/access-control-service-interface/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/api/AccessControlRuleService.java +++ b/access-control-service-interface/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/api/AccessControlRuleService.java @@ -25,7 +25,6 @@ import java.util.Set; import org.eclipse.tractusx.semantics.accesscontrol.api.exception.DenyAccessException; -import org.eclipse.tractusx.semantics.accesscontrol.api.model.AccessRule; import org.eclipse.tractusx.semantics.accesscontrol.api.model.ShellVisibilityContext; import org.eclipse.tractusx.semantics.accesscontrol.api.model.ShellVisibilityCriteria; import org.eclipse.tractusx.semantics.accesscontrol.api.model.SpecificAssetId; @@ -39,6 +38,4 @@ List filterValidSpecificAssetIdsForLookup( Map fetchVisibilityCriteriaForShells( List shellContexts, String bpn ); - Set fetchApplicableRulesForPartner( String bpn ) throws DenyAccessException; - } diff --git a/access-control-service-interface/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/api/model/AccessRule.java b/access-control-service-interface/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/api/model/AccessRule.java deleted file mode 100644 index 5126254d..00000000 --- a/access-control-service-interface/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/api/model/AccessRule.java +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH and others - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * - ******************************************************************************/ - -package org.eclipse.tractusx.semantics.accesscontrol.api.model; - -import java.util.Set; - -import lombok.NonNull; - -public record AccessRule(@NonNull Set requiredSpecificAssetIds, @NonNull Set visibleSemanticIds) { - -} diff --git a/access-control-service-interface/src/test/java/org/eclipse/tractusx/semantics/accesscontrol/api/model/AccessRuleTest.java b/access-control-service-interface/src/test/java/org/eclipse/tractusx/semantics/accesscontrol/api/model/AccessRuleTest.java deleted file mode 100644 index a001be53..00000000 --- a/access-control-service-interface/src/test/java/org/eclipse/tractusx/semantics/accesscontrol/api/model/AccessRuleTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH and others - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * - ******************************************************************************/ -package org.eclipse.tractusx.semantics.accesscontrol.api.model; - -import static org.assertj.core.api.Assertions.*; - -import java.util.Set; -import java.util.stream.Stream; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -class AccessRuleTest { - - public static Stream nullProvider() { - return Stream. builder() - .add( Arguments.of( null, null ) ) - .add( Arguments.of( Set.of(), null ) ) - .add( Arguments.of( null, Set.of() ) ) - .build(); - } - - @ParameterizedTest - @MethodSource( "nullProvider" ) - void testConstructorCalledWithNullExpectException( Set requiredSpecificAssetIds, Set visibleSemanticIds ) { - assertThatThrownBy( () -> new AccessRule( requiredSpecificAssetIds, visibleSemanticIds ) ) - .isExactlyInstanceOf( NullPointerException.class ); - } - - @Test - void testConstructorCalledWithValidDataExpectSuccess() { - final Set requiredSpecificAssetIds = Set.of( new SpecificAssetId( "name1", "value1" ) ); - final Set visibleSemanticIds = Set.of( "name2" ); - - AccessRule actual = new AccessRule( requiredSpecificAssetIds, visibleSemanticIds ); - - assertThat( actual.requiredSpecificAssetIds() ).isEqualTo( requiredSpecificAssetIds ); - assertThat( actual.visibleSemanticIds() ).isEqualTo( visibleSemanticIds ); - } -} \ No newline at end of file diff --git a/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/service/SqlBackedAccessControlRuleService.java b/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/service/SqlBackedAccessControlRuleService.java index 5084a54d..5293d71f 100644 --- a/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/service/SqlBackedAccessControlRuleService.java +++ b/access-control-service-sql-impl/src/main/java/org/eclipse/tractusx/semantics/accesscontrol/sql/service/SqlBackedAccessControlRuleService.java @@ -30,8 +30,8 @@ import java.util.stream.Stream; import org.eclipse.tractusx.semantics.accesscontrol.api.AccessControlRuleService; -import org.eclipse.tractusx.semantics.accesscontrol.api.model.ShellVisibilityContext; import org.eclipse.tractusx.semantics.accesscontrol.api.exception.DenyAccessException; +import org.eclipse.tractusx.semantics.accesscontrol.api.model.ShellVisibilityContext; import org.eclipse.tractusx.semantics.accesscontrol.api.model.ShellVisibilityCriteria; import org.eclipse.tractusx.semantics.accesscontrol.api.model.SpecificAssetId; import org.eclipse.tractusx.semantics.accesscontrol.sql.model.AccessRule; @@ -96,14 +96,6 @@ public Map fetchVisibilityCriteriaForShells( Li .collect( Collectors.toMap( ShellVisibilityCriteria::aasId, Function.identity() ) ); } - @Override - public Set fetchApplicableRulesForPartner( String bpn ) throws DenyAccessException { - return findPotentiallyMatchingAccessControlRules( bpn ) - .map( accessControlRule -> new org.eclipse.tractusx.semantics.accesscontrol.api.model.AccessRule( - accessControlRule.getMandatorySpecificAssetIds(), accessControlRule.getVisibleSemanticIds() ) ) - .collect( Collectors.toSet() ); - } - private Stream findPotentiallyMatchingAccessControlRules( String bpn ) throws DenyAccessException { List allByBpn = repository.findAllByBpnWithinValidityPeriod( bpn, bpnWildcard ); if ( allByBpn == null || allByBpn.isEmpty() ) { diff --git a/access-control-service-sql-impl/src/test/java/org/eclipse/tractusx/semantics/accesscontrol/sql/service/SqlBackedAccessControlRuleServiceTest.java b/access-control-service-sql-impl/src/test/java/org/eclipse/tractusx/semantics/accesscontrol/sql/service/SqlBackedAccessControlRuleServiceTest.java index d20ec886..ed2a3f88 100644 --- a/access-control-service-sql-impl/src/test/java/org/eclipse/tractusx/semantics/accesscontrol/sql/service/SqlBackedAccessControlRuleServiceTest.java +++ b/access-control-service-sql-impl/src/test/java/org/eclipse/tractusx/semantics/accesscontrol/sql/service/SqlBackedAccessControlRuleServiceTest.java @@ -29,9 +29,8 @@ import java.util.UUID; import java.util.stream.Stream; -import org.eclipse.tractusx.semantics.accesscontrol.api.model.ShellVisibilityContext; import org.eclipse.tractusx.semantics.accesscontrol.api.exception.DenyAccessException; -import org.eclipse.tractusx.semantics.accesscontrol.api.model.AccessRule; +import org.eclipse.tractusx.semantics.accesscontrol.api.model.ShellVisibilityContext; import org.eclipse.tractusx.semantics.accesscontrol.api.model.SpecificAssetId; import org.eclipse.tractusx.semantics.accesscontrol.sql.repository.AccessControlRuleRepository; import org.eclipse.tractusx.semantics.accesscontrol.sql.repository.FileBasedAccessControlRuleRepository; @@ -155,22 +154,4 @@ void testFetchVisibilityCriteriaForShellWhenMatchingSpecificAssetIdsProvidedExpe assertThat( actual.visibleSemanticIds() ).isEqualTo( expectedSemanticIds ); assertThat( actual.visibleSpecificAssetIdNames() ).isEqualTo( expectedSpecificAssetIdNames ); } - - @Test - void testFetchApplicableRulesForPartnerWhenBpnNotFoundExpectException() { - assertThatThrownBy( () -> underTest.fetchApplicableRulesForPartner( BPNB ) ) - .isInstanceOf( DenyAccessException.class ); - } - - @Test - void testFetchApplicableRulesForPartnerWhenBpnFoundExpectRuleList() throws DenyAccessException { - Set actual = underTest.fetchApplicableRulesForPartner( BPNA ); - - assertThat( actual ).hasSize( 2 ) - .isEqualTo( Set.of( - new AccessRule( Set.of( MANUFACTURER_PART_ID_99991, CUSTOMER_PART_ID_ACME001, REVISION_NUMBER_01 ), - Set.of( PRODUCT_CARBON_FOOTPRINTV_1_1_0 ) ), - new AccessRule( Set.of( MANUFACTURER_PART_ID_99991, CUSTOMER_PART_ID_ACME001, PART_INSTANCE_ID_00001 ), Set.of( TRACEABILITYV_1_1_0 ) ) - ) ); - } -} \ No newline at end of file +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/controller/SubmodelDescriptorAuthorizationApiDelegate.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/controller/SubmodelDescriptorAuthorizationApiDelegate.java new file mode 100644 index 00000000..70d443ee --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/controller/SubmodelDescriptorAuthorizationApiDelegate.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH and others + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * + ******************************************************************************/ +package org.eclipse.tractusx.semantics.registry.controller; + +import org.eclipse.tractusx.semantics.aas.registry.api.SubmodelDescriptorApiDelegate; +import org.eclipse.tractusx.semantics.aas.registry.model.SubmodelEndpointAuthorization; +import org.eclipse.tractusx.semantics.registry.service.ShellService; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +@Service +public class SubmodelDescriptorAuthorizationApiDelegate implements SubmodelDescriptorApiDelegate { + + private final ShellService shellService; + + public SubmodelDescriptorAuthorizationApiDelegate( ShellService shellService ) { + this.shellService = shellService; + } + + @Override + public ResponseEntity postSubmodelDescriptorAuthorized( + SubmodelEndpointAuthorization submodelEndpointAuthorization, String externalSubjectId ) { + boolean visible = shellService.hasAccessToShellWithVisibleSubmodelEndpoint( submodelEndpointAuthorization.getSubmodelEndpointUrl(), externalSubjectId ); + if ( visible ) { + return ResponseEntity.ok().build(); + } else { + return ResponseEntity.status( HttpStatus.FORBIDDEN ).build(); + } + } +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/repository/ShellRepository.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/repository/ShellRepository.java index 95650b64..ebbf7ebf 100644 --- a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/repository/ShellRepository.java +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/repository/ShellRepository.java @@ -129,4 +129,16 @@ List findExternalShellIdsByIdentifiersByAnyMatch( @Param( "keyValueCombi @Param( "publicWildcardAllowedTypes" ) List publicWildcardAllowedTypes, @Param( "owningTenantId" ) String owningTenantId, @Param( "globalAssetId" ) String globalAssetId ); + + @Query( """ + SELECT s + FROM Shell s + WHERE + s.id IN ( + SELECT filterendpoint.submodel.shellId.id + FROM SubmodelEndpoint filterendpoint + WHERE filterendpoint.endpointAddress = :endpointAddress + ) + """) + List findAllBySubmodelEndpointAddress( String endpointAddress ); } diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/security/AuthorizationEvaluator.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/security/AuthorizationEvaluator.java index 5f5926eb..52a7dbdc 100644 --- a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/security/AuthorizationEvaluator.java +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/security/AuthorizationEvaluator.java @@ -1,6 +1,5 @@ -/******************************************************************************** - * Copyright (c) 2021-2023 Robert Bosch Manufacturing Solutions GmbH - * Copyright (c) 2021-2023 Contributors to the Eclipse Foundation +/******************************************************************************* + * Copyright (c) 2021 Robert Bosch Manufacturing Solutions GmbH and others * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -16,18 +15,20 @@ * under the License. * * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ + * + ******************************************************************************/ package org.eclipse.tractusx.semantics.registry.security; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; +import static org.eclipse.tractusx.semantics.registry.security.AuthorizationEvaluator.Roles.*; import java.util.Collection; import java.util.Map; -import static org.eclipse.tractusx.semantics.registry.security.AuthorizationEvaluator.Roles.*; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; + +import lombok.extern.slf4j.Slf4j; /** * This class contains methods validating JWT tokens for correctness and ensuring that the JWT token contains a desired role. @@ -49,65 +50,69 @@ @Slf4j public class AuthorizationEvaluator { - private final String clientId; - - public AuthorizationEvaluator(String clientId) { - this.clientId = clientId; - } - - public boolean hasRoleViewDigitalTwin() { - return containsRole(ROLE_VIEW_DIGITAL_TWIN); - } - - public boolean hasRoleAddDigitalTwin() { - return containsRole(ROLE_ADD_DIGITAL_TWIN); - } - - public boolean hasRoleUpdateDigitalTwin() { - return containsRole(ROLE_UPDATE_DIGITAL_TWIN); - } - - public boolean hasRoleDeleteDigitalTwin() { - return containsRole(ROLE_DELETE_DIGITAL_TWIN); - } - - private boolean containsRole(String role){ - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if(!(authentication instanceof JwtAuthenticationToken)){ - return false; - } - - JwtAuthenticationToken jwtAuthenticationToken = (JwtAuthenticationToken) (authentication); - Map claims = jwtAuthenticationToken.getToken().getClaims(); - - Object resourceAccess = claims.get("resource_access"); - if (!(resourceAccess instanceof Map)) { - return false; - } - - Object resource = ((Map) resourceAccess).get(clientId); - if(!(resource instanceof Map)){ - return false; - } - - Object roles = ((Map)resource).get("roles"); - if(!(roles instanceof Collection)){ - return false; - } - - Collection rolesList = (Collection ) roles; - return rolesList.contains(role); - } - - /** - * Represents the roles defined for the registry. - */ - public static final class Roles { - public static final String ROLE_VIEW_DIGITAL_TWIN = "view_digital_twin"; - public static final String ROLE_UPDATE_DIGITAL_TWIN = "update_digital_twin"; - public static final String ROLE_ADD_DIGITAL_TWIN = "add_digital_twin"; - public static final String ROLE_DELETE_DIGITAL_TWIN = "delete_digital_twin"; - } - + private final String clientId; + + public AuthorizationEvaluator( String clientId ) { + this.clientId = clientId; + } + + public boolean hasRoleViewDigitalTwin() { + return containsRole( ROLE_VIEW_DIGITAL_TWIN ); + } + + public boolean hasRoleAddDigitalTwin() { + return containsRole( ROLE_ADD_DIGITAL_TWIN ); + } + + public boolean hasRoleUpdateDigitalTwin() { + return containsRole( ROLE_UPDATE_DIGITAL_TWIN ); + } + + public boolean hasRoleDeleteDigitalTwin() { + return containsRole( ROLE_DELETE_DIGITAL_TWIN ); + } + + public boolean hasRoleSubmodelAccessControl() { + return containsRole( ROLE_SUBMODEL_ACCESS_CONTROL ); + } + + private boolean containsRole( String role ) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if ( !(authentication instanceof JwtAuthenticationToken) ) { + return false; + } + + JwtAuthenticationToken jwtAuthenticationToken = (JwtAuthenticationToken) (authentication); + Map claims = jwtAuthenticationToken.getToken().getClaims(); + + Object resourceAccess = claims.get( "resource_access" ); + if ( !(resourceAccess instanceof Map) ) { + return false; + } + + Object resource = ((Map) resourceAccess).get( clientId ); + if ( !(resource instanceof Map) ) { + return false; + } + + Object roles = ((Map) resource).get( "roles" ); + if ( !(roles instanceof Collection) ) { + return false; + } + + Collection rolesList = (Collection) roles; + return rolesList.contains( role ); + } + + /** + * Represents the roles defined for the registry. + */ + public static final class Roles { + public static final String ROLE_VIEW_DIGITAL_TWIN = "view_digital_twin"; + public static final String ROLE_UPDATE_DIGITAL_TWIN = "update_digital_twin"; + public static final String ROLE_ADD_DIGITAL_TWIN = "add_digital_twin"; + public static final String ROLE_DELETE_DIGITAL_TWIN = "delete_digital_twin"; + public static final String ROLE_SUBMODEL_ACCESS_CONTROL = "submodel_access_control"; + } } diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/security/OAuthSecurityConfig.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/security/OAuthSecurityConfig.java index ffe01b59..6966f1eb 100644 --- a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/security/OAuthSecurityConfig.java +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/security/OAuthSecurityConfig.java @@ -1,6 +1,5 @@ -/******************************************************************************** - * Copyright (c) 2021-2023 Robert Bosch Manufacturing Solutions GmbH - * Copyright (c) 2021-2023 Contributors to the Eclipse Foundation +/******************************************************************************* + * Copyright (c) 2021 Robert Bosch Manufacturing Solutions GmbH and others * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -16,7 +15,8 @@ * under the License. * * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ + * + ******************************************************************************/ package org.eclipse.tractusx.semantics.registry.security; import org.eclipse.tractusx.semantics.RegistryProperties; @@ -68,10 +68,13 @@ protected SecurityFilterChain configure(HttpSecurity http) throws Exception { //getDescription allowed for reader .requestMatchers( HttpMethod.GET, "/**/description" ).access( "@authorizationEvaluator.hasRoleViewDigitalTwin()" ) + + //submodel access control requires special role + .requestMatchers( HttpMethod.POST, "/**/submodel-descriptor/authorized" ).access( "@authorizationEvaluator.hasRoleSubmodelAccessControl()" ) ) .csrf(CsrfConfigurer::disable) - .sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .oauth2ResourceServer(oauth2ResourceServerConfigurer -> oauth2ResourceServerConfigurer.jwt()); + .sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy( SessionCreationPolicy.STATELESS ) ) + .oauth2ResourceServer(oauth2ResourceServerConfigurer -> oauth2ResourceServerConfigurer.jwt() ); return http.build(); } diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/service/GranularShellAccessHandler.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/service/GranularShellAccessHandler.java index 78bd7ba1..753e9c9d 100644 --- a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/service/GranularShellAccessHandler.java +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/service/GranularShellAccessHandler.java @@ -30,8 +30,8 @@ import org.eclipse.tractusx.semantics.RegistryProperties; import org.eclipse.tractusx.semantics.accesscontrol.api.AccessControlRuleService; -import org.eclipse.tractusx.semantics.accesscontrol.api.model.ShellVisibilityContext; import org.eclipse.tractusx.semantics.accesscontrol.api.exception.DenyAccessException; +import org.eclipse.tractusx.semantics.accesscontrol.api.model.ShellVisibilityContext; import org.eclipse.tractusx.semantics.accesscontrol.api.model.ShellVisibilityCriteria; import org.eclipse.tractusx.semantics.accesscontrol.api.model.SpecificAssetId; import org.eclipse.tractusx.semantics.registry.model.ReferenceKeyType; @@ -71,7 +71,8 @@ public Specification shellFilterSpecification( String sortFieldName, Shel } @Override - public List filterToVisibleShellIdsForLookup( Set userQuery, List shellIdentifiers, String externalSubjectId ) + public List filterToVisibleShellIdsForLookup( Set userQuery, List shellIdentifiers, + String externalSubjectId ) throws DenyAccessException { List idsInTheExistingOrder = shellIdentifiers.stream() .map( ShellIdentifierMinimal::shellId ) @@ -107,7 +108,7 @@ public List filterToVisibleShellIdsForLookup( Set userQ @Override @Nullable public Shell filterShellProperties( Shell shell, String externalSubjectId ) { - if ( externalSubjectId.equals( owningTenantId ) ) { + if ( owningTenantId.equals( externalSubjectId ) ) { return shell; } @@ -125,7 +126,7 @@ public Shell filterShellProperties( Shell shell, String externalSubjectId ) { @Override public List filterListOfShellProperties( List shells, String externalSubjectId ) { - if ( externalSubjectId.equals( owningTenantId ) ) { + if ( owningTenantId.equals( externalSubjectId ) ) { return shells; } @@ -133,6 +134,9 @@ public List filterListOfShellProperties( List shells, String exter .map( this::toShellVisibilityContext ) .toList(); final var visibilityCriteria = accessControlRuleService.fetchVisibilityCriteriaForShells( visibilityContexts, externalSubjectId ); + if ( visibilityCriteria.isEmpty() ) { + return Collections.emptyList(); + } return shells.stream() .map( shell -> filterShellContents( shell, visibilityCriteria.get( shell.getIdExternal() ) ) ) .filter( Objects::nonNull ) diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/service/ShellService.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/service/ShellService.java index ee402096..23618088 100644 --- a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/service/ShellService.java +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/service/ShellService.java @@ -492,9 +492,18 @@ public List saveBatch( List shells ) { } ).collect( Collectors.toList() ); } + public boolean hasAccessToShellWithVisibleSubmodelEndpoint( String endpointAddress, String externalSubjectId ) { + List shells = shellRepository.findAllBySubmodelEndpointAddress( endpointAddress ); + List filtered = shellAccessHandler.filterListOfShellProperties( shells, externalSubjectId ); + return filtered.stream() + .filter( Objects::nonNull ) + .anyMatch( shell -> shell.getSubmodels().stream() + .anyMatch( submodel -> submodel.getEndpoints().stream() + .anyMatch( endpoint -> Objects.equals( endpointAddress, endpoint.getEndpointAddress() ) ) ) ); + } + private Shell doFindShellByExternalIdWithoutFiltering( String externalShellId ) { return shellRepository.findByIdExternal( externalShellId ) .orElseThrow( () -> new EntityNotFoundException( String.format( "Shell for identifier %s not found", externalShellId ) ) ); } - } diff --git a/backend/src/main/resources/static/aas-registry-openapi.yaml b/backend/src/main/resources/static/aas-registry-openapi.yaml index c56f0ab1..fe328e0c 100644 --- a/backend/src/main/resources/static/aas-registry-openapi.yaml +++ b/backend/src/main/resources/static/aas-registry-openapi.yaml @@ -1,6 +1,5 @@ ############################################################### -# Copyright (c) 2021-2023 Robert Bosch Manufacturing Solutions GmbH -# Copyright (c) 2021-2023 Contributors to the Eclipse Foundation +# Copyright (c) 2021 Robert Bosch Manufacturing Solutions GmbH and others # # See the NOTICE file(s) distributed with this work for additional # information regarding copyright ownership. @@ -840,6 +839,48 @@ paths: description: Asset identifier key-value-pairs deleted successfully x-semanticIds: - https://admin-shell.io/aas/API/DeleteAllAssetLinksById/1/0/RC02 + /submodel-descriptor/authorized: + post: + tags: + - _PRIVATE_ Submodel Access Control Registry API + summary: "Private endpoint that verifies whether the caller has access to the specified submodel endpoint or not." + operationId: PostSubmodelDescriptorAuthorized + parameters: + - $ref: '#/components/parameters/ExternalSubjectIdHeader' + requestBody: + description: The URL of the target request + content: + application/json: + schema: + $ref: '#/components/schemas/SubmodelEndpointAuthorization' + required: true + responses: + "200": + description: The caller has access to the specified endpoint + "400": + description: "Bad Request, e.g. the request parameters of the format of the request body is wrong." + content: + application/json: + schema: + $ref: '#/components/schemas/Result' + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/Result' + "500": + description: Internal Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/Result' + default: + description: Default error handling for unmentioned status codes + content: + application/json: + schema: + $ref: '#/components/schemas/Result' components: securitySchemes: bearerAuth: @@ -1501,6 +1542,19 @@ components: type: string value: type: string + SubmodelEndpointAuthorization: + type: object + properties: + submodelEndpointUrl: + type: string + description: The full submodel endpoint URL requested by the end user (starting with the EDC Data Plane base URL and including the request path) + pattern: "^(http|https)://.+$" + minimum: 10 + maximum: 2048 + example: https://edc-dataplane/public/submodel/api/v2/path?param=true + required: + - submodelEndpointUrl + additionalProperties: false responses: bad-request: description: "Bad Request, e.g. the request parameters of the format of the request body is wrong." @@ -1594,7 +1648,7 @@ components: in: header name: Edc-Bpn schema: - type: String + type: string description: Incoming external_subject_id/bpn # format: byte examples: diff --git a/backend/src/test/java/org/eclipse/tractusx/semantics/registry/GranularAssetAdministrationShellApiSecurityTest.java b/backend/src/test/java/org/eclipse/tractusx/semantics/registry/GranularAssetAdministrationShellApiSecurityTest.java index 528e26fc..7cb1ae0c 100644 --- a/backend/src/test/java/org/eclipse/tractusx/semantics/registry/GranularAssetAdministrationShellApiSecurityTest.java +++ b/backend/src/test/java/org/eclipse/tractusx/semantics/registry/GranularAssetAdministrationShellApiSecurityTest.java @@ -19,14 +19,27 @@ ******************************************************************************/ package org.eclipse.tractusx.semantics.registry; +import static org.eclipse.tractusx.semantics.registry.TestUtil.getEncodedValue; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.util.List; +import java.util.UUID; + import org.eclipse.tractusx.semantics.RegistryProperties; +import org.eclipse.tractusx.semantics.aas.registry.model.AssetAdministrationShellDescriptor; +import org.eclipse.tractusx.semantics.aas.registry.model.SpecificAssetId; +import org.eclipse.tractusx.semantics.aas.registry.model.SubmodelDescriptor; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultHandlers; @SpringBootTest @AutoConfigureMockMvc @@ -34,6 +47,9 @@ @EnableConfigurationProperties( RegistryProperties.class ) public class GranularAssetAdministrationShellApiSecurityTest extends AssetAdministrationShellApiSecurityTest { + private static final String HTTP_EDC_DATA_PLANE_URL = "{\"submodelEndpointUrl\": \"http://edc-data-plane/url\"}"; + private static final String EXISTING_URL = "{\"submodelEndpointUrl\": \"http://endpoint-address\"}"; + @Nested @DisplayName( "Authentication Tests" ) class SecurityTests extends AssetAdministrationShellApiSecurityTest.SecurityTests { @@ -249,4 +265,152 @@ public void testGetDescriptionReadRoleExpectUnauthorized() throws Exception { super.testGetDescriptionReadRoleExpectUnauthorized(); } } + + @Nested + @DisplayName( "Submodel endpoint authorization Tests" ) + class SubmodelEndpointAuthorizationApiTest { + + @Test + void testPostSubmodelDescriptorAuthorizedWithoutTokenExpectForbidden() throws Exception { + mvc.perform( + MockMvcRequestBuilders + .post( "/api/v3.0/submodel-descriptor/authorized" ) + .contentType( MediaType.APPLICATION_JSON ) + .content( HTTP_EDC_DATA_PLANE_URL ) + .header( EXTERNAL_SUBJECT_ID_HEADER, jwtTokenFactory.tenantOne().getTenantId() ) + ) + .andDo( MockMvcResultHandlers.print() ) + .andExpect( status().isUnauthorized() ); + } + + @Test + void testPostSubmodelDescriptorAuthorizedWithoutAppropriateRoleExpectForbidden() throws Exception { + mvc.perform( + MockMvcRequestBuilders + .post( "/api/v3.0/submodel-descriptor/authorized" ) + .contentType( MediaType.APPLICATION_JSON ) + .with( jwtTokenFactory.readTwin() ) + .content( HTTP_EDC_DATA_PLANE_URL ) + .header( EXTERNAL_SUBJECT_ID_HEADER, jwtTokenFactory.tenantOne().getTenantId() ) + ) + .andDo( MockMvcResultHandlers.print() ) + .andExpect( status().isForbidden() ); + } + + @Test + void testPostSubmodelDescriptorAuthorizedWithoutContentExpectBadRequest() throws Exception { + mvc.perform( + MockMvcRequestBuilders + .post( "/api/v3.0/submodel-descriptor/authorized" ) + .contentType( MediaType.APPLICATION_JSON ) + .with( jwtTokenFactory.tenantOne().submodelAccessControl() ) + .header( EXTERNAL_SUBJECT_ID_HEADER, jwtTokenFactory.tenantOne().getTenantId() ) + ) + .andDo( MockMvcResultHandlers.print() ) + .andExpect( status().isBadRequest() ); + } + + @Test + void testPostSubmodelDescriptorAuthorizedWithoutTenantIdExpectForbidden() throws Exception { + mvc.perform( + MockMvcRequestBuilders + .post( "/api/v3.0/submodel-descriptor/authorized" ) + .contentType( MediaType.APPLICATION_JSON ) + .with( jwtTokenFactory.tenantOne().submodelAccessControl() ) + .content( HTTP_EDC_DATA_PLANE_URL ) + ) + .andDo( MockMvcResultHandlers.print() ) + .andExpect( status().isForbidden() ); + } + + @Test + void testPostSubmodelDescriptorAuthorizedWithoutAnyShellsExpectForbidden() throws Exception { + mvc.perform( + MockMvcRequestBuilders + .post( "/api/v3.0/submodel-descriptor/authorized" ) + .contentType( MediaType.APPLICATION_JSON ) + .with( jwtTokenFactory.tenantOne().submodelAccessControl() ) + .content( HTTP_EDC_DATA_PLANE_URL ) + .header( EXTERNAL_SUBJECT_ID_HEADER, jwtTokenFactory.tenantOne().getTenantId() ) + ) + .andDo( MockMvcResultHandlers.print() ) + .andExpect( status().isForbidden() ); + } + + @Test + @Disabled("disabled while we have no way to create dynamic rules") + void testPostSubmodelDescriptorAuthorizedWithoutMatchingSemanticIdExpectForbidden() throws Exception { + AssetAdministrationShellDescriptor shellPayload = TestUtil.createCompleteAasDescriptor( UUID.randomUUID().toString(), "http://endpoint-address" ); + shellPayload.setId( UUID.randomUUID().toString() ); + + SpecificAssetId asset = TestUtil.createSpecificAssetId( "tenantTwo", "value_2_private", List.of( jwtTokenFactory.tenantTwo().getTenantId() ) ); + shellPayload.setSpecificAssetIds( List.of( asset ) ); + performShellCreateRequest( mapper.writeValueAsString( shellPayload ) ); + + //Tenant two should not have access due to the random specificAssetId + mvc.perform( + MockMvcRequestBuilders + .post( "/api/v3.0/submodel-descriptor/authorized" ) + .contentType( MediaType.APPLICATION_JSON ) + .with( jwtTokenFactory.tenantTwo().submodelAccessControl() ) + .content( EXISTING_URL ) + .header( EXTERNAL_SUBJECT_ID_HEADER, jwtTokenFactory.tenantTwo().getTenantId() ) + ) + .andDo( MockMvcResultHandlers.print() ) + .andExpect( status().isForbidden() ); + } + + @Test + void testPostSubmodelDescriptorAuthorizedWithMatchingShellAndSemanticIdExpectSuccess() throws Exception { + AssetAdministrationShellDescriptor shellPayload = TestUtil.createCompleteAasDescriptor(); + shellPayload.setSpecificAssetIds( null ); + shellPayload.setId( UUID.randomUUID().toString() ); + + SpecificAssetId asset = TestUtil.createSpecificAssetId( "tenantTwo", "value_2_private", List.of( jwtTokenFactory.tenantTwo().getTenantId() ) ); + shellPayload.setSpecificAssetIds( List.of( asset ) ); + performShellCreateRequest( mapper.writeValueAsString( shellPayload ) ); + + SubmodelDescriptor submodel = TestUtil.createSubmodel(); + performSubmodelCreateRequest( mapper.writeValueAsString( submodel ), getEncodedValue( shellPayload.getId() ) ); + + //Tenant two should have access due to the default semantic Id value + mvc.perform( + MockMvcRequestBuilders + .post( "/api/v3.0/submodel-descriptor/authorized" ) + .contentType( MediaType.APPLICATION_JSON ) + .with( jwtTokenFactory.tenantTwo().submodelAccessControl() ) + .content( EXISTING_URL ) + .header( EXTERNAL_SUBJECT_ID_HEADER, jwtTokenFactory.tenantTwo().getTenantId() ) + ) + .andDo( MockMvcResultHandlers.print() ) + .andExpect( status().isOk() ); + } + + @Test + @Disabled("disabled while we have no way to create dynamic rules") + void testPostSubmodelDescriptorAuthorizedWithoutMatchingShellExpectForbidden() throws Exception { + AssetAdministrationShellDescriptor shellPayload = TestUtil.createCompleteAasDescriptor(); + shellPayload.setSpecificAssetIds( null ); + shellPayload.setId( UUID.randomUUID().toString() ); + + SpecificAssetId asset = TestUtil.createSpecificAssetId( "tenantTwo", "value_2_private", List.of( jwtTokenFactory.tenantTwo().getTenantId() ) ); + shellPayload.setSpecificAssetIds( List.of( asset ) ); + performShellCreateRequest( mapper.writeValueAsString( shellPayload ) ); + + SubmodelDescriptor submodel = TestUtil.createSubmodel(); + performSubmodelCreateRequest( mapper.writeValueAsString( submodel ), getEncodedValue( shellPayload.getId() ) ); + + //Tenant three should have access due to the non-visible shell + mvc.perform( + MockMvcRequestBuilders + .post( "/api/v3.0/submodel-descriptor/authorized" ) + .contentType( MediaType.APPLICATION_JSON ) + .with( jwtTokenFactory.tenantThree().submodelAccessControl() ) + .content( EXISTING_URL ) + .header( EXTERNAL_SUBJECT_ID_HEADER, jwtTokenFactory.tenantThree().getTenantId() ) + ) + .andDo( MockMvcResultHandlers.print() ) + .andExpect( status().isForbidden() ); + } + } } diff --git a/backend/src/test/java/org/eclipse/tractusx/semantics/registry/JwtTokenFactory.java b/backend/src/test/java/org/eclipse/tractusx/semantics/registry/JwtTokenFactory.java index 9a30ce73..201b45e4 100644 --- a/backend/src/test/java/org/eclipse/tractusx/semantics/registry/JwtTokenFactory.java +++ b/backend/src/test/java/org/eclipse/tractusx/semantics/registry/JwtTokenFactory.java @@ -1,6 +1,5 @@ -/******************************************************************************** - * Copyright (c) 2021-2023 Robert Bosch Manufacturing Solutions GmbH - * Copyright (c) 2021-2023 Contributors to the Eclipse Foundation +/******************************************************************************* + * Copyright (c) 2021 Robert Bosch Manufacturing Solutions GmbH and others * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -16,17 +15,11 @@ * under the License. * * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ + * + ******************************************************************************/ package org.eclipse.tractusx.semantics.registry; -import lombok.Value; -import net.minidev.json.JSONArray; - -import org.eclipse.tractusx.semantics.registry.security.AuthorizationEvaluator; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.oauth2.jwt.Jwt; -import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; -import org.springframework.test.web.servlet.request.RequestPostProcessor; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; import java.util.Collection; import java.util.Collections; @@ -34,127 +27,140 @@ import java.util.List; import java.util.Map; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; +import org.eclipse.tractusx.semantics.registry.security.AuthorizationEvaluator; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; +import org.springframework.test.web.servlet.request.RequestPostProcessor; -public class JwtTokenFactory { +import lombok.Value; +import net.minidev.json.JSONArray; - private static final String TENANT_ONE = "TENANT_ONE"; - private static final String TENANT_TWO = "TENANT_TWO"; - private static final String TENANT_THREE = "TENANT_THREE"; - - private final Tenant tenantOne; - private final Tenant tenantTwo; - private final Tenant tenantThree; - - public JwtTokenFactory(String publicClientId){ - this.tenantOne = new Tenant(publicClientId, TENANT_ONE); - this.tenantTwo = new Tenant(publicClientId, TENANT_TWO); - this.tenantThree = new Tenant(publicClientId, TENANT_THREE); - } - public RequestPostProcessor allRoles(){ - return tenantOne.allRoles(); - } - - public RequestPostProcessor readTwin(){ - return tenantOne.readTwin(); - } - - public RequestPostProcessor addTwin(){ - return tenantOne.addTwin(); - } - - public RequestPostProcessor updateTwin(){ - return tenantOne.updateTwin(); - } - - public RequestPostProcessor deleteTwin(){ - return tenantOne.deleteTwin(); - } - - public RequestPostProcessor withoutResourceAccess(){ - return tenantOne.withoutResourceAccess(); - } - - public RequestPostProcessor withoutRoles(){ - return tenantOne.withoutRoles(); - } - - public Tenant tenantOne() { - return tenantOne; - } - public Tenant tenantTwo() { - return tenantTwo; - } - public Tenant tenantThree() { - return tenantThree; - } - - @Value - public static class Tenant{ - String publicClientId; - String tenantId; - - public RequestPostProcessor allRoles(){ - return authenticationWithRoles(tenantId, - List.of(AuthorizationEvaluator.Roles.ROLE_VIEW_DIGITAL_TWIN, - AuthorizationEvaluator.Roles.ROLE_ADD_DIGITAL_TWIN, - AuthorizationEvaluator.Roles.ROLE_UPDATE_DIGITAL_TWIN, - AuthorizationEvaluator.Roles.ROLE_DELETE_DIGITAL_TWIN) - ); - } - - public RequestPostProcessor readTwin(){ - return authenticationWithRoles(tenantId, List.of(AuthorizationEvaluator.Roles.ROLE_VIEW_DIGITAL_TWIN)); - } - - public RequestPostProcessor addTwin(){ - return authenticationWithRoles(tenantId, List.of(AuthorizationEvaluator.Roles.ROLE_ADD_DIGITAL_TWIN)); - } - - public RequestPostProcessor updateTwin(){ - return authenticationWithRoles(tenantId,List.of(AuthorizationEvaluator.Roles.ROLE_UPDATE_DIGITAL_TWIN)); - } - - public RequestPostProcessor deleteTwin(){ - return authenticationWithRoles(tenantId, List.of(AuthorizationEvaluator.Roles.ROLE_DELETE_DIGITAL_TWIN)); - } - - public RequestPostProcessor withoutResourceAccess(){ - Jwt jwt = Jwt.withTokenValue("token") - .header("alg", "none") - .claim("sub", "user") - .build(); - Collection authorities = Collections.emptyList(); - return authentication(new JwtAuthenticationToken(jwt, authorities)); - } - - public RequestPostProcessor withoutRoles(){ - Jwt jwt = Jwt.withTokenValue("token") - .header("alg", "none") - .claim("sub", "user") - .claim("resource_access", Map.of(publicClientId, new HashMap())) - .build(); - Collection authorities = Collections.emptyList(); - return authentication(new JwtAuthenticationToken(jwt, authorities)); - } - - private RequestPostProcessor authenticationWithRoles(String tenantId, List roles){ - Jwt jwt = Jwt.withTokenValue("token") - .header("alg", "none") - .claim("sub", "user") - .claim("resource_access", Map.of(publicClientId, Map.of("roles", toJsonArray(roles) ))) - .build(); - Collection authorities = Collections.emptyList(); - return authentication(new JwtAuthenticationToken(jwt, authorities)); - } - - private static JSONArray toJsonArray(List elements){ - JSONArray jsonArray = new JSONArray(); - for (String element : elements){ - jsonArray.appendElement(element); - } - return jsonArray; - } - } +public class JwtTokenFactory { + private static final String TENANT_ONE = "TENANT_ONE"; + private static final String TENANT_TWO = "TENANT_TWO"; + private static final String TENANT_THREE = "TENANT_THREE"; + + private final Tenant tenantOne; + private final Tenant tenantTwo; + private final Tenant tenantThree; + + public JwtTokenFactory( String publicClientId ) { + this.tenantOne = new Tenant( publicClientId, TENANT_ONE ); + this.tenantTwo = new Tenant( publicClientId, TENANT_TWO ); + this.tenantThree = new Tenant( publicClientId, TENANT_THREE ); + } + + public RequestPostProcessor allRoles() { + return tenantOne.allRoles(); + } + + public RequestPostProcessor readTwin() { + return tenantOne.readTwin(); + } + + public RequestPostProcessor addTwin() { + return tenantOne.addTwin(); + } + + public RequestPostProcessor updateTwin() { + return tenantOne.updateTwin(); + } + + public RequestPostProcessor deleteTwin() { + return tenantOne.deleteTwin(); + } + + public RequestPostProcessor withoutResourceAccess() { + return tenantOne.withoutResourceAccess(); + } + + public RequestPostProcessor withoutRoles() { + return tenantOne.withoutRoles(); + } + + public Tenant tenantOne() { + return tenantOne; + } + + public Tenant tenantTwo() { + return tenantTwo; + } + + public Tenant tenantThree() { + return tenantThree; + } + + @Value + public static class Tenant { + String publicClientId; + String tenantId; + + public RequestPostProcessor allRoles() { + return authenticationWithRoles( tenantId, + List.of( AuthorizationEvaluator.Roles.ROLE_VIEW_DIGITAL_TWIN, + AuthorizationEvaluator.Roles.ROLE_ADD_DIGITAL_TWIN, + AuthorizationEvaluator.Roles.ROLE_UPDATE_DIGITAL_TWIN, + AuthorizationEvaluator.Roles.ROLE_DELETE_DIGITAL_TWIN ) + ); + } + + public RequestPostProcessor readTwin() { + return authenticationWithRoles( tenantId, List.of( AuthorizationEvaluator.Roles.ROLE_VIEW_DIGITAL_TWIN ) ); + } + + public RequestPostProcessor addTwin() { + return authenticationWithRoles( tenantId, List.of( AuthorizationEvaluator.Roles.ROLE_ADD_DIGITAL_TWIN ) ); + } + + public RequestPostProcessor updateTwin() { + return authenticationWithRoles( tenantId, List.of( AuthorizationEvaluator.Roles.ROLE_UPDATE_DIGITAL_TWIN ) ); + } + + public RequestPostProcessor deleteTwin() { + return authenticationWithRoles( tenantId, List.of( AuthorizationEvaluator.Roles.ROLE_DELETE_DIGITAL_TWIN ) ); + } + + public RequestPostProcessor submodelAccessControl() { + return authenticationWithRoles( tenantId, List.of( AuthorizationEvaluator.Roles.ROLE_SUBMODEL_ACCESS_CONTROL ) ); + } + + public RequestPostProcessor withoutResourceAccess() { + Jwt jwt = Jwt.withTokenValue( "token" ) + .header( "alg", "none" ) + .claim( "sub", "user" ) + .build(); + Collection authorities = Collections.emptyList(); + return authentication( new JwtAuthenticationToken( jwt, authorities ) ); + } + + public RequestPostProcessor withoutRoles() { + Jwt jwt = Jwt.withTokenValue( "token" ) + .header( "alg", "none" ) + .claim( "sub", "user" ) + .claim( "resource_access", Map.of( publicClientId, new HashMap() ) ) + .build(); + Collection authorities = Collections.emptyList(); + return authentication( new JwtAuthenticationToken( jwt, authorities ) ); + } + + private RequestPostProcessor authenticationWithRoles( String tenantId, List roles ) { + Jwt jwt = Jwt.withTokenValue( "token" ) + .header( "alg", "none" ) + .claim( "sub", "user" ) + .claim( "resource_access", Map.of( publicClientId, Map.of( "roles", toJsonArray( roles ) ) ) ) + .build(); + Collection authorities = Collections.emptyList(); + return authentication( new JwtAuthenticationToken( jwt, authorities ) ); + } + + private static JSONArray toJsonArray( List elements ) { + JSONArray jsonArray = new JSONArray(); + for ( String element : elements ) { + jsonArray.appendElement( element ); + } + return jsonArray; + } + } } diff --git a/backend/src/test/java/org/eclipse/tractusx/semantics/registry/TestUtil.java b/backend/src/test/java/org/eclipse/tractusx/semantics/registry/TestUtil.java index 65caf284..b37e9959 100644 --- a/backend/src/test/java/org/eclipse/tractusx/semantics/registry/TestUtil.java +++ b/backend/src/test/java/org/eclipse/tractusx/semantics/registry/TestUtil.java @@ -1,6 +1,5 @@ -/******************************************************************************** - * Copyright (c) 2021-2023 Robert Bosch Manufacturing Solutions GmbH - * Copyright (c) 2021-2023 Contributors to the Eclipse Foundation +/******************************************************************************* + * Copyright (c) 2021 Robert Bosch Manufacturing Solutions GmbH and others * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -16,12 +15,10 @@ * under the License. * * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ + * + ******************************************************************************/ package org.eclipse.tractusx.semantics.registry; -import org.apache.commons.lang3.RandomStringUtils; -import org.eclipse.tractusx.semantics.aas.registry.model.*; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; @@ -29,235 +26,253 @@ import java.util.List; import java.util.UUID; +import org.apache.commons.lang3.RandomStringUtils; +import org.eclipse.tractusx.semantics.aas.registry.model.AssetAdministrationShellDescriptor; +import org.eclipse.tractusx.semantics.aas.registry.model.AssetKind; +import org.eclipse.tractusx.semantics.aas.registry.model.Endpoint; +import org.eclipse.tractusx.semantics.aas.registry.model.Key; +import org.eclipse.tractusx.semantics.aas.registry.model.KeyTypes; +import org.eclipse.tractusx.semantics.aas.registry.model.LangStringNameType; +import org.eclipse.tractusx.semantics.aas.registry.model.LangStringTextType; +import org.eclipse.tractusx.semantics.aas.registry.model.ProtocolInformation; +import org.eclipse.tractusx.semantics.aas.registry.model.ProtocolInformationSecurityAttributes; +import org.eclipse.tractusx.semantics.aas.registry.model.Reference; +import org.eclipse.tractusx.semantics.aas.registry.model.ReferenceTypes; +import org.eclipse.tractusx.semantics.aas.registry.model.SpecificAssetId; +import org.eclipse.tractusx.semantics.aas.registry.model.SubmodelDescriptor; + import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; public class TestUtil { - public static AssetAdministrationShellDescriptor createCompleteAasDescriptor() { - AssetAdministrationShellDescriptor assetAdministrationShellDescriptor = new AssetAdministrationShellDescriptor(); - LangStringNameType displayName = new LangStringNameType(); - displayName.setLanguage("de"); - displayName.setText("this is an example description1"); - assetAdministrationShellDescriptor.setDisplayName(List.of(displayName)); - assetAdministrationShellDescriptor.setGlobalAssetId( "globalAssetId example" ); - assetAdministrationShellDescriptor.setAssetType( "AssetType" ); - assetAdministrationShellDescriptor.setAssetKind( AssetKind.INSTANCE ); - assetAdministrationShellDescriptor.setId("fb7ebcc2-5731-4948-aeaa-c9e9692decf5"); - assetAdministrationShellDescriptor.setIdShort(RandomStringUtils.random(10, true, true)); - - Reference specificAssetIdReference = new Reference(); - specificAssetIdReference.setType( ReferenceTypes.MODELREFERENCE ); - Key specificAssetIdKey = new Key(); - specificAssetIdKey.setType( KeyTypes.ASSETADMINISTRATIONSHELL ); - specificAssetIdKey.setValue( "specificAssetIdReference key" ); - specificAssetIdReference.setKeys( List.of(specificAssetIdKey) ); - - Reference externalSubjectIdReference = new Reference(); - externalSubjectIdReference.setType( ReferenceTypes.EXTERNALREFERENCE ); - Key subjectIdKey = new Key(); - subjectIdKey.setType( KeyTypes.ASSETADMINISTRATIONSHELL ); - subjectIdKey.setValue( "ExternalSubject key value" ); - externalSubjectIdReference.setKeys( List.of(subjectIdKey) ); - - Key assetIdKey = new Key(); - assetIdKey.setType( KeyTypes.BASICEVENTELEMENT ); - assetIdKey.setValue( "assetIdKey value" ); - - - Reference assetIdReference = new Reference(); - assetIdReference.setType( ReferenceTypes.EXTERNALREFERENCE ); - assetIdReference.setKeys( List.of(assetIdKey) ); - - SpecificAssetId specificAssetId1 = new SpecificAssetId(); - specificAssetId1.setName("identifier1KeyExample"); - specificAssetId1.setValue("identifier1ValueExample"); - specificAssetId1.setSemanticId( specificAssetIdReference ); - specificAssetId1.setSupplementalSemanticIds( List.of(assetIdReference) ); - specificAssetId1.setExternalSubjectId(externalSubjectIdReference ); - - SpecificAssetId specificAssetId2 = new SpecificAssetId(); - specificAssetId2.setName("identifier2KeyExample"); - specificAssetId2.setValue("identifier2ValueExample"); - specificAssetId2.setSemanticId( specificAssetIdReference ); - specificAssetId2.setSupplementalSemanticIds( List.of(assetIdReference) ); - specificAssetId2.setExternalSubjectId( externalSubjectIdReference ); - assetAdministrationShellDescriptor.setSpecificAssetIds(List.of(specificAssetId1, specificAssetId2)); - - LangStringTextType description1 = new LangStringTextType(); - description1.setLanguage("de"); - description1.setText("hello text"); - LangStringTextType description2 = new LangStringTextType(); - description2.setLanguage("en"); - description2.setText("hello s"); - assetAdministrationShellDescriptor.setDescription(List.of(description1, description2)); - assetAdministrationShellDescriptor.setDescription(List.of(description1, description2)); - - ProtocolInformation protocolInformation = new ProtocolInformation(); - protocolInformation.setEndpointProtocol("endpointProtocolExample"); - protocolInformation.setHref("endpointAddressExample"); - protocolInformation.setEndpointProtocolVersion(List.of("e")); - protocolInformation.setSubprotocol("subprotocolExample"); - protocolInformation.setSubprotocolBody("subprotocolBodyExample"); - protocolInformation.setSubprotocolBodyEncoding("subprotocolBodyExample"); - ProtocolInformationSecurityAttributes securityAttributes = new ProtocolInformationSecurityAttributes(); - securityAttributes.setType(ProtocolInformationSecurityAttributes.TypeEnum.NONE); - securityAttributes.setKey( "Security Attribute key" ); - securityAttributes.setValue( "Security Attribute value" ); - protocolInformation.setSecurityAttributes(List.of(securityAttributes)); - - Endpoint endpoint = new Endpoint(); - endpoint.setInterface("interfaceNameExample"); - endpoint.setProtocolInformation(protocolInformation); - - Reference submodelSemanticReference = new Reference(); - submodelSemanticReference.setType(ReferenceTypes.EXTERNALREFERENCE); - Key key = new Key(); - key.setType(KeyTypes.SUBMODEL); - key.setValue("semanticIdExample"); - submodelSemanticReference.setKeys(List.of(key)); - submodelSemanticReference.setKeys( List.of(key) ); - - - Reference submodelSupplemSemanticIdReference = new Reference(); - submodelSupplemSemanticIdReference.setType( ReferenceTypes.EXTERNALREFERENCE ); - Key submodelSupplemSemanticIdkey = new Key(); - submodelSupplemSemanticIdkey.setType( KeyTypes.SUBMODEL ); - submodelSupplemSemanticIdkey.setValue( "supplementalsemanticIdExample value" ); - submodelSupplemSemanticIdReference.setKeys( List.of(submodelSupplemSemanticIdkey) ); - - - SubmodelDescriptor submodelDescriptor = new SubmodelDescriptor(); - submodelDescriptor.setId(UUID.randomUUID().toString()); - submodelDescriptor.setDisplayName( List.of(displayName) ); - submodelDescriptor.setIdShort(RandomStringUtils.random(10, true, true)); - submodelDescriptor.setSemanticId(submodelSemanticReference); - submodelDescriptor.setSupplementalSemanticId( List.of(submodelSupplemSemanticIdReference) ); - submodelDescriptor.setDescription(List.of(description1, description2)); - submodelDescriptor.setEndpoints(List.of(endpoint)); - List submodelDescriptors = new ArrayList<>(); - submodelDescriptors.add( submodelDescriptor ); - assetAdministrationShellDescriptor.setSubmodelDescriptors(submodelDescriptors); - return assetAdministrationShellDescriptor; - } - - public static SubmodelDescriptor createSubmodel(){ - SubmodelDescriptor submodelDescriptor = new SubmodelDescriptor(); - submodelDescriptor.setId(UUID.randomUUID().toString()); - submodelDescriptor.setIdShort("idShortExample"); - - Reference submodelSemanticReference = new Reference(); - submodelSemanticReference.setType(ReferenceTypes.EXTERNALREFERENCE); - Key key = new Key(); - key.setType(KeyTypes.SUBMODEL); - key.setValue("semanticIdExample"); - submodelSemanticReference.setKeys(List.of(key)); - - submodelSemanticReference.setKeys( List.of(key) ); - submodelDescriptor.setSemanticId(submodelSemanticReference); - - LangStringTextType description1 = new LangStringTextType(); - description1.setLanguage("de"); - description1.setText("hello text"); - LangStringTextType description2 = new LangStringTextType(); - description2.setLanguage("en"); - description2.setText("hello s"); - - LangStringNameType displayName = new LangStringNameType(); - displayName.setLanguage( "en" ); - displayName.setText( "this is submodel display name" ); - - ProtocolInformation protocolInformation = new ProtocolInformation(); - protocolInformation.setEndpointProtocol("endpointProtocolExample"); - protocolInformation.setHref("endpointAddressExample"); - protocolInformation.setEndpointProtocolVersion(List.of("e")); - protocolInformation.setSubprotocol("subprotocolExample"); - protocolInformation.setSubprotocolBody("subprotocolBodyExample"); - protocolInformation.setSubprotocolBodyEncoding("subprotocolBodyExample"); - - ProtocolInformationSecurityAttributes securityAttributes = new ProtocolInformationSecurityAttributes(); - securityAttributes.setType(ProtocolInformationSecurityAttributes.TypeEnum.NONE); - securityAttributes.setKey( "Security Attribute key" ); - securityAttributes.setValue( "Security Attribute value" ); - protocolInformation.setSecurityAttributes(List.of(securityAttributes)); - - Endpoint endpoint = new Endpoint(); - endpoint.setInterface("interfaceNameExample"); - endpoint.setProtocolInformation(protocolInformation); - - Reference submodelSupplemSemanticIdReference = new Reference(); - submodelSupplemSemanticIdReference.setType( ReferenceTypes.EXTERNALREFERENCE ); - Key submodelSupplemSemanticIdkey = new Key(); - submodelSupplemSemanticIdkey.setType( KeyTypes.SUBMODEL ); - submodelSupplemSemanticIdkey.setValue( "supplementalsemanticIdExample value" ); - submodelSupplemSemanticIdReference.setKeys( List.of(submodelSupplemSemanticIdkey) ); - - submodelDescriptor.setSupplementalSemanticId( List.of(submodelSupplemSemanticIdReference) ); - submodelDescriptor.setDescription(List.of(description1, description2)); - submodelDescriptor.setDisplayName( List.of(displayName) ); - submodelDescriptor.setEndpoints(List.of(endpoint)); - return submodelDescriptor; - } - - public static SpecificAssetId createSpecificAssetId(){ - SpecificAssetId specificAssetId1 = new SpecificAssetId(); - specificAssetId1.setName("identifier1KeyExample"); - specificAssetId1.setValue("identifier1ValueExample"); - - Reference reference = new Reference(); - reference.setType(ReferenceTypes.EXTERNALREFERENCE); - Key key = new Key(); - key.setType(KeyTypes.SUBMODEL); - key.setValue("key"); - reference.setKeys(List.of(key)); - - specificAssetId1.setSupplementalSemanticIds(List.of(reference)); - specificAssetId1.setExternalSubjectId(reference ); - return specificAssetId1; - } - - public static SpecificAssetId createSpecificAssetId(String name, String value, List externalSubjectIds){ - SpecificAssetId specificAssetId1 = new SpecificAssetId(); - specificAssetId1.setName(name); - specificAssetId1.setValue(value); - - if(externalSubjectIds!=null && !externalSubjectIds.isEmpty()) { - Reference reference = new Reference(); - reference.setType(ReferenceTypes.EXTERNALREFERENCE); - List keys = new ArrayList<>(); - externalSubjectIds.forEach( externalSubjectId-> { - Key key = new Key(); - key.setType(KeyTypes.SUBMODEL); - key.setValue(externalSubjectId); - keys.add( key ); - }); - reference.setKeys(keys); - specificAssetId1.setExternalSubjectId(reference); - } - - Key assetIdKey = new Key(); - assetIdKey.setType( KeyTypes.BASICEVENTELEMENT ); - assetIdKey.setValue( "assetIdKey value" ); - - Reference assetIdReference = new Reference(); - assetIdReference.setType( ReferenceTypes.EXTERNALREFERENCE ); - assetIdReference.setKeys( List.of(assetIdKey) ); - specificAssetId1.setSemanticId( assetIdReference); - specificAssetId1.setSupplementalSemanticIds( List.of(assetIdReference) ); - return specificAssetId1; - } - - public static String getEncodedValue(String shellId){ - return Base64.getUrlEncoder().encodeToString(shellId.getBytes()); - } - - public static byte[] serialize(Object obj) throws IOException { + public static AssetAdministrationShellDescriptor createCompleteAasDescriptor() { + return createCompleteAasDescriptor( "semanticIdExample", "http://endpoint-address" ); + } + + public static AssetAdministrationShellDescriptor createCompleteAasDescriptor( String semanticId, String endpointUrl ) { + AssetAdministrationShellDescriptor assetAdministrationShellDescriptor = new AssetAdministrationShellDescriptor(); + LangStringNameType displayName = new LangStringNameType(); + displayName.setLanguage( "de" ); + displayName.setText( "this is an example description1" ); + assetAdministrationShellDescriptor.setDisplayName( List.of( displayName ) ); + assetAdministrationShellDescriptor.setGlobalAssetId( "globalAssetId example" ); + assetAdministrationShellDescriptor.setAssetType( "AssetType" ); + assetAdministrationShellDescriptor.setAssetKind( AssetKind.INSTANCE ); + assetAdministrationShellDescriptor.setId( "fb7ebcc2-5731-4948-aeaa-c9e9692decf5" ); + assetAdministrationShellDescriptor.setIdShort( RandomStringUtils.random( 10, true, true ) ); + + Reference specificAssetIdReference = new Reference(); + specificAssetIdReference.setType( ReferenceTypes.MODELREFERENCE ); + Key specificAssetIdKey = new Key(); + specificAssetIdKey.setType( KeyTypes.ASSETADMINISTRATIONSHELL ); + specificAssetIdKey.setValue( "specificAssetIdReference key" ); + specificAssetIdReference.setKeys( List.of( specificAssetIdKey ) ); + + Reference externalSubjectIdReference = new Reference(); + externalSubjectIdReference.setType( ReferenceTypes.EXTERNALREFERENCE ); + Key subjectIdKey = new Key(); + subjectIdKey.setType( KeyTypes.ASSETADMINISTRATIONSHELL ); + subjectIdKey.setValue( "ExternalSubject key value" ); + externalSubjectIdReference.setKeys( List.of( subjectIdKey ) ); + + Key assetIdKey = new Key(); + assetIdKey.setType( KeyTypes.BASICEVENTELEMENT ); + assetIdKey.setValue( "assetIdKey value" ); + + Reference assetIdReference = new Reference(); + assetIdReference.setType( ReferenceTypes.EXTERNALREFERENCE ); + assetIdReference.setKeys( List.of( assetIdKey ) ); + + SpecificAssetId specificAssetId1 = new SpecificAssetId(); + specificAssetId1.setName( "identifier1KeyExample" ); + specificAssetId1.setValue( "identifier1ValueExample" ); + specificAssetId1.setSemanticId( specificAssetIdReference ); + specificAssetId1.setSupplementalSemanticIds( List.of( assetIdReference ) ); + specificAssetId1.setExternalSubjectId( externalSubjectIdReference ); + + SpecificAssetId specificAssetId2 = new SpecificAssetId(); + specificAssetId2.setName( "identifier2KeyExample" ); + specificAssetId2.setValue( "identifier2ValueExample" ); + specificAssetId2.setSemanticId( specificAssetIdReference ); + specificAssetId2.setSupplementalSemanticIds( List.of( assetIdReference ) ); + specificAssetId2.setExternalSubjectId( externalSubjectIdReference ); + assetAdministrationShellDescriptor.setSpecificAssetIds( List.of( specificAssetId1, specificAssetId2 ) ); + + LangStringTextType description1 = new LangStringTextType(); + description1.setLanguage( "de" ); + description1.setText( "hello text" ); + LangStringTextType description2 = new LangStringTextType(); + description2.setLanguage( "en" ); + description2.setText( "hello s" ); + assetAdministrationShellDescriptor.setDescription( List.of( description1, description2 ) ); + + ProtocolInformation protocolInformation = new ProtocolInformation(); + protocolInformation.setEndpointProtocol( "endpointProtocolExample" ); + protocolInformation.setHref( endpointUrl ); + protocolInformation.setEndpointProtocolVersion( List.of( "e" ) ); + protocolInformation.setSubprotocol( "subprotocolExample" ); + protocolInformation.setSubprotocolBody( "subprotocolBodyExample" ); + protocolInformation.setSubprotocolBodyEncoding( "subprotocolBodyExample" ); + ProtocolInformationSecurityAttributes securityAttributes = new ProtocolInformationSecurityAttributes(); + securityAttributes.setType( ProtocolInformationSecurityAttributes.TypeEnum.NONE ); + securityAttributes.setKey( "Security Attribute key" ); + securityAttributes.setValue( "Security Attribute value" ); + protocolInformation.setSecurityAttributes( List.of( securityAttributes ) ); + + Endpoint endpoint = new Endpoint(); + endpoint.setInterface( "interfaceNameExample" ); + endpoint.setProtocolInformation( protocolInformation ); + + Reference submodelSemanticReference = new Reference(); + submodelSemanticReference.setType( ReferenceTypes.EXTERNALREFERENCE ); + Key key = new Key(); + key.setType( KeyTypes.SUBMODEL ); + key.setValue( semanticId ); + submodelSemanticReference.setKeys( List.of( key ) ); + + Reference submodelSupplemSemanticIdReference = new Reference(); + submodelSupplemSemanticIdReference.setType( ReferenceTypes.EXTERNALREFERENCE ); + Key submodelSupplemSemanticIdkey = new Key(); + submodelSupplemSemanticIdkey.setType( KeyTypes.SUBMODEL ); + submodelSupplemSemanticIdkey.setValue( "supplementalsemanticIdExample value" ); + submodelSupplemSemanticIdReference.setKeys( List.of( submodelSupplemSemanticIdkey ) ); + + SubmodelDescriptor submodelDescriptor = new SubmodelDescriptor(); + submodelDescriptor.setId( UUID.randomUUID().toString() ); + submodelDescriptor.setDisplayName( List.of( displayName ) ); + submodelDescriptor.setIdShort( RandomStringUtils.random( 10, true, true ) ); + submodelDescriptor.setSemanticId( submodelSemanticReference ); + submodelDescriptor.setSupplementalSemanticId( List.of( submodelSupplemSemanticIdReference ) ); + submodelDescriptor.setDescription( List.of( description1, description2 ) ); + submodelDescriptor.setEndpoints( List.of( endpoint ) ); + List submodelDescriptors = new ArrayList<>(); + submodelDescriptors.add( submodelDescriptor ); + assetAdministrationShellDescriptor.setSubmodelDescriptors( submodelDescriptors ); + return assetAdministrationShellDescriptor; + } + + public static SubmodelDescriptor createSubmodel() { + return createSubmodel( "semanticIdExample", "http://endpoint-address" ); + } + + public static SubmodelDescriptor createSubmodel( String semanticId, String endpointUrl ) { + SubmodelDescriptor submodelDescriptor = new SubmodelDescriptor(); + submodelDescriptor.setId( UUID.randomUUID().toString() ); + submodelDescriptor.setIdShort( "idShortExample" ); + + Reference submodelSemanticReference = new Reference(); + submodelSemanticReference.setType( ReferenceTypes.EXTERNALREFERENCE ); + Key key = new Key(); + key.setType( KeyTypes.SUBMODEL ); + key.setValue( semanticId ); + submodelSemanticReference.setKeys( List.of( key ) ); + + submodelSemanticReference.setKeys( List.of( key ) ); + submodelDescriptor.setSemanticId( submodelSemanticReference ); + + LangStringTextType description1 = new LangStringTextType(); + description1.setLanguage( "de" ); + description1.setText( "hello text" ); + LangStringTextType description2 = new LangStringTextType(); + description2.setLanguage( "en" ); + description2.setText( "hello s" ); + + LangStringNameType displayName = new LangStringNameType(); + displayName.setLanguage( "en" ); + displayName.setText( "this is submodel display name" ); + + ProtocolInformation protocolInformation = new ProtocolInformation(); + protocolInformation.setEndpointProtocol( "endpointProtocolExample" ); + protocolInformation.setHref( endpointUrl ); + protocolInformation.setEndpointProtocolVersion( List.of( "e" ) ); + protocolInformation.setSubprotocol( "subprotocolExample" ); + protocolInformation.setSubprotocolBody( "subprotocolBodyExample" ); + protocolInformation.setSubprotocolBodyEncoding( "subprotocolBodyExample" ); + + ProtocolInformationSecurityAttributes securityAttributes = new ProtocolInformationSecurityAttributes(); + securityAttributes.setType( ProtocolInformationSecurityAttributes.TypeEnum.NONE ); + securityAttributes.setKey( "Security Attribute key" ); + securityAttributes.setValue( "Security Attribute value" ); + protocolInformation.setSecurityAttributes( List.of( securityAttributes ) ); + + Endpoint endpoint = new Endpoint(); + endpoint.setInterface( "interfaceNameExample" ); + endpoint.setProtocolInformation( protocolInformation ); + + Reference submodelSupplemSemanticIdReference = new Reference(); + submodelSupplemSemanticIdReference.setType( ReferenceTypes.EXTERNALREFERENCE ); + Key submodelSupplemSemanticIdkey = new Key(); + submodelSupplemSemanticIdkey.setType( KeyTypes.SUBMODEL ); + submodelSupplemSemanticIdkey.setValue( "supplementalsemanticIdExample value" ); + submodelSupplemSemanticIdReference.setKeys( List.of( submodelSupplemSemanticIdkey ) ); + + submodelDescriptor.setSupplementalSemanticId( List.of( submodelSupplemSemanticIdReference ) ); + submodelDescriptor.setDescription( List.of( description1, description2 ) ); + submodelDescriptor.setDisplayName( List.of( displayName ) ); + submodelDescriptor.setEndpoints( List.of( endpoint ) ); + return submodelDescriptor; + } + + public static SpecificAssetId createSpecificAssetId() { + SpecificAssetId specificAssetId1 = new SpecificAssetId(); + specificAssetId1.setName( "identifier1KeyExample" ); + specificAssetId1.setValue( "identifier1ValueExample" ); + + Reference reference = new Reference(); + reference.setType( ReferenceTypes.EXTERNALREFERENCE ); + Key key = new Key(); + key.setType( KeyTypes.SUBMODEL ); + key.setValue( "key" ); + reference.setKeys( List.of( key ) ); + + specificAssetId1.setSupplementalSemanticIds( List.of( reference ) ); + specificAssetId1.setExternalSubjectId( reference ); + return specificAssetId1; + } + + public static SpecificAssetId createSpecificAssetId( String name, String value, List externalSubjectIds ) { + SpecificAssetId specificAssetId1 = new SpecificAssetId(); + specificAssetId1.setName( name ); + specificAssetId1.setValue( value ); + + if ( externalSubjectIds != null && !externalSubjectIds.isEmpty() ) { + Reference reference = new Reference(); + reference.setType( ReferenceTypes.EXTERNALREFERENCE ); + List keys = new ArrayList<>(); + externalSubjectIds.forEach( externalSubjectId -> { + Key key = new Key(); + key.setType( KeyTypes.SUBMODEL ); + key.setValue( externalSubjectId ); + keys.add( key ); + } ); + reference.setKeys( keys ); + specificAssetId1.setExternalSubjectId( reference ); + } + + Key assetIdKey = new Key(); + assetIdKey.setType( KeyTypes.BASICEVENTELEMENT ); + assetIdKey.setValue( "assetIdKey value" ); + + Reference assetIdReference = new Reference(); + assetIdReference.setType( ReferenceTypes.EXTERNALREFERENCE ); + assetIdReference.setKeys( List.of( assetIdKey ) ); + specificAssetId1.setSemanticId( assetIdReference ); + specificAssetId1.setSupplementalSemanticIds( List.of( assetIdReference ) ); + return specificAssetId1; + } + + public static String getEncodedValue( String shellId ) { + return Base64.getUrlEncoder().encodeToString( shellId.getBytes() ); + } + + public static byte[] serialize( Object obj ) throws IOException { ByteArrayOutputStream os = new ByteArrayOutputStream(); ObjectMapper mapper = new ObjectMapper(); - mapper.enable( SerializationFeature.INDENT_OUTPUT); - mapper.setSerializationInclusion( JsonInclude.Include.NON_NULL); - mapper.writeValue(os, obj); + mapper.enable( SerializationFeature.INDENT_OUTPUT ); + mapper.setSerializationInclusion( JsonInclude.Include.NON_NULL ); + mapper.writeValue( os, obj ); return os.toByteArray(); } } \ No newline at end of file