mirror of
https://github.com/keycloak/keycloak.git
synced 2026-05-26 13:50:48 +00:00
add revoke endpoint to issued credentials APIs
Closes #46207 Signed-off-by: Giuseppe Graziano <g.graziano94@gmail.com>
This commit is contained in:
committed by
Marek Posolda
parent
629e86afd2
commit
77b1d13578
+4
@@ -45,4 +45,8 @@ public interface UserVerifiableCredentialResource {
|
||||
@Path("issued-credentials")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
List<IssuedVerifiableCredentialRepresentation> getIssuedCredentials();
|
||||
|
||||
@DELETE
|
||||
@Path("issued-credentials/{id}")
|
||||
void revokeIssuedCredential(@PathParam("id") String credentialId);
|
||||
}
|
||||
|
||||
+5
@@ -886,6 +886,11 @@ public class UserCacheSession implements UserCache, OnCreateComponent, OnUpdateC
|
||||
return getDelegate().getIssuedVerifiableCredentialsStreamByUser(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeIssuedVerifiableCredential(String credentialId) {
|
||||
return getDelegate().removeIssuedVerifiableCredential(credentialId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNotBeforeForUser(RealmModel realm, UserModel user, int notBefore) {
|
||||
if (!isRegisteredForInvalidation(realm, user.getId())) {
|
||||
|
||||
@@ -1127,6 +1127,17 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore, JpaUs
|
||||
return closing(query.getResultStream()).map(this::toIssuedVcModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeIssuedVerifiableCredential(String credentialId) {
|
||||
IssuedVerifiableCredentialEntity entity = em.find(IssuedVerifiableCredentialEntity.class, credentialId, LockModeType.PESSIMISTIC_WRITE);
|
||||
if (entity == null) {
|
||||
return false;
|
||||
}
|
||||
em.remove(entity);
|
||||
em.flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Could override this to provide a custom behavior.
|
||||
protected void ensureEmailConstraint(List<UserEntity> users, RealmModel realm) {
|
||||
UserEntity user = users.get(0);
|
||||
|
||||
@@ -986,6 +986,15 @@ public class UserStorageManager extends AbstractStorageManager<UserStorageProvid
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeIssuedVerifiableCredential(String credentialId) {
|
||||
if (StorageId.isLocalStorage(credentialId)) {
|
||||
return localStorage().removeIssuedVerifiableCredential(credentialId);
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Issued verifiable credential operations not yet supported on federated users");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNotBeforeForUser(RealmModel realm, UserModel user, int notBefore) {
|
||||
if (StorageId.isLocalStorage(user.getId())) {
|
||||
|
||||
@@ -356,4 +356,12 @@ public interface UserProvider extends Provider,
|
||||
*/
|
||||
Stream<IssuedVerifiableCredentialModel> getIssuedVerifiableCredentialsStreamByUser(String userId);
|
||||
|
||||
/**
|
||||
* Remove an issued verifiable credential by its ID.
|
||||
*
|
||||
* @param credentialId the ID of the issued credential to remove
|
||||
* @return {@code true} if the credential was removed, {@code false} if it was not found
|
||||
*/
|
||||
boolean removeIssuedVerifiableCredential(String credentialId);
|
||||
|
||||
}
|
||||
|
||||
+23
@@ -207,6 +207,29 @@ public class UserVerifiableCredentialResource {
|
||||
.toList();
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("issued-credentials/{id}")
|
||||
@Tag(name = KeycloakOpenAPI.Admin.Tags.USERS)
|
||||
@Operation(summary = "Revoke an issued verifiable credential")
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "204", description = "No Content"),
|
||||
@APIResponse(responseCode = "403", description = "Forbidden"),
|
||||
@APIResponse(responseCode = "404", description = "Not Found")
|
||||
})
|
||||
public void revokeIssuedCredential(@PathParam("id") String credentialId) {
|
||||
auth.users().requireManage(user);
|
||||
checkOid4VCIEnabled();
|
||||
|
||||
boolean removed = session.users().removeIssuedVerifiableCredential(credentialId);
|
||||
if (!removed) {
|
||||
logger.warn(String.format("Issued verifiable credential with ID '%s' not found for user '%s' in realm '%s'.",
|
||||
credentialId, user.getUsername(), realm.getName()));
|
||||
throw new NotFoundException("Issued verifiable credential not found");
|
||||
}
|
||||
|
||||
adminEvent.operation(OperationType.DELETE).resourcePath(session.getContext().getUri()).success();
|
||||
}
|
||||
|
||||
private void checkOid4VCIEnabled() {
|
||||
if (!Profile.isFeatureEnabled(Profile.Feature.OID4VC_VCI)) {
|
||||
throw ErrorResponse.error("Feature " + Profile.Feature.OID4VC_VCI.getKey() + " not enabled", Response.Status.BAD_REQUEST);
|
||||
|
||||
+20
@@ -178,6 +178,26 @@ public class IssuedVerifiableCredentialTest extends AbstractUserTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DatabaseTest
|
||||
public void testRevokeIssuedCredential() {
|
||||
String userId = createUser();
|
||||
createIssuedVcViaModelLayer(userId, CREDENTIAL_TYPE_1, "wallet-123", "rev-001");
|
||||
|
||||
UserResource userResource = managedRealm.admin().users().get(userId);
|
||||
List<IssuedVerifiableCredentialRepresentation> issuedCreds = userResource.verifiableCredentials().getIssuedCredentials();
|
||||
assertThat(issuedCreds, hasSize(1));
|
||||
|
||||
String credentialId = issuedCreds.get(0).getId();
|
||||
|
||||
// Revoke the credential
|
||||
userResource.verifiableCredentials().revokeIssuedCredential(credentialId);
|
||||
|
||||
// Verify
|
||||
List<IssuedVerifiableCredentialRepresentation> afterRevoke = userResource.verifiableCredentials().getIssuedCredentials();
|
||||
assertThat(afterRevoke, empty());
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
|
||||
protected void createIssuedVcViaModelLayer(String userId, String credentialType,
|
||||
|
||||
Reference in New Issue
Block a user