Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add ParticipantContextStore #224

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
import com.apicatalog.ld.signature.SignatureSuite;
import org.eclipse.edc.identityhub.defaults.EdcScopeToCriterionTransformer;
import org.eclipse.edc.identityhub.defaults.InMemoryCredentialStore;
import org.eclipse.edc.identityhub.defaults.InMemoryParticipantContextStore;
import org.eclipse.edc.identityhub.spi.ScopeToCriterionTransformer;
import org.eclipse.edc.identityhub.spi.model.IdentityHubConstants;
import org.eclipse.edc.identityhub.spi.store.CredentialStore;
import org.eclipse.edc.identityhub.spi.store.ParticipantContextStore;
import org.eclipse.edc.identityhub.token.rules.ClaimIsPresentRule;
import org.eclipse.edc.identitytrust.verification.SignatureSuiteRegistry;
import org.eclipse.edc.jsonld.util.JacksonJsonLd;
Expand Down Expand Up @@ -64,10 +66,15 @@ public void initialize(ServiceExtensionContext context) {
}

@Provider(isDefault = true)
public CredentialStore createInMemStore() {
public CredentialStore createDefaultCredentialStore() {
return new InMemoryCredentialStore();
}

@Provider(isDefault = true)
public ParticipantContextStore createDefaultParticipantContextStore() {
return new InMemoryParticipantContextStore();
}

@Provider(isDefault = true)
public ScopeToCriterionTransformer createScopeTransformer(ServiceExtensionContext context) {
context.getMonitor().warning("Using the default EdcScopeToCriterionTransformer. This is not intended for production use and should be replaced " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,79 +18,19 @@
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.function.Predicate;
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, new CriterionToCredentialResourceConverter());

@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 {
// if no filter is present, we return true
Predicate<Object> fallback = querySpec.getFilterExpression().isEmpty() ? x -> true : x -> false;
var result = queryResolver.query(store.values().stream(), querySpec, Predicate::or, fallback);
return success(result);
} finally {
lock.readLock().unlock();
}
}
/**
* In-memory variant of the {@link CredentialStore} that is thread-safe.
*/
public class InMemoryCredentialStore extends InMemoryEntityStore<VerifiableCredentialResource> implements CredentialStore {

@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();
}
protected String getId(VerifiableCredentialResource newObject) {
return newObject.getId();
}

@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();
}
protected QueryResolver<VerifiableCredentialResource> createQueryResolver() {
return new ReflectionBasedQueryResolver<>(VerifiableCredentialResource.class, new CriterionToCredentialResourceConverter());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Copyright (c) 2024 Metaform Systems, Inc.
*
* 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:
* Metaform Systems, Inc. - initial API and implementation
*
*/

package org.eclipse.edc.identityhub.defaults;

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.function.Predicate;
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;

/**
* Base class for in-mem entity stores, that implement basic CRUD operations.
*/
abstract class InMemoryEntityStore<T> {
protected final Map<String, T> store = new HashMap<>();
protected final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
protected final QueryResolver<T> queryResolver = createQueryResolver();

/**
* Creates a new entity if none exists.
*
* @param newObject the new object to insert.
* @return failure if an object with the same ID already exists.
*/
public StoreResult<Void> create(T newObject) {
lock.writeLock().lock();
var id = getId(newObject);
try {
if (store.containsKey(id)) {
return alreadyExists("A VerifiableCredentialResource with ID %s already exists".formatted(id));
}
store.put(id, newObject);
return success(null);
} finally {
lock.writeLock().unlock();
}
}

/**
* Performs a query using the given query parameters.
*
* @param querySpec A non-null QuerySpec.
* @return A (potentially empty) Stream of objects. Callers must close the stream.
*/
public StoreResult<Stream<T>> query(QuerySpec querySpec) {
lock.readLock().lock();
try {
// if no filter is present, we return true
Predicate<Object> fallback = querySpec.getFilterExpression().isEmpty() ? x -> true : x -> false;
var result = queryResolver.query(store.values().stream(), querySpec, Predicate::or, fallback);
return success(result);
} finally {
lock.readLock().unlock();
}
}

/**
* Replaces an existing entity with a new object.
*
* @param newObject the new entity
* @return failure if an object with the same ID was not found.
*/
public StoreResult<Void> update(T newObject) {
lock.writeLock().lock();
try {
var id = getId(newObject);
if (!store.containsKey(id)) {
return notFound("A VerifiableCredentialResource with ID %s was not found".formatted(id));
}
store.put(id, newObject);
return success();
} finally {
lock.writeLock().unlock();
}
}

/**
* Deletes the object with the given ID
*
* @param id The ID of the object to delete.
* @return failure if an object with the given ID was not found.
*/
public StoreResult<Void> deleteById(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();
}
}

protected abstract String getId(T newObject);

protected abstract QueryResolver<T> createQueryResolver();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2024 Metaform Systems, Inc.
*
* 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:
* Metaform Systems, Inc. - initial API and implementation
*
*/

package org.eclipse.edc.identityhub.defaults;

import org.eclipse.edc.connector.core.store.CriterionToPredicateConverterImpl;
import org.eclipse.edc.connector.core.store.ReflectionBasedQueryResolver;
import org.eclipse.edc.identityhub.spi.model.participant.ParticipantContext;
import org.eclipse.edc.identityhub.spi.store.ParticipantContextStore;
import org.eclipse.edc.spi.query.QueryResolver;

/**
* In-memory variant of the {@link ParticipantContextStore} that is thread-safe.
*/
public class InMemoryParticipantContextStore extends InMemoryEntityStore<ParticipantContext> implements ParticipantContextStore {
@Override
protected String getId(ParticipantContext newObject) {
return newObject.getParticipantId();
}

@Override
protected QueryResolver<ParticipantContext> createQueryResolver() {
return new ReflectionBasedQueryResolver<>(ParticipantContext.class, new CriterionToPredicateConverterImpl());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

package org.eclipse.edc.identityhub.defaults;

import org.eclipse.edc.identityhub.credentials.store.test.CredentialStoreTestBase;
import org.eclipse.edc.identityhub.spi.store.CredentialStore;
import org.eclipse.edc.identityhub.store.test.CredentialStoreTestBase;

class InMemoryCredentialStoreTest extends CredentialStoreTestBase {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (c) 2024 Metaform Systems, Inc.
*
* 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:
* Metaform Systems, Inc. - initial API and implementation
*
*/

package org.eclipse.edc.identityhub.defaults;

import org.eclipse.edc.identityhub.spi.store.ParticipantContextStore;
import org.eclipse.edc.identityhub.store.test.ParticipantContextStoreTestBase;

class InMemoryParticipantContextStoreTest extends ParticipantContextStoreTestBase {

private final InMemoryParticipantContextStore store = new InMemoryParticipantContextStore();

@Override
protected ParticipantContextStore getStore() {
return store;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public StoreResult<Void> update(VerifiableCredentialResource credentialResource)
}

@Override
public StoreResult<Void> delete(String id) {
public StoreResult<Void> deleteById(String id) {
Objects.requireNonNull(id);
return transactionContext.execute(() -> {
try (var connection = getConnection()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@

package org.eclipse.edc.identityhub.store.sql.credentials;

import org.eclipse.edc.identityhub.credentials.store.test.CredentialStoreTestBase;
import org.eclipse.edc.identityhub.spi.store.CredentialStore;
import org.eclipse.edc.identityhub.store.sql.credentials.schema.postgres.PostgresDialectStatements;
import org.eclipse.edc.identityhub.store.test.CredentialStoreTestBase;
import org.eclipse.edc.junit.annotations.ComponentTest;
import org.eclipse.edc.spi.types.TypeManager;
import org.eclipse.edc.sql.QueryExecutor;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright (c) 2024 Metaform Systems, Inc.
*
* 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:
* Metaform Systems, Inc. - initial API and implementation
*
*/

plugins {
`java-library`
}

dependencies {
api(project(":spi:identity-hub-store-spi"))
implementation(libs.edc.core.sql) // for the SqlStatements
implementation(libs.edc.spi.transaction.datasource)

testImplementation(testFixtures(project(":spi:identity-hub-store-spi")))
testImplementation(testFixtures(libs.edc.core.sql))
testImplementation(libs.edc.junit)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright (c) 2024 Metaform Systems, Inc.
*
* 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:
* Metaform Systems, Inc. - initial API and implementation
*
*/

-- only intended for and tested with Postgres!
CREATE TABLE participant_context
(
participant_id VARCHAR PRIMARY KEY NOT NULL, -- ID of the ParticipantContext
created_date BIGINT NOT NULL, -- POSIX timestamp of the creation of the PC
last_modified_date BIGINT, -- POSIX timestamp of the last modified date
state INTEGER NOT NULL, -- 0 = CREATED, 1 = ACTIVE, 2 = DEACTIVATED
api_token_alias VARCHAR NOT NULL -- alias under which this PC's api token is stored in the vault
);
CREATE UNIQUE INDEX participant_context_participant_id_uindex ON participant_context USING btree (participant_id);

Loading
Loading