Skip to content

Commit

Permalink
feat(Ssi): add credential issuer and credential subject id validation…
Browse files Browse the repository at this point in the history
… rules
  • Loading branch information
wolf4ood committed Jun 30, 2023
1 parent 28d6773 commit e04a2ea
Show file tree
Hide file tree
Showing 30 changed files with 561 additions and 108 deletions.
2 changes: 2 additions & 0 deletions charts/tractusx-connector-azure-vault/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down Expand Up @@ -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"` | |
Expand Down
1 change: 1 addition & 0 deletions charts/tractusx-connector-azure-vault/README.md.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions charts/tractusx-connector-azure-vault/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ controlplane:
miw:
url: ""
authorityId: ""
authorityIssuer: ""
oauth:
tokenurl: ""
client:
Expand Down
1 change: 1 addition & 0 deletions charts/tractusx-connector-memory/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"` | |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions charts/tractusx-connector-memory/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ runtime:
miw:
url: ""
authorityId: ""
authorityIssuer: ""
oauth:
tokenurl: ""
client:
Expand Down
2 changes: 2 additions & 0 deletions charts/tractusx-connector/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down Expand Up @@ -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"` | |
Expand Down
1 change: 1 addition & 0 deletions charts/tractusx-connector/README.md.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions charts/tractusx-connector/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ controlplane:
miw:
url: ""
authorityId: ""
authorityIssuer: ""
oauth:
tokenurl: ""
client:
Expand Down
1 change: 1 addition & 0 deletions docs/samples/example-dataspace/plato-values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ controlplane:
miw:
url: ""
authorityId: ""
authorityIssuer: ""
oauth:
tokenurl: ""
client:
Expand Down
1 change: 1 addition & 0 deletions docs/samples/example-dataspace/sokrates-values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ controlplane:
miw:
url: ""
authorityId: ""
authorityIssuer: ""
oauth:
tokenurl: ""
client:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ dependencies {
implementation(libs.jakartaJson)
implementation(libs.nimbus.jwt)

testImplementation(testFixtures(project(":spi:ssi-spi")))
testImplementation(testFixtures(libs.edc.junit))
}
Original file line number Diff line number Diff line change
@@ -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));
}
}
Original file line number Diff line number Diff line change
@@ -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<Void> checkRule(@NotNull ClaimToken toVerify, @Nullable Map<String, Object> 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<Void> 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<String> extractIssuer(JsonObject credential) {
return this.credentialIssuerExtractor.extract(credential)
.compose(this::extractIssuerValue);
}

private Result<String> extractIssuerValue(JsonObject issuer) {
var issuerValue = issuer.getString(ID);
if (issuerValue == null) {
return Result.failure("Failed to find the issuer");
} else {
return Result.success(issuerValue);
}
}
}
Loading

0 comments on commit e04a2ea

Please sign in to comment.