From e04a2ea919ccf6b6c6eb8902f093f530d30c0a0a Mon Sep 17 00:00:00 2001 From: Enrico Risa Date: Thu, 29 Jun 2023 23:46:05 +0200 Subject: [PATCH] feat(Ssi): add credential issuer and credential subject id validation rules --- .../tractusx-connector-azure-vault/README.md | 2 + .../README.md.gotmpl | 1 + .../templates/deployment-controlplane.yaml | 2 + .../values.yaml | 1 + charts/tractusx-connector-memory/README.md | 1 + .../templates/deployment-runtime.yaml | 2 + charts/tractusx-connector-memory/values.yaml | 1 + charts/tractusx-connector/README.md | 2 + charts/tractusx-connector/README.md.gotmpl | 1 + .../templates/deployment-controlplane.yaml | 2 + charts/tractusx-connector/values.yaml | 1 + .../example-dataspace/plato-values.yaml | 1 + .../example-dataspace/sokrates-values.yaml | 1 + .../CredentialIdentityExtractorTest.java | 6 +- .../extractor/fixtures/Credentials.java | 97 ------------------- .../build.gradle.kts | 1 + .../miw/SsiMiwValidationRuleExtension.java | 50 ++++++++++ .../SsiCredentialIssuerValidationRule.java | 95 ++++++++++++++++++ .../SsiCredentialSubjectIdValidationRule.java | 96 ++++++++++++++++++ ...rg.eclipse.edc.spi.system.ServiceExtension | 3 +- .../SsiMiwValidationRuleExtensionTest.java | 60 ++++++++++++ ...SsiCredentialIssuerValidationRuleTest.java | 60 ++++++++++++ ...CredentialSubjectIdValidationRuleTest.java | 77 +++++++++++++++ .../lifecycle/TestRuntimeConfiguration.java | 2 + .../edc/tests/catalog/MiwSsiCatalogTest.java | 1 + .../SsiContractNegotiationInMemoryTest.java | 6 +- ...HttpConsumerPullWithProxyInMemoryTest.java | 6 +- .../tractusx/edc/token/MiwDispatcher.java | 8 +- .../ssi/spi/jsonld/CredentialsNamespaces.java | 2 + .../iam/ssi/spi/jsonld/SummaryCredential.java | 81 +++++++++++++++- 30 files changed, 561 insertions(+), 108 deletions(-) delete mode 100644 edc-extensions/ssi/ssi-identity-extractor/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/extractor/fixtures/Credentials.java create mode 100644 edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwValidationRuleExtension.java create mode 100644 edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialIssuerValidationRule.java create mode 100644 edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialSubjectIdValidationRule.java create mode 100644 edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwValidationRuleExtensionTest.java create mode 100644 edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialIssuerValidationRuleTest.java create mode 100644 edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialSubjectIdValidationRuleTest.java diff --git a/charts/tractusx-connector-azure-vault/README.md b/charts/tractusx-connector-azure-vault/README.md index a549705e7..6120a1b93 100644 --- a/charts/tractusx-connector-azure-vault/README.md +++ b/charts/tractusx-connector-azure-vault/README.md @@ -33,6 +33,7 @@ This chart is intended for use with an _existing_ PostgreSQL database and an _ex Be sure to provide the following configuration entries to your Tractus-X EDC Helm chart: - `controlplane.ssi.miw.url`: the URL - `controlplane.ssi.miw.authorityId`: the BPN of the issuer authority +- `controlplane.ssi.miw.authorityIssuer`: the DID URL of the issuer authority - `controlplane.ssi.oauth.tokenurl`: the URL (of KeyCloak), where access tokens can be obtained - `controlplane.ssi.oauth.client.id`: client ID for KeyCloak - `controlplane.ssi.oauth.client.secretAlias`: the alias under which the client secret is stored in the vault. Defaults to `client-secret`. @@ -166,6 +167,7 @@ helm install my-release tractusx-edc/tractusx-connector-azure-vault --version 0. | controlplane.service.annotations | object | `{}` | | | controlplane.service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | | controlplane.ssi.miw.authorityId | string | `""` | | +| controlplane.ssi.miw.authorityIssuer | string | `""` | | | controlplane.ssi.miw.url | string | `""` | | | controlplane.ssi.oauth.client.id | string | `""` | | | controlplane.ssi.oauth.client.secretAlias | string | `"client-secret"` | | diff --git a/charts/tractusx-connector-azure-vault/README.md.gotmpl b/charts/tractusx-connector-azure-vault/README.md.gotmpl index b2de42ced..9c5815b40 100644 --- a/charts/tractusx-connector-azure-vault/README.md.gotmpl +++ b/charts/tractusx-connector-azure-vault/README.md.gotmpl @@ -33,6 +33,7 @@ Be sure to provide the following configuration entries to your Tractus-X EDC Helm chart: - `controlplane.ssi.miw.url`: the URL - `controlplane.ssi.miw.authorityId`: the BPN of the issuer authority +- `controlplane.ssi.miw.authorityIssuer`: the DID URL of the issuer authority - `controlplane.ssi.oauth.tokenurl`: the URL (of KeyCloak), where access tokens can be obtained - `controlplane.ssi.oauth.client.id`: client ID for KeyCloak - `controlplane.ssi.oauth.client.secretAlias`: the alias under which the client secret is stored in the vault. Defaults to `client-secret`. diff --git a/charts/tractusx-connector-azure-vault/templates/deployment-controlplane.yaml b/charts/tractusx-connector-azure-vault/templates/deployment-controlplane.yaml index 362594114..1e2ba9cfa 100644 --- a/charts/tractusx-connector-azure-vault/templates/deployment-controlplane.yaml +++ b/charts/tractusx-connector-azure-vault/templates/deployment-controlplane.yaml @@ -122,6 +122,8 @@ spec: value: {{ .Values.controlplane.ssi.miw.url }} - name: "TX_SSI_MIW_AUTHORITY_ID" value: {{ .Values.controlplane.ssi.miw.authorityId }} + - name: "TX_SSI_MIW_AUTHORITY_ISSUER" + value: {{ .Values.controlplane.ssi.miw.authorityIssuer }} - name: "TX_SSI_OAUTH_TOKEN_URL" value: {{ .Values.controlplane.ssi.oauth.tokenurl }} - name: "TX_SSI_OAUTH_CLIENT_ID" diff --git a/charts/tractusx-connector-azure-vault/values.yaml b/charts/tractusx-connector-azure-vault/values.yaml index c8d4a82aa..d9072a2e8 100644 --- a/charts/tractusx-connector-azure-vault/values.yaml +++ b/charts/tractusx-connector-azure-vault/values.yaml @@ -134,6 +134,7 @@ controlplane: miw: url: "" authorityId: "" + authorityIssuer: "" oauth: tokenurl: "" client: diff --git a/charts/tractusx-connector-memory/README.md b/charts/tractusx-connector-memory/README.md index 35542a7d0..b681b89fc 100644 --- a/charts/tractusx-connector-memory/README.md +++ b/charts/tractusx-connector-memory/README.md @@ -163,6 +163,7 @@ helm install my-release tractusx-edc/tractusx-connector-memory --version 0.5.0-r | runtime.service.annotations | object | `{}` | | | runtime.service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | | runtime.ssi.miw.authorityId | string | `""` | | +| runtime.ssi.miw.authorityIssuer | string | `""` | | | runtime.ssi.miw.url | string | `""` | | | runtime.ssi.oauth.client.id | string | `""` | | | runtime.ssi.oauth.client.secretAlias | string | `"client-secret"` | | diff --git a/charts/tractusx-connector-memory/templates/deployment-runtime.yaml b/charts/tractusx-connector-memory/templates/deployment-runtime.yaml index c2a1d61b7..e520db65c 100644 --- a/charts/tractusx-connector-memory/templates/deployment-runtime.yaml +++ b/charts/tractusx-connector-memory/templates/deployment-runtime.yaml @@ -122,6 +122,8 @@ spec: value: {{ .Values.runtime.ssi.miw.url }} - name: "TX_SSI_MIW_AUTHORITY_ID" value: {{ .Values.runtime.ssi.miw.authorityId }} + - name: "TX_SSI_MIW_AUTHORITY_ISSUER" + value: {{ .Values.runtime.ssi.miw.authorityIssuer }} - name: "TX_SSI_OAUTH_TOKEN_URL" value: {{ .Values.runtime.ssi.oauth.tokenurl }} - name: "TX_SSI_OAUTH_CLIENT_ID" diff --git a/charts/tractusx-connector-memory/values.yaml b/charts/tractusx-connector-memory/values.yaml index 199eabc01..3e4d16f4e 100644 --- a/charts/tractusx-connector-memory/values.yaml +++ b/charts/tractusx-connector-memory/values.yaml @@ -133,6 +133,7 @@ runtime: miw: url: "" authorityId: "" + authorityIssuer: "" oauth: tokenurl: "" client: diff --git a/charts/tractusx-connector/README.md b/charts/tractusx-connector/README.md index 6ec625e63..e886f9d04 100644 --- a/charts/tractusx-connector/README.md +++ b/charts/tractusx-connector/README.md @@ -30,6 +30,7 @@ This chart is intended for use with an _existing_ PostgreSQL database and an _ex Be sure to provide the following configuration entries to your Tractus-X EDC Helm chart: - `controlplane.ssi.miw.url`: the URL - `controlplane.ssi.miw.authorityId`: the BPN of the issuer authority +- `controlplane.ssi.miw.authorityIssuer`: the DID URL of the issuer authority - `controlplane.ssi.oauth.tokenurl`: the URL (of KeyCloak), where access tokens can be obtained - `controlplane.ssi.oauth.client.id`: client ID for KeyCloak - `controlplane.ssi.oauth.client.secretAlias`: the alias under which the client secret is stored in the vault. Defaults to `client-secret`. @@ -160,6 +161,7 @@ helm install my-release tractusx-edc/tractusx-connector --version 0.5.0-rc3 \ | controlplane.service.annotations | object | `{}` | | | controlplane.service.type | string | `"ClusterIP"` | [Service type](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) to expose the running application on a set of Pods as a network service. | | controlplane.ssi.miw.authorityId | string | `""` | | +| controlplane.ssi.miw.authorityIssuer | string | `""` | | | controlplane.ssi.miw.url | string | `""` | | | controlplane.ssi.oauth.client.id | string | `""` | | | controlplane.ssi.oauth.client.secretAlias | string | `"client-secret"` | | diff --git a/charts/tractusx-connector/README.md.gotmpl b/charts/tractusx-connector/README.md.gotmpl index 195706caf..ce36f84e8 100644 --- a/charts/tractusx-connector/README.md.gotmpl +++ b/charts/tractusx-connector/README.md.gotmpl @@ -30,6 +30,7 @@ Be sure to provide the following configuration entries to your Tractus-X EDC Helm chart: - `controlplane.ssi.miw.url`: the URL - `controlplane.ssi.miw.authorityId`: the BPN of the issuer authority +- `controlplane.ssi.miw.authorityIssuer`: the DID URL of the issuer authority - `controlplane.ssi.oauth.tokenurl`: the URL (of KeyCloak), where access tokens can be obtained - `controlplane.ssi.oauth.client.id`: client ID for KeyCloak - `controlplane.ssi.oauth.client.secretAlias`: the alias under which the client secret is stored in the vault. Defaults to `client-secret`. diff --git a/charts/tractusx-connector/templates/deployment-controlplane.yaml b/charts/tractusx-connector/templates/deployment-controlplane.yaml index 27eafea69..ba4e8420d 100644 --- a/charts/tractusx-connector/templates/deployment-controlplane.yaml +++ b/charts/tractusx-connector/templates/deployment-controlplane.yaml @@ -122,6 +122,8 @@ spec: value: {{ .Values.controlplane.ssi.miw.url }} - name: "TX_SSI_MIW_AUTHORITY_ID" value: {{ .Values.controlplane.ssi.miw.authorityId }} + - name: "TX_SSI_MIW_AUTHORITY_ISSUER" + value: {{ .Values.controlplane.ssi.miw.authorityIssuer }} - name: "TX_SSI_OAUTH_TOKEN_URL" value: {{ .Values.controlplane.ssi.oauth.tokenurl }} - name: "TX_SSI_OAUTH_CLIENT_ID" diff --git a/charts/tractusx-connector/values.yaml b/charts/tractusx-connector/values.yaml index 1960d08c2..5273098a3 100644 --- a/charts/tractusx-connector/values.yaml +++ b/charts/tractusx-connector/values.yaml @@ -135,6 +135,7 @@ controlplane: miw: url: "" authorityId: "" + authorityIssuer: "" oauth: tokenurl: "" client: diff --git a/docs/samples/example-dataspace/plato-values.yaml b/docs/samples/example-dataspace/plato-values.yaml index 92bc09ce9..222f67237 100644 --- a/docs/samples/example-dataspace/plato-values.yaml +++ b/docs/samples/example-dataspace/plato-values.yaml @@ -44,6 +44,7 @@ controlplane: miw: url: "" authorityId: "" + authorityIssuer: "" oauth: tokenurl: "" client: diff --git a/docs/samples/example-dataspace/sokrates-values.yaml b/docs/samples/example-dataspace/sokrates-values.yaml index e05bf87a5..ab1d73e77 100644 --- a/docs/samples/example-dataspace/sokrates-values.yaml +++ b/docs/samples/example-dataspace/sokrates-values.yaml @@ -43,6 +43,7 @@ controlplane: miw: url: "" authorityId: "" + authorityIssuer: "" oauth: tokenurl: "" client: diff --git a/edc-extensions/ssi/ssi-identity-extractor/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/extractor/CredentialIdentityExtractorTest.java b/edc-extensions/ssi/ssi-identity-extractor/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/extractor/CredentialIdentityExtractorTest.java index fcbb900ae..3fb8aa154 100644 --- a/edc-extensions/ssi/ssi-identity-extractor/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/extractor/CredentialIdentityExtractorTest.java +++ b/edc-extensions/ssi/ssi-identity-extractor/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/extractor/CredentialIdentityExtractorTest.java @@ -26,14 +26,14 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.eclipse.edc.spi.agent.ParticipantAgent.PARTICIPANT_IDENTITY; -import static org.eclipse.tractusx.edc.iam.ssi.identity.extractor.fixtures.Credentials.SIMPLE_VP; -import static org.eclipse.tractusx.edc.iam.ssi.identity.extractor.fixtures.Credentials.SUMMARY_VP_NO_HOLDER; -import static org.eclipse.tractusx.edc.iam.ssi.identity.extractor.fixtures.Credentials.SUMMARY_VP_NO_SUBJECT; import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.CX_SUMMARY_NS_V1; import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.VP_PROPERTY; import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTextFixtures.createObjectMapper; import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTextFixtures.expand; +import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.SummaryCredential.SIMPLE_VP; import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.SummaryCredential.SUMMARY_VP; +import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.SummaryCredential.SUMMARY_VP_NO_HOLDER; +import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.SummaryCredential.SUMMARY_VP_NO_SUBJECT; public class CredentialIdentityExtractorTest { diff --git a/edc-extensions/ssi/ssi-identity-extractor/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/extractor/fixtures/Credentials.java b/edc-extensions/ssi/ssi-identity-extractor/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/extractor/fixtures/Credentials.java deleted file mode 100644 index fd14612eb..000000000 --- a/edc-extensions/ssi/ssi-identity-extractor/src/test/java/org/eclipse/tractusx/edc/iam/ssi/identity/extractor/fixtures/Credentials.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * 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 - * - * SPDX-License-Identifier: Apache-2.0 - * - * Contributors: - * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation - * - */ - -package org.eclipse.tractusx.edc.iam.ssi.identity.extractor.fixtures; - -public interface Credentials { - - String SIMPLE_VP = """ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1" - ], - "type": "VerifiablePresentation", - "verifiableCredential": [ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1" - ], - "id": "urn:uuid:12345678-1234-1234-1234-123456789abc", - "type": [ - "VerifiableCredential" - ], - "issuer": "did:web:a016-203-129-213-99.ngrok-free.app:BPNL000000000000", - "issuanceDate": "2023-06-02T12:00:00Z", - "expirationDate": "2022-06-16T18:56:59Z", - "credentialSubject": { - "id": "did:web:a016-203-129-213-99.ngrok-free.app:BPNL000000000000" - } - } - ] - } - """; - - String SUMMARY_VP_NO_HOLDER = """ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1" - ], - "type": "VerifiablePresentation", - "verifiableCredential": [ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://w3id.org/2023/catenax/credentials/summary/v1" - ], - "id": "urn:uuid:12345678-1234-1234-1234-123456789abc", - "type": [ - "VerifiableCredential", - "SummaryCredential" - ], - "issuer": "did:web:a016-203-129-213-99.ngrok-free.app:BPNL000000000000", - "issuanceDate": "2023-06-02T12:00:00Z", - "expirationDate": "2022-06-16T18:56:59Z", - "credentialSubject": { - "id": "did:web:a016-203-129-213-99.ngrok-free.app:BPNL000000000000" - } - } - ] - } - """; - - String SUMMARY_VP_NO_SUBJECT = """ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1" - ], - "type": "VerifiablePresentation", - "verifiableCredential": [ - { - "@context": [ - "https://www.w3.org/2018/credentials/v1", - "https://w3id.org/2023/catenax/credentials/summary/v1" - ], - "id": "urn:uuid:12345678-1234-1234-1234-123456789abc", - "type": [ - "VerifiableCredential", - "SummaryCredential" - ], - "issuer": "did:web:a016-203-129-213-99.ngrok-free.app:BPNL000000000000", - "issuanceDate": "2023-06-02T12:00:00Z", - "expirationDate": "2022-06-16T18:56:59Z" - } - ] - } - """; -} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/build.gradle.kts b/edc-extensions/ssi/ssi-miw-credential-client/build.gradle.kts index e06e947e6..37455f538 100644 --- a/edc-extensions/ssi/ssi-miw-credential-client/build.gradle.kts +++ b/edc-extensions/ssi/ssi-miw-credential-client/build.gradle.kts @@ -27,5 +27,6 @@ dependencies { implementation(libs.jakartaJson) implementation(libs.nimbus.jwt) + testImplementation(testFixtures(project(":spi:ssi-spi"))) testImplementation(testFixtures(libs.edc.junit)) } diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwValidationRuleExtension.java b/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwValidationRuleExtension.java new file mode 100644 index 000000000..807f1c8a6 --- /dev/null +++ b/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwValidationRuleExtension.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.edc.iam.ssi.miw; + +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.spi.monitor.Monitor; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.tractusx.edc.iam.ssi.miw.rule.SsiCredentialIssuerValidationRule; +import org.eclipse.tractusx.edc.iam.ssi.miw.rule.SsiCredentialSubjectIdValidationRule; +import org.eclipse.tractusx.edc.iam.ssi.spi.SsiValidationRuleRegistry; + +@Extension(SsiMiwValidationRuleExtension.EXTENSION_NAME) +public class SsiMiwValidationRuleExtension implements ServiceExtension { + + @Setting(value = "MIW Authority Issuer") + public static final String MIW_AUTHORITY_ISSUER = "tx.ssi.miw.authority.issuer"; + protected static final String EXTENSION_NAME = "SSI MIW validation rules extension"; + @Inject + private SsiValidationRuleRegistry registry; + + @Inject + private Monitor monitor; + + @Override + public String name() { + return EXTENSION_NAME; + } + + @Override + public void initialize(ServiceExtensionContext context) { + var authorityIssuer = context.getConfig().getString(MIW_AUTHORITY_ISSUER); + registry.addRule(new SsiCredentialSubjectIdValidationRule(monitor)); + registry.addRule(new SsiCredentialIssuerValidationRule(authorityIssuer, monitor)); + } +} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialIssuerValidationRule.java b/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialIssuerValidationRule.java new file mode 100644 index 000000000..5af534c1a --- /dev/null +++ b/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialIssuerValidationRule.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.edc.iam.ssi.miw.rule; + +import jakarta.json.JsonObject; +import org.eclipse.edc.jwt.spi.TokenValidationRule; +import org.eclipse.edc.spi.iam.ClaimToken; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdFieldExtractor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +import static java.lang.String.format; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; +import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.CREDENTIAL_ISSUER; +import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.SUMMARY_CREDENTIAL_TYPE; +import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.VP_PROPERTY; +import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTypeFunctions.extractObjectsOfType; + +public class SsiCredentialIssuerValidationRule implements TokenValidationRule { + + private static final String SUBJECT_ISSUER_EXTRACTOR_PREFIX = "Credential issuer extractor:"; + + private static final String SUBJECT_ISSUER_FIELD_ALIAS = "issuer"; + + private final String credentialIssuer; + + private final Monitor monitor; + + private final JsonLdFieldExtractor credentialIssuerExtractor = JsonLdFieldExtractor.Builder.newInstance() + .field(CREDENTIAL_ISSUER) + .fieldAlias(SUBJECT_ISSUER_FIELD_ALIAS) + .errorPrefix(SUBJECT_ISSUER_EXTRACTOR_PREFIX) + .build(); + + public SsiCredentialIssuerValidationRule(String credentialIssuer, Monitor monitor) { + this.credentialIssuer = credentialIssuer; + this.monitor = monitor; + } + + @Override + public Result checkRule(@NotNull ClaimToken toVerify, @Nullable Map additional) { + + var vp = (JsonObject) toVerify.getClaim(VP_PROPERTY); + + return Optional.ofNullable(vp) + .map(v -> extractObjectsOfType(SUMMARY_CREDENTIAL_TYPE, v)) + .orElse(Stream.empty()) + .map(this::extractIssuer) + .findFirst() + .orElseGet(() -> Result.failure("Failed to extract credential subject from the membership credential")) + .compose(this::validateCredentialIssuer) + .onFailure(failure -> monitor.severe(failure.getFailureDetail())); + + } + + private Result validateCredentialIssuer(String credentialSubjectId) { + if (credentialIssuer.equals(credentialSubjectId)) { + return Result.success(); + } else { + return Result.failure(format("Invalid credential issuer: expected %s, found %s", credentialIssuer, credentialSubjectId)); + } + } + + private Result extractIssuer(JsonObject credential) { + return this.credentialIssuerExtractor.extract(credential) + .compose(this::extractIssuerValue); + } + + private Result extractIssuerValue(JsonObject issuer) { + var issuerValue = issuer.getString(ID); + if (issuerValue == null) { + return Result.failure("Failed to find the issuer"); + } else { + return Result.success(issuerValue); + } + } +} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialSubjectIdValidationRule.java b/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialSubjectIdValidationRule.java new file mode 100644 index 000000000..b4d1b90bf --- /dev/null +++ b/edc-extensions/ssi/ssi-miw-credential-client/src/main/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialSubjectIdValidationRule.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.edc.iam.ssi.miw.rule; + +import jakarta.json.JsonObject; +import org.eclipse.edc.jwt.spi.TokenValidationRule; +import org.eclipse.edc.spi.iam.ClaimToken; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdFieldExtractor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +import static java.lang.String.format; +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; +import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.ISSUER; +import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.CREDENTIAL_SUBJECT; +import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.SUMMARY_CREDENTIAL_TYPE; +import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.VP_PROPERTY; +import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTypeFunctions.extractObjectsOfType; + +public class SsiCredentialSubjectIdValidationRule implements TokenValidationRule { + + private static final String CREDENTIAL_SUBJECT_EXTRACTOR_PREFIX = "Credential subject extractor:"; + private static final String CREDENTIAL_SUBJECT_FIELD_ALIAS = "credentialSubject"; + + private final Monitor monitor; + + private final JsonLdFieldExtractor credentialSubjectExtractor = JsonLdFieldExtractor.Builder.newInstance() + .field(CREDENTIAL_SUBJECT) + .fieldAlias(CREDENTIAL_SUBJECT_FIELD_ALIAS) + .errorPrefix(CREDENTIAL_SUBJECT_EXTRACTOR_PREFIX) + .build(); + + public SsiCredentialSubjectIdValidationRule(Monitor monitor) { + this.monitor = monitor; + } + + @Override + public Result checkRule(@NotNull ClaimToken toVerify, @Nullable Map additional) { + var issuer = toVerify.getStringClaim(ISSUER); + + if (issuer == null) { + return Result.failure("Required issuer (iss) claim is missing in token"); + } + var vp = (JsonObject) toVerify.getClaim(VP_PROPERTY); + + return Optional.ofNullable(vp) + .map(v -> extractObjectsOfType(SUMMARY_CREDENTIAL_TYPE, v)) + .orElse(Stream.empty()) + .map(this::extractSubjectId) + .findFirst() + .orElseGet(() -> Result.failure("Failed to extract credential subject from the membership credential")) + .compose(credentialSubjectId -> validateCredentialSubjectId(credentialSubjectId, issuer)) + .onFailure((failure -> monitor.severe(failure.getFailureDetail()))); + + } + + private Result validateCredentialSubjectId(String credentialSubjectId, String issuer) { + if (issuer.equals(credentialSubjectId)) { + return Result.success(); + } else { + return Result.failure(format("Issuer %s and credential subject id %s don't match", issuer, credentialSubjectId)); + } + } + + private Result extractSubjectId(JsonObject credential) { + return this.credentialSubjectExtractor.extract(credential) + .compose(this::extractId); + } + + private Result extractId(JsonObject credentialSubject) { + var id = credentialSubject.getString(ID); + if (id == null) { + return Result.failure("Failed to find the id in credential subject"); + } else { + return Result.success(id); + } + } +} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension b/edc-extensions/ssi/ssi-miw-credential-client/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension index 36fdd24f4..e57f222b4 100644 --- a/edc-extensions/ssi/ssi-miw-credential-client/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension +++ b/edc-extensions/ssi/ssi-miw-credential-client/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension @@ -14,4 +14,5 @@ org.eclipse.tractusx.edc.iam.ssi.miw.SsiMiwCredentialClientExtension org.eclipse.tractusx.edc.iam.ssi.miw.SsiMiwApiClientExtension -org.eclipse.tractusx.edc.iam.ssi.miw.SsiMiwOauth2ClientExtension \ No newline at end of file +org.eclipse.tractusx.edc.iam.ssi.miw.SsiMiwOauth2ClientExtension +org.eclipse.tractusx.edc.iam.ssi.miw.SsiMiwValidationRuleExtension diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwValidationRuleExtensionTest.java b/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwValidationRuleExtensionTest.java new file mode 100644 index 000000000..a69c5851d --- /dev/null +++ b/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/SsiMiwValidationRuleExtensionTest.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.edc.iam.ssi.miw; + +import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.system.configuration.Config; +import org.eclipse.edc.spi.system.injection.ObjectFactory; +import org.eclipse.tractusx.edc.iam.ssi.miw.rule.SsiCredentialIssuerValidationRule; +import org.eclipse.tractusx.edc.iam.ssi.miw.rule.SsiCredentialSubjectIdValidationRule; +import org.eclipse.tractusx.edc.iam.ssi.spi.SsiValidationRuleRegistry; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.eclipse.tractusx.edc.iam.ssi.miw.SsiMiwValidationRuleExtension.MIW_AUTHORITY_ISSUER; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(DependencyInjectionExtension.class) +public class SsiMiwValidationRuleExtensionTest { + + SsiMiwValidationRuleExtension extension; + ServiceExtensionContext context; + + SsiValidationRuleRegistry registry = mock(SsiValidationRuleRegistry.class); + + @BeforeEach + void setup(ObjectFactory factory, ServiceExtensionContext context) { + this.context = spy(context); + context.registerService(SsiValidationRuleRegistry.class, registry); + extension = factory.constructInstance(SsiMiwValidationRuleExtension.class); + } + + @Test + void initialize(ServiceExtensionContext context) { + var config = mock(Config.class); + when(context.getConfig()).thenReturn(config); + when(config.getString(MIW_AUTHORITY_ISSUER)).thenReturn("issuer"); + extension.initialize(context); + verify(registry).addRule(isA(SsiCredentialSubjectIdValidationRule.class)); + verify(registry).addRule(isA(SsiCredentialIssuerValidationRule.class)); + verify(config).getString(MIW_AUTHORITY_ISSUER); + } +} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialIssuerValidationRuleTest.java b/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialIssuerValidationRuleTest.java new file mode 100644 index 000000000..34afa8c57 --- /dev/null +++ b/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialIssuerValidationRuleTest.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.edc.iam.ssi.miw.rule; + +import com.fasterxml.jackson.core.JsonProcessingException; +import jakarta.json.JsonObject; +import org.eclipse.edc.spi.iam.ClaimToken; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.SummaryContext; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.CX_SUMMARY_NS_V1; +import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.VP_PROPERTY; +import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTextFixtures.createObjectMapper; +import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTextFixtures.expand; +import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.SummaryCredential.SUMMARY_VP; +import static org.mockito.Mockito.mock; + +public class SsiCredentialIssuerValidationRuleTest { + + static final Map CONTEXT_CACHE = Map.of(CX_SUMMARY_NS_V1, SummaryContext.SUMMARY_CONTEXT); + + SsiCredentialIssuerValidationRule validationRule; + + @Test + void checkRule() throws JsonProcessingException { + validationRule = new SsiCredentialIssuerValidationRule("did:web:issuer-a016-203-129-213-99.ngrok-free.app:BPNL000000000000", mock(Monitor.class)); + var vp = expand(createObjectMapper().readValue(SUMMARY_VP, JsonObject.class), CONTEXT_CACHE); + var claimToken = ClaimToken.Builder.newInstance().claim(VP_PROPERTY, vp).build(); + var result = validationRule.checkRule(claimToken, Map.of()); + + assertThat(result.succeeded()).isTrue(); + } + + + @Test + void checkRule_shouldFail_whenIssuerIsWrong() throws JsonProcessingException { + validationRule = new SsiCredentialIssuerValidationRule("issuer", mock(Monitor.class)); + var vp = expand(createObjectMapper().readValue(SUMMARY_VP, JsonObject.class), CONTEXT_CACHE); + var claimToken = ClaimToken.Builder.newInstance().claim(VP_PROPERTY, vp).build(); + var result = validationRule.checkRule(claimToken, Map.of()); + + assertThat(result.succeeded()).isFalse(); + } +} diff --git a/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialSubjectIdValidationRuleTest.java b/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialSubjectIdValidationRuleTest.java new file mode 100644 index 000000000..b719accc3 --- /dev/null +++ b/edc-extensions/ssi/ssi-miw-credential-client/src/test/java/org/eclipse/tractusx/edc/iam/ssi/miw/rule/SsiCredentialSubjectIdValidationRuleTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.edc.iam.ssi.miw.rule; + +import com.fasterxml.jackson.core.JsonProcessingException; +import jakarta.json.JsonObject; +import org.eclipse.edc.spi.iam.ClaimToken; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.SummaryContext; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.ISSUER; +import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.CX_SUMMARY_NS_V1; +import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.CredentialsNamespaces.VP_PROPERTY; +import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTextFixtures.createObjectMapper; +import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.JsonLdTextFixtures.expand; +import static org.eclipse.tractusx.edc.iam.ssi.spi.jsonld.SummaryCredential.SUMMARY_VP; +import static org.mockito.Mockito.mock; + +public class SsiCredentialSubjectIdValidationRuleTest { + + static final Map CONTEXT_CACHE = Map.of(CX_SUMMARY_NS_V1, SummaryContext.SUMMARY_CONTEXT); + + SsiCredentialSubjectIdValidationRule validationRule = new SsiCredentialSubjectIdValidationRule(mock(Monitor.class)); + + @Test + void checkRule() throws JsonProcessingException { + var vp = expand(createObjectMapper().readValue(SUMMARY_VP, JsonObject.class), CONTEXT_CACHE); + var claimToken = ClaimToken.Builder.newInstance() + .claim(VP_PROPERTY, vp) + .claim(ISSUER, "did:web:a016-203-129-213-99.ngrok-free.app:BPNL000000000000").build(); + + var result = validationRule.checkRule(claimToken, Map.of()); + + assertThat(result.succeeded()).isTrue(); + } + + @Test + void checkRule_shouldFail_whenIssuerMissingInClaims() throws JsonProcessingException { + var vp = expand(createObjectMapper().readValue(SUMMARY_VP, JsonObject.class), CONTEXT_CACHE); + var claimToken = ClaimToken.Builder.newInstance() + .claim(VP_PROPERTY, vp) + .build(); + + var result = validationRule.checkRule(claimToken, Map.of()); + + assertThat(result.succeeded()).isFalse(); + } + + @Test + void checkRule_shouldFail_whenWrongIssuerInClaims() throws JsonProcessingException { + var vp = expand(createObjectMapper().readValue(SUMMARY_VP, JsonObject.class), CONTEXT_CACHE); + var claimToken = ClaimToken.Builder.newInstance() + .claim(VP_PROPERTY, vp) + .claim(ISSUER, "wrong").build(); + + var result = validationRule.checkRule(claimToken, Map.of()); + + assertThat(result.succeeded()).isFalse(); + } + +} diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/TestRuntimeConfiguration.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/TestRuntimeConfiguration.java index 4fc4f4f36..022b79ada 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/TestRuntimeConfiguration.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/TestRuntimeConfiguration.java @@ -119,6 +119,7 @@ public static Map sokratesSsiConfiguration() { put("tx.ssi.oauth.client.id", "client_id"); put("tx.ssi.oauth.client.secret.alias", "client_secret_alias"); put("tx.ssi.miw.authority.id", "authorityId"); + put("tx.ssi.miw.authority.issuer", "did:web:a016-203-129-213-99.ngrok-free.app:BPNL000000000000"); put("tx.vault.seed.secrets", "client_secret_alias:client_secret"); put("tx.ssi.endpoint.audience", SOKRATES_DSP_CALLBACK); } @@ -200,6 +201,7 @@ public static Map platoSsiConfiguration() { put("tx.ssi.oauth.client.id", "client_id"); put("tx.ssi.oauth.client.secret.alias", "client_secret_alias"); put("tx.ssi.miw.authority.id", "authorityId"); + put("tx.ssi.miw.authority.issuer", "did:web:a016-203-129-213-99.ngrok-free.app:BPNL000000000000"); put("tx.vault.seed.secrets", "client_secret_alias:client_secret"); put("tx.ssi.endpoint.audience", PLATO_DSP_CALLBACK); } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/MiwSsiCatalogTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/MiwSsiCatalogTest.java index f7cbf0ec3..15f42ee22 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/MiwSsiCatalogTest.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/catalog/MiwSsiCatalogTest.java @@ -56,6 +56,7 @@ public static Map sokratesSsiMiwConfiguration() { put("tx.ssi.oauth.client.id", "miw_private_client"); put("tx.ssi.oauth.client.secret.alias", "client_secret_alias"); put("tx.ssi.miw.authority.id", "BPNL000000000000"); + put("tx.ssi.miw.authority.issuer", "did:web:localhost%3A8080:BPNL000000000000"); put("tx.vault.seed.secrets", "client_secret_alias:miw_private_client"); put("tx.ssi.endpoint.audience", SOKRATES_DSP_CALLBACK); } diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/negotiation/SsiContractNegotiationInMemoryTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/negotiation/SsiContractNegotiationInMemoryTest.java index 7429a1b71..6523b2358 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/negotiation/SsiContractNegotiationInMemoryTest.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/negotiation/SsiContractNegotiationInMemoryTest.java @@ -67,11 +67,13 @@ void setup() throws IOException { miwPlatoServer = new MockWebServer(); oauthServer = new MockWebServer(); + var credentialSubjectId = "did:web:a016-203-129-213-99.ngrok-free.app:BPNL000000000000"; + miwSokratesServer.start(MIW_SOKRATES_PORT); - miwSokratesServer.setDispatcher(new MiwDispatcher(SOKRATES_BPN, SUMMARY_VC_TEMPLATE, PLATO_DSP_CALLBACK)); + miwSokratesServer.setDispatcher(new MiwDispatcher(SOKRATES_BPN, SUMMARY_VC_TEMPLATE, credentialSubjectId, PLATO_DSP_CALLBACK)); miwPlatoServer.start(MIW_PLATO_PORT); - miwPlatoServer.setDispatcher(new MiwDispatcher(PLATO_BPN, SUMMARY_VC_TEMPLATE, SOKRATES_DSP_CALLBACK)); + miwPlatoServer.setDispatcher(new MiwDispatcher(PLATO_BPN, SUMMARY_VC_TEMPLATE, credentialSubjectId, SOKRATES_DSP_CALLBACK)); oauthServer.start(OAUTH_PORT); oauthServer.setDispatcher(new KeycloakDispatcher()); diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/SsiHttpConsumerPullWithProxyInMemoryTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/SsiHttpConsumerPullWithProxyInMemoryTest.java index d026d47e4..38991a864 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/SsiHttpConsumerPullWithProxyInMemoryTest.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/transfer/SsiHttpConsumerPullWithProxyInMemoryTest.java @@ -70,11 +70,13 @@ void setup() throws IOException { miwPlatoServer = new MockWebServer(); oauthServer = new MockWebServer(); + var credentialSubjectId = "did:web:a016-203-129-213-99.ngrok-free.app:BPNL000000000000"; + miwSokratesServer.start(MIW_SOKRATES_PORT); - miwSokratesServer.setDispatcher(new MiwDispatcher(SOKRATES_BPN, SUMMARY_VC_TEMPLATE, PLATO_DSP_CALLBACK)); + miwSokratesServer.setDispatcher(new MiwDispatcher(SOKRATES_BPN, SUMMARY_VC_TEMPLATE, credentialSubjectId, PLATO_DSP_CALLBACK)); miwPlatoServer.start(MIW_PLATO_PORT); - miwPlatoServer.setDispatcher(new MiwDispatcher(PLATO_BPN, SUMMARY_VC_TEMPLATE, SOKRATES_DSP_CALLBACK)); + miwPlatoServer.setDispatcher(new MiwDispatcher(PLATO_BPN, SUMMARY_VC_TEMPLATE, credentialSubjectId, SOKRATES_DSP_CALLBACK)); oauthServer.start(OAUTH_PORT); oauthServer.setDispatcher(new KeycloakDispatcher()); diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/token/MiwDispatcher.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/token/MiwDispatcher.java index c0c70f9b7..15ae67e4a 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/token/MiwDispatcher.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/token/MiwDispatcher.java @@ -45,13 +45,16 @@ public class MiwDispatcher extends Dispatcher { private static final TypeManager MAPPER = new TypeManager(); - + private final String audience; + private final String credentialSubjectId; + private final Map summaryVc; - public MiwDispatcher(String bpn, String vcFile, String audience) { + public MiwDispatcher(String bpn, String vcFile, String credentialSubjectId, String audience) { this.audience = audience; + this.credentialSubjectId = credentialSubjectId; var json = format(readVcContent(vcFile), bpn); summaryVc = MAPPER.readValue(json, new TypeReference<>() { }); @@ -107,6 +110,7 @@ private MockResponse presentationValidationResponse() { private JWTClaimsSet createClaims(Instant exp, Map presentation) { return new JWTClaimsSet.Builder() + .issuer(credentialSubjectId) .claim("vp", presentation) .audience(audience) .expirationTime(Date.from(exp)) diff --git a/spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/CredentialsNamespaces.java b/spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/CredentialsNamespaces.java index 164511ef9..ecac8fc7a 100644 --- a/spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/CredentialsNamespaces.java +++ b/spi/ssi-spi/src/main/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/CredentialsNamespaces.java @@ -31,4 +31,6 @@ public interface CredentialsNamespaces { String CX_USE_CASE_NS_V1 = CX_USE_CASE_NS + "/v1"; String CX_SUMMARY_CREDENTIAL = "SummaryCredential"; String CREDENTIAL_SUBJECT = W3C_VC_PREFIX + "#credentialSubject"; + String CREDENTIAL_ISSUER = W3C_VC_PREFIX + "#issuer"; + } diff --git a/spi/ssi-spi/src/testFixtures/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/SummaryCredential.java b/spi/ssi-spi/src/testFixtures/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/SummaryCredential.java index 7f190449b..207194ded 100644 --- a/spi/ssi-spi/src/testFixtures/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/SummaryCredential.java +++ b/spi/ssi-spi/src/testFixtures/java/org/eclipse/tractusx/edc/iam/ssi/spi/jsonld/SummaryCredential.java @@ -35,7 +35,7 @@ public interface SummaryCredential { "VerifiableCredential", "SummaryCredential" ], - "issuer": "did:web:a016-203-129-213-99.ngrok-free.app:BPNL000000000000", + "issuer": "did:web:issuer-a016-203-129-213-99.ngrok-free.app:BPNL000000000000", "issuanceDate": "2023-06-02T12:00:00Z", "expirationDate": "2022-06-16T18:56:59Z", "credentialSubject": { @@ -66,4 +66,83 @@ public interface SummaryCredential { ] } """; + + String SIMPLE_VP = """ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1" + ], + "type": "VerifiablePresentation", + "verifiableCredential": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1" + ], + "id": "urn:uuid:12345678-1234-1234-1234-123456789abc", + "type": [ + "VerifiableCredential" + ], + "issuer": "did:web:a016-203-129-213-99.ngrok-free.app:BPNL000000000000", + "issuanceDate": "2023-06-02T12:00:00Z", + "expirationDate": "2022-06-16T18:56:59Z", + "credentialSubject": { + "id": "did:web:a016-203-129-213-99.ngrok-free.app:BPNL000000000000" + } + } + ] + } + """; + + String SUMMARY_VP_NO_HOLDER = """ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1" + ], + "type": "VerifiablePresentation", + "verifiableCredential": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/2023/catenax/credentials/summary/v1" + ], + "id": "urn:uuid:12345678-1234-1234-1234-123456789abc", + "type": [ + "VerifiableCredential", + "SummaryCredential" + ], + "issuer": "did:web:no-holder.ngrok-free.app:BPNL000000000000", + "issuanceDate": "2023-06-02T12:00:00Z", + "expirationDate": "2022-06-16T18:56:59Z", + "credentialSubject": { + "id": "did:web:a016-203-129-213-99.ngrok-free.app:BPNL000000000000" + } + } + ] + } + """; + + String SUMMARY_VP_NO_SUBJECT = """ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1" + ], + "type": "VerifiablePresentation", + "verifiableCredential": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/2023/catenax/credentials/summary/v1" + ], + "id": "urn:uuid:12345678-1234-1234-1234-123456789abc", + "type": [ + "VerifiableCredential", + "SummaryCredential" + ], + "issuer": "did:web:a016-203-129-213-99.ngrok-free.app:BPNL000000000000", + "issuanceDate": "2023-06-02T12:00:00Z", + "expirationDate": "2022-06-16T18:56:59Z" + } + ] + } + """; }