Skip to content

Commit 24bbc7e

Browse files
feat: reduced mocking in the pg‑runtime test classes (#2552)
1 parent d4880a6 commit 24bbc7e

File tree

9 files changed

+292
-34
lines changed

9 files changed

+292
-34
lines changed

edc-tests/compatibility-tests/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ plugins {
2222
`java-library`
2323
}
2424

25+
configurations.all {
26+
exclude("org.eclipse.edc", "decentralized-claims-core")
27+
}
28+
2529
dependencies {
2630
testImplementation(libs.edc.junit)
2731
testImplementation(libs.edc.lib.cryptocommon)

edc-tests/e2e-fixtures/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ dependencies {
5454

5555
testFixturesApi(testFixtures(libs.edc.api.management.test.fixtures))
5656

57+
testFixturesApi(libs.edc.iam.decentralized.claims.core)
58+
testFixturesApi(libs.edc.verifiablecredentials.jwt)
59+
5760
testFixturesApi(libs.awaitility)
5861
testFixturesApi(libs.aws.s3)
5962
testFixturesApi(libs.azure.storage.blob)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/********************************************************************************
2+
* Copyright (c) 2026 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
3+
* Copyright (c) 2025 Cofinity-X GmbH
4+
*
5+
* See the NOTICE file(s) distributed with this work for additional
6+
* information regarding copyright ownership.
7+
*
8+
* This program and the accompanying materials are made available under the
9+
* terms of the Apache License, Version 2.0 which is available at
10+
* https://www.apache.org/licenses/LICENSE-2.0.
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15+
* License for the specific language governing permissions and limitations
16+
* under the License.
17+
*
18+
* SPDX-License-Identifier: Apache-2.0
19+
********************************************************************************/
20+
21+
package org.eclipse.tractusx.edc.tests;
22+
23+
import org.eclipse.edc.iam.did.spi.resolution.DidPublicKeyResolver;
24+
import org.eclipse.edc.jwt.validation.jti.JtiValidationStore;
25+
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
26+
import org.eclipse.edc.runtime.metamodel.annotation.Provider;
27+
import org.eclipse.edc.spi.iam.IdentityService;
28+
import org.eclipse.edc.spi.system.ServiceExtension;
29+
30+
public class MockIdentityServiceExtension implements ServiceExtension {
31+
@Inject
32+
private IdentityService identityService; //ensure the original IdentityService dependencies are injected before overriding with the mocked class
33+
34+
@Inject
35+
private JtiValidationStore jtiValidationStore;
36+
37+
@Inject
38+
private DidPublicKeyResolver didPublicKeyResolver;
39+
40+
private final String bpn;
41+
private final String did;
42+
43+
public MockIdentityServiceExtension(String bpn, String did) {
44+
this.bpn = bpn;
45+
this.did = did;
46+
}
47+
48+
@Provider
49+
public IdentityService mockIdentityService() {
50+
var mockTokenValidationService = new MockTokenValidationService();
51+
var mockTokenValidationAction = new MockTokenValidationAction(mockTokenValidationService, didPublicKeyResolver, jtiValidationStore);
52+
return new MockVcIdentityService(bpn, did, mockTokenValidationAction);
53+
}
54+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/********************************************************************************
2+
* Copyright (c) 2026 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information regarding copyright ownership.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Apache License, Version 2.0 which is available at
9+
* https://www.apache.org/licenses/LICENSE-2.0.
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
* License for the specific language governing permissions and limitations
15+
* under the License.
16+
*
17+
* SPDX-License-Identifier: Apache-2.0
18+
********************************************************************************/
19+
20+
package org.eclipse.tractusx.edc.tests;
21+
22+
import com.nimbusds.jwt.SignedJWT;
23+
import org.eclipse.edc.iam.decentralizedclaims.spi.validation.TokenValidationAction;
24+
import org.eclipse.edc.jwt.validation.jti.JtiValidationStore;
25+
import org.eclipse.edc.keys.spi.PublicKeyResolver;
26+
import org.eclipse.edc.spi.iam.ClaimToken;
27+
import org.eclipse.edc.spi.iam.TokenRepresentation;
28+
import org.eclipse.edc.spi.result.Result;
29+
import org.eclipse.edc.token.rules.AudienceValidationRule;
30+
import org.eclipse.edc.token.rules.ExpirationIssuedAtValidationRule;
31+
import org.eclipse.edc.token.rules.NotBeforeValidationRule;
32+
import org.eclipse.edc.token.spi.TokenValidationRule;
33+
import org.eclipse.edc.token.spi.TokenValidationService;
34+
import org.eclipse.edc.verifiablecredentials.jwt.rules.HasSubjectRule;
35+
import org.eclipse.edc.verifiablecredentials.jwt.rules.IssuerEqualsSubjectRule;
36+
import org.eclipse.edc.verifiablecredentials.jwt.rules.IssuerKeyIdValidationRule;
37+
import org.eclipse.edc.verifiablecredentials.jwt.rules.JtiValidationRule;
38+
import org.eclipse.edc.verifiablecredentials.jwt.rules.SubJwkIsNullRule;
39+
import org.eclipse.edc.verifiablecredentials.jwt.rules.TokenNotNullRule;
40+
41+
import java.time.Clock;
42+
import java.util.ArrayList;
43+
44+
public class MockTokenValidationAction implements TokenValidationAction {
45+
46+
private final TokenValidationService tokenValidationService;
47+
private final PublicKeyResolver publicKeyResolver;
48+
private final JtiValidationStore jtiValidationStore;
49+
private final Clock clock = Clock.systemUTC();
50+
51+
public MockTokenValidationAction(TokenValidationService tokenValidationService, PublicKeyResolver publicKeyResolver, JtiValidationStore jtiValidationStore) {
52+
this.tokenValidationService = tokenValidationService;
53+
this.publicKeyResolver = publicKeyResolver;
54+
this.jtiValidationStore = jtiValidationStore;
55+
}
56+
57+
public Result<ClaimToken> validate(String participantContextId, TokenRepresentation tokenRepresentation) {
58+
try {
59+
var signedJwt = SignedJWT.parse(tokenRepresentation.getToken());
60+
var keyId = signedJwt.getHeader().getKeyID();
61+
var rules = new ArrayList<TokenValidationRule>();
62+
63+
rules.add(new IssuerEqualsSubjectRule());
64+
rules.add(new SubJwkIsNullRule());
65+
rules.add(new ExpirationIssuedAtValidationRule(clock, 5, false));
66+
rules.add(new TokenNotNullRule());
67+
rules.add(new NotBeforeValidationRule(clock, 4, true));
68+
rules.add(new HasSubjectRule());
69+
rules.add(new JtiValidationRule(jtiValidationStore, null));
70+
rules.add(new IssuerKeyIdValidationRule(keyId));
71+
rules.add(new AudienceValidationRule(audResolver(tokenRepresentation)));
72+
73+
return tokenValidationService.validate(tokenRepresentation, publicKeyResolver, rules);
74+
} catch (Exception e) {
75+
throw new RuntimeException(e);
76+
}
77+
}
78+
79+
private String audResolver(TokenRepresentation tokenRepresentation) {
80+
try {
81+
var jwt = SignedJWT.parse(tokenRepresentation.getToken());
82+
return jwt.getJWTClaimsSet().getAudience().get(0);
83+
} catch (Exception e) {
84+
throw new RuntimeException("Failed to extract audience from token", e);
85+
}
86+
}
87+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/********************************************************************************
2+
* Copyright (c) 2026 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information regarding copyright ownership.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Apache License, Version 2.0 which is available at
9+
* https://www.apache.org/licenses/LICENSE-2.0.
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
* License for the specific language governing permissions and limitations
15+
* under the License.
16+
*
17+
* SPDX-License-Identifier: Apache-2.0
18+
********************************************************************************/
19+
20+
package org.eclipse.tractusx.edc.tests;
21+
22+
import com.nimbusds.jwt.SignedJWT;
23+
import org.eclipse.edc.keys.spi.PublicKeyResolver;
24+
import org.eclipse.edc.spi.iam.ClaimToken;
25+
import org.eclipse.edc.spi.iam.TokenRepresentation;
26+
import org.eclipse.edc.spi.result.AbstractResult;
27+
import org.eclipse.edc.spi.result.Result;
28+
import org.eclipse.edc.token.spi.TokenValidationRule;
29+
import org.eclipse.edc.token.spi.TokenValidationService;
30+
31+
import java.text.ParseException;
32+
import java.util.List;
33+
34+
public class MockTokenValidationService implements TokenValidationService {
35+
36+
@Override
37+
public Result<ClaimToken> validate(TokenRepresentation tokenRepresentation, PublicKeyResolver publicKeyResolver, List<TokenValidationRule> rules) {
38+
var token = tokenRepresentation.getToken();
39+
var additional = tokenRepresentation.getAdditional();
40+
try {
41+
var signedJwt = SignedJWT.parse(token);
42+
var tokenBuilder = ClaimToken.Builder.newInstance();
43+
44+
signedJwt.getJWTClaimsSet().getClaims().entrySet().stream()
45+
.filter(entry -> entry.getValue() != null)
46+
.forEach(entry -> tokenBuilder.claim(entry.getKey(), entry.getValue()));
47+
48+
var claimToken = tokenBuilder.build();
49+
var errors = rules.stream()
50+
.map(r -> r.checkRule(claimToken, additional))
51+
.reduce(Result::merge)
52+
.stream()
53+
.filter(AbstractResult::failed)
54+
.flatMap(r -> r.getFailureMessages().stream())
55+
.toList();
56+
57+
if (!errors.isEmpty()) {
58+
return Result.failure(errors);
59+
}
60+
61+
return Result.success(claimToken);
62+
63+
} catch (ParseException e) {
64+
return Result.failure("Failed to decode token");
65+
}
66+
}
67+
}

edc-tests/e2e-fixtures/src/testFixtures/java/org/eclipse/tractusx/edc/tests/MockVcIdentityService.java

Lines changed: 70 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
package org.eclipse.tractusx.edc.tests;
2121

22-
import com.fasterxml.jackson.core.type.TypeReference;
22+
import org.eclipse.edc.iam.decentralizedclaims.spi.validation.TokenValidationAction;
2323
import org.eclipse.edc.iam.verifiablecredentials.spi.model.CredentialSubject;
2424
import org.eclipse.edc.iam.verifiablecredentials.spi.model.Issuer;
2525
import org.eclipse.edc.iam.verifiablecredentials.spi.model.VerifiableCredential;
@@ -33,10 +33,16 @@
3333
import org.eclipse.edc.spi.types.TypeManager;
3434

3535
import java.time.Instant;
36+
import java.util.Base64;
3637
import java.util.List;
3738
import java.util.Map;
3839

39-
import static java.lang.String.format;
40+
import static org.eclipse.edc.iam.decentralizedclaims.spi.SelfIssuedTokenConstants.PRESENTATION_TOKEN_CLAIM;
41+
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.AUDIENCE;
42+
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.EXPIRATION_TIME;
43+
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.ISSUED_AT;
44+
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.ISSUER;
45+
import static org.eclipse.edc.jwt.spi.JwtRegisteredClaimNames.SUBJECT;
4046

4147
/**
4248
* An {@link IdentityService} that will inject the BPN claim in every token.
@@ -49,59 +55,94 @@ public class MockVcIdentityService implements IdentityService {
4955
private final String businessPartnerNumber;
5056
private final String did;
5157
private final TypeManager typeManager = new JacksonTypeManager();
52-
53-
public MockVcIdentityService(String businessPartnerNumber, String did) {
58+
private final TokenValidationAction tokenValidationAction;
59+
60+
public MockVcIdentityService(String businessPartnerNumber, String did, TokenValidationAction tokenValidationAction) {
5461
this.businessPartnerNumber = businessPartnerNumber;
5562
this.did = did;
63+
this.tokenValidationAction = tokenValidationAction;
5664
}
57-
65+
5866
@Override
5967
public Result<TokenRepresentation> obtainClientCredentials(String participantContextId, TokenParameters parameters) {
60-
var credentials = List.of(membershipCredential(), dataExchangeGovernanceCredential());
61-
var token = Map.of(VC_CLAIM, credentials);
62-
6368
var tokenRepresentation = TokenRepresentation.Builder.newInstance()
64-
.token(typeManager.writeValueAsString(token))
69+
.token(getTestToken(parameters.getStringClaim("aud")))
6570
.build();
71+
6672
return Result.success(tokenRepresentation);
6773
}
68-
74+
6975
@Override
7076
public Result<ClaimToken> verifyJwtToken(String participantContextId, TokenRepresentation tokenRepresentation, VerificationContext verificationContext) {
7177
var token = tokenRepresentation.getToken().replace("Bearer ", "");
72-
var tokenParsed = typeManager.readValue(token, Map.class);
73-
74-
if (tokenParsed.containsKey(VC_CLAIM)) {
75-
var credentials = typeManager.getMapper().convertValue(tokenParsed.get(VC_CLAIM), new TypeReference<List<VerifiableCredential>>(){});
76-
var claimToken = ClaimToken.Builder.newInstance()
77-
.claim(VC_CLAIM, credentials)
78-
.build();
79-
return Result.success(claimToken);
78+
tokenRepresentation = tokenRepresentation.toBuilder().token(token).build();
79+
var claimTokenResult = tokenValidationAction.validate(participantContextId, tokenRepresentation);
80+
81+
if (claimTokenResult.failed()) {
82+
return claimTokenResult;
8083
}
81-
return Result.failure(format("Expected %s claim, but token did not contain them", VC_CLAIM));
84+
85+
var claimToken = claimTokenResult.getContent();
86+
var bpnlConsumer = claimToken.getStringClaim(BUSINESS_PARTNER_NUMBER_CLAIM);
87+
var didConsumer = claimToken.getStringClaim(ISSUER);
88+
var credentials = List.of(membershipCredential(bpnlConsumer, didConsumer), dataExchangeGovernanceCredential(bpnlConsumer, didConsumer));
89+
90+
var claimTokenWithVc = ClaimToken.Builder.newInstance()
91+
.claim(VC_CLAIM, credentials)
92+
.build();
93+
94+
return Result.success(claimTokenWithVc);
8295
}
83-
84-
private VerifiableCredential membershipCredential() {
96+
97+
private String getTestToken(String aud) {
98+
var header = Map.of(
99+
"alg", "ES256K",
100+
"typ", "JWT",
101+
"kid", did + "#key-1"
102+
);
103+
104+
var now = Instant.now();
105+
var payload = new java.util.HashMap<String, Object>();
106+
payload.put(ISSUER, did);
107+
payload.put(SUBJECT, did);
108+
payload.put(AUDIENCE, aud);
109+
payload.put(EXPIRATION_TIME, now.plusSeconds(3600).getEpochSecond());
110+
payload.put(ISSUED_AT, now.getEpochSecond());
111+
payload.put(PRESENTATION_TOKEN_CLAIM, "token");
112+
payload.put(BUSINESS_PARTNER_NUMBER_CLAIM, businessPartnerNumber);
113+
114+
var signature = "signature";
115+
116+
String headerJson = typeManager.writeValueAsString(header);
117+
String payloadJson = typeManager.writeValueAsString(payload);
118+
String encodedHeader = Base64.getUrlEncoder().withoutPadding().encodeToString(headerJson.getBytes());
119+
String encodedToken = Base64.getUrlEncoder().withoutPadding().encodeToString(payloadJson.getBytes());
120+
String encodedSignature = Base64.getUrlEncoder().withoutPadding().encodeToString(signature.getBytes());
121+
122+
return (encodedHeader + "." + encodedToken + "." + encodedSignature);
123+
}
124+
125+
private VerifiableCredential dataExchangeGovernanceCredential(String bpnlConsumer, String didConsumer) {
85126
return VerifiableCredential.Builder.newInstance()
86127
.type("VerifiableCredential")
87-
.type("MembershipCredential")
128+
.type("DataExchangeGovernanceCredential")
88129
.credentialSubject(CredentialSubject.Builder.newInstance()
89-
.id(did)
90-
.claim("holderIdentifier", businessPartnerNumber)
130+
.id(didConsumer)
131+
.claim("holderIdentifier", bpnlConsumer)
132+
.claim("contractVersion", "1.0")
91133
.build())
92134
.issuer(new Issuer("issuer", Map.of()))
93135
.issuanceDate(Instant.now())
94136
.build();
95137
}
96138

97-
private VerifiableCredential dataExchangeGovernanceCredential() {
139+
private VerifiableCredential membershipCredential(String bpnlConsumer, String didConsumer) {
98140
return VerifiableCredential.Builder.newInstance()
99141
.type("VerifiableCredential")
100-
.type("DataExchangeGovernanceCredential")
142+
.type("MembershipCredential")
101143
.credentialSubject(CredentialSubject.Builder.newInstance()
102-
.id(did)
103-
.claim("holderIdentifier", businessPartnerNumber)
104-
.claim("contractVersion", "1.0")
144+
.id(didConsumer)
145+
.claim("holderIdentifier", bpnlConsumer)
105146
.build())
106147
.issuer(new Issuer("issuer", Map.of()))
107148
.issuanceDate(Instant.now())

0 commit comments

Comments
 (0)