Skip to content
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 @@ -6,6 +6,10 @@ plugins {
dependencies {
api(project(":spi:common:decentralized-claims-spi"))
api(project(":spi:common:policy:request-policy-context-spi"))
api(project(":spi:common:transaction-spi"))
api(project(":spi:control-plane:catalog-spi"))
api(project(":spi:control-plane:contract-spi"))
api(project(":spi:control-plane:transfer-spi"))
implementation(project(":spi:common:json-ld-spi"))
implementation(project(":spi:common:http-spi"))
implementation(project(":spi:common:keys-spi"))
Expand All @@ -15,6 +19,7 @@ dependencies {
implementation(project(":core:common:lib:util-lib"))
implementation(project(":core:common:lib:crypto-common-lib"))
implementation(project(":core:common:lib:token-lib"))
implementation(project(":core:common:lib:store-lib"))
implementation(project(":extensions:common:crypto:lib:jws2020-lib"))
implementation(project(":extensions:common:crypto:jwt-verifiable-credentials"))
implementation(project(":extensions:common:crypto:ldp-verifiable-credentials"))
Expand All @@ -29,6 +34,7 @@ dependencies {
testImplementation(testFixtures(project(":spi:common:decentralized-claims-spi")))
testImplementation(testFixtures(project(":spi:common:verifiable-credentials-spi")))
testImplementation(project(":core:common:lib:json-ld-lib"))
testImplementation(project(":core:common:lib:query-lib"))
testImplementation(project(":extensions:common:json-ld"))
testImplementation(libs.nimbus.jwt)
testImplementation(libs.awaitility)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
import org.eclipse.edc.iam.decentralizedclaims.core.defaults.DefaultTrustedIssuerRegistry;
import org.eclipse.edc.iam.decentralizedclaims.core.defaults.InMemorySignatureSuiteRegistry;
import org.eclipse.edc.iam.decentralizedclaims.core.scope.DcpScopeExtractorRegistry;
import org.eclipse.edc.iam.decentralizedclaims.core.scope.defaults.InMemoryDcpScopeStore;
import org.eclipse.edc.iam.decentralizedclaims.spi.ClaimTokenCreatorFunction;
import org.eclipse.edc.iam.decentralizedclaims.spi.scope.ScopeExtractorRegistry;
import org.eclipse.edc.iam.decentralizedclaims.spi.scope.store.DcpScopeStore;
import org.eclipse.edc.iam.decentralizedclaims.spi.verification.SignatureSuiteRegistry;
import org.eclipse.edc.iam.verifiablecredentials.spi.validation.TrustedIssuerRegistry;
import org.eclipse.edc.jwt.signer.spi.JwsSignerProvider;
Expand All @@ -32,6 +34,7 @@
import org.eclipse.edc.runtime.metamodel.annotation.Setting;
import org.eclipse.edc.spi.iam.AudienceResolver;
import org.eclipse.edc.spi.iam.ClaimToken;
import org.eclipse.edc.spi.query.CriterionOperatorRegistry;
import org.eclipse.edc.spi.result.Result;
import org.eclipse.edc.spi.system.ServiceExtension;

Expand Down Expand Up @@ -62,6 +65,8 @@ public class DcpDefaultServicesExtension implements ServiceExtension {
private JwsSignerProvider externalSigner;
@Inject
private JtiValidationStore jtiValidationStore;
@Inject
private CriterionOperatorRegistry criterionOperatorRegistry;

@Provider(isDefault = true)
public TrustedIssuerRegistry createInMemoryIssuerRegistry() {
Expand Down Expand Up @@ -98,5 +103,9 @@ public ClaimTokenCreatorFunction defaultClaimTokenFunction() {
return success(b.build());
};
}


@Provider(isDefault = true)
public DcpScopeStore scopeStore() {
return new InMemoryDcpScopeStore(criterionOperatorRegistry);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright (c) 2025 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.iam.decentralizedclaims.core;

import org.eclipse.edc.iam.decentralizedclaims.spi.scope.DcpScope;
import org.eclipse.edc.iam.decentralizedclaims.spi.scope.DcpScopeRegistry;
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.EdcException;
import org.eclipse.edc.spi.monitor.Monitor;
import org.eclipse.edc.spi.system.ServiceExtension;
import org.eclipse.edc.spi.system.ServiceExtensionContext;
import org.eclipse.edc.spi.system.configuration.Config;

import static org.eclipse.edc.iam.decentralizedclaims.core.DynamicDcpScopeConfigurationExtension.NAME;


@Extension(NAME)
public class DynamicDcpScopeConfigurationExtension implements ServiceExtension {

public static final String NAME = "DCP Dynamic Scope Configuration Extension";

public static final String CONFIG_PREFIX = "edc.iam.dcp.scopes";
public static final String CONFIG_ALIAS = CONFIG_PREFIX + ".<scopeAlias>.";

@Setting(context = CONFIG_ALIAS, description = "ID of the scope.")
public static final String ID_SUFFIX = "id";
@Setting(context = CONFIG_ALIAS, description = "Additional properties of the issuer.")
public static final String TYPE_SUFFIX = "type";
@Setting(context = CONFIG_ALIAS, description = "The value of the scope.")
public static final String VALUE_SUFFIX = "value";

@Setting(context = CONFIG_ALIAS, description = "Prefix mapping for the scope.")
public static final String PREFIX_MAPPING_SUFFIX = "prefix-mapping";
@Setting(context = CONFIG_ALIAS, description = "Profile the scope.", defaultValue = "*")
public static final String PROFILE_SUFFIX = "profile";

@Inject
private DcpScopeRegistry scopeRegistry;

@Inject
private Monitor monitor;

@Override
public void initialize(ServiceExtensionContext context) {
var config = context.getConfig(CONFIG_PREFIX);
var configs = config.partition().toList();
configs.forEach(this::addScope);

}

private void addScope(Config config) {
var id = config.getString(ID_SUFFIX);
var type = config.getString(TYPE_SUFFIX);
var value = config.getString(VALUE_SUFFIX);
var prefixMapping = config.getString(PREFIX_MAPPING_SUFFIX, null);
var profile = config.getString(PROFILE_SUFFIX, "*");

var scope = DcpScope.Builder.newInstance().id(id)
.type(DcpScope.Type.valueOf(type.toUpperCase()))
.value(value)
.prefixMapping(prefixMapping)
.profile(profile)
.build();

scopeRegistry.register(scope).orElseThrow(e -> new EdcException("Failed to register DCP scope with id " + id));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2025 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.iam.decentralizedclaims.core;

import org.eclipse.edc.iam.decentralizedclaims.core.scope.DcpScopeRegistryImpl;
import org.eclipse.edc.iam.decentralizedclaims.core.scope.DefaultScopeMappingFunction;
import org.eclipse.edc.iam.decentralizedclaims.core.scope.DynamicScopeExtractor;
import org.eclipse.edc.iam.decentralizedclaims.spi.scope.DcpScopeRegistry;
import org.eclipse.edc.iam.decentralizedclaims.spi.scope.ScopeExtractorRegistry;
import org.eclipse.edc.iam.decentralizedclaims.spi.scope.store.DcpScopeStore;
import org.eclipse.edc.policy.context.request.spi.RequestCatalogPolicyContext;
import org.eclipse.edc.policy.context.request.spi.RequestContractNegotiationPolicyContext;
import org.eclipse.edc.policy.context.request.spi.RequestTransferProcessPolicyContext;
import org.eclipse.edc.policy.engine.spi.PolicyEngine;
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
import org.eclipse.edc.runtime.metamodel.annotation.Provider;
import org.eclipse.edc.spi.system.ServiceExtension;
import org.eclipse.edc.spi.system.ServiceExtensionContext;
import org.eclipse.edc.transaction.spi.TransactionContext;

import static org.eclipse.edc.iam.decentralizedclaims.core.DynamicDcpScopeExtension.NAME;

@Extension(NAME)
public class DynamicDcpScopeExtension implements ServiceExtension {

public static final String NAME = "DCP Dynamic Scope Extension";

@Inject
private PolicyEngine policyEngine;

@Inject
private TransactionContext transactionContext;

@Inject
private DcpScopeStore scopeStore;


@Inject
private ScopeExtractorRegistry scopeExtractorRegistry;

private DcpScopeRegistry registry;

@Override
public void initialize(ServiceExtensionContext context) {
var contextMappingFunction = new DefaultScopeMappingFunction(registry());

policyEngine.registerPostValidator(RequestCatalogPolicyContext.class, contextMappingFunction::apply);
policyEngine.registerPostValidator(RequestContractNegotiationPolicyContext.class, contextMappingFunction::apply);
policyEngine.registerPostValidator(RequestTransferProcessPolicyContext.class, contextMappingFunction::apply);

scopeExtractorRegistry.registerScopeExtractor(new DynamicScopeExtractor(registry()));

}

@Provider
public DcpScopeRegistry registry() {
if (registry == null) {
registry = new DcpScopeRegistryImpl(transactionContext, scopeStore);
}
return registry;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright (c) 2026 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.iam.decentralizedclaims.core.scope;

import org.eclipse.edc.iam.decentralizedclaims.spi.scope.DcpScope;
import org.eclipse.edc.iam.decentralizedclaims.spi.scope.DcpScopeRegistry;
import org.eclipse.edc.iam.decentralizedclaims.spi.scope.store.DcpScopeStore;
import org.eclipse.edc.spi.query.Criterion;
import org.eclipse.edc.spi.query.QuerySpec;
import org.eclipse.edc.spi.result.ServiceResult;
import org.eclipse.edc.transaction.spi.TransactionContext;

import java.util.List;

/**
* Implementation of {@link DcpScopeRegistry}.
*/
public class DcpScopeRegistryImpl implements DcpScopeRegistry {

private final TransactionContext transactionContext;
private final DcpScopeStore store;

public DcpScopeRegistryImpl(TransactionContext transactionContext, DcpScopeStore store) {
this.transactionContext = transactionContext;
this.store = store;
}

@Override
public ServiceResult<Void> register(DcpScope scope) {
return transactionContext.execute(() -> store.save(scope).flatMap(ServiceResult::from));
}

@Override
public ServiceResult<Void> remove(String scopeId) {
return transactionContext.execute(() -> store.delete(scopeId).flatMap(ServiceResult::from));
}

@Override
public ServiceResult<List<DcpScope>> getDefaultScopes() {
var query = QuerySpec.Builder.newInstance()
.filter(Criterion.criterion("type", "=", DcpScope.Type.DEFAULT.name()))
.build();
return transactionContext.execute(() -> store.query(query).flatMap(ServiceResult::from));
}

@Override
public ServiceResult<List<DcpScope>> getScopeMapping() {
var query = QuerySpec.Builder.newInstance()
.filter(Criterion.criterion("type", "=", DcpScope.Type.POLICY.name()))
.build();
return transactionContext.execute(() -> store.query(query).flatMap(ServiceResult::from));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2025 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.iam.decentralizedclaims.core.scope;

import org.eclipse.edc.iam.decentralizedclaims.spi.scope.DcpScope;
import org.eclipse.edc.iam.decentralizedclaims.spi.scope.DcpScopeRegistry;
import org.eclipse.edc.policy.context.request.spi.RequestPolicyContext;
import org.eclipse.edc.policy.engine.spi.PolicyValidatorRule;
import org.eclipse.edc.policy.model.Policy;

import java.util.HashSet;

import static org.eclipse.edc.iam.decentralizedclaims.spi.scope.DcpScope.WILDCARD;

/**
* A policy validator rule that adds default DCP scopes to the request context based on the profile.
*/
public class DefaultScopeMappingFunction implements PolicyValidatorRule<RequestPolicyContext> {
private final DcpScopeRegistry scopeRegistry;

public DefaultScopeMappingFunction(DcpScopeRegistry scopeRegistry) {
this.scopeRegistry = scopeRegistry;
}

@Override
public Boolean apply(Policy policy, RequestPolicyContext context) {
var defaultScopes = scopeRegistry.getDefaultScopes();
if (defaultScopes.failed()) {
context.reportProblem("Failed to retrieve default scopes: " + defaultScopes.getFailureMessages());
return false;
}
var defaultScopeList = defaultScopes.getContent().stream()
.filter(scope -> filterScope(scope, context))
.map(DcpScope::getValue).toList();
var requestScopeBuilder = context.requestScopeBuilder();
var rq = requestScopeBuilder.build();
var existingScope = rq.getScopes();
var newScopes = new HashSet<>(defaultScopeList);
newScopes.addAll(existingScope);
requestScopeBuilder.scopes(newScopes);
return true;
}


boolean filterScope(DcpScope scope, RequestPolicyContext context) {
return scope.getProfile().equals(WILDCARD) || scope.getProfile().equals(context.requestContext().getMessage().getProtocol());
}
}
Loading