Skip to content

Commit

Permalink
feat: add CredentialStore + inmem implementation (#164)
Browse files Browse the repository at this point in the history
* feat(store): adds a CredentialStore + inmem impl

* DEPENDENCIES
  • Loading branch information
paullatzelsperger committed Nov 7, 2023
1 parent 084a28d commit d47d56d
Show file tree
Hide file tree
Showing 15 changed files with 688 additions and 7 deletions.
16 changes: 13 additions & 3 deletions .github/workflows/verify.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ jobs:
run: ./gradlew checkstyleMain checkstyleTest checkstyleTestFixtures

Verify-Launcher:
# disabled temporarily
if: false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -35,8 +33,20 @@ jobs:
- name: 'Build Docker image'
run: docker build -t identity-hub ./launcher

- name: 'Create public key file'
run: |
mkdir -p keys
openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout keys/key.pem -out keys/cert.pem -subj="/CN=www.foo.bar"
- name: 'Start Identity Hub'
run: docker run -d --rm --name identity-hub identity-hub
run: |
docker run -d --rm --name identity-hub \
-v $(pwd)/keys:/opt/keys \
-e "EDC_IH_IAM_PUBLICKEY_PATH=/opt/keys/key.pem" \
-e "EDC_IH_IAM_ID=did:web:test" \
-e "WEB_HTTP_RESOLUTION_PORT=10001" \
-e "WEB_HTTP_RESOLUTION_PATH=/api/v1/resolution/" \
identity-hub:latest
- name: 'Wait for Identity Hub to be healthy'
uses: raschmitt/wait-for-healthy-container@v1
Expand Down
1 change: 1 addition & 0 deletions DEPENDENCIES
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ maven/mavencentral/org.eclipse.edc/http/0.3.2-SNAPSHOT, Apache-2.0, approved, te
maven/mavencentral/org.eclipse.edc/identity-did-core/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc
maven/mavencentral/org.eclipse.edc/identity-did-crypto/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc
maven/mavencentral/org.eclipse.edc/identity-did-spi/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc
maven/mavencentral/org.eclipse.edc/identity-did-web/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc
maven/mavencentral/org.eclipse.edc/identity-trust-service/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc
maven/mavencentral/org.eclipse.edc/identity-trust-spi/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc
maven/mavencentral/org.eclipse.edc/jersey-core/0.3.2-SNAPSHOT, Apache-2.0, approved, technology.edc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@

package org.eclipse.edc.identityservice.api.validation;

import jakarta.json.JsonArray;
import jakarta.json.JsonObject;
import jakarta.json.JsonValue;
import org.eclipse.edc.identityhub.spi.model.PresentationQuery;
import org.eclipse.edc.jsonld.spi.JsonLdKeywords;
import org.eclipse.edc.validator.spi.ValidationResult;
import org.eclipse.edc.validator.spi.Validator;

Expand All @@ -30,18 +33,37 @@
public class PresentationQueryValidator implements Validator<JsonObject> {
@Override
public ValidationResult validate(JsonObject input) {
if (input == null) {
return failure(violation("Presentation query was null", "."));
}
var scope = input.get(PresentationQuery.PRESENTATION_QUERY_SCOPE_PROPERTY);

var presentationDef = input.get(PresentationQuery.PRESENTATION_QUERY_DEFINITION_PROPERTY);

if (scope == null && presentationDef == null) {
if (isNullObject(scope) && isNullObject(presentationDef)) {
return failure(violation("Must contain either a 'scope' or a 'presentationDefinition' property.", null));
}

if (scope != null && presentationDef != null) {
if (!isNullObject(scope) && !isNullObject(presentationDef)) {
return failure(violation("Must contain either a 'scope' or a 'presentationDefinition', not both.", null));
}

return success();
}

/**
* Checks if the given JsonValue object is the Null-Object. Due to JSON-LD expansion, a {@code "presentation_query": null}
* would get expanded to an array, thus a simple equals-null check is not sufficient.
*
* @param value the JsonValue to check
* @return true if the JsonValue object is either null, or its value type is NULL, false otherwise
*/
private boolean isNullObject(JsonValue value) {
if (value instanceof JsonArray) {
value = ((JsonArray) value).get(0).asJsonObject().get(JsonLdKeywords.VALUE);

return value.getValueType() == JsonValue.ValueType.NULL;
}
return value == null;
}
}
2 changes: 2 additions & 0 deletions core/identity-hub-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ plugins {

dependencies {
api(project(":spi:identity-hub-spi"))
api(project(":spi:identity-hub-store-spi"))
implementation(libs.edc.core.connector) // for the CriterionToPredicateConverterImpl
implementation(libs.edc.spi.jsonld)
implementation(libs.edc.iatp.service) // JWT validator
implementation(libs.edc.core.crypto) // JWT verifier
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* 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.edc.identityhub;

import org.eclipse.edc.identityhub.defaults.InMemoryCredentialStore;
import org.eclipse.edc.identityhub.spi.generator.PresentationGenerator;
import org.eclipse.edc.identityhub.spi.resolution.CredentialQueryResolver;
import org.eclipse.edc.identityhub.spi.store.CredentialStore;
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
import org.eclipse.edc.runtime.metamodel.annotation.Provider;
import org.eclipse.edc.spi.system.ServiceExtension;
import org.eclipse.edc.spi.system.ServiceExtensionContext;

@Extension("Default Services Extension")
public class DefaultServicesExtension implements ServiceExtension {
@Provider(isDefault = true)
public CredentialStore createInMemStore() {
return new InMemoryCredentialStore();

}

@Provider(isDefault = true)
public CredentialQueryResolver createCredentialResolver(ServiceExtensionContext context) {
context.getMonitor().warning(" #### Creating a default NOOP CredentialQueryResolver, that will always return 'null'!");
return (query, issuerScopes) -> null;
}

@Provider(isDefault = true)
public PresentationGenerator createPresentationGenerator(ServiceExtensionContext context) {
context.getMonitor().warning(" #### Creating a default NOOP PresentationGenerator, that will always return 'null'!");
return (credentials, presentationDefinition) -> null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* 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.edc.identityhub.defaults;

import org.eclipse.edc.connector.core.store.ReflectionBasedQueryResolver;
import org.eclipse.edc.identityhub.spi.store.CredentialStore;
import org.eclipse.edc.identityhub.spi.store.model.VerifiableCredentialResource;
import org.eclipse.edc.spi.query.QueryResolver;
import org.eclipse.edc.spi.query.QuerySpec;
import org.eclipse.edc.spi.result.StoreResult;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Stream;

import static org.eclipse.edc.spi.result.StoreResult.alreadyExists;
import static org.eclipse.edc.spi.result.StoreResult.notFound;
import static org.eclipse.edc.spi.result.StoreResult.success;

public class InMemoryCredentialStore implements CredentialStore {
private final Map<String, VerifiableCredentialResource> store = new HashMap<>();
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
private final QueryResolver<VerifiableCredentialResource> queryResolver = new ReflectionBasedQueryResolver<>(VerifiableCredentialResource.class);

@Override
public StoreResult<Void> create(VerifiableCredentialResource credentialResource) {
lock.writeLock().lock();
var id = credentialResource.getId();
try {
if (store.containsKey(id)) {
return alreadyExists("A VerifiableCredentialResource with ID %s already exists".formatted(id));
}
store.put(id, credentialResource);
return success(null);
} finally {
lock.writeLock().unlock();
}
}

@Override
public StoreResult<Stream<VerifiableCredentialResource>> query(QuerySpec querySpec) {
lock.readLock().lock();
try {
var result = queryResolver.query(store.values().stream(), querySpec);
return success(result);
} finally {
lock.readLock().unlock();
}
}

@Override
public StoreResult<Void> update(VerifiableCredentialResource credentialResource) {
lock.writeLock().lock();
try {
var id = credentialResource.getId();
if (!store.containsKey(id)) {
return notFound("A VerifiableCredentialResource with ID %s was not found".formatted(id));
}
store.put(id, credentialResource);
return success();
} finally {
lock.writeLock().unlock();
}
}

@Override
public StoreResult<Void> delete(String id) {
lock.writeLock().lock();
try {
if (!store.containsKey(id)) {
return notFound("A VerifiableCredentialResource with ID %s was not found".formatted(id));
}
store.remove(id);
return success();
} finally {
lock.writeLock().unlock();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@
#
#

org.eclipse.edc.identityhub.core.CoreServicesExtension
org.eclipse.edc.identityhub.core.CoreServicesExtension
org.eclipse.edc.identityhub.DefaultServicesExtension
Loading

0 comments on commit d47d56d

Please sign in to comment.