Skip to content

Commit 037dbb6

Browse files
authored
feat: remove the vc claim mapping from ParticipantAgentContextMapper (#5548)
1 parent 60a48c0 commit 037dbb6

19 files changed

Lines changed: 540 additions & 93 deletions

File tree

core/common/cel-core/build.gradle.kts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ plugins {
1919
dependencies {
2020
api(project(":spi:common:cel-spi"))
2121
api(project(":spi:common:transaction-spi"))
22-
api(project(":spi:common:verifiable-credentials-spi"))
2322
api(project(":spi:control-plane:control-plane-spi"))
2423
api(project(":spi:policy-monitor:policy-monitor-spi"))
2524

core/common/cel-core/src/main/java/org/eclipse/edc/policy/cel/CelPolicyCoreExtension.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import org.eclipse.edc.policy.cel.engine.CelExpressionEngineImpl;
2323
import org.eclipse.edc.policy.cel.function.CelExpressionFunction;
2424
import org.eclipse.edc.policy.cel.function.context.AgreementContextMapper;
25+
import org.eclipse.edc.policy.cel.function.context.CelParticipantAgentClaimMapperRegistry;
26+
import org.eclipse.edc.policy.cel.function.context.CelParticipantAgentClaimMapperRegistryImpl;
2527
import org.eclipse.edc.policy.cel.function.context.ParticipantAgentContextMapper;
2628
import org.eclipse.edc.policy.cel.function.context.PolicyMonitorContextMapper;
2729
import org.eclipse.edc.policy.cel.function.context.TransferProcessContextMapper;
@@ -72,6 +74,9 @@ public class CelPolicyCoreExtension implements ServiceExtension {
7274
@Inject
7375
private RuleBindingRegistry ruleBindingRegistry;
7476

77+
78+
private CelParticipantAgentClaimMapperRegistry claimMapperRegistry;
79+
7580
@Inject
7681
private Monitor monitor;
7782

@@ -87,9 +92,9 @@ public void initialize(ServiceExtensionContext context) {
8792
ruleBindingRegistry.dynamicBind(policyExpressionEngine()::evaluationScopes);
8893

8994
List.of(Permission.class, Duty.class, Prohibition.class).forEach(c -> {
90-
bindFunction(new CelExpressionFunction<>(policyExpressionEngine(), new TransferProcessContextMapper(new AgreementContextMapper(), new ParticipantAgentContextMapper<>())), c, TransferProcessPolicyContext.class);
91-
bindFunction(new CelExpressionFunction<>(policyExpressionEngine(), new ParticipantAgentContextMapper<>()), c, ContractNegotiationPolicyContext.class);
92-
bindFunction(new CelExpressionFunction<>(policyExpressionEngine(), new ParticipantAgentContextMapper<>()), c, CatalogPolicyContext.class);
95+
bindFunction(new CelExpressionFunction<>(policyExpressionEngine(), new TransferProcessContextMapper(new AgreementContextMapper(), new ParticipantAgentContextMapper<>(claimMapperRegistry()))), c, TransferProcessPolicyContext.class);
96+
bindFunction(new CelExpressionFunction<>(policyExpressionEngine(), new ParticipantAgentContextMapper<>(claimMapperRegistry())), c, ContractNegotiationPolicyContext.class);
97+
bindFunction(new CelExpressionFunction<>(policyExpressionEngine(), new ParticipantAgentContextMapper<>(claimMapperRegistry())), c, CatalogPolicyContext.class);
9398
bindFunction(new CelExpressionFunction<>(policyExpressionEngine(), new PolicyMonitorContextMapper(new AgreementContextMapper())), c, PolicyMonitorContext.class);
9499
});
95100

@@ -116,4 +121,12 @@ private <C extends PolicyContext, R extends Rule> void bindFunction(DynamicAtomi
116121
policyEngine.registerFunction(contextClass, ruleClass, function);
117122
}
118123

124+
@Provider
125+
public CelParticipantAgentClaimMapperRegistry claimMapperRegistry() {
126+
if (claimMapperRegistry == null) {
127+
claimMapperRegistry = new CelParticipantAgentClaimMapperRegistryImpl();
128+
}
129+
return claimMapperRegistry;
130+
}
131+
119132
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright (c) 2026 Metaform Systems, Inc.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Apache License, Version 2.0 which is available at
6+
* https://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* SPDX-License-Identifier: Apache-2.0
9+
*
10+
* Contributors:
11+
* Metaform Systems, Inc. - initial API and implementation
12+
*
13+
*/
14+
15+
package org.eclipse.edc.policy.cel.function.context;
16+
17+
import org.eclipse.edc.participant.spi.ParticipantAgent;
18+
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
22+
public class CelParticipantAgentClaimMapperRegistryImpl implements CelParticipantAgentClaimMapperRegistry {
23+
24+
protected List<CelParticipantAgentClaimMapper> claimMappers = new ArrayList<>();
25+
26+
@Override
27+
public void registerClaimMapper(CelParticipantAgentClaimMapper claimMapper) {
28+
claimMappers.add(claimMapper);
29+
}
30+
31+
@Override
32+
public List<CelClaim> mapClaim(ParticipantAgent agent) {
33+
return claimMappers.stream()
34+
.map(mapper -> mapper.mapClaim(agent))
35+
.toList();
36+
}
37+
}

core/common/cel-core/src/main/java/org/eclipse/edc/policy/cel/function/context/ParticipantAgentContextMapper.java

Lines changed: 17 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,13 @@
1414

1515
package org.eclipse.edc.policy.cel.function.context;
1616

17-
import org.eclipse.edc.iam.verifiablecredentials.spi.model.CredentialSubject;
18-
import org.eclipse.edc.iam.verifiablecredentials.spi.model.VerifiableCredential;
17+
18+
import org.eclipse.edc.participant.spi.ParticipantAgent;
1919
import org.eclipse.edc.participant.spi.ParticipantAgentPolicyContext;
2020
import org.eclipse.edc.spi.result.Result;
2121

2222
import java.util.HashMap;
23-
import java.util.List;
2423
import java.util.Map;
25-
import java.util.stream.Collectors;
2624

2725
/**
2826
* Supplies participant agent-related context data for CEL expression evaluation.
@@ -31,6 +29,12 @@
3129
*/
3230
public class ParticipantAgentContextMapper<C extends ParticipantAgentPolicyContext> implements CelContextMapper<C> {
3331

32+
private final CelParticipantAgentClaimMapperRegistry claimMapperRegistry;
33+
34+
public ParticipantAgentContextMapper(CelParticipantAgentClaimMapperRegistry claimMapperRegistry) {
35+
this.claimMapperRegistry = claimMapperRegistry;
36+
}
37+
3438
private Result<Map<String, Object>> agent(C context) {
3539
if (context.participantAgent() == null) {
3640
return Result.failure("Participant agent is null");
@@ -39,46 +43,23 @@ private Result<Map<String, Object>> agent(C context) {
3943
return Result.success(Map.of("agent", Map.ofEntries(
4044
Map.entry("id", id),
4145
Map.entry("attributes", context.participantAgent().getAttributes()),
42-
Map.entry("claims", toClaimsMap(context.participantAgent().getClaims()))
46+
Map.entry("claims", toClaimsMap(context.participantAgent()))
4347
)));
4448
}
4549

46-
// Converts the 'vc' claim to a list of verifiable credential maps.
47-
// TODO Currently, hardcoded here; in the future, consider using a more generic approach.
48-
private Map<String, Object> toClaimsMap(Map<String, Object> claims) {
49-
var mappedClaims = new HashMap<>(claims);
50-
if (claims.get("vc") == null) {
51-
return mappedClaims;
52-
}
53-
mappedClaims.put("vc", toVcList(claims.get("vc")));
54-
return mappedClaims;
55-
}
5650

57-
private List<Map<String, Object>> toVcList(Object vcClaim) {
58-
if (vcClaim instanceof List<?> vcList) {
59-
return vcList.stream()
60-
.filter(item -> item instanceof VerifiableCredential)
61-
.map(item -> toMap((VerifiableCredential) item))
62-
.toList();
63-
}
64-
return List.of();
65-
}
51+
private Map<String, Object> toClaimsMap(ParticipantAgent agent) {
52+
var mappedClaims = new HashMap<>(agent.getClaims());
6653

67-
private Map<String, Object> toMap(VerifiableCredential credential) {
68-
var cred = new HashMap<String, Object>();
69-
cred.put("@context", credential.getContext());
70-
cred.put("id", credential.getId());
71-
cred.put("type", credential.getType());
72-
cred.put("credentialSubject", credential.getCredentialSubject().stream().map(this::toMap).collect(Collectors.toList()));
73-
cred.put("issuer", credential.getIssuer().id());
74-
cred.put("issuanceDate", credential.getIssuanceDate().toString());
75-
return cred;
76-
}
54+
claimMapperRegistry.mapClaim(agent)
55+
.stream()
56+
.filter(claim -> claim.value() != null)
57+
.forEach(claim -> mappedClaims.put(claim.name(), claim.value()));
7758

78-
private Map<String, Object> toMap(CredentialSubject subject) {
79-
return subject.getClaims();
59+
return mappedClaims;
8060
}
8161

62+
8263
@Override
8364
public Result<Map<String, Object>> mapContext(C context) {
8465
return agent(context);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright (c) 2026 Metaform Systems, Inc.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Apache License, Version 2.0 which is available at
6+
* https://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* SPDX-License-Identifier: Apache-2.0
9+
*
10+
* Contributors:
11+
* Metaform Systems, Inc. - initial API and implementation
12+
*
13+
*/
14+
15+
package org.eclipse.edc.policy.cel.function.context;
16+
17+
import org.eclipse.edc.participant.spi.ParticipantAgent;
18+
import org.junit.jupiter.api.Test;
19+
20+
import java.util.List;
21+
import java.util.Map;
22+
23+
import static org.assertj.core.api.Assertions.assertThat;
24+
25+
public class CelParticipantAgentClaimMapperRegistryImplTest {
26+
27+
@Test
28+
void mapClaim_emptyRegistry_returnsEmptyList() {
29+
var registry = new CelParticipantAgentClaimMapperRegistryImpl();
30+
var agent = new ParticipantAgent("agent-id", Map.of(), Map.of());
31+
32+
var result = registry.mapClaim(agent);
33+
34+
assertThat(result).isNotNull().isEmpty();
35+
}
36+
37+
@Test
38+
void registerClaimMapper_mapsClaimsInRegistrationOrder() {
39+
var registry = new CelParticipantAgentClaimMapperRegistryImpl();
40+
var agent = new ParticipantAgent("agent-id", Map.of(), Map.of());
41+
42+
registry.registerClaimMapper(a -> new CelClaim("claim1", "value1"));
43+
registry.registerClaimMapper(a -> new CelClaim("claim2", "value2"));
44+
45+
List<CelClaim> result = registry.mapClaim(agent);
46+
47+
assertThat(result).hasSize(2);
48+
assertThat(result.get(0).name()).isEqualTo("claim1");
49+
assertThat(result.get(0).value()).isEqualTo("value1");
50+
assertThat(result.get(1).name()).isEqualTo("claim2");
51+
assertThat(result.get(1).value()).isEqualTo("value2");
52+
}
53+
54+
@Test
55+
void mapClaim_includesClaimsWithNullValue() {
56+
var registry = new CelParticipantAgentClaimMapperRegistryImpl();
57+
var agent = new ParticipantAgent("agent-id", Map.of(), Map.of());
58+
59+
registry.registerClaimMapper(a -> new CelClaim("nullable", null));
60+
61+
var result = registry.mapClaim(agent);
62+
63+
assertThat(result).hasSize(1);
64+
assertThat(result.get(0).name()).isEqualTo("nullable");
65+
assertThat(result.get(0).value()).isNull();
66+
}
67+
}
68+

core/common/cel-core/src/test/java/org/eclipse/edc/policy/cel/function/context/ParticipantAgentContextMapperTest.java

Lines changed: 18 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,23 @@
1414

1515
package org.eclipse.edc.policy.cel.function.context;
1616

17-
import org.assertj.core.api.Assertions;
18-
import org.eclipse.edc.iam.verifiablecredentials.spi.model.CredentialSubject;
19-
import org.eclipse.edc.iam.verifiablecredentials.spi.model.Issuer;
20-
import org.eclipse.edc.iam.verifiablecredentials.spi.model.VerifiableCredential;
2117
import org.eclipse.edc.participant.spi.ParticipantAgent;
2218
import org.eclipse.edc.participant.spi.ParticipantAgentPolicyContext;
2319
import org.junit.jupiter.api.Test;
2420

25-
import java.time.Instant;
2621
import java.util.List;
2722
import java.util.Map;
2823

24+
import static org.assertj.core.api.Assertions.assertThat;
2925
import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat;
26+
import static org.mockito.ArgumentMatchers.any;
3027
import static org.mockito.Mockito.mock;
3128
import static org.mockito.Mockito.when;
3229

3330
public class ParticipantAgentContextMapperTest {
3431

35-
private final ParticipantAgentContextMapper<ParticipantAgentPolicyContext> mapper = new ParticipantAgentContextMapper<>();
32+
private final CelParticipantAgentClaimMapperRegistry claimMapperRegistry = mock();
33+
private final ParticipantAgentContextMapper<ParticipantAgentPolicyContext> mapper = new ParticipantAgentContextMapper<>(claimMapperRegistry);
3634

3735
@SuppressWarnings("unchecked")
3836
@Test
@@ -45,66 +43,35 @@ void mapContext() {
4543

4644
assertThat(result).isSucceeded().satisfies(map -> {
4745
var agentMap = (Map<String, Object>) map.get("agent");
48-
Assertions.assertThat(agentMap.get("id")).isEqualTo("agent-id");
49-
Assertions.assertThat(agentMap.get("attributes")).isEqualTo(attributes);
50-
Assertions.assertThat(agentMap.get("claims")).isEqualTo(Map.of());
46+
assertThat(agentMap.get("id")).isEqualTo("agent-id");
47+
assertThat(agentMap.get("attributes")).isEqualTo(attributes);
48+
assertThat(agentMap.get("claims")).isEqualTo(Map.of());
5149
});
5250

5351
}
5452

5553
@SuppressWarnings("unchecked")
5654
@Test
57-
void mapContext_withCredentials() {
55+
void mapContext_claimsMapper() {
5856
var ctx = mock(ParticipantAgentPolicyContext.class);
57+
when(claimMapperRegistry.mapClaim(any())).thenReturn(List.of(
58+
new CelClaim("claim1", "value1"),
59+
new CelClaim("claim2", "value2")
60+
));
5961
var attributes = Map.of("key1", "value1", "key2", "value2");
6062

61-
var credentials = credentials();
62-
Map<String, Object> claims = Map.of(
63-
"vc", credentials
64-
);
65-
var participantAgent = new ParticipantAgent("agent-id", claims, attributes);
63+
var participantAgent = new ParticipantAgent("agent-id", Map.of(), attributes);
6664
when(ctx.participantAgent()).thenReturn(participantAgent);
6765
var result = mapper.mapContext(ctx);
6866

6967
assertThat(result).isSucceeded().satisfies(map -> {
7068
var agentMap = (Map<String, Object>) map.get("agent");
71-
72-
var expectedMap = Map.of("vc", List.of(
73-
Map.of(
74-
"@context", List.of("https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"),
75-
"id", "http://example.edu/credentials/3732",
76-
"type", List.of("VerifiableCredential", "AlumniCredential"),
77-
"issuer", "https://example.edu/issuers/14",
78-
"issuanceDate", credentials.get(0).getIssuanceDate().toString(),
79-
"credentialSubject", List.of(Map.of(
80-
"alumniOf", "Example University",
81-
"degree", Map.of(
82-
"type", "BachelorDegree",
83-
"name", "Bachelor of Science and Arts"
84-
)
85-
))
86-
)
69+
assertThat(agentMap.get("id")).isEqualTo("agent-id");
70+
assertThat(agentMap.get("attributes")).isEqualTo(attributes);
71+
assertThat(agentMap.get("claims")).isEqualTo(Map.of(
72+
"claim1", "value1",
73+
"claim2", "value2"
8774
));
88-
Assertions.assertThat(agentMap.get("claims")).isEqualTo(expectedMap);
8975
});
9076
}
91-
92-
@SuppressWarnings("unchecked")
93-
private List<VerifiableCredential> credentials() {
94-
var vc = VerifiableCredential.Builder.newInstance()
95-
.contexts(List.of("https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"))
96-
.id("http://example.edu/credentials/3732")
97-
.types(List.of("VerifiableCredential", "AlumniCredential"))
98-
.issuer(new Issuer("https://example.edu/issuers/14", Map.of()))
99-
.issuanceDate(Instant.now())
100-
.credentialSubject(CredentialSubject.Builder.newInstance()
101-
.claim("alumniOf", "Example University")
102-
.claim("degree", Map.of(
103-
"type", "BachelorDegree",
104-
"name", "Bachelor of Science and Arts"
105-
))
106-
.build())
107-
.build();
108-
return List.of(vc);
109-
}
11077
}

core/common/cel-core/src/test/java/org/eclipse/edc/policy/cel/function/context/TransferProcessContextMapperTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public class TransferProcessContextMapperTest {
3131

3232
private final TransferProcessContextMapper mapper = new TransferProcessContextMapper(
3333
new AgreementContextMapper(),
34-
new ParticipantAgentContextMapper<>()
34+
new ParticipantAgentContextMapper<>(mock())
3535
);
3636

3737
@SuppressWarnings("unchecked")

extensions/common/api/management-api-configuration/src/main/resources/management-api-version.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
{
99
"version": "4.0.0-beta",
1010
"urlPath": "/v4beta",
11-
"lastUpdated": "2026-02-27T08:43:01Z",
11+
"lastUpdated": "2026-03-12T08:43:01Z",
1212
"maturity": "beta"
1313
}
1414
]

0 commit comments

Comments
 (0)