Skip to content

Commit

Permalink
Implement feature functionality:
Browse files Browse the repository at this point in the history
* SpecificAssetIds default closed: If no externalSubjectId is set, only the owner has access to this specificAssetId
* make specificAssetId public with wildcardprefix: PUBLIC_READABLE: If externalsubjectId includes the prefix, the specificAssetId is visible for everyone
  • Loading branch information
tunacicek committed Aug 22, 2023
1 parent b26e331 commit 2f11cd7
Show file tree
Hide file tree
Showing 17 changed files with 723 additions and 176 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

package org.eclipse.tractusx.semantics;

import java.util.List;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
Expand All @@ -35,6 +37,18 @@ public class RegistryProperties {

private final Idm idm = new Idm();

/**
* This wildcard prefix is used to make specificAssetIds public for everyone.
* The default-value "PUBLIC_READABLE" is used by all catenaX participants.
*/
@NotEmpty(message = "externalSubjectIdWildcardPrefix must not be empty")
private String externalSubjectIdWildcardPrefix;

/**
* This wildcard-allowed-types is used to make only specificAssetIds public for defined types.
*/
private List<String> externalSubjectIdWildcardAllowedTypes;

/**
* Properties for Identity Management system
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ public ResponseEntity<Void> deleteAllAssetLinksById(byte[] aasIdentifier) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@Override
public ResponseEntity<Void> deleteSubmodelDescriptorByIdThroughSuperpath( byte[] aasIdentifier, byte[] submodelIdentifier ) {
shellService.deleteSubmodel(getDecodedId( aasIdentifier ), getDecodedId( submodelIdentifier ),getExternalSubjectIdOrEmpty( null ));
public ResponseEntity<Void> deleteSubmodelDescriptorByIdThroughSuperpath( byte[] aasIdentifier, byte[] submodelIdentifier, @RequestHeader String externalSubjectId ) {
shellService.deleteSubmodel(getDecodedId( aasIdentifier ), getDecodedId( submodelIdentifier ),getExternalSubjectIdOrEmpty( externalSubjectId ));
return new ResponseEntity<>(HttpStatus.NO_CONTENT);

}
Expand All @@ -96,7 +96,7 @@ public ResponseEntity<GetAssetAdministrationShellDescriptorsResult> getAllAssetA
@Override
// new todo: correct implementation
public ResponseEntity<GetSubmodelDescriptorsResult> getAllSubmodelDescriptorsThroughSuperpath( byte[] aasIdentifier, Integer limit, String cursor, @RequestHeader String externalSubjectId ) {
Shell savedShell = shellService.findShellByExternalId(getDecodedId( aasIdentifier ),getExternalSubjectIdOrEmpty(externalSubjectId));
Shell savedShell = shellService.findShellByExternalIdAndExternalSubjectId(getDecodedId( aasIdentifier ),getExternalSubjectIdOrEmpty(externalSubjectId));
SubmodelCollectionDto dto = shellService.findAllSubmodel( limit,cursor, savedShell);
GetSubmodelDescriptorsResult result= submodelMapper.toApiDto( dto );
return new ResponseEntity<>(result, HttpStatus.OK);
Expand All @@ -105,13 +105,13 @@ public ResponseEntity<GetSubmodelDescriptorsResult> getAllSubmodelDescriptorsThr
@Override
public ResponseEntity<AssetAdministrationShellDescriptor> getAssetAdministrationShellDescriptorById( byte[] aasIdentifier, @RequestHeader String externalSubjectId ) {
String decodedAasIdentifier = getDecodedId( aasIdentifier );
Shell saved = shellService.findShellByExternalId(decodedAasIdentifier, getExternalSubjectIdOrEmpty(externalSubjectId));
Shell saved = shellService.findShellByExternalIdAndExternalSubjectId(decodedAasIdentifier, getExternalSubjectIdOrEmpty(externalSubjectId));
return new ResponseEntity<>(shellMapper.toApiDto(saved), HttpStatus.OK);
}

@Override
public ResponseEntity<SubmodelDescriptor> getSubmodelDescriptorByIdThroughSuperpath( byte[] aasIdentifier, byte[] submodelIdentifier ) {
Submodel submodel = shellService.findSubmodelByExternalId(getDecodedId( aasIdentifier ), getDecodedId( submodelIdentifier ),getExternalSubjectIdOrEmpty( null ));
public ResponseEntity<SubmodelDescriptor> getSubmodelDescriptorByIdThroughSuperpath( byte[] aasIdentifier, byte[] submodelIdentifier, @RequestHeader String externalSubjectId ) {
Submodel submodel = shellService.findSubmodelByExternalId(getDecodedId( aasIdentifier ), getDecodedId( submodelIdentifier ),getExternalSubjectIdOrEmpty( externalSubjectId ));
return new ResponseEntity<>(submodelMapper.toApiDto(submodel), HttpStatus.OK);
}

Expand All @@ -125,27 +125,27 @@ public ResponseEntity<AssetAdministrationShellDescriptor> postAssetAdministratio
}

@Override
public ResponseEntity<SubmodelDescriptor> postSubmodelDescriptorThroughSuperpath( byte[] aasIdentifier, SubmodelDescriptor submodelDescriptor ) {
public ResponseEntity<SubmodelDescriptor> postSubmodelDescriptorThroughSuperpath( byte[] aasIdentifier, @RequestHeader String externalSubjectId, SubmodelDescriptor submodelDescriptor ) {
Submodel toBeSaved = submodelMapper.fromApiDto(submodelDescriptor);
toBeSaved.setIdExternal( submodelDescriptor.getId() );
shellService.mapSubmodel( Set.of(toBeSaved) );
Submodel savedSubModel = shellService.save(getDecodedId( aasIdentifier ), toBeSaved, getExternalSubjectIdOrEmpty(null));
Submodel savedSubModel = shellService.save(getDecodedId( aasIdentifier ), toBeSaved, getExternalSubjectIdOrEmpty(externalSubjectId));
return new ResponseEntity<>(submodelMapper.toApiDto(savedSubModel), HttpStatus.CREATED);
}

@Override
public ResponseEntity<Void> putAssetAdministrationShellDescriptorById( byte[] aasIdentifier, AssetAdministrationShellDescriptor assetAdministrationShellDescriptor ) {
public ResponseEntity<Void> putAssetAdministrationShellDescriptorById( byte[] aasIdentifier, AssetAdministrationShellDescriptor assetAdministrationShellDescriptor, @RequestHeader String externalSubjectId ) {
Shell shell = shellMapper.fromApiDto( assetAdministrationShellDescriptor );
Shell shellFromDb = shellService.findShellByExternalId( getDecodedId( aasIdentifier),getExternalSubjectIdOrEmpty(null) );
Shell shellFromDb = shellService.findShellByExternalId( getDecodedId( aasIdentifier),getExternalSubjectIdOrEmpty(externalSubjectId) );
shellService.update( shell.withId( shellFromDb.getId() ).withIdExternal(getDecodedId(aasIdentifier) ),getDecodedId(aasIdentifier));
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}

@Override
public ResponseEntity<Void> putSubmodelDescriptorByIdThroughSuperpath( byte[] aasIdentifier, byte[] submodelIdentifier, SubmodelDescriptor submodelDescriptor ) {
shellService.deleteSubmodel(getDecodedId( aasIdentifier ), getDecodedId( submodelIdentifier ),getExternalSubjectIdOrEmpty( null ));
public ResponseEntity<Void> putSubmodelDescriptorByIdThroughSuperpath( byte[] aasIdentifier, byte[] submodelIdentifier, @RequestHeader String externalSubjectId, SubmodelDescriptor submodelDescriptor ) {
shellService.deleteSubmodel(getDecodedId( aasIdentifier ), getDecodedId( submodelIdentifier ),getExternalSubjectIdOrEmpty( externalSubjectId ));
submodelDescriptor.setId( getDecodedId( submodelIdentifier ));
postSubmodelDescriptorThroughSuperpath(aasIdentifier,submodelDescriptor);
postSubmodelDescriptorThroughSuperpath(aasIdentifier,externalSubjectId,submodelDescriptor);
return new ResponseEntity<>( HttpStatus.NO_CONTENT );
}

Expand All @@ -161,14 +161,14 @@ public ResponseEntity<GetAllAssetAdministrationShellIdsByAssetLink200Response> g
}

@Override
public ResponseEntity<List<SpecificAssetId>> getAllAssetLinksById(byte[] aasIdentifier,@RequestHeader String externalSubjectId) {
public ResponseEntity<List<SpecificAssetId>> getAllAssetLinksById(byte[] aasIdentifier,@RequestHeader String externalSubjectId) {
Set<ShellIdentifier> identifiers = shellService.findShellIdentifiersByExternalShellId(getDecodedId( aasIdentifier ),getExternalSubjectIdOrEmpty(externalSubjectId));
return new ResponseEntity<>(shellMapper.toApiDto(identifiers), HttpStatus.OK);
}

@Override
public ResponseEntity<List<SpecificAssetId>> postAllAssetLinksById(byte[] aasIdentifier, List<SpecificAssetId> specificAssetId) {
Set<ShellIdentifier> shellIdentifiers = shellService.save(getDecodedId( aasIdentifier ), shellMapper.fromApiDto(specificAssetId),getExternalSubjectIdOrEmpty( null ));
@Override
public ResponseEntity<List<SpecificAssetId>> postAllAssetLinksById(byte[] aasIdentifier, List<SpecificAssetId> specificAssetId, @RequestHeader String externalSubjectId ) {
Set<ShellIdentifier> shellIdentifiers = shellService.save(getDecodedId( aasIdentifier ), shellMapper.fromApiDto(specificAssetId),getExternalSubjectIdOrEmpty( externalSubjectId ));
List<SpecificAssetId> list = shellMapper.toApiDto(shellIdentifiers);
return new ResponseEntity<>(list, HttpStatus.CREATED);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@

import org.eclipse.tractusx.semantics.registry.model.Shell;
import org.eclipse.tractusx.semantics.registry.model.projection.ShellMinimal;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
Expand All @@ -31,7 +34,26 @@

@Repository
public interface ShellRepository extends JpaRepository<Shell, UUID>, JpaSpecificationExecutor<Shell> {
Optional<Shell> findByIdExternal( String idExternal );
Optional<Shell> findByIdExternal( @Param( "idExternal" ) String idExternal);

@Query( value = "select * from shell s " +
"where s.id_external = :idExternal and (" +
":tenantId = :owningTenantId " +
"or not exists (select si.id from shell_identifier si where s.id = si.fk_shell_id) " +
"or s.id in (" +
"select si.fk_shell_id from shell_identifier si where exists (" +
"select sider.ref_key_value from SHELL_IDENTIFIER_EXTERNAL_SUBJECT_REFERENCE_KEY sider " +
"where (sider.ref_key_value = :tenantId " +
"or (sider.ref_key_value = :publicWildcardPrefix and si.namespace in (:publicWildcardAllowedTypes) )) " +
"and sider.FK_SI_EXTERNAL_SUBJECT_REFERENCE_ID="+
"(select sies.id from SHELL_IDENTIFIER_EXTERNAL_SUBJECT_REFERENCE sies where sies.FK_SHELL_IDENTIFIER_EXTERNAL_SUBJECT_ID=si.id)"+
"))" +
")",nativeQuery = true )
Optional<Shell> findByIdExternalAndExternalSubjectId( @Param( "idExternal" ) String idExternal,
@Param("tenantId") String tenantId,
@Param("owningTenantId") String owningTenantId,
@Param ("publicWildcardPrefix") String publicWildcardPrefix,
@Param ("publicWildcardAllowedTypes") List<String> publicWildcardAllowedTypes);

@Query( "SELECT new org.eclipse.tractusx.semantics.registry.model.projection.ShellMinimal(s.id,s.createdDate) FROM Shell s where s.idExternal = :idExternal" )
Optional<ShellMinimal> findMinimalRepresentationByIdExternal(@Param("idExternal") String idExternal );
Expand All @@ -55,15 +77,17 @@ public interface ShellRepository extends JpaRepository<Shell, UUID>, JpaSpecific
@Query( value = "select s.id_external from shell s where s.id in (" +
"select si.fk_shell_id from shell_identifier si " +
"where concat(si.namespace,si.identifier) in (:keyValueCombinations) " +
"and (:tenantId = :owningTenantId or :tenantId = (" +
"Select sider.ref_key_value from SHELL_IDENTIFIER_EXTERNAL_SUBJECT_REFERENCE_KEY sider where FK_SI_EXTERNAL_SUBJECT_REFERENCE_ID="+
"and (:tenantId = :owningTenantId or exists (" +
"Select sider.ref_key_value from SHELL_IDENTIFIER_EXTERNAL_SUBJECT_REFERENCE_KEY sider where (sider.ref_key_value = :tenantId or (sider.ref_key_value = :publicWildcardPrefix and si.namespace in (:publicWildcardAllowedTypes) )) and sider.FK_SI_EXTERNAL_SUBJECT_REFERENCE_ID="+
"(select sies.id from SHELL_IDENTIFIER_EXTERNAL_SUBJECT_REFERENCE sies where sies.FK_SHELL_IDENTIFIER_EXTERNAL_SUBJECT_ID=si.id)"+
")) group by si.fk_shell_id " +
"having count(*) = :keyValueCombinationsSize " +
")",nativeQuery = true )
List<String> findExternalShellIdsByIdentifiersByExactMatch(@Param("keyValueCombinations") List<String> keyValueCombinations,
@Param("keyValueCombinationsSize") int keyValueCombinationsSize,
@Param("tenantId") String tenantId,
@Param ("publicWildcardPrefix") String publicWildcardPrefix,
@Param ("publicWildcardAllowedTypes") List<String> publicWildcardAllowedTypes,
@Param("owningTenantId") String owningTenantId);

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package org.eclipse.tractusx.semantics.registry.service;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.eclipse.tractusx.semantics.RegistryProperties;
import org.eclipse.tractusx.semantics.registry.model.Shell;
import org.eclipse.tractusx.semantics.registry.model.ShellIdentifier;
import org.eclipse.tractusx.semantics.registry.model.ShellIdentifierExternalSubjectReferenceKey;
import org.springframework.stereotype.Service;

import lombok.extern.slf4j.Slf4j;

/********************************************************************************
* Copyright (c) 2021-2023 Robert Bosch Manufacturing Solutions GmbH
* Copyright (c) 2021-2023 Contributors to the Eclipse Foundation
*
* 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
********************************************************************************/
@Slf4j
@Service
public class ShellAccessHandler {
private final String owningTenantId;
private final String externalSubjectIdWildcardPrefix;
private final List<String> externalSubjectIdWildcardAllowedTypes;

public ShellAccessHandler( RegistryProperties registryProperties ) {
this.owningTenantId = registryProperties.getIdm().getOwningTenantId();
this.externalSubjectIdWildcardPrefix = registryProperties.getExternalSubjectIdWildcardPrefix();
this.externalSubjectIdWildcardAllowedTypes = registryProperties.getExternalSubjectIdWildcardAllowedTypes();
}

/**
* This method filter out the shell-properties based on externalSubjectId in the specificAssetIds.<br>
* 1. Condition: The owner of the shell has full access to the shell.<br>
* 2. Condition: If the given @param externalSubjectId is included in one of the specificAssetIds, all shell-properties are visible. Only the list of specificAssetIds are limited to given externalSubjectId.<br>
* 3. Condition: If the given @param externalSubjectId is not included in one of the specificAssetIds, only few properties are visible:idShort, submodelDescriptors
*
*
* @param shell
* @param externalSubjectId externalSubjectId/tenantId
* @return filtered Shell
*/
public Shell filterShellProperties( Shell shell, String externalSubjectId ) {
if ( externalSubjectId.equals( owningTenantId ) ) {
return shell;
}

Set<ShellIdentifier> filteredIdentifiers = filterSpecificAssetIdsByTenantId( shell.getIdentifiers(), externalSubjectId );
boolean hasOnlyPublicAccess = !filteredIdentifiers.stream().anyMatch( shellIdentifier -> {
if ( shellIdentifier.getExternalSubjectId() == null ) {
return false;
}
return shellIdentifier.getExternalSubjectId().getKeys().stream().anyMatch( key -> key.getValue().equals( externalSubjectId ) );
}
);

if ( hasOnlyPublicAccess ) {
return new Shell()
.withIdentifiers( filteredIdentifiers )
.withSubmodels( shell.getSubmodels() )
.withIdExternal( shell.getIdExternal() )
.withId( shell.getId() )
.withCreatedDate( shell.getCreatedDate() );
}
return shell.withIdentifiers( filteredIdentifiers );
}

private Set<ShellIdentifier> filterSpecificAssetIdsByTenantId( Set<ShellIdentifier> shellIdentifiers, String tenantId ) {
// the owning tenant should always see all identifiers
if ( tenantId.equals( owningTenantId ) ) {
return shellIdentifiers;
}

Set<ShellIdentifier> externalSubjectIdSet = new HashSet<>();
for ( ShellIdentifier identifier : shellIdentifiers ) {
if ( identifier.getExternalSubjectId() != null ) {
Set<ShellIdentifierExternalSubjectReferenceKey> optionalReferenceKey =
identifier.getExternalSubjectId().getKeys().stream().filter( shellIdentifierExternalSubjectReferenceKey ->
// Match if externalSubjectId = tenantId
shellIdentifierExternalSubjectReferenceKey.getValue().equals( tenantId ) ||
// or match if externalSubjectId = externalSubjectIdWildcardPrefix and key of identifier (for example manufacturerPartId) is allowing wildcard.
(shellIdentifierExternalSubjectReferenceKey.getValue().equals( externalSubjectIdWildcardPrefix ) &&
externalSubjectIdWildcardAllowedTypes.contains( identifier.getKey() )) ).collect( Collectors.toSet() );
if ( optionalReferenceKey != null && !optionalReferenceKey.isEmpty() ) {
identifier.getExternalSubjectId().setKeys( optionalReferenceKey );
externalSubjectIdSet.add( identifier );
}
}
}
return externalSubjectIdSet;
}
}
Loading

0 comments on commit 2f11cd7

Please sign in to comment.