Skip to content

Commit

Permalink
Backport: fix: prevent default DCP identity extractor from being regi…
Browse files Browse the repository at this point in the history
…stered (#1598)

* fix: prevent default identity extractor from being registered (#1597)

* fix(dcp): prevent default identity extractor from being registered

* fix unit tests

* DEPENDENCIES, helm docs
  • Loading branch information
paullatzelsperger authored Oct 1, 2024
1 parent f33b729 commit 7e409f1
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 39 deletions.
2 changes: 1 addition & 1 deletion DEPENDENCIES
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.17.1, Apache
maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.13.5, Apache-2.0, approved, #2133
maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.14.1, Apache-2.0 AND MIT, approved, #4303
maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.16.2, Apache-2.0 AND MIT, approved, #11602
maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.17.1, , approved, #13665
maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.17.1, Apache-2.0 AND MIT, approved, #13665
maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.11.0, Apache-2.0, approved, CQ23093
maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.13.4.2, Apache-2.0, approved, #2134
maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.13.5, Apache-2.0, approved, #2134
Expand Down
4 changes: 2 additions & 2 deletions charts/tractusx-connector-azure-vault/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# tractusx-connector-azure-vault

![Version: 0.7.5](https://img.shields.io/badge/Version-0.7.5-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.7.5](https://img.shields.io/badge/AppVersion-0.7.5-informational?style=flat-square)
![Version: 0.7.6](https://img.shields.io/badge/Version-0.7.6-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.7.6](https://img.shields.io/badge/AppVersion-0.7.6-informational?style=flat-square)

A Helm chart for Tractus-X Eclipse Data Space Connector. The connector deployment consists of two runtime consists of a
Control Plane and a Data Plane. Note that _no_ external dependencies such as a PostgreSQL database and Azure KeyVault are included.
Expand Down Expand Up @@ -44,7 +44,7 @@ Combined, run this shell command to start the in-memory Tractus-X EDC runtime:

```shell
helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev
helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0.7.5 \
helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0.7.6 \
-f <path-to>/tractusx-connector-azure-vault-test.yaml \
--set vault.azure.name=$AZURE_VAULT_NAME \
--set vault.azure.client=$AZURE_CLIENT_ID \
Expand Down
4 changes: 2 additions & 2 deletions charts/tractusx-connector-memory/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# tractusx-connector-memory

![Version: 0.7.5](https://img.shields.io/badge/Version-0.7.5-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.7.5](https://img.shields.io/badge/AppVersion-0.7.5-informational?style=flat-square)
![Version: 0.7.6](https://img.shields.io/badge/Version-0.7.6-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.7.6](https://img.shields.io/badge/AppVersion-0.7.6-informational?style=flat-square)

A Helm chart for Tractus-X Eclipse Data Space Connector based on memory. Please only use this for development or testing purposes, never in production workloads!

Expand Down Expand Up @@ -41,7 +41,7 @@ Combined, run this shell command to start the in-memory Tractus-X EDC runtime:

```shell
helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev
helm install my-release tractusx-edc/tractusx-connector-memory --version 0.7.5 \
helm install my-release tractusx-edc/tractusx-connector-memory --version 0.7.6 \
-f <path-to>/tractusx-connector-memory-test.yaml \
--set vault.secrets="client-secret:$YOUR_CLIENT_SECRET"
```
Expand Down
4 changes: 2 additions & 2 deletions charts/tractusx-connector/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# tractusx-connector

![Version: 0.7.5](https://img.shields.io/badge/Version-0.7.5-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.7.5](https://img.shields.io/badge/AppVersion-0.7.5-informational?style=flat-square)
![Version: 0.7.6](https://img.shields.io/badge/Version-0.7.6-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.7.6](https://img.shields.io/badge/AppVersion-0.7.6-informational?style=flat-square)

A Helm chart for Tractus-X Eclipse Data Space Connector. The connector deployment consists of two runtime consists of a
Control Plane and a Data Plane. Note that _no_ external dependencies such as a PostgreSQL database and HashiCorp Vault are included.
Expand Down Expand Up @@ -44,7 +44,7 @@ Combined, run this shell command to start the in-memory Tractus-X EDC runtime:

```shell
helm repo add tractusx-edc https://eclipse-tractusx.github.io/charts/dev
helm install my-release tractusx-edc/tractusx-connector --version 0.7.5 \
helm install my-release tractusx-edc/tractusx-connector --version 0.7.6 \
-f <path-to>/tractusx-connector-test.yaml
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@

package org.eclipse.tractusx.edc.iam.iatp;

import org.eclipse.edc.iam.identitytrust.spi.DcpParticipantAgentServiceExtension;
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
import org.eclipse.edc.spi.agent.ParticipantAgentService;
import org.eclipse.edc.runtime.metamodel.annotation.Provider;
import org.eclipse.edc.spi.system.ServiceExtension;
import org.eclipse.edc.spi.system.ServiceExtensionContext;
import org.eclipse.tractusx.edc.iam.iatp.identity.IatpIdentityExtractor;

import static org.eclipse.tractusx.edc.iam.iatp.IatpDefaultScopeExtension.NAME;
Expand All @@ -33,17 +32,21 @@ public class IatpIdentityExtension implements ServiceExtension {


static final String NAME = "Tractusx IATP identity extension";
@Inject
private ParticipantAgentService participantAgentService;
private final IatpIdentityExtractor iatpIdentityExtractor = new IatpIdentityExtractor();

@Override
public String name() {
return NAME;
}

@Override
public void initialize(ServiceExtensionContext context) {
participantAgentService.register(new IatpIdentityExtractor());

/**
* This provider method is mandatory, because it prevents the {@code DefaultDcpParticipantAgentServiceExtension} from being
* registered, which would cause a race condition in the identity extractors
*/
@Provider
public DcpParticipantAgentServiceExtension extractor() {
return iatpIdentityExtractor;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

package org.eclipse.tractusx.edc.iam.iatp.identity;

import org.eclipse.edc.iam.identitytrust.spi.DcpParticipantAgentServiceExtension;
import org.eclipse.edc.iam.verifiablecredentials.spi.model.VerifiableCredential;
import org.eclipse.edc.spi.EdcException;
import org.eclipse.edc.spi.agent.ParticipantAgentServiceExtension;
Expand All @@ -38,7 +39,7 @@
* Implementation of {@link ParticipantAgentServiceExtension} which extracts the identity of a participant
* from the MembershipCredential
*/
public class IatpIdentityExtractor implements ParticipantAgentServiceExtension {
public class IatpIdentityExtractor implements DcpParticipantAgentServiceExtension {

private static final String VC_CLAIM = "vc";
private static final String IDENTITY_CREDENTIAL = "MembershipCredential";
Expand All @@ -56,7 +57,7 @@ public class IatpIdentityExtractor implements ParticipantAgentServiceExtension {
.findFirst()
.flatMap(this::getIdentifier)
.map(identity -> Map.of(PARTICIPANT_IDENTITY, identity))
.orElseThrow(() -> new EdcException("Failed to fetch %s property from %s credential".formatted(IDENTITY_PROPERTY, IDENTITY_CREDENTIAL)));
.orElseThrow(() -> new EdcException("Required credential type '%s' not present in ClaimToken, cannot extract property '%s'".formatted(IDENTITY_CREDENTIAL, IDENTITY_PROPERTY)));


}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,18 @@
package org.eclipse.tractusx.edc.iam.iatp;

import org.eclipse.edc.junit.extensions.DependencyInjectionExtension;
import org.eclipse.edc.spi.agent.ParticipantAgentService;
import org.eclipse.edc.spi.system.ServiceExtensionContext;
import org.eclipse.tractusx.edc.iam.iatp.identity.IatpIdentityExtractor;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.assertj.core.api.Assertions.assertThat;

@ExtendWith(DependencyInjectionExtension.class)
public class IatpIdentityExtensionTest {

private final ParticipantAgentService participantAgentService = mock();

@BeforeEach
void setup(ServiceExtensionContext context) {
context.registerService(ParticipantAgentService.class, participantAgentService);
}

@Test
void initialize(ServiceExtensionContext context, IatpIdentityExtension extension) {
extension.initialize(context);
verify(participantAgentService).register(isA(IatpIdentityExtractor.class));
void initialize(IatpIdentityExtension extension) {
assertThat(extension.extractor()).isInstanceOf(IatpIdentityExtractor.class);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -62,36 +62,36 @@ void attributesFor(VerifiableCredential credential) {
}

@Test
void attributesFor_Fails_WhenCredentialNotFound() {
void attributesFor_fails_WhenCredentialNotFound() {
assertThatThrownBy(() -> extractor.attributesFor(ClaimToken.Builder.newInstance().claim("vc", List.of(vc("FooCredential", Map.of("foo", "bar")))).build()))
.isInstanceOf(EdcException.class)
.hasMessageContaining("Failed to fetch");
.hasMessage("Required credential type 'MembershipCredential' not present in ClaimToken, cannot extract property 'holderIdentifier'");
}

@Test
void attributesFor_Fails_whenNoVcClaims() {
void attributesFor_fails_whenNoVcClaims() {
assertThatThrownBy(() -> extractor.attributesFor(ClaimToken.Builder.newInstance().build()))
.isInstanceOf(EdcException.class)
.hasMessageContaining("Failed to fetch credentials from the claim token: ClaimToken did not contain a 'vc' claim");
}

@Test
void attributesFor_Fails_whenNullVcClaims() {
void attributesFor_fails_whenNullVcClaims() {

assertThatThrownBy(() -> extractor.attributesFor(ClaimToken.Builder.newInstance().claim("vc", null).build()))
.isInstanceOf(EdcException.class)
.hasMessageContaining("Failed to fetch credentials from the claim token: ClaimToken did not contain a 'vc' claim");
}

@Test
void attributesFor_Fails_WhenVcClaimIsNotList() {
void attributesFor_fails_WhenVcClaimIsNotList() {
assertThatThrownBy(() -> extractor.attributesFor(ClaimToken.Builder.newInstance().claim("vc", "wrong").build()))
.isInstanceOf(EdcException.class)
.hasMessageContaining("Failed to fetch credentials from the claim token: ClaimToken contains a 'vc' claim, but the type is incorrect. Expected java.util.List, got java.lang.String.");
}

@Test
void attributesFor_Fails_WhenVcClaimsIsEmptyList() {
void attributesFor_fails_WhenVcClaimsIsEmptyList() {
assertThatThrownBy(() -> extractor.attributesFor(ClaimToken.Builder.newInstance().claim("vc", List.of()).build()))
.isInstanceOf(EdcException.class)
.hasMessageContaining("Failed to fetch credentials from the claim token: ClaimToken contains a 'vc' claim but it did not contain any VerifiableCredentials.");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/********************************************************************************
* Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
*
* 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.edc.tests.transfer;

import org.eclipse.edc.iam.verifiablecredentials.spi.model.CredentialSubject;
import org.eclipse.edc.iam.verifiablecredentials.spi.model.Issuer;
import org.eclipse.edc.iam.verifiablecredentials.spi.model.VerifiableCredential;
import org.eclipse.edc.junit.annotations.EndToEndTest;
import org.eclipse.edc.junit.extensions.RuntimeExtension;
import org.eclipse.edc.spi.EdcException;
import org.eclipse.edc.spi.agent.ParticipantAgentService;
import org.eclipse.edc.spi.iam.ClaimToken;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import java.time.Instant;
import java.util.List;
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.eclipse.tractusx.edc.tests.transfer.iatp.runtime.Runtimes.iatpRuntime;

/**
* This test asserts that the ParticipantAgent's identity is determined by the "credentialSubject.holderIdentifier" property.
* Due to how the extractors are used and registered, this must be tested using a fully-fledged runtime.
*/
@EndToEndTest
public class IdentityExtractorTest implements IatpParticipants {

@RegisterExtension
protected static final RuntimeExtension CONSUMER_RUNTIME = iatpRuntime(CONSUMER.getName(), CONSUMER.iatpConfiguration(PROVIDER), CONSUMER.getKeyPair());

@Test
void verifyCorrectParticipantAgentId(ParticipantAgentService participantAgentService) {
var claimtoken = ClaimToken.Builder.newInstance()
.claim("vc", List.of(createCredential().build()))
.build();
var agent = participantAgentService.createFor(claimtoken);

assertThat(agent.getIdentity()).isEqualTo("the-holder");
}

@Test
void verifyAgentId_whenNoMembershipCredential(ParticipantAgentService participantAgentService) {
var claimtoken = ClaimToken.Builder.newInstance()
.claim("vc", List.of(createCredential().types(List.of("VerifiableCredential")).build()))
.build();
assertThatThrownBy(() -> participantAgentService.createFor(claimtoken)).isInstanceOf(EdcException.class)
.hasMessage("Required credential type 'MembershipCredential' not present in ClaimToken, cannot extract property 'holderIdentifier'");
}

@SuppressWarnings({ "unchecked", "rawtypes" })
private VerifiableCredential.Builder createCredential() {
return VerifiableCredential.Builder.newInstance()
.types(List.of("VerifiableCredential", "MembershipCredential"))
.id("test-id")
.issuanceDate(Instant.now())
.issuer(new Issuer("test-issuer", Map.of()))
.credentialSubject(CredentialSubject.Builder.newInstance()
.id("test-id")
.claim("holderIdentifier", "the-holder")
.build());
}

}

0 comments on commit 7e409f1

Please sign in to comment.