diff --git a/DEPENDENCIES b/DEPENDENCIES
index 3e14cde6..fce27e7b 100644
--- a/DEPENDENCIES
+++ b/DEPENDENCIES
@@ -10,9 +10,11 @@ maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jdk8/2.15.4,
maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.15.4, Apache-2.0, approved, #7930
maven/mavencentral/com.fasterxml.jackson.module/jackson-module-parameter-names/2.15.4, Apache-2.0, approved, #8803
maven/mavencentral/com.fasterxml/classmate/1.6.0, Apache-2.0, approved, clearlydefined
+maven/mavencentral/com.github.ben-manes.caffeine/caffeine/3.1.8, Apache-2.0, approved, clearlydefined
maven/mavencentral/com.github.stephenc.jcip/jcip-annotations/1.0-1, Apache-2.0, approved, CQ21949
maven/mavencentral/com.google.code.findbugs/jsr305/3.0.2, Apache-2.0, approved, #20
maven/mavencentral/com.google.errorprone/error_prone_annotations/2.18.0, Apache-2.0, approved, clearlydefined
+maven/mavencentral/com.google.errorprone/error_prone_annotations/2.21.1, Apache-2.0, approved, #9834
maven/mavencentral/com.google.guava/failureaccess/1.0.1, Apache-2.0, approved, CQ22654
maven/mavencentral/com.google.guava/guava/32.1.1-jre, Apache-2.0 AND CC0-1.0 AND LicenseRef-Public-Domain, approved, #9229
maven/mavencentral/com.google.guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava, Apache-2.0, approved, CQ22657
@@ -20,7 +22,12 @@ maven/mavencentral/com.google.j2objc/j2objc-annotations/2.8, Apache-2.0, approve
maven/mavencentral/com.h2database/h2/2.2.220, (EPL-1.0 OR MPL-2.0) AND (LGPL-3.0-or-later OR EPL-1.0 OR MPL-2.0), approved, #9322
maven/mavencentral/com.nimbusds/nimbus-jose-jwt/9.24.4, Apache-2.0, approved, clearlydefined
maven/mavencentral/com.opencsv/opencsv/5.7.1, Apache-2.0, approved, clearlydefined
+maven/mavencentral/com.squareup.okhttp3/okhttp/4.12.0, Apache-2.0, approved, #11156
+maven/mavencentral/com.squareup.okio/okio-jvm/3.6.0, Apache-2.0, approved, #11158
+maven/mavencentral/com.squareup.okio/okio/3.6.0, Apache-2.0, approved, #11155
maven/mavencentral/com.zaxxer/HikariCP/5.0.1, Apache-2.0, approved, clearlydefined
+maven/mavencentral/dev.failsafe/failsafe-okhttp/3.3.2, Apache-2.0, approved, #9178
+maven/mavencentral/dev.failsafe/failsafe/3.3.2, Apache-2.0, approved, #9268
maven/mavencentral/io.github.classgraph/classgraph/4.8.149, MIT, approved, CQ22530
maven/mavencentral/io.micrometer/micrometer-commons/1.12.4, Apache-2.0 AND (Apache-2.0 AND MIT), approved, #11679
maven/mavencentral/io.micrometer/micrometer-core/1.12.4, Apache-2.0 AND (Apache-2.0 AND MIT), approved, #11678
@@ -38,6 +45,7 @@ maven/mavencentral/jakarta.transaction/jakarta.transaction-api/2.0.1, EPL-2.0 OR
maven/mavencentral/jakarta.validation/jakarta.validation-api/3.0.2, Apache-2.0, approved, ee4j.validation
maven/mavencentral/jakarta.websocket/jakarta.websocket-api/2.1.1, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.websocket
maven/mavencentral/jakarta.websocket/jakarta.websocket-client-api/2.1.1, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.websocket
+maven/mavencentral/jakarta.ws.rs/jakarta.ws.rs-api/3.1.0, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.rest
maven/mavencentral/jakarta.xml.bind/jakarta.xml.bind-api/4.0.2, BSD-3-Clause, approved, ee4j.jaxb
maven/mavencentral/javax.activation/javax.activation-api/1.2.0, (CDDL-1.1 OR GPL-2.0 WITH Classpath-exception-2.0) AND Apache-2.0, approved, CQ18740
maven/mavencentral/javax.xml.bind/jaxb-api/2.3.1, CDDL-1.1 OR GPL-2.0-only WITH Classpath-exception-2.0, approved, CQ16911
@@ -48,7 +56,22 @@ maven/mavencentral/org.apache.commons/commons-text/1.10.0, Apache-2.0, approved,
maven/mavencentral/org.apache.logging.log4j/log4j-api/2.21.1, Apache-2.0 AND (Apache-2.0 AND LGPL-2.0-or-later), approved, #11079
maven/mavencentral/org.apache.logging.log4j/log4j-to-slf4j/2.21.1, Apache-2.0, approved, #11919
maven/mavencentral/org.aspectj/aspectjweaver/1.9.21, Apache-2.0 AND BSD-3-Clause AND EPL-1.0 AND BSD-3-Clause AND Apache-1.1, approved, #7695
+maven/mavencentral/org.checkerframework/checker-qual/3.37.0, MIT, approved, clearlydefined
maven/mavencentral/org.checkerframework/checker-qual/3.42.0, MIT, approved, clearlydefined
+maven/mavencentral/org.eclipse.edc/connector-core/0.6.0, Apache-2.0, approved, technology.edc
+maven/mavencentral/org.eclipse.edc/core-spi/0.6.0, Apache-2.0, approved, technology.edc
+maven/mavencentral/org.eclipse.edc/data-address-http-data-spi/0.6.0, Apache-2.0, approved, technology.edc
+maven/mavencentral/org.eclipse.edc/data-plane-http-spi/0.6.0, Apache-2.0, approved, technology.edc
+maven/mavencentral/org.eclipse.edc/data-plane-spi/0.6.0, Apache-2.0, approved, technology.edc
+maven/mavencentral/org.eclipse.edc/http-spi/0.6.0, Apache-2.0, approved, technology.edc
+maven/mavencentral/org.eclipse.edc/keys-spi/0.6.0, Apache-2.0, approved, technology.edc
+maven/mavencentral/org.eclipse.edc/policy-engine-spi/0.6.0, Apache-2.0, approved, technology.edc
+maven/mavencentral/org.eclipse.edc/policy-model/0.6.0, Apache-2.0, approved, technology.edc
+maven/mavencentral/org.eclipse.edc/runtime-metamodel/0.6.0, Apache-2.0, approved, technology.edc
+maven/mavencentral/org.eclipse.edc/transaction-datasource-spi/0.6.0, Apache-2.0, approved, technology.edc
+maven/mavencentral/org.eclipse.edc/transaction-spi/0.6.0, Apache-2.0, approved, technology.edc
+maven/mavencentral/org.eclipse.edc/transform-spi/0.6.0, Apache-2.0, approved, technology.edc
+maven/mavencentral/org.eclipse.edc/validator-spi/0.6.0, Apache-2.0, approved, technology.edc
maven/mavencentral/org.eclipse.jetty.ee10.websocket/jetty-ee10-websocket-jakarta-client/12.0.7, EPL-2.0 OR Apache-2.0, approved, rt.jetty
maven/mavencentral/org.eclipse.jetty.ee10.websocket/jetty-ee10-websocket-jakarta-common/12.0.7, EPL-2.0 OR Apache-2.0, approved, rt.jetty
maven/mavencentral/org.eclipse.jetty.ee10.websocket/jetty-ee10-websocket-jakarta-server/12.0.7, EPL-2.0 OR Apache-2.0, approved, rt.jetty
@@ -75,10 +98,16 @@ maven/mavencentral/org.eclipse.jetty/jetty-server/12.0.7, EPL-2.0 OR Apache-2.0,
maven/mavencentral/org.eclipse.jetty/jetty-session/12.0.7, EPL-2.0 OR Apache-2.0, approved, rt.jetty
maven/mavencentral/org.eclipse.jetty/jetty-util/12.0.7, EPL-2.0 OR Apache-2.0, approved, rt.jetty
maven/mavencentral/org.eclipse.jetty/jetty-xml/12.0.7, EPL-2.0 OR Apache-2.0, approved, rt.jetty
+maven/mavencentral/org.glassfish.jersey.core/jersey-server/3.1.5, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jersey
maven/mavencentral/org.glassfish/jakarta.json/2.0.1, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jsonp
maven/mavencentral/org.hibernate.orm/hibernate-core/6.4.4.Final, LGPL-2.1-or-later AND (EPL-2.0 OR BSD-3-Clause) AND MIT, approved, #12490
maven/mavencentral/org.hibernate.validator/hibernate-validator/8.0.1.Final, Apache-2.0, approved, clearlydefined
maven/mavencentral/org.jboss.logging/jboss-logging/3.5.3.Final, Apache-2.0, approved, #9471
+maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-common/1.9.23, Apache-2.0, approved, #14186
+maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.9.23, Apache-2.0, approved, #14188
+maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.9.23, Apache-2.0, approved, #14185
+maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib/1.9.23, Apache-2.0, approved, #11827
+maven/mavencentral/org.jetbrains/annotations/24.1.0, Apache-2.0, approved, clearlydefined
maven/mavencentral/org.liquibase/liquibase-core/4.19.1, Apache-2.0, approved, clearlydefined
maven/mavencentral/org.mapstruct/mapstruct/1.5.3.Final, Apache-2.0, approved, #6277
maven/mavencentral/org.openapitools/jackson-databind-nullable/0.1.0, Apache-2.0, approved, clearlydefined
diff --git a/docs/EDC-extension-configuration.md b/docs/EDC-extension-configuration.md
new file mode 100644
index 00000000..b9c702d2
--- /dev/null
+++ b/docs/EDC-extension-configuration.md
@@ -0,0 +1,220 @@
+# Digital Twin Registry Data Plane HTTP Access Control Eclipse Dataspace Connector extension
+
+## 1 Introduction and goals
+
+The *Eclipse Dataspace Connector (EDC)* can provide access to many different backend services. These services have different security capabilities.
+
+### High level requirement
+The *Eclipse Dataspace Connector* extension can provide a granular access control solution for authorization of *Submodel* backend requests based on the registered
+*Submodel* endpoints of the *Shells* which are available for the Data Consumer through the *Digital Twin Registry (DTR)*.
+
+### Requirements
+
+- *Submodel* backends cannot be accessed through *Eclipse Dataspace Connector* unless:
+ - their __exact URL__ is registered in the *Digital Twin Registry* as a *Submodel* endpoint `href`; AND
+ - both the *Shell* containing the *Submodel* endpoint and the Submodels are visible to the Data Consumer.
+- Multiple *Digital Twin Registry* instances might be registered for the same *Eclipse Dataspace Connector Data Plane* instance and *Eclipse Dataspace Connector* extension.
+- The access control solution uses the already existing `Edc-Bpn` HTTP header, containing the *BPN* of the Data Consumer.
+- The extension requires at least one registered *Digital Twin Registry* using granular access control ([as defined here](README.md#granular-access-control-implementation))
+
+### Assumptions
+
+- The provider *Control Plane EDC* includes the `Edc-Bpn` header in the `DataAddress`.
+
+### Stakeholders
+
+| Role | Description | Goal, Intention |
+|---------------|--------------------------------------------------------------|-------------------------------------------|
+| Data Consumer | uses the DTR | wants to find endpoints for Shells |
+| Data Provider | runs its own DTR and provides endpoints to his Shells | wants to provide Shells |
+
+## 2 Architecture and constraints
+
+### System context (Logical view)
+
+```mermaid
+C4Context
+ System_Ext(client, "Client", "Client application")
+ System_Ext(edcCP, "Consumer EDC", "EDC Control Plane")
+ System(edcDP, "Provider EDC", "Provider EDC Data Plane with the extension ")
+ System_Ext(idp, "OAuth2 IDM", "Provides tokens for DTR access")
+ System_Ext(dtr, "Digital Twin Registry", "DTR instance registered with EDC")
+ System_Ext(backend, "Submodel backend", "System of record for submodel data")
+
+ Rel(client, edcCP, "Get EDR")
+ Rel(client, edcDP, "Call with EDR")
+ Rel(edcDP, idp, "Get token")
+ Rel(edcDP, dtr, "Call for access verification")
+ Rel(edcDP, backend, "Proxy HTTP call if access granted")
+
+ UpdateLayoutConfig($c4ShapeInRow="2", $c4BoundaryInRow="1")
+```
+
+### Constraints
+- The Data Provider must configure at least one *Digital Twin Registry* instance in order to use the *DTR Data Plane HTTP Access Control EDC extension*.
+- The extension depends on the EDC Signaling API (no backport for legacy data access APIs)
+
+## 3 Runtime-view
+
+### Call sequences (Runtime view)
+
+#### Prerequisites
+
+In order to perform any of the following actions, we must assume that:
+
+1. The *Digital Twin Registry* and the *Submodel* backend are both registered in *EDC* as assets
+2. The *Submodel* backend asset configured to proxy the request path and query parameters
+3. The client knows how to access the *Digital Twin Registry* through *EDC*
+4. The client received the *Submodel* backend endpoint address from the *Digital Twin Registry* containing:
+ 1. The full *EDC Data Plane* URL including the proxied path and query parameters (if any) in `href`
+ 2. The Id of the *EDC* asset referencing the *Submodel* backend
+ 3. The provider *EDC control Plane's* URL
+5. The *DTR Data Plane HTTP Access Control* extension is properly configured to call the *Digital Twin Registry* when the
+ `DataAddress` URL points to an endpoint of the *Submodel* backend
+6. The provider *Control Plane EDC* includes the `Edc-Bpn` header in the `DataAddress`
+
+
+#### Happy case - The client has access to the protected *Submodel* resource
+
+When the client has access to the protected *Submodel* details (based on the BPN and the *EDC Data Plane* request URL), then
+the extension obtains an `OAuth2` access token for accessing the *Digital Twin Registry* to perform the additional access
+verification step. The request is allowed through to the *Submodel* backend when the confirmation arrives from the *Digital
+Twin Registry*. This flow can be seen below.
+
+```mermaid
+sequenceDiagram
+ autonumber
+
+ participant CLI as Client app
+ participant CCP as Consumer EDC
(Control Plane)
+ participant PCP as Provider EDC
(Control Plane)
+ participant PDP as Provider EDC
(Data Plane)
+ participant IDP as OAuth2 IDM
+ participant DTR as Digital Twin Registry
+ participant SMB as Submodel backend
+
+ CLI ->> CCP : Get EDR for Submodel backend
+ CCP -->> PCP : Negotiate for EDR
+ PCP -->> CCP : Return EDR
+ CCP -->> CLI : Return EDR
+ CLI ->> PDP : Request Submodel backend data
+ PDP ->> PDP : Evaluate whether DTR check
is needed based on config
+ PDP ->> IDP : Get OAuth2 token for DTR
+ IDP -->> PDP : Return token
+ PDP ->> DTR : Verify Submodel endpoint access using Edc-Bpn and
the EDC Provider Data Plane URL of the client request
+ DTR -->> PDP : Grant access
+ PDP ->> SMB : Proxy Submodel call
+ SMB -->> PDP : Return Submodel data
+ PDP -->> CLI : Return Submodel data
+```
+
+#### Exceptional case - The client has no access to the protected *Submodel* resource
+
+When the client has NO access to the protected *Submodel* details (based on the *BPN* and the *EDC Data Plane* request URL),
+then using the same flow, the extension will receive an error response form the *Digital Twin Registry* and as a result it
+will not allow the *Submodel* request through by triggering a `HTTP 403` response from the *EDC*.
+
+```mermaid
+sequenceDiagram
+ autonumber
+
+ participant CLI as Client app
+ participant CCP as Consumer EDC
(Control Plane)
+ participant PCP as Provider EDC
(Control Plane)
+ participant PDP as Provider EDC
(Data Plane)
+ participant IDP as OAuth2 IDM
+ participant DTR as Digital Twin Registry
+
+ CLI ->> CCP : Get EDR for Submodel backend
+ CCP -->> PCP : Negotiate for EDR
+ PCP -->> CCP : Return EDR
+ CCP -->> CLI : Return EDR
+ CLI ->> PDP : Request Submodel backend data
+ PDP ->> PDP : Evaluate whether DTR check
is needed based on config
+ PDP ->> IDP : Get OAuth2 token for DTR
+ IDP -->> PDP : Return token
+ PDP ->> DTR : Verify Submodel endpoint access using Edc-Bpn and
the EDC Provider Data Plane URL of the client request
+ DTR -->> PDP : Deny access
+ PDP ->> PDP : Return HTTP 403 error response instead of Submodel data
+ PDP -->> CLI : Return error response
+```
+
+#### Happy case - The requested resource does not require *DTR* verification
+
+Thanks to the configurable RegExp pattern controlling the access control mechanism, we can continue to use backends
+which do not require additional granular access control measures. In this case, the request URL won't match the pattern
+and the extension will neither obtain an `OAuth2` token nor call the *Digital Twin Registry* as seen below.
+
+```mermaid
+sequenceDiagram
+ autonumber
+
+ participant CLI as Client app
+ participant CCP as Consumer EDC
(Control Plane)
+ participant PCP as Provider EDC
(Control Plane)
+ participant PDP as Provider EDC
(Data Plane)
+ participant SMB as Submodel backend
+
+ CLI ->> CCP : Get EDR for Submodel backend
+ CCP -->> PCP : Negotiate for EDR
+ PCP -->> CCP : Return EDR
+ CCP -->> CLI : Return EDR
+ CLI ->> PDP : Request Submodel backend data
+ PDP ->> PDP : Evaluate whether DTR check
is needed based on config
+ PDP ->> SMB : Proxy Submodel call
+ SMB -->> PDP : Return Submodel data
+ PDP -->> CLI : Return Submodel data
+```
+
+## 4 Configuration
+
+### Prerequisites
+
+1. A *Digital Twin Registry* configured to be using granular access control
+2. Access to the *IDM* to create/configure a new user
+3. The ability to add secrets to the Vault used by provider *EDC Data Plane*
+
+### Required roles to access the *Digital Twin Registry* from the extension
+
+The *Digital Twin Registry's* Submodel authorization API endpoint requires the `submodel_access_control` `OAuth2` role. The credentials used by *EDC* must be
+configured in the *IDM* to have this role.
+
+> [!WARNING]
+> It is recommended to use a set of credentials which are created specifically for this extension and only add the aforementioned `submodel_access_control` `OAuth2` role to limit the associated risk in case the credentials become compromised.
+
+### Adding the credentials to the Vault
+
+The client secret of the credentials must be added to the secrets manager (Vault) used by the *EDC Data Plane* instance using the extension. The alias/path of the
+secret will be required during the configuration.
+
+### Extension configuration
+
+```properties
+# Configure the URL which is used when the clients are reaching the EDC Data Plane's /public/v2/ endpoint
+edc.granular.access.verification.edc.data.plane.baseUrl=http://edc-data-plane:9051/public/v2/
+
+# List the names of each DTR instance we intend to configure (comma separated list)
+edc.granular.access.verification.dtr.names=default
+
+# Configure each DTR instance using the following properties.
+# "use the edc.granular.access.verification.dtr.config.." prefix with each name listed above
+
+# How long should we cache the response we have received from DTR?
+edc.granular.access.verification.dtr.config.default.dtr.decision.cache.duration.minutes=1
+# A RegExp pattern that can match the submodel endpoints in scope for DTR access control
+edc.granular.access.verification.dtr.config.default.aspect.model.url.pattern=http:\/\/submodel:8080\/path\/.*
+# The full URL of the access control verification endpoint of the DTR instance
+edc.granular.access.verification.dtr.config.default.dtr.access.verification.endpoint.url=http://dtr:8080/aas-registry-api/v3.0/submodel-descriptor/authorized
+# The OAuth2 configuration for accessing the DTR
+# Token endpoint URL
+edc.granular.access.verification.dtr.config.default.oauth2.token.endpoint.url=http://oauth2-iam:8080/iam/access-management/v1/tenants/00000000-0000-0000-0000-000000000000/openid-connect/token
+# Scope (audience) of the token we want to obtain
+edc.granular.access.verification.dtr.config.default.oauth2.token.scope=aud:dtr
+# OAuth2 client Id
+edc.granular.access.verification.dtr.config.default.oauth2.token.clientId=dtr_client
+# The name (path) of the secret where the OAuth2 client secret is stored in the Vault
+edc.granular.access.verification.dtr.config.default.oauth2.token.clientSecret.path=dtrsecret
+```
+
+> [!NOTE]
+> If none of these properties are configured, the extension is turned off.
diff --git a/docs/README.md b/docs/README.md
index 2c23ef0c..20c1027d 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -679,6 +679,9 @@ The granular access control implementation is provided as an alternative option
4. Restricting access to *Digital Twin* details which are `"PUBLIC_READABLE"`
(only showing the `id`, the public readable `specificAssetId` names and values, the `createdDate` and the filtered `submodelDescriptors` )
+> [!NOTE]
+> We have created an EDC extension to let you leverage the benefits of granular access control to the full extent. Please read the related details [here](EDC-extension-configuration.md)!
+
##### Configuring granular access control
To enable granular access control (instead of the classic implementation), the `registry.useGranularAccessControl` configuration HELM chart property must be set to `"true"`.
diff --git a/libraries/edc-extension/pom.xml b/libraries/edc-extension/pom.xml
index f3ac2e7b..83ce97f5 100644
--- a/libraries/edc-extension/pom.xml
+++ b/libraries/edc-extension/pom.xml
@@ -55,6 +55,20 @@
org.eclipse.edc
connector-core
+
+ org.glassfish.jersey.core
+ jersey-server
+
+
+ org.glassfish.jersey.core
+ *
+
+
+ jakarta.inject
+ *
+
+
+
org.eclipse.edc
data-plane-spi
@@ -80,6 +94,16 @@
mockito-core
test
+
+ org.eclipse.edc
+ junit
+ test
+
+
+ org.glassfish.jersey.core
+ jersey-common
+ test
+
org.assertj
assertj-core
diff --git a/libraries/edc-extension/src/main/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/DtrDataPlaneAccessControlConfigExtension.java b/libraries/edc-extension/src/main/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/DtrDataPlaneAccessControlConfigExtension.java
new file mode 100644
index 00000000..8a7d3f49
--- /dev/null
+++ b/libraries/edc-extension/src/main/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/DtrDataPlaneAccessControlConfigExtension.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH and others
+ * Copyright (c) 2024 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
+ ******************************************************************************/
+
+package org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol;
+
+import org.eclipse.edc.runtime.metamodel.annotation.Extension;
+import org.eclipse.edc.runtime.metamodel.annotation.Provides;
+import org.eclipse.edc.runtime.metamodel.annotation.Setting;
+import org.eclipse.edc.spi.system.ServiceExtension;
+import org.eclipse.edc.spi.system.ServiceExtensionContext;
+
+@Extension( value = "DTR Data Plane HTTP Access Control Config" )
+@Provides( HttpAccessControlCheckClientConfig.class )
+public class DtrDataPlaneAccessControlConfigExtension implements ServiceExtension {
+
+ @Setting( value = "Contains the base URL of the EDC data plane endpoint where the data plane requests are sent by the end users." )
+ public static final String EDC_DATA_PLANE_BASE_URL = "edc.granular.access.verification.edc.data.plane.baseUrl";
+ @Setting( value = "Comma separated list of DTR configuration names used as keys for DTR clients." )
+ public static final String EDC_DTR_CONFIG_NAMES = "edc.granular.access.verification.dtr.names";
+ /**
+ * Prefix for individual DTR configurations.
+ */
+ public static final String EDC_DTR_CONFIG_PREFIX = "edc.granular.access.verification.dtr.config.";
+ /**
+ * Configuration property suffix for the configuration of DTR decision cache. The cache is turned off if set to 0.
+ */
+ public static final String DTR_DECISION_CACHE_MINUTES = "dtr.decision.cache.duration.minutes";
+ /**
+ * Configuration property suffix for the pattern to allow for the recognition of aspect model requests which need
+ * to be handled by DTR access control.
+ */
+ public static final String ASPECT_MODEL_URL_PATTERN = "aspect.model.url.pattern";
+ /**
+ * Configuration property suffix for the URL where DTR can be reached.
+ */
+ public static final String DTR_ACCESS_VERIFICATION_URL = "dtr.access.verification.endpoint.url";
+ /**
+ * Configuration property suffix for the URL where OAUTH2 tokens can be obtained for the DTR requests.
+ */
+ public static final String OAUTH2_TOKEN_ENDPOINT_URL = "oauth2.token.endpoint.url";
+ /**
+ * Configuration property suffix for the scope we need to use for OAUTH2 token requests when we need to access DTR.
+ */
+ public static final String OAUTH2_TOKEN_SCOPE = "oauth2.token.scope";
+ /**
+ * Configuration property suffix for the client id we need to use for OAUTH2 token requests when we need to access DTR.
+ */
+ public static final String OAUTH2_TOKEN_CLIENT_ID = "oauth2.token.clientId";
+
+ /**
+ * Configuration property suffix for the path where we can find the client secret in vault for the OAUTH2 token requests when we need to access DTR.
+ */
+ public static final String OAUTH2_TOKEN_CLIENT_SECRET_PATH = "oauth2.token.clientSecret.path";
+
+ @Override
+ public String name() {
+ return "DTR Data Plane Access Control Service";
+ }
+
+ @Override
+ public void initialize( final ServiceExtensionContext context ) {
+ context.getMonitor().info( "Initializing " + name() );
+ final HttpAccessControlCheckClientConfig config = new HttpAccessControlCheckClientConfig( context );
+ if ( config.getDtrClientConfigMap().isEmpty() ) {
+ //turn off the extension if no DTR is configured
+ context.getMonitor().warning( EDC_DTR_CONFIG_NAMES + " is not configured, DTR access control is turned OFF." );
+ }
+ context.registerService( HttpAccessControlCheckClientConfig.class, config );
+ }
+}
diff --git a/libraries/edc-extension/src/main/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/DtrDataPlaneAccessControlService.java b/libraries/edc-extension/src/main/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/DtrDataPlaneAccessControlService.java
new file mode 100644
index 00000000..7eb8d33b
--- /dev/null
+++ b/libraries/edc-extension/src/main/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/DtrDataPlaneAccessControlService.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH and others
+ * Copyright (c) 2024 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
+ ******************************************************************************/
+
+package org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol;
+
+import static org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol.client.DtrAccessVerificationClient.HEADER_EDC_BPN;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.eclipse.edc.connector.dataplane.spi.iam.DataPlaneAccessControlService;
+import org.eclipse.edc.spi.iam.ClaimToken;
+import org.eclipse.edc.spi.monitor.Monitor;
+import org.eclipse.edc.spi.result.Result;
+import org.eclipse.edc.spi.types.domain.DataAddress;
+import org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol.client.DtrAccessVerificationClient;
+import org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol.client.HttpAccessVerificationClient;
+import org.glassfish.jersey.server.internal.routing.UriRoutingContext;
+
+public class DtrDataPlaneAccessControlService implements DataPlaneAccessControlService {
+ static final String ADDRESS_HEADER_EDC_BPN = "header:" + DtrAccessVerificationClient.HEADER_EDC_BPN;
+ static final String ADDRESS_ASSET_BASE_URL = "https://w3id.org/edc/v0.0.1/ns/baseUrl";
+
+ private final Monitor monitor;
+
+ private final HttpAccessControlCheckClientConfig config;
+
+ private final Map clients;
+
+ public DtrDataPlaneAccessControlService(
+ final Monitor monitor,
+ final Map clients,
+ final HttpAccessControlCheckClientConfig config ) {
+ this.monitor = monitor;
+ this.clients = clients;
+ this.config = config;
+ }
+
+ @Override
+ public Result checkAccess( ClaimToken claimToken, DataAddress address, Map additionalData, Map requestData ) {
+ if ( isNotActive() ) {
+ //no DTR clients are registered, therefore the extension is turned off
+ return Result.success();
+ }
+ if ( !requestData.containsKey( "path" ) || !(requestData.get( "path" ) instanceof UriRoutingContext uriRoutingContext) ) {
+ monitor.warning( "Skipping access control check as requestData.path does not contain uriRoutingContext: "
+ + requestData.getOrDefault( "path", null ) );
+ return Result.success();
+ }
+ final var requestUri = uriRoutingContext.getRequestUri();
+ final var path = requestUri.getPath().replaceFirst( "^/public/v2/", "" );
+ final var queryString = requestUri.getQuery();
+ final Optional baseUrlNoTrailingSlash = Optional.ofNullable( address.getStringProperty( ADDRESS_ASSET_BASE_URL ) )
+ .map( url -> url.replaceFirst( "/$", "" ) );
+ if ( baseUrlNoTrailingSlash.isEmpty() ) {
+ monitor.severe( "Failed to obtain base URL from address!" );
+ return Result.failure( "" );
+ }
+ final var targetUrl = baseUrlNoTrailingSlash.get() + "/" + path;
+ final Map additionalHeaders = Optional.ofNullable( address.getStringProperty( ADDRESS_HEADER_EDC_BPN ) )
+ .map( consumerBpn -> Map.of( HEADER_EDC_BPN, consumerBpn ) )
+ .orElse( Collections.emptyMap() );
+ final var relevantClients = clients.values().stream()
+ .filter( client -> client.isAspectModelCall( targetUrl ) )
+ .collect( Collectors.toSet() );
+ if ( !relevantClients.isEmpty() && relevantClients.stream()
+ .noneMatch( client -> client.shouldAllowAccess( path, queryString, additionalHeaders ) ) ) {
+ return Result.failure( "Forbidden." );
+ }
+
+ return Result.success();
+ }
+
+ protected boolean isNotActive() {
+ return config.getDtrClientConfigMap().isEmpty();
+ }
+}
diff --git a/libraries/edc-extension/src/main/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/DtrDataPlaneAccessControlServiceExtension.java b/libraries/edc-extension/src/main/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/DtrDataPlaneAccessControlServiceExtension.java
index 72e2837b..d5e67d01 100644
--- a/libraries/edc-extension/src/main/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/DtrDataPlaneAccessControlServiceExtension.java
+++ b/libraries/edc-extension/src/main/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/DtrDataPlaneAccessControlServiceExtension.java
@@ -20,58 +20,28 @@
package org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol;
-import org.eclipse.edc.connector.dataplane.spi.iam.DataPlaneAccessTokenService;
+import java.util.HashMap;
+
+import org.eclipse.edc.connector.dataplane.spi.iam.DataPlaneAccessControlService;
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
-import org.eclipse.edc.runtime.metamodel.annotation.Setting;
+import org.eclipse.edc.runtime.metamodel.annotation.Provider;
+import org.eclipse.edc.runtime.metamodel.annotation.Provides;
+import org.eclipse.edc.runtime.metamodel.annotation.Requires;
import org.eclipse.edc.spi.http.EdcHttpClient;
import org.eclipse.edc.spi.monitor.Monitor;
import org.eclipse.edc.spi.security.Vault;
import org.eclipse.edc.spi.system.ServiceExtension;
-import org.eclipse.edc.spi.system.ServiceExtensionContext;
import org.eclipse.edc.spi.types.TypeManager;
+import org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol.client.DtrAccessVerificationClient;
+import org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol.client.DtrOauth2TokenClient;
+import org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol.client.HttpAccessVerificationClient;
+import org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol.client.Oauth2TokenClient;
-@Extension( value = "Data Plane HTTP Access Control" )
+@Extension( value = "DTR Data Plane HTTP Access Control Service" )
+@Provides( DataPlaneAccessControlService.class )
+@Requires( { HttpAccessControlCheckClientConfig.class, TypeManager.class, Vault.class, EdcHttpClient.class } )
public class DtrDataPlaneAccessControlServiceExtension implements ServiceExtension {
-
- @Setting( value = "Contains the base URL of the EDC data plane endpoint where the data plane requests are sent by the end users." )
- public static final String EDC_DATA_PLANE_BASE_URL = "edc.granular.access.verification.edc.data.plane.baseUrl";
- @Setting( value = "Comma separated list of DTR configuration names used as keys for DTR clients." )
- public static final String EDC_DTR_CONFIG_NAMES = "edc.granular.access.verification.dtr.names";
- /**
- * Prefix for individual DTR configurations.
- */
- public static final String EDC_DTR_CONFIG_PREFIX = "edc.granular.access.verification.dtr.config.";
- /**
- * Configuration property suffix for the configuration of DTR decision cache. The cache is turned off if set to 0.
- */
- public static final String DTR_DECISION_CACHE_MINUTES = "dtr.decision.cache.duration.minutes";
- /**
- * Configuration property suffix for the pattern to allow for the recognition of aspect model requests which need
- * to be handled by DTR access control.
- */
- public static final String ASPECT_MODEL_URL_PATTERN = "aspect.model.url.pattern";
- /**
- * Configuration property suffix for the URL where DTR can be reached.
- */
- public static final String DTR_ACCESS_VERIFICATION_URL = "dtr.access.verification.endpoint.url";
- /**
- * Configuration property suffix for the URL where OAUTH2 tokens can be obtained for the DTR requests.
- */
- public static final String OAUTH2_TOKEN_ENDPOINT_URL = "oauth2.token.endpoint.url";
- /**
- * Configuration property suffix for the scope we need to use for OAUTH2 token requests when we need to access DTR.
- */
- public static final String OAUTH2_TOKEN_SCOPE = "oauth2.token.scope";
- /**
- * Configuration property suffix for the client id we need to use for OAUTH2 token requests when we need to access DTR.
- */
- public static final String OAUTH2_TOKEN_CLIENT_ID = "oauth2.token.clientId";
-
- /**
- * Configuration property suffix for the path where we can find the client secret in vault for the OAUTH2 token requests when we need to access DTR.
- */
- public static final String OAUTH2_TOKEN_CLIENT_SECRET_PATH = "oauth2.token.clientSecret.path";
@Inject
private Monitor monitor;
@Inject
@@ -81,7 +51,6 @@ public class DtrDataPlaneAccessControlServiceExtension implements ServiceExtensi
@Inject
private Vault vault;
@Inject
- private DataPlaneAccessTokenService dataPlaneAccessTokenService;
private HttpAccessControlCheckClientConfig config;
@Override
@@ -89,9 +58,16 @@ public String name() {
return "DTR Data Plane Access Control Service";
}
- @Override
- public void initialize( final ServiceExtensionContext context ) {
- monitor.info( "Initializing " + name() );
- config = new HttpAccessControlCheckClientConfig( context );
+ @Provider
+ public DataPlaneAccessControlService dataPlaneAccessControlService() {
+ final var dtrClients = new HashMap();
+ config.getDtrClientConfigMap().forEach( ( k, v ) -> {
+ final Oauth2TokenClient tokenClient = new DtrOauth2TokenClient( monitor, httpClient, typeManager, vault, v );
+ final HttpAccessVerificationClient client = new DtrAccessVerificationClient( monitor, httpClient, tokenClient, typeManager, config, v );
+ dtrClients.put( k, client );
+ } );
+ final var dtrDataPlaneAccessControlService = new DtrDataPlaneAccessControlService( monitor, dtrClients, config );
+ monitor.info( "Registering DtrDataPlaneAccessControlService..." );
+ return dtrDataPlaneAccessControlService;
}
}
diff --git a/libraries/edc-extension/src/main/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/HttpAccessControlCheckClientConfig.java b/libraries/edc-extension/src/main/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/HttpAccessControlCheckClientConfig.java
index 547821ee..b0bff6f7 100644
--- a/libraries/edc-extension/src/main/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/HttpAccessControlCheckClientConfig.java
+++ b/libraries/edc-extension/src/main/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/HttpAccessControlCheckClientConfig.java
@@ -20,7 +20,7 @@
package org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol;
-import static org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol.DtrDataPlaneAccessControlServiceExtension.*;
+import static org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol.DtrDataPlaneAccessControlConfigExtension.*;
import java.util.Arrays;
import java.util.Map;
diff --git a/libraries/edc-extension/src/main/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/HttpAccessControlCheckDtrClientConfig.java b/libraries/edc-extension/src/main/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/HttpAccessControlCheckDtrClientConfig.java
index d1cf2ca1..d541141f 100644
--- a/libraries/edc-extension/src/main/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/HttpAccessControlCheckDtrClientConfig.java
+++ b/libraries/edc-extension/src/main/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/HttpAccessControlCheckDtrClientConfig.java
@@ -20,7 +20,7 @@
package org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol;
-import static org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol.DtrDataPlaneAccessControlServiceExtension.*;
+import static org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol.DtrDataPlaneAccessControlConfigExtension.*;
import org.eclipse.edc.spi.system.configuration.Config;
diff --git a/libraries/edc-extension/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/libraries/edc-extension/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension
new file mode 100644
index 00000000..ba74baa8
--- /dev/null
+++ b/libraries/edc-extension/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension
@@ -0,0 +1,22 @@
+#################################################################################
+# Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH and others
+# Copyright (c) 2024 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
+#################################################################################
+
+org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol.DtrDataPlaneAccessControlConfigExtension
+org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol.DtrDataPlaneAccessControlServiceExtension
diff --git a/libraries/edc-extension/src/test/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/DtrDataPlaneAccessControlConfigExtensionTest.java b/libraries/edc-extension/src/test/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/DtrDataPlaneAccessControlConfigExtensionTest.java
new file mode 100644
index 00000000..ca4520c7
--- /dev/null
+++ b/libraries/edc-extension/src/test/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/DtrDataPlaneAccessControlConfigExtensionTest.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH and others
+ * Copyright (c) 2024 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
+ ******************************************************************************/
+
+package org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol;
+
+import static org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol.DtrDataPlaneAccessControlConfigExtension.*;
+import static org.mockito.Mockito.*;
+
+import org.assertj.core.api.Assertions;
+import org.eclipse.edc.junit.extensions.DependencyInjectionExtension;
+import org.eclipse.edc.spi.http.EdcHttpClient;
+import org.eclipse.edc.spi.monitor.Monitor;
+import org.eclipse.edc.spi.security.Vault;
+import org.eclipse.edc.spi.system.ServiceExtensionContext;
+import org.eclipse.edc.spi.system.configuration.Config;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@ExtendWith( DependencyInjectionExtension.class )
+class DtrDataPlaneAccessControlConfigExtensionTest {
+
+ @Mock
+ private Monitor monitor;
+ @Mock
+ private Vault vault;
+ @Mock
+ private EdcHttpClient httpClient;
+
+ private AutoCloseable openMocks;
+
+ @BeforeEach
+ void setUp( final ServiceExtensionContext context ) {
+ openMocks = MockitoAnnotations.openMocks( this );
+ doReturn( monitor ).when( context ).getMonitor();
+ context.registerService( Monitor.class, monitor );
+ context.registerService( EdcHttpClient.class, httpClient );
+ context.registerService( Vault.class, vault );
+ }
+
+ @AfterEach
+ void tearDown() {
+ Assertions.assertThatNoException().isThrownBy( () -> openMocks.close() );
+ }
+
+ @Test
+ void test_Initialize_ShouldSuccessfullyProcessConfiguration_WhenCalled( final DtrDataPlaneAccessControlConfigExtension extension,
+ final ServiceExtensionContext context ) {
+ //given
+ doReturn( "default" ).when( context )
+ .getSetting( eq( EDC_DTR_CONFIG_NAMES ), eq( "" ) );
+ doReturn( "http://local-edc-wiremock:18080/aspect-model-api/" ).when( context )
+ .getSetting( eq( EDC_DTR_CONFIG_PREFIX + "default" + ASPECT_MODEL_URL_PATTERN ), anyString() );
+ doReturn( mock( Config.class ) ).when( context )
+ .getConfig( EDC_DTR_CONFIG_PREFIX + "default" );
+
+ //when
+ extension.initialize( context );
+
+ //then
+ verify( context ).registerService( eq(HttpAccessControlCheckClientConfig.class), any() );
+ }
+}
diff --git a/libraries/edc-extension/src/test/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/DtrDataPlaneAccessControlServiceExtensionTest.java b/libraries/edc-extension/src/test/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/DtrDataPlaneAccessControlServiceExtensionTest.java
new file mode 100644
index 00000000..77f3a7a2
--- /dev/null
+++ b/libraries/edc-extension/src/test/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/DtrDataPlaneAccessControlServiceExtensionTest.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH and others
+ * Copyright (c) 2024 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
+ ******************************************************************************/
+
+package org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.when;
+
+import java.util.Map;
+
+import org.assertj.core.api.Assertions;
+import org.eclipse.edc.connector.dataplane.spi.iam.DataPlaneAccessControlService;
+import org.eclipse.edc.junit.extensions.DependencyInjectionExtension;
+import org.eclipse.edc.spi.http.EdcHttpClient;
+import org.eclipse.edc.spi.monitor.Monitor;
+import org.eclipse.edc.spi.security.Vault;
+import org.eclipse.edc.spi.system.ServiceExtensionContext;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@ExtendWith( DependencyInjectionExtension.class )
+class DtrDataPlaneAccessControlServiceExtensionTest {
+
+ @Mock
+ private Monitor monitor;
+ @Mock
+ private Vault vault;
+ @Mock
+ private EdcHttpClient httpClient;
+ @Mock
+ private HttpAccessControlCheckClientConfig httpClientConfig;
+ @Mock
+ private HttpAccessControlCheckDtrClientConfig dtrClientConfig;
+
+ private AutoCloseable openMocks;
+
+ @BeforeEach
+ void setUp( final ServiceExtensionContext context ) {
+ openMocks = MockitoAnnotations.openMocks( this );
+ context.registerService( Monitor.class, monitor );
+ context.registerService( EdcHttpClient.class, httpClient );
+ context.registerService( Vault.class, vault );
+ context.registerService( HttpAccessControlCheckClientConfig.class, httpClientConfig );
+ }
+
+ @AfterEach
+ void tearDown() {
+ Assertions.assertThatNoException().isThrownBy( () -> openMocks.close() );
+ }
+
+ @Test
+ void test_DataPlaneAccessControlService_ShouldBeActive_WhenClientMapIsNotEmpty( final DtrDataPlaneAccessControlServiceExtension extension,
+ final ServiceExtensionContext context ) {
+ //given
+ when( httpClientConfig.getDtrClientConfigMap() ).thenReturn( Map.of( "default", dtrClientConfig ) );
+
+ //when
+ DataPlaneAccessControlService actual = extension.dataPlaneAccessControlService();
+
+ //then
+ assertThat( actual ).isInstanceOf( DtrDataPlaneAccessControlService.class );
+ assertThat( ((DtrDataPlaneAccessControlService) actual).isNotActive() ).isFalse();
+ }
+
+ @Test
+ void test_DataPlaneAccessControlService_ShouldNotBeActive_WhenClientMapIsEmpty( final DtrDataPlaneAccessControlServiceExtension extension,
+ final ServiceExtensionContext context ) {
+ //given
+ when( httpClientConfig.getDtrClientConfigMap() ).thenReturn( Map.of() );
+
+ //when
+ DataPlaneAccessControlService actual = extension.dataPlaneAccessControlService();
+
+ //then
+ assertThat( actual ).isInstanceOf( DtrDataPlaneAccessControlService.class );
+ assertThat( ((DtrDataPlaneAccessControlService) actual).isNotActive() ).isTrue();
+ }
+}
diff --git a/libraries/edc-extension/src/test/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/DtrDataPlaneAccessControlServiceTest.java b/libraries/edc-extension/src/test/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/DtrDataPlaneAccessControlServiceTest.java
new file mode 100644
index 00000000..0775362c
--- /dev/null
+++ b/libraries/edc-extension/src/test/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/DtrDataPlaneAccessControlServiceTest.java
@@ -0,0 +1,233 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH and others
+ * Copyright (c) 2024 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
+ ******************************************************************************/
+
+package org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol.DtrDataPlaneAccessControlService.*;
+import static org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol.client.DtrAccessVerificationClient.HEADER_EDC_BPN;
+import static org.mockito.Mockito.*;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.Map;
+import java.util.stream.Stream;
+
+import org.eclipse.edc.spi.iam.ClaimToken;
+import org.eclipse.edc.spi.monitor.Monitor;
+import org.eclipse.edc.spi.result.Result;
+import org.eclipse.edc.spi.types.domain.DataAddress;
+import org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol.client.DtrAccessVerificationClient;
+import org.glassfish.jersey.server.internal.routing.UriRoutingContext;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+@TestInstance( TestInstance.Lifecycle.PER_METHOD )
+class DtrDataPlaneAccessControlServiceTest {
+
+ private static final String BPN_0001 = "BPN0001";
+
+ final Monitor monitor = mock();
+ final ClaimToken claimToken = mock();
+ final UriRoutingContext uriInfo = mock( UriRoutingContext.class );
+ final DataAddress address = mock();
+ final HttpAccessControlCheckClientConfig config = mock();
+ final HttpAccessControlCheckDtrClientConfig dtrConfig = mock();
+ final DtrAccessVerificationClient client = mock();
+ DtrDataPlaneAccessControlService underTest;
+
+ public static Stream aspectModelParameterProvider() {
+ return Stream. builder()
+ .add( Arguments.of(
+ "http://backend.example.com",
+ "api/submodel-resource",
+ "param1=value1",
+ "http://backend.example.com/api/submodel-resource",
+ "http://edc.example.com/public/v2/api/submodel-resource?param1=value1"
+ ) )
+ .add( Arguments.of(
+ "http://backend.example.com/",
+ "api/submodel-resource",
+ "param1=value1",
+ "http://backend.example.com/api/submodel-resource",
+ "http://edc.example.com/public/v2/api/submodel-resource?param1=value1"
+ ) )
+ .build();
+ }
+
+ @BeforeEach
+ void initMocks() {
+ when( config.getEdcDataPlaneBaseUrl() ).thenReturn( "http://edc.example.com/" );
+ when( dtrConfig.getAspectModelUrlPattern() ).thenReturn( "http:\\/\\/backend\\.example\\.com\\/api\\/.*" );
+ when( config.getDtrClientConfigMap() ).thenReturn( Map.of( "0", dtrConfig ) );
+
+ underTest = new DtrDataPlaneAccessControlService( monitor, Map.of( "0", client ), config );
+ }
+
+ @Test
+ void test_DtrLookupCall_ShouldSucceed_WhenBpnHeaderIsPresent() {
+ //given
+ final Map additionalData = Map.of();
+ final Map requestData = Map.of( "method", "GET", "path", uriInfo );
+ when( address.getStringProperty( ADDRESS_ASSET_BASE_URL ) ).thenReturn( "http://dtr.example.com" );
+ when( address.getStringProperty( ADDRESS_HEADER_EDC_BPN ) ).thenReturn( BPN_0001 );
+ when( client.isAspectModelCall( anyString() ) ).thenReturn( false );
+ when( uriInfo.getRequestUri() ).thenReturn( URI.create( "http://edc.example.com/public/v2/api/dtr/resource?param1=value1" ) );
+
+ //when
+ final Result actual = underTest.checkAccess( claimToken, address, additionalData, requestData );
+
+ //then
+ verify( address ).getStringProperty( ADDRESS_HEADER_EDC_BPN );
+ verify( address ).getStringProperty( ADDRESS_ASSET_BASE_URL );
+ assertThat( actual ).isNotNull();
+ assertThat( actual.succeeded() ).isTrue();
+ }
+
+ @Test
+ void test_DtrLookupCall_ShouldSucceed_WhenBpnHeaderIsMissing() {
+ final Map additionalData = Map.of();
+ final Map requestData = Map.of( "method", "GET", "path", uriInfo );
+ when( address.getStringProperty( ADDRESS_ASSET_BASE_URL ) ).thenReturn( "http://dtr.example.com" );
+ when( address.getStringProperty( ADDRESS_HEADER_EDC_BPN ) ).thenReturn( null );
+ when( client.isAspectModelCall( anyString() ) ).thenReturn( false );
+ when( uriInfo.getRequestUri() ).thenReturn( URI.create( "http://edc.example.com/public/v2/api/dtr/resource?param1=value1" ) );
+
+ //when
+ final Result actual = underTest.checkAccess( claimToken, address, additionalData, requestData );
+
+ //then
+ verify( address ).getStringProperty( ADDRESS_HEADER_EDC_BPN );
+ verify( address ).getStringProperty( ADDRESS_ASSET_BASE_URL );
+ assertThat( actual ).isNotNull();
+ assertThat( actual.succeeded() ).isTrue();
+ }
+
+ @ParameterizedTest
+ @MethodSource( "aspectModelParameterProvider" )
+ void test_AspectModelBackendRequest_ShouldSucceed_WhenBpnHeaderIsPresentAndDtrRespondsWithOk(
+ String assetBaseUrl, String proxyPath, String queryString, String fullBackendUrl, String fullDataPlaneUrl ) {
+ //given
+ final Map additionalData = Map.of();
+ final Map requestData = Map.of( "method", "GET", "path", uriInfo );
+ when( address.getStringProperty( ADDRESS_ASSET_BASE_URL ) ).thenReturn( assetBaseUrl );
+ when( address.getStringProperty( ADDRESS_HEADER_EDC_BPN ) ).thenReturn( BPN_0001 );
+ when( client.isAspectModelCall( fullBackendUrl ) ).thenReturn( true );
+ when( uriInfo.getRequestUri() ).thenReturn( URI.create( fullDataPlaneUrl ) );
+ when( client.shouldAllowAccess( proxyPath, queryString, Map.of( HEADER_EDC_BPN, BPN_0001 ) ) ).thenReturn( true );
+
+ //when
+ final Result actual = underTest.checkAccess( claimToken, address, additionalData, requestData );
+
+ //then
+ verify( monitor, never() ).info( anyString() );
+ verify( client ).isAspectModelCall( fullBackendUrl );
+ verify( client ).shouldAllowAccess( proxyPath, queryString, Map.of( HEADER_EDC_BPN, BPN_0001 ) );
+ verify( address ).getStringProperty( ADDRESS_HEADER_EDC_BPN );
+ verify( address ).getStringProperty( ADDRESS_ASSET_BASE_URL );
+ assertThat( actual ).isNotNull();
+ assertThat( actual.succeeded() ).isTrue();
+ }
+
+ @ParameterizedTest
+ @MethodSource( "aspectModelParameterProvider" )
+ void test_AspectModelBackendRequest_ShouldReturnFailure_WhenBpnHeaderIsPresentAndDtrRespondsWithError(
+ String assetBaseUrl, String proxyPath, String queryString, String fullBackendUrl, String fullDataPlaneUrl ) {
+ //given
+ final Map additionalData = Map.of();
+ final Map requestData = Map.of( "method", "GET", "path", uriInfo );
+ when( address.getStringProperty( ADDRESS_ASSET_BASE_URL ) ).thenReturn( assetBaseUrl );
+ when( address.getStringProperty( ADDRESS_HEADER_EDC_BPN ) ).thenReturn( BPN_0001 );
+ when( client.isAspectModelCall( fullBackendUrl ) ).thenReturn( true );
+ when( uriInfo.getRequestUri() ).thenReturn( URI.create( fullDataPlaneUrl ) );
+ when( client.shouldAllowAccess( proxyPath, queryString, Map.of( HEADER_EDC_BPN, BPN_0001 ) ) ).thenReturn( false );
+
+ //when
+ final Result actual = underTest.checkAccess( claimToken, address, additionalData, requestData );
+
+ //then
+ verify( monitor, never() ).info( anyString() );
+ verify( client ).isAspectModelCall( fullBackendUrl );
+ verify( client ).shouldAllowAccess( proxyPath, queryString, Map.of( HEADER_EDC_BPN, BPN_0001 ) );
+ verify( address ).getStringProperty( ADDRESS_HEADER_EDC_BPN );
+ verify( address ).getStringProperty( ADDRESS_ASSET_BASE_URL );
+ assertThat( actual ).isNotNull();
+ assertThat( actual.failed() ).isTrue();
+ }
+
+ @ParameterizedTest
+ @MethodSource( "aspectModelParameterProvider" )
+ void test_AspectModelBackendRequest_ShouldReturnFailure_WhenBpnHeaderIsMissing(
+ String assetBaseUrl, String proxyPath, String queryString, String fullBackendUrl, String fullDataPlaneUrl ) {
+ //given
+ final Map additionalData = Map.of();
+ final Map requestData = Map.of( "method", "GET", "path", uriInfo );
+ when( address.getStringProperty( ADDRESS_ASSET_BASE_URL ) ).thenReturn( assetBaseUrl );
+ when( address.getStringProperty( ADDRESS_HEADER_EDC_BPN ) ).thenReturn( null );
+ when( client.isAspectModelCall( fullBackendUrl ) ).thenReturn( true );
+ when( uriInfo.getRequestUri() ).thenReturn( URI.create( fullDataPlaneUrl ) );
+ when( client.shouldAllowAccess( proxyPath, queryString, Collections.emptyMap() ) ).thenReturn( false );
+
+ //when
+ final Result actual = underTest.checkAccess( claimToken, address, additionalData, requestData );
+
+ //then
+ verify( monitor, never() ).info( anyString() );
+ verify( client ).isAspectModelCall( fullBackendUrl );
+ verify( client ).shouldAllowAccess( proxyPath, queryString, Collections.emptyMap() );
+ verifyNoMoreInteractions( client );
+ verify( address ).getStringProperty( ADDRESS_HEADER_EDC_BPN );
+ verify( address ).getStringProperty( ADDRESS_ASSET_BASE_URL );
+ assertThat( actual ).isNotNull();
+ assertThat( actual.failed() ).isTrue();
+ }
+
+ @ParameterizedTest
+ @MethodSource( "aspectModelParameterProvider" )
+ void test_AspectModelBackendRequest_ShouldReturnFailure_WhenBpnHeaderIsPresentButDtrDeniesAccess(
+ String assetBaseUrl, String proxyPath, String queryString, String fullBackendUrl, String fullDataPlaneUrl ) {
+ //given
+ final Map additionalData = Map.of();
+ final Map requestData = Map.of( "method", "GET", "path", uriInfo );
+ when( address.getStringProperty( ADDRESS_ASSET_BASE_URL ) ).thenReturn( assetBaseUrl );
+ when( address.getStringProperty( ADDRESS_HEADER_EDC_BPN ) ).thenReturn( BPN_0001 );
+ when( client.isAspectModelCall( fullBackendUrl ) ).thenReturn( true );
+ when( uriInfo.getRequestUri() ).thenReturn( URI.create( fullDataPlaneUrl ) );
+ when( client.shouldAllowAccess( proxyPath, queryString, Map.of( HEADER_EDC_BPN, BPN_0001 ) ) ).thenReturn( false );
+
+ //when
+ final Result actual = underTest.checkAccess( claimToken, address, additionalData, requestData );
+
+ //then
+ verify( monitor, never() ).info( anyString() );
+ verify( client ).isAspectModelCall( fullBackendUrl );
+ verify( client ).shouldAllowAccess( proxyPath, queryString, Map.of( HEADER_EDC_BPN, BPN_0001 ) );
+ verifyNoMoreInteractions( client );
+ verify( address ).getStringProperty( ADDRESS_HEADER_EDC_BPN );
+ verify( address ).getStringProperty( ADDRESS_ASSET_BASE_URL );
+ assertThat( actual ).isNotNull();
+ assertThat( actual.failed() ).isTrue();
+ }
+
+}
diff --git a/libraries/edc-extension/src/test/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/HttpAccessControlCheckClientConfigTest.java b/libraries/edc-extension/src/test/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/HttpAccessControlCheckClientConfigTest.java
index 57cda353..7234f6e5 100644
--- a/libraries/edc-extension/src/test/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/HttpAccessControlCheckClientConfigTest.java
+++ b/libraries/edc-extension/src/test/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/HttpAccessControlCheckClientConfigTest.java
@@ -21,7 +21,7 @@
package org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol.DtrDataPlaneAccessControlServiceExtension.*;
+import static org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol.DtrDataPlaneAccessControlConfigExtension.*;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;
diff --git a/libraries/edc-extension/src/test/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/HttpAccessControlCheckDtrClientConfigTest.java b/libraries/edc-extension/src/test/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/HttpAccessControlCheckDtrClientConfigTest.java
index 3916a022..f2abec47 100644
--- a/libraries/edc-extension/src/test/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/HttpAccessControlCheckDtrClientConfigTest.java
+++ b/libraries/edc-extension/src/test/java/org/eclipse/tractusx/semantics/edc/dataplane/http/accesscontrol/HttpAccessControlCheckDtrClientConfigTest.java
@@ -21,7 +21,7 @@
package org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol.DtrDataPlaneAccessControlServiceExtension.*;
+import static org.eclipse.tractusx.semantics.edc.dataplane.http.accesscontrol.DtrDataPlaneAccessControlConfigExtension.*;
import static org.mockito.Mockito.*;
import org.eclipse.edc.spi.system.configuration.Config;
diff --git a/pom.xml b/pom.xml
index 5e944e36..1327b9bd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -96,7 +96,7 @@
4.19.1
- 0.5.2-20240327-SNAPSHOT
+ 0.6.0
3.24.2
@@ -344,6 +344,18 @@
10.1.18
test
+
+ org.eclipse.edc
+ junit
+ ${edc.version}
+ test
+
+
+ junit
+ junit
+
+
+
@@ -467,11 +479,6 @@
maven-central
https://repo1.maven.org/maven2/
-
- Maven Snapshots
- maven-snapshots
- https://oss.sonatype.org/content/repositories/snapshots/
-