Skip to content

Commit b5c1406

Browse files
chore: add method to obtain revocation status as string(s) (#4429)
* chore: add getRevocationStatus method * DEPENDENCIES
1 parent a9520c2 commit b5c1406

File tree

4 files changed

+107
-7
lines changed

4 files changed

+107
-7
lines changed

DEPENDENCIES

-7
Original file line numberDiff line numberDiff line change
@@ -291,10 +291,6 @@ maven/mavencentral/org.hamcrest/hamcrest-core/2.2, BSD-3-Clause, approved, clear
291291
maven/mavencentral/org.hamcrest/hamcrest/2.1, BSD-3-Clause, approved, clearlydefined
292292
maven/mavencentral/org.hamcrest/hamcrest/2.2, BSD-3-Clause, approved, clearlydefined
293293
maven/mavencentral/org.hdrhistogram/HdrHistogram/2.2.2, BSD-2-Clause AND CC0-1.0 AND CC0-1.0, approved, #14828
294-
maven/mavencentral/org.jacoco/org.jacoco.agent/0.8.11, EPL-2.0, approved, CQ23285
295-
maven/mavencentral/org.jacoco/org.jacoco.ant/0.8.11, EPL-2.0, approved, #1068
296-
maven/mavencentral/org.jacoco/org.jacoco.core/0.8.11, EPL-2.0, approved, CQ23283
297-
maven/mavencentral/org.jacoco/org.jacoco.report/0.8.11, EPL-2.0 AND Apache-2.0, approved, CQ23284
298294
maven/mavencentral/org.javassist/javassist/3.28.0-GA, Apache-2.0 OR LGPL-2.1-or-later OR MPL-1.1, approved, #327
299295
maven/mavencentral/org.javassist/javassist/3.30.2-GA, Apache-2.0 AND LGPL-2.1-or-later AND MPL-1.1, approved, #12108
300296
maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib-common/1.9.10, Apache-2.0, approved, #14186
@@ -326,12 +322,9 @@ maven/mavencentral/org.mockito/mockito-inline/5.2.0, MIT, approved, clearlydefin
326322
maven/mavencentral/org.mozilla/rhino/1.7.7.2, MPL-2.0 AND BSD-3-Clause AND ISC, approved, CQ16320
327323
maven/mavencentral/org.objenesis/objenesis/3.3, Apache-2.0, approved, clearlydefined
328324
maven/mavencentral/org.opentest4j/opentest4j/1.3.0, Apache-2.0, approved, #9713
329-
maven/mavencentral/org.ow2.asm/asm-commons/9.6, BSD-3-Clause, approved, #10775
330325
maven/mavencentral/org.ow2.asm/asm-commons/9.7, BSD-3-Clause, approved, #14075
331-
maven/mavencentral/org.ow2.asm/asm-tree/9.6, BSD-3-Clause, approved, #10773
332326
maven/mavencentral/org.ow2.asm/asm-tree/9.7, BSD-3-Clause, approved, #14073
333327
maven/mavencentral/org.ow2.asm/asm/9.1, BSD-3-Clause, approved, CQ23029
334-
maven/mavencentral/org.ow2.asm/asm/9.6, BSD-3-Clause, approved, #10776
335328
maven/mavencentral/org.ow2.asm/asm/9.7, BSD-3-Clause, approved, #14076
336329
maven/mavencentral/org.postgresql/postgresql/42.7.3, BSD-2-Clause AND Apache-2.0, approved, #11681
337330
maven/mavencentral/org.reflections/reflections/0.10.2, Apache-2.0 AND WTFPL, approved, clearlydefined

extensions/common/iam/verifiable-credentials/src/main/java/org/eclipse/edc/iam/verifiablecredentials/revocation/RevocationServiceRegistryImpl.java

+25
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@
2323

2424
import java.util.HashMap;
2525
import java.util.Map;
26+
import java.util.Objects;
27+
import java.util.stream.Collectors;
28+
import java.util.stream.Stream;
29+
30+
import static java.util.Optional.ofNullable;
2631

2732
public class RevocationServiceRegistryImpl implements RevocationServiceRegistry {
2833
private final Map<String, RevocationListService> entries = new HashMap<>();
@@ -46,6 +51,26 @@ public Result<Void> checkValidity(VerifiableCredential credential) {
4651
.orElse(Result.success());
4752
}
4853

54+
@Override
55+
public Result<String> getRevocationStatus(VerifiableCredential credential) {
56+
return credential.getCredentialStatus()
57+
.stream()
58+
.map(credentialStatus -> getRevocationStatusInternal(credentialStatus, credential))
59+
.reduce((r1, r2) -> {
60+
if (r1.succeeded() && r2.succeeded()) {
61+
return Result.success(Stream.of(r1.getContent(), r2.getContent()).filter(Objects::nonNull).collect(Collectors.joining(", ")));
62+
}
63+
return r1.merge(r2);
64+
})
65+
.orElse(Result.success(null));
66+
}
67+
68+
private Result<String> getRevocationStatusInternal(CredentialStatus credentialStatus, VerifiableCredential credential) {
69+
return ofNullable(entries.get(credentialStatus.type()))
70+
.map(service -> service.getStatusPurpose(credential))
71+
.orElse(Result.success(null));
72+
}
73+
4974
private Result<Void> checkRevocation(CredentialStatus credentialStatus) {
5075
var service = entries.get(credentialStatus.type());
5176
if (service == null) {

extensions/common/iam/verifiable-credentials/src/test/java/org/eclipse/edc/iam/verifiablecredentials/RevocationServiceRegistryImplTest.java

+72
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.eclipse.edc.iam.verifiablecredentials.spi.RevocationListService;
1919
import org.eclipse.edc.iam.verifiablecredentials.spi.TestFunctions;
2020
import org.eclipse.edc.iam.verifiablecredentials.spi.model.CredentialStatus;
21+
import org.eclipse.edc.iam.verifiablecredentials.spi.model.VerifiableCredential;
2122
import org.eclipse.edc.spi.result.Result;
2223
import org.junit.jupiter.api.Test;
2324

@@ -26,6 +27,9 @@
2627
import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat;
2728
import static org.mockito.ArgumentMatchers.any;
2829
import static org.mockito.Mockito.mock;
30+
import static org.mockito.Mockito.times;
31+
import static org.mockito.Mockito.verify;
32+
import static org.mockito.Mockito.verifyNoInteractions;
2933
import static org.mockito.Mockito.when;
3034

3135
class RevocationServiceRegistryImplTest {
@@ -92,4 +96,72 @@ void checkValidity_allInvalid_shouldReturnFailure() {
9296
assertThat(registry.checkValidity(cred)).isFailed()
9397
.detail().contains("test failure");
9498
}
99+
100+
@Test
101+
void getRevocationStatus() {
102+
var mockService = mock(RevocationListService.class);
103+
registry.addService("test-type", mockService);
104+
when(mockService.getStatusPurpose(any(VerifiableCredential.class))).thenReturn(Result.success(null));
105+
106+
var cred = TestFunctions.createCredentialBuilder().credentialStatus(new CredentialStatus("test-id", "test-type", Map.of())).build();
107+
assertThat(registry.getRevocationStatus(cred)).isSucceeded();
108+
}
109+
110+
@Test
111+
void getRevocationStatus_whenNoCredentialStatus() {
112+
var mockService = mock(RevocationListService.class);
113+
registry.addService("test-type", mockService);
114+
when(mockService.getStatusPurpose(any(VerifiableCredential.class))).thenReturn(Result.success(null));
115+
116+
var cred = TestFunctions.createCredentialBuilder().build();
117+
assertThat(registry.checkValidity(cred)).isSucceeded();
118+
verifyNoInteractions(mockService);
119+
}
120+
121+
@Test
122+
void getRevocationStatus_whenNoServiceFound_shouldReturnSuccess() {
123+
var mockService = mock(RevocationListService.class);
124+
registry.addService("test-type", mockService);
125+
126+
var cred = TestFunctions.createCredentialBuilder().build();
127+
assertThat(registry.checkValidity(cred)).isSucceeded();
128+
verifyNoInteractions(mockService);
129+
}
130+
131+
@Test
132+
void getRevocationStatus_oneRevoked_shouldReturnFailure() {
133+
var mockService = mock(RevocationListService.class);
134+
registry.addService("test-type", mockService);
135+
when(mockService.getStatusPurpose(any(VerifiableCredential.class)))
136+
.thenReturn(Result.success(null))
137+
.thenReturn(Result.success("revocation"));
138+
139+
var cred = TestFunctions.createCredentialBuilder()
140+
.credentialStatus(new CredentialStatus("test-id", "test-type", Map.of()))
141+
.credentialStatus(new CredentialStatus("test-id", "test-type", Map.of()))
142+
.build();
143+
144+
assertThat(registry.getRevocationStatus(cred)).isSucceeded()
145+
.isEqualTo("revocation");
146+
verify(mockService, times(2)).getStatusPurpose(any(VerifiableCredential.class));
147+
148+
}
149+
150+
@Test
151+
void getRevocationStatus_alInvalid_shouldReturnFailure() {
152+
var mockService = mock(RevocationListService.class);
153+
registry.addService("test-type", mockService);
154+
when(mockService.getStatusPurpose(any(VerifiableCredential.class)))
155+
.thenReturn(Result.success("suspension"))
156+
.thenReturn(Result.success("revocation"));
157+
158+
var cred = TestFunctions.createCredentialBuilder()
159+
.credentialStatus(new CredentialStatus("test-id", "test-type", Map.of()))
160+
.credentialStatus(new CredentialStatus("test-id", "test-type", Map.of()))
161+
.build();
162+
163+
assertThat(registry.getRevocationStatus(cred)).isSucceeded()
164+
.isEqualTo("suspension, revocation");
165+
verify(mockService, times(2)).getStatusPurpose(any(VerifiableCredential.class));
166+
}
95167
}

spi/common/verifiable-credentials-spi/src/main/java/org/eclipse/edc/iam/verifiablecredentials/spi/model/RevocationServiceRegistry.java

+10
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,14 @@ public interface RevocationServiceRegistry {
3939
* @return {@link Result#success()} if the VC does not contain a {@link CredentialStatus}, or if all {@link CredentialStatus} objects are valid (= not revoked, not suspended), {@link Result#failure(String)} otherwise.
4040
*/
4141
Result<Void> checkValidity(VerifiableCredential credential);
42+
43+
/**
44+
* Gets the revocation status of a {@link VerifiableCredential}. If no {@link RevocationListService} was registered for a particular
45+
* type, the implementation must return {@link Result#success()} with a {@code null} content. If the credential does not contain any credentialStatus,
46+
* the result is {@link Result#success()} as well.
47+
*
48+
* @param credential The VC
49+
* @return {@link Result#success()} if the VC does not contain a {@link CredentialStatus}, or if all {@link CredentialStatus} objects are valid (= not revoked, not suspended), {@link Result#failure(String)} otherwise.
50+
*/
51+
Result<String> getRevocationStatus(VerifiableCredential credential);
4252
}

0 commit comments

Comments
 (0)