Skip to content

Commit

Permalink
Implement new DTR validation API (for submodel request)
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
istvan-nagy-epam committed Feb 14, 2024
1 parent 9b3646f commit 238cafe
Show file tree
Hide file tree
Showing 15 changed files with 766 additions and 564 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -39,6 +38,4 @@ List<String> filterValidSpecificAssetIdsForLookup(

Map<String, ShellVisibilityCriteria> fetchVisibilityCriteriaForShells( List<ShellVisibilityContext> shellContexts, String bpn );

Set<AccessRule> fetchApplicableRulesForPartner( String bpn ) throws DenyAccessException;

}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -96,14 +96,6 @@ public Map<String, ShellVisibilityCriteria> fetchVisibilityCriteriaForShells( Li
.collect( Collectors.toMap( ShellVisibilityCriteria::aasId, Function.identity() ) );
}

@Override
public Set<org.eclipse.tractusx.semantics.accesscontrol.api.model.AccessRule> 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<AccessRulePolicy> findPotentiallyMatchingAccessControlRules( String bpn ) throws DenyAccessException {
List<AccessRule> allByBpn = repository.findAllByBpnWithinValidityPeriod( bpn, bpnWildcard );
if ( allByBpn == null || allByBpn.isEmpty() ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<AccessRule> 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 ) )
) );
}
}
}
Original file line number Diff line number Diff line change
@@ -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<Void> 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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,16 @@ List<String> findExternalShellIdsByIdentifiersByAnyMatch( @Param( "keyValueCombi
@Param( "publicWildcardAllowedTypes" ) List<String> 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<Shell> findAllBySubmodelEndpointAddress( String endpointAddress );
}
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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.
Expand All @@ -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<String, Object> claims = jwtAuthenticationToken.getToken().getClaims();

Object resourceAccess = claims.get("resource_access");
if (!(resourceAccess instanceof Map)) {
return false;
}

Object resource = ((Map<String, Object>) resourceAccess).get(clientId);
if(!(resource instanceof Map)){
return false;
}

Object roles = ((Map<String, Object>)resource).get("roles");
if(!(roles instanceof Collection)){
return false;
}

Collection<String> rolesList = (Collection<String> ) 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<String, Object> claims = jwtAuthenticationToken.getToken().getClaims();

Object resourceAccess = claims.get( "resource_access" );
if ( !(resourceAccess instanceof Map) ) {
return false;
}

Object resource = ((Map<String, Object>) resourceAccess).get( clientId );
if ( !(resource instanceof Map) ) {
return false;
}

Object roles = ((Map<String, Object>) resource).get( "roles" );
if ( !(roles instanceof Collection) ) {
return false;
}

Collection<String> rolesList = (Collection<String>) 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";
}
}

Loading

0 comments on commit 238cafe

Please sign in to comment.