diff --git a/docker-compose.yaml b/docker-compose.yaml index 31a083c..1e3c816 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -6,8 +6,8 @@ services: volumes: - .:/app working_dir: /app - # Run only unit tests (no real credentials). Full suite: mvn test with env set. - command: mvn test -Dtest=ProjectsClientTest,AwsAuthProviderTest + # Run only unit tests (no real credentials). Full suite: mvn test -q with env set. + command: mvn test -Dtest=RevokeTokenInputTest,AuthClientTest,AwsAuthProviderTest,ProjectsClientTest format: image: cimg/openjdk:21.0 diff --git a/pom.xml b/pom.xml index 024aa82..ac061ec 100644 --- a/pom.xml +++ b/pom.xml @@ -154,13 +154,13 @@ org.mockito mockito-core - 5.5.0 + 5.8.0 test org.mockito mockito-junit-jupiter - 5.5.0 + 5.8.0 test diff --git a/src/main/java/com/infisical/sdk/api/ApiClient.java b/src/main/java/com/infisical/sdk/api/ApiClient.java index e417240..ae9e5cb 100644 --- a/src/main/java/com/infisical/sdk/api/ApiClient.java +++ b/src/main/java/com/infisical/sdk/api/ApiClient.java @@ -5,6 +5,7 @@ import com.squareup.okhttp.*; import com.squareup.okhttp.Request; import java.io.IOException; +import java.util.Collections; import java.util.Map; public class ApiClient { @@ -95,6 +96,14 @@ public R post(String url, T requestBody, Class responseType) throws In } String responseJson = responseBody.string(); + if (responseJson == null || responseJson.trim().isEmpty()) { + return null; + } + + if (responseType == Void.class) { + return null; + } + return gson.fromJson(responseJson, responseType); } } catch (IOException e) { @@ -102,6 +111,11 @@ public R post(String url, T requestBody, Class responseType) throws In } } + /** POST with empty JSON body. Delegates to {@link #post(String, Object, Class)}. */ + public R post(String url, Class responseType) throws InfisicalException { + return post(url, Collections.emptyMap(), responseType); + } + public R get(String baseUrl, Map queryParams, Class responseType) throws InfisicalException { try { diff --git a/src/main/java/com/infisical/sdk/models/RevokeTokenInput.java b/src/main/java/com/infisical/sdk/models/RevokeTokenInput.java new file mode 100644 index 0000000..abbd135 --- /dev/null +++ b/src/main/java/com/infisical/sdk/models/RevokeTokenInput.java @@ -0,0 +1,20 @@ +package com.infisical.sdk.models; + +import com.google.gson.annotations.SerializedName; +import com.infisical.sdk.util.Helper; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class RevokeTokenInput { + @SerializedName("accessToken") + private String accessToken; + + public String validate() { + if (Helper.isNullOrEmpty(accessToken)) { + return "Access token is required"; + } + return null; + } +} diff --git a/src/main/java/com/infisical/sdk/resources/AuthClient.java b/src/main/java/com/infisical/sdk/resources/AuthClient.java index 38cac5d..b74ce92 100644 --- a/src/main/java/com/infisical/sdk/resources/AuthClient.java +++ b/src/main/java/com/infisical/sdk/resources/AuthClient.java @@ -5,6 +5,7 @@ import com.infisical.sdk.models.AwsAuthLoginInput; import com.infisical.sdk.models.LdapAuthLoginInput; import com.infisical.sdk.models.MachineIdentityCredential; +import com.infisical.sdk.models.RevokeTokenInput; import com.infisical.sdk.models.UniversalAuthLoginInput; import com.infisical.sdk.util.InfisicalException; import java.util.function.Consumer; @@ -58,4 +59,16 @@ public void AwsAuthLogin(AwsAuthLoginInput input) throws InfisicalException { public void SetAccessToken(String accessToken) { this.onAuthenticate.accept(accessToken); } + + public void RevokeToken(String accessToken) throws InfisicalException { + RevokeTokenInput input = RevokeTokenInput.builder().accessToken(accessToken).build(); + + String validationMsg = input.validate(); + if (validationMsg != null) { + throw new InfisicalException(validationMsg); + } + + String url = String.format("%s%s", this.apiClient.GetBaseUrl(), "/api/v1/auth/token/revoke"); + this.apiClient.post(url, input, Void.class); + } } diff --git a/src/test/java/com/infisical/sdk/models/RevokeTokenInputTest.java b/src/test/java/com/infisical/sdk/models/RevokeTokenInputTest.java new file mode 100644 index 0000000..cd467f7 --- /dev/null +++ b/src/test/java/com/infisical/sdk/models/RevokeTokenInputTest.java @@ -0,0 +1,33 @@ +package com.infisical.sdk.models; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.Test; + +public class RevokeTokenInputTest { + + @Test + public void validate_returnsNull_whenAccessTokenIsSet() { + RevokeTokenInput input = RevokeTokenInput.builder().accessToken("token-123").build(); + assertNull(input.validate()); + } + + @Test + public void validate_returnsMessage_whenAccessTokenIsNull() { + RevokeTokenInput input = RevokeTokenInput.builder().accessToken(null).build(); + assertNotNull(input.validate()); + } + + @Test + public void validate_returnsMessage_whenAccessTokenIsEmpty() { + RevokeTokenInput input = RevokeTokenInput.builder().accessToken("").build(); + assertNotNull(input.validate()); + } + + @Test + public void validate_returnsMessage_whenAccessTokenIsWhitespace() { + RevokeTokenInput input = RevokeTokenInput.builder().accessToken(" ").build(); + assertNotNull(input.validate()); + } +} diff --git a/src/test/java/com/infisical/sdk/resources/AuthClientTest.java b/src/test/java/com/infisical/sdk/resources/AuthClientTest.java new file mode 100644 index 0000000..8fcef82 --- /dev/null +++ b/src/test/java/com/infisical/sdk/resources/AuthClientTest.java @@ -0,0 +1,52 @@ +package com.infisical.sdk.resources; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.infisical.sdk.api.ApiClient; +import com.infisical.sdk.models.RevokeTokenInput; +import com.infisical.sdk.util.InfisicalException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class AuthClientTest { + + @Mock + private ApiClient apiClient; + + @Test + public void RevokeToken_throwsWhenAccessTokenIsNull() { + AuthClient authClient = new AuthClient(apiClient, token -> {}); + + InfisicalException ex = assertThrows(InfisicalException.class, () -> authClient.RevokeToken(null)); + assertEquals("Access token is required", ex.getMessage()); + } + + @Test + public void RevokeToken_throwsWhenAccessTokenIsEmpty() { + AuthClient authClient = new AuthClient(apiClient, token -> {}); + + InfisicalException ex = assertThrows(InfisicalException.class, () -> authClient.RevokeToken("")); + assertEquals("Access token is required", ex.getMessage()); + } + + @Test + public void RevokeToken_callsPostWithCorrectUrlAndBody() throws InfisicalException { + when(apiClient.GetBaseUrl()).thenReturn("http://localhost"); + AuthClient authClient = new AuthClient(apiClient, token -> {}); + + authClient.RevokeToken("token-123"); + + verify(apiClient).post( + eq("http://localhost/api/v1/auth/token/revoke"), + any(RevokeTokenInput.class), + eq(Void.class)); + } +}