Skip to content

Commit

Permalink
feat: Add issuance process core services (#546)
Browse files Browse the repository at this point in the history
* Add issuance process core services

* Add modules settings

* Add missing annotation

* Fix checkstyle
  • Loading branch information
jimmarino authored Feb 4, 2025
1 parent 44b890f commit 425fbda
Show file tree
Hide file tree
Showing 42 changed files with 2,060 additions and 0 deletions.
33 changes: 33 additions & 0 deletions extensions/issuance/issuance-credentials/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2025 Cofinity-X
*
* 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
*
*/

plugins {
`java-library`
`java-test-fixtures`
`maven-publish`
}

dependencies {
api(project(":spi:issuance-credentials-spi"))
api(libs.edc.spi.core)

implementation(libs.edc.spi.validator)

testImplementation(libs.edc.lib.json)

testFixturesImplementation(libs.edc.spi.identity.did)
testFixturesImplementation(libs.junit.jupiter.api)
testFixturesImplementation(libs.edc.junit)
testFixturesImplementation(libs.assertj)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright (c) 2025 Cofinity-X
*
* 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:
* Cofinity-X - initial API and implementation
*
*/

package org.eclipse.edc.identityhub.issuance.credentials.attestation;

import org.eclipse.edc.identityhub.spi.issuance.credentials.attestation.AttestationContext;
import org.eclipse.edc.identityhub.spi.issuance.credentials.attestation.AttestationDefinitionStore;
import org.eclipse.edc.identityhub.spi.issuance.credentials.attestation.AttestationPipeline;
import org.eclipse.edc.identityhub.spi.issuance.credentials.attestation.AttestationSourceFactory;
import org.eclipse.edc.identityhub.spi.issuance.credentials.attestation.AttestationSourceFactoryRegistry;
import org.eclipse.edc.identityhub.spi.issuance.credentials.model.AttestationDefinition;
import org.eclipse.edc.spi.result.Result;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import static java.util.Objects.requireNonNull;

/**
* Holds registered {@link AttestationSourceFactory}s that performs attestation pipeline evaluations.
*/
public class AttestationPipelineImpl implements AttestationPipeline, AttestationSourceFactoryRegistry {
private Map<String, AttestationSourceFactory> factories = new HashMap<>();
private AttestationDefinitionStore store;

public AttestationPipelineImpl(AttestationDefinitionStore store) {
this.store = store;
}

@Override
public Set<String> registeredTypes() {
return factories.keySet();
}

@Override
public void registerFactory(String type, AttestationSourceFactory factory) {
factories.put(type, factory);
}

@Override
public Result<Map<String, Object>> evaluate(Set<String> attestations, AttestationContext context) {
var collated = new HashMap<String, Object>();
for (var attestationId : attestations) {
// if the attestation is not found it is a programming error
var definition = requireNonNull(store.resolveDefinition(attestationId), "Unknown attestation: " + attestationId);
var result = execute(definition, context);
if (result.failed()) {
return result;
}
collated.putAll(result.getContent());
}
return Result.success(collated);
}

private Result<Map<String, Object>> execute(AttestationDefinition definition, AttestationContext context) {
var factory = requireNonNull(factories.get(definition.attestationType()), "Unknown attestation type: " + definition.attestationType());
return requireNonNull(factory.createSource(definition), "Invalid definition for type: " + definition.attestationType()).execute(context);
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2025 Cofinity-X
*
* 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:
* Cofinity-X - initial API and implementation
*
*/

package org.eclipse.edc.identityhub.issuance.credentials.attestation;

import org.eclipse.edc.identityhub.spi.issuance.credentials.attestation.AttestationContext;
import org.eclipse.edc.spi.iam.ClaimToken;
import org.jetbrains.annotations.Nullable;

import java.util.Map;

import static java.util.Objects.requireNonNull;

/**
* Default context.
*/
public class DefaultAttestationContext implements AttestationContext {
private Map<String, ClaimToken> claims;
private String participantId;

@Override
public @Nullable ClaimToken getClaimToken(String type) {
return claims.get(type);
}

@Override
public String participantId() {
return participantId;
}

public Map<String, ClaimToken> getClaims() {
return claims;
}

public DefaultAttestationContext(String participantId, Map<String, ClaimToken> claims) {
this.participantId = requireNonNull(participantId, "participantId");
this.claims = requireNonNull(claims, "claims");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2025 Cofinity-X
*
* 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:
* Cofinity-X - initial API and implementation
*
*/

package org.eclipse.edc.identityhub.issuance.credentials.attestation.presentation;

import org.eclipse.edc.identityhub.spi.issuance.credentials.attestation.AttestationContext;
import org.eclipse.edc.identityhub.spi.issuance.credentials.attestation.AttestationSource;
import org.eclipse.edc.spi.result.Result;

import java.util.Map;

import static java.util.Collections.emptyMap;
import static java.util.Objects.requireNonNull;
import static org.eclipse.edc.spi.result.Result.failure;
import static org.eclipse.edc.spi.result.Result.success;

/**
* Resolves an attestation that is a verifiable presentation.
*/
public class PresentationAttestationSource implements AttestationSource {
private final String claimType;
private final String outputClaim;
private final boolean required;

public PresentationAttestationSource(String claimType, String outputClaim, boolean required) {
this.claimType = requireNonNull(claimType, "claimType");
this.outputClaim = requireNonNull(outputClaim, "outputClaim");
this.required = required;
}

@Override
public Result<Map<String, Object>> execute(AttestationContext context) {
var claimToken = context.getClaimToken(claimType);
if (claimToken == null) {
return !required ? success(emptyMap()) : failure("Claim token not found");
}
return success(Map.of(outputClaim, claimToken.getClaims()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2025 Cofinity-X
*
* 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:
* Cofinity-X - initial API and implementation
*
*/

package org.eclipse.edc.identityhub.issuance.credentials.attestation.presentation;

import org.eclipse.edc.identityhub.spi.issuance.credentials.attestation.AttestationSource;
import org.eclipse.edc.identityhub.spi.issuance.credentials.attestation.AttestationSourceFactory;
import org.eclipse.edc.identityhub.spi.issuance.credentials.model.AttestationDefinition;

/**
* Creates an attestation source that requires a verifiable credential.
* <p>
* Example configuration is:
* <pre>
* {
* "id": "123",
* "attestationType": "presentation",
* "configuration": {
* "credentialType": "https://example.com/ExampleCredential",
* "required": true,
* "outputClaim": "exampleCredential"
* }
* }
* </pre>
*/
public class PresentationAttestationSourceFactory implements AttestationSourceFactory {
private static final String CREDENTIAL_TYPE = "credentialType";
private static final String OUTPUT_CLAIM = "outputClaim";
private static final String REQUIRED = "required";

@Override
public AttestationSource createSource(AttestationDefinition definition) {
var configuration = definition.configuration();
var credentialType = (String) configuration.get(CREDENTIAL_TYPE);
var outputClaim = (String) configuration.get(OUTPUT_CLAIM);
var required = (Boolean) configuration.getOrDefault(REQUIRED, true);
return new PresentationAttestationSource(credentialType, outputClaim, required);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2025 Cofinity-X
*
* 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:
* Cofinity-X - initial API and implementation
*
*/

package org.eclipse.edc.identityhub.issuance.credentials.attestation.presentation;


import org.eclipse.edc.identityhub.spi.issuance.credentials.model.AttestationDefinition;
import org.eclipse.edc.validator.spi.ValidationResult;
import org.eclipse.edc.validator.spi.Validator;

import static java.lang.String.format;
import static org.eclipse.edc.validator.spi.ValidationResult.failure;
import static org.eclipse.edc.validator.spi.ValidationResult.success;
import static org.eclipse.edc.validator.spi.Violation.violation;

/**
* Validates presentation attestation definitions.
*/
public class PresentationAttestatonSourceValidator implements Validator<AttestationDefinition> {
private static final String ATTESTATION_TYPE = "presentation";
private static final String CREDENTIAL_TYPE = "credentialType";
private static final String OUTPUT_CLAIM = "outputClaim";

@Override
public ValidationResult validate(AttestationDefinition definition) {
if (!ATTESTATION_TYPE.equals(definition.attestationType())) {
return failure(violation("Expecting attestation type: " + ATTESTATION_TYPE, ATTESTATION_TYPE));
}
if (!definition.configuration().containsKey(CREDENTIAL_TYPE)) {
return failure(violation(format("No %s specified", CREDENTIAL_TYPE), CREDENTIAL_TYPE));
}
if (!definition.configuration().containsKey(OUTPUT_CLAIM)) {
return failure(violation(format("No %s specified", OUTPUT_CLAIM), OUTPUT_CLAIM));
}
return success();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2025 Cofinity-X
*
* 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:
* Cofinity-X - initial API and implementation
*
*/

package org.eclipse.edc.identityhub.issuance.credentials.json;

import org.eclipse.edc.spi.result.Result;

import java.util.Map;

import static java.lang.String.format;
import static java.lang.String.join;
import static org.eclipse.edc.spi.result.Result.failure;
import static org.eclipse.edc.spi.result.Result.success;

/**
* Navigates Json types and resolves property values.
*/
public class JsonNavigator {

public static Result<Object> navigateProperty(String[] path, Map<String, Object> input, boolean required) {
Object result = input;
for (var property : path) {
if (!(result instanceof Map)) {
return failure(format("Unexpected type at segment %s for path %s", property, join(".", path)));
}
//noinspection rawtypes
result = ((Map) result).get(property);
if (result == null) {
break;
}
}
return result == null && required ? failure(format("Property not found for path %s", join(".", path))) : success(result);
}

private JsonNavigator() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2025 Cofinity-X
*
* 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:
* Cofinity-X - initial API and implementation
*
*/

package org.eclipse.edc.identityhub.issuance.credentials.rule;

import org.eclipse.edc.identityhub.spi.issuance.credentials.rule.CredentialRuleFactory;
import org.eclipse.edc.identityhub.spi.issuance.credentials.rule.CredentialRuleFactoryRegistry;
import org.jetbrains.annotations.Nullable;

import java.util.HashMap;
import java.util.Map;

import static java.util.Objects.requireNonNull;

public class CredentialRuleFactoryRegistryImpl implements CredentialRuleFactoryRegistry {
private Map<String, CredentialRuleFactory> factories = new HashMap<>();

@Override
public void registerFactory(String type, CredentialRuleFactory factory) {
factories.put(type, factory);
}

@Override
public @Nullable CredentialRuleFactory resolveFactory(String type) {
return requireNonNull(factories.get(type), "Unknown rule type: " + type);
}
}
Loading

0 comments on commit 425fbda

Please sign in to comment.