From 31b5bd7b58cac1abccf1b0b457492158a6cc930b Mon Sep 17 00:00:00 2001 From: Victor Stanchev Date: Sat, 24 May 2025 17:51:59 +0300 Subject: [PATCH] feat: add API key deletion functionality for applications --- .../paths/admin_v1_apps_id_api-key.yaml | 23 +++++++++ .../ClientApplicationController.java | 10 ++-- .../api/service/ClientApplicationService.java | 12 ++++- .../service/ClientApplicationServiceTest.java | 48 ++++++++++++++++++- 4 files changed, 88 insertions(+), 5 deletions(-) diff --git a/docs/openapi/paths/admin_v1_apps_id_api-key.yaml b/docs/openapi/paths/admin_v1_apps_id_api-key.yaml index 54038c9..2b2fed3 100644 --- a/docs/openapi/paths/admin_v1_apps_id_api-key.yaml +++ b/docs/openapi/paths/admin_v1_apps_id_api-key.yaml @@ -24,3 +24,26 @@ get: description: Not Found security: - admin-api: [] + +delete: + tags: + - Applications + summary: Delete an application's API key + description: Deletes the API key for a specific application by its ID. + operationId: deleteApiKey + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + responses: + '204': + description: No Content + '401': + description: Unauthorized + '404': + description: Not Found + security: + - admin-api: [] diff --git a/src/main/java/com/helioauth/passkeys/api/controller/ClientApplicationController.java b/src/main/java/com/helioauth/passkeys/api/controller/ClientApplicationController.java index b764ff1..b85e8dc 100644 --- a/src/main/java/com/helioauth/passkeys/api/controller/ClientApplicationController.java +++ b/src/main/java/com/helioauth/passkeys/api/controller/ClientApplicationController.java @@ -22,8 +22,6 @@ import com.helioauth.passkeys.api.generated.models.ApplicationApiKey; import com.helioauth.passkeys.api.generated.models.EditApplicationRequest; import com.helioauth.passkeys.api.service.ClientApplicationService; - -import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.val; import org.springframework.http.ResponseEntity; @@ -31,6 +29,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import jakarta.validation.Valid; import java.net.URI; import java.util.List; import java.util.UUID; @@ -80,4 +79,9 @@ public ResponseEntity delete(@PathVariable UUID id) { return deleted ? ResponseEntity.noContent().build() : ResponseEntity.notFound().build(); } -} \ No newline at end of file + + public ResponseEntity deleteApiKey(@PathVariable UUID id) { + boolean deleted = clientApplicationService.deleteApiKey(id); + return deleted ? ResponseEntity.noContent().build() : ResponseEntity.notFound().build(); + } +} diff --git a/src/main/java/com/helioauth/passkeys/api/service/ClientApplicationService.java b/src/main/java/com/helioauth/passkeys/api/service/ClientApplicationService.java index fd9bc64..dfc94f4 100644 --- a/src/main/java/com/helioauth/passkeys/api/service/ClientApplicationService.java +++ b/src/main/java/com/helioauth/passkeys/api/service/ClientApplicationService.java @@ -16,7 +16,6 @@ package com.helioauth.passkeys.api.service; -import com.helioauth.passkeys.api.domain.ClientApplication; import com.helioauth.passkeys.api.domain.ClientApplicationRepository; import com.helioauth.passkeys.api.generated.models.AddApplicationRequest; import com.helioauth.passkeys.api.generated.models.Application; @@ -89,6 +88,17 @@ public boolean delete(UUID id) { return false; } + @Transactional + public boolean deleteApiKey(UUID id) { + return repository.findById(id) + .map(application -> { + application.setApiKey(null); + repository.save(application); + return true; + }) + .orElse(false); + } + private String generateApiKey() { byte[] buffer = new byte[16]; random.nextBytes(buffer); diff --git a/src/test/java/com/helioauth/passkeys/api/service/ClientApplicationServiceTest.java b/src/test/java/com/helioauth/passkeys/api/service/ClientApplicationServiceTest.java index 4b4b7f2..41924d5 100644 --- a/src/test/java/com/helioauth/passkeys/api/service/ClientApplicationServiceTest.java +++ b/src/test/java/com/helioauth/passkeys/api/service/ClientApplicationServiceTest.java @@ -40,8 +40,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -197,4 +200,47 @@ public void getClientApplicationApiKeyTest() { assertTrue(result.isPresent()); assertEquals(apiKey, result.get().getApiKey()); } -} \ No newline at end of file + + @Test + public void testDeleteApiKey_success() { + // Setup + UUID appId = UUID.randomUUID(); + ClientApplication application = ClientApplication.builder() + .id(appId) + .name("Test App") + .apiKey("someapikey") + .relyingPartyHostname("localhost") + .createdAt(Instant.now()) + .updatedAt(Instant.now()) + .build(); + + when(repository.findById(appId)).thenReturn(Optional.of(application)); + // Mock repository.save to return the application object after modifying it in the service + when(repository.save(any(ClientApplication.class))).thenReturn(application); + + // Execute + boolean result = service.deleteApiKey(appId); + + // Validate + assertTrue(result); + assertNull(application.getApiKey()); // Verify the API key was set to null + verify(repository, times(1)).findById(appId); + verify(repository, times(1)).save(application); // Verify save was called with the modified object + } + + @Test + public void testDeleteApiKey_notFound() { + // Setup + UUID appId = UUID.randomUUID(); + + when(repository.findById(appId)).thenReturn(Optional.empty()); + + // Execute + boolean result = service.deleteApiKey(appId); + + // Validate + assertFalse(result); + verify(repository, times(1)).findById(appId); + verify(repository, never()).save(any(ClientApplication.class)); // Verify save was not called + } +}