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")
|
@Path("issued-credentials")
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
List<IssuedVerifiableCredentialRepresentation> getIssuedCredentials();
|
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);
|
return getDelegate().getIssuedVerifiableCredentialsStreamByUser(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeIssuedVerifiableCredential(String credentialId) {
|
||||||
|
return getDelegate().removeIssuedVerifiableCredential(credentialId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setNotBeforeForUser(RealmModel realm, UserModel user, int notBefore) {
|
public void setNotBeforeForUser(RealmModel realm, UserModel user, int notBefore) {
|
||||||
if (!isRegisteredForInvalidation(realm, user.getId())) {
|
if (!isRegisteredForInvalidation(realm, user.getId())) {
|
||||||
|
|||||||
@@ -1127,6 +1127,17 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore, JpaUs
|
|||||||
return closing(query.getResultStream()).map(this::toIssuedVcModel);
|
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.
|
// Could override this to provide a custom behavior.
|
||||||
protected void ensureEmailConstraint(List<UserEntity> users, RealmModel realm) {
|
protected void ensureEmailConstraint(List<UserEntity> users, RealmModel realm) {
|
||||||
UserEntity user = users.get(0);
|
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
|
@Override
|
||||||
public void setNotBeforeForUser(RealmModel realm, UserModel user, int notBefore) {
|
public void setNotBeforeForUser(RealmModel realm, UserModel user, int notBefore) {
|
||||||
if (StorageId.isLocalStorage(user.getId())) {
|
if (StorageId.isLocalStorage(user.getId())) {
|
||||||
|
|||||||
@@ -356,4 +356,12 @@ public interface UserProvider extends Provider,
|
|||||||
*/
|
*/
|
||||||
Stream<IssuedVerifiableCredentialModel> getIssuedVerifiableCredentialsStreamByUser(String userId);
|
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();
|
.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() {
|
private void checkOid4VCIEnabled() {
|
||||||
if (!Profile.isFeatureEnabled(Profile.Feature.OID4VC_VCI)) {
|
if (!Profile.isFeatureEnabled(Profile.Feature.OID4VC_VCI)) {
|
||||||
throw ErrorResponse.error("Feature " + Profile.Feature.OID4VC_VCI.getKey() + " not enabled", Response.Status.BAD_REQUEST);
|
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
|
// Helper methods
|
||||||
|
|
||||||
protected void createIssuedVcViaModelLayer(String userId, String credentialType,
|
protected void createIssuedVcViaModelLayer(String userId, String credentialType,
|
||||||
|
|||||||
Reference in New Issue
Block a user