From 235fd1a27b6af484d0de88bbd1739c3e28706754 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Wed, 12 Nov 2025 12:03:19 +0200 Subject: [PATCH] Add isExpired endpoint --- .../server/controller/ApiKeyController.java | 20 ++++++++ .../controller/ApiKeyControllerTest.java | 48 +++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/application/src/main/java/org/thingsboard/server/controller/ApiKeyController.java b/application/src/main/java/org/thingsboard/server/controller/ApiKeyController.java index d9df135811..8b20c0beed 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ApiKeyController.java +++ b/application/src/main/java/org/thingsboard/server/controller/ApiKeyController.java @@ -136,6 +136,26 @@ public class ApiKeyController extends BaseController { return apiKeyService.saveApiKey(apiKey.getTenantId(), apiKey); } + + @ApiOperation(value = "Check if API key is expired (isApiKeyExpired)", + notes = "Returns true if the API key is expired, false otherwise. " + + "Referencing a non-existing ApiKey Id will cause a 'Not Found' error." + + AVAILABLE_FOR_ANY_AUTHORIZED_USER) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN', 'CUSTOMER_USER')") + @GetMapping(value = "/apiKey/{id}/expired") + public boolean isApiKeyExpired( + @Parameter(description = API_KEY_ID_PARAM_DESCRIPTION, required = true) + @PathVariable UUID id) throws ThingsboardException { + ApiKeyId apiKeyId = new ApiKeyId(id); + ApiKey apiKey = checkApiKeyId(apiKeyId, Operation.READ); + + if (apiKey.getExpirationTime() > 0) { + return System.currentTimeMillis() > apiKey.getExpirationTime(); + } + + return false; + } + @ApiOperation(value = "Delete API key by ID (deleteApiKey)", notes = "Deletes the API key. Referencing non-existing ApiKey Id will cause an error." + AVAILABLE_FOR_ANY_AUTHORIZED_USER) @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN', 'CUSTOMER_USER')") diff --git a/application/src/test/java/org/thingsboard/server/controller/ApiKeyControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/ApiKeyControllerTest.java index 167839e7f3..0350b9acf4 100644 --- a/application/src/test/java/org/thingsboard/server/controller/ApiKeyControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/ApiKeyControllerTest.java @@ -119,6 +119,54 @@ public class ApiKeyControllerTest extends AbstractControllerTest { doDelete("/api/apiKey/" + savedApiKey.getId()).andExpect(status().isOk()); } + @Test + public void testIsApiKeyExpired() throws Exception { + doGet("/api/apiKey/" + UUID.randomUUID() + "/expired").andExpect(status().isNotFound()); + + ApiKeyInfo apiKeyInfo = constructApiKeyInfo("Test API key description", true); + doPost("/api/apiKey", apiKeyInfo, ApiKey.class); + + PageData pageData = doGetTypedWithPageLink("/api/apiKeys/" + tenantAdminUserId + "?", new TypeReference<>() {}, new PageLink(10, 0)); + Assert.assertEquals(1, pageData.getData().size()); + ApiKeyInfo savedApiKeyNoExpiration = pageData.getData().get(0); + + Boolean isExpiredNoExpiration = doGet("/api/apiKey/" + savedApiKeyNoExpiration.getId().getId() + "/expired", Boolean.class); + Assert.assertNotNull(isExpiredNoExpiration); + Assert.assertFalse(isExpiredNoExpiration); + + doDelete("/api/apiKey/" + savedApiKeyNoExpiration.getId()).andExpect(status().isOk()); + + ApiKeyInfo apiKeyInfoFutureExpiration = constructApiKeyInfo("Test API key future expiration", true); + long futureExpirationTime = System.currentTimeMillis() + 3600000; + apiKeyInfoFutureExpiration.setExpirationTime(futureExpirationTime); + doPost("/api/apiKey", apiKeyInfoFutureExpiration, ApiKey.class); + + PageData pageData2 = doGetTypedWithPageLink("/api/apiKeys/" + tenantAdminUserId + "?", new TypeReference<>() {}, new PageLink(10, 0)); + Assert.assertEquals(1, pageData2.getData().size()); + ApiKeyInfo savedApiKeyFuture = pageData2.getData().get(0); + + Boolean isExpiredFuture = doGet("/api/apiKey/" + savedApiKeyFuture.getId().getId() + "/expired", Boolean.class); + Assert.assertNotNull(isExpiredFuture); + Assert.assertFalse(isExpiredFuture); + + doDelete("/api/apiKey/" + savedApiKeyFuture.getId()).andExpect(status().isOk()); + + ApiKeyInfo apiKeyInfoPastExpiration = constructApiKeyInfo("Test API key past expiration", true); + long pastExpirationTime = System.currentTimeMillis() - 3600000; + apiKeyInfoPastExpiration.setExpirationTime(pastExpirationTime); + doPost("/api/apiKey", apiKeyInfoPastExpiration, ApiKey.class); + + PageData pageData3 = doGetTypedWithPageLink("/api/apiKeys/" + tenantAdminUserId + "?", new TypeReference<>() {}, new PageLink(10, 0)); + Assert.assertEquals(1, pageData3.getData().size()); + ApiKeyInfo savedApiKeyPast = pageData3.getData().get(0); + + Boolean isExpiredPast = doGet("/api/apiKey/" + savedApiKeyPast.getId().getId() + "/expired", Boolean.class); + Assert.assertNotNull(isExpiredPast); + Assert.assertTrue(isExpiredPast); + + doDelete("/api/apiKey/" + savedApiKeyPast.getId()).andExpect(status().isOk()); + } + @Test public void testDeleteApiKey() throws Exception { doDelete("/api/apiKey/" + UUID.randomUUID()).andExpect(status().isNotFound());