From 7c024a3b11bee515df5c097aab82bcf686535782 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Thu, 2 Oct 2025 11:41:27 -0700 Subject: [PATCH 01/32] Add aws auth login stuff --- pom.xml | 4 +- .../java/com/infisical/sdk/api/ApiClient.java | 340 +++++++++--------- .../sdk/models/AwsAuthLoginInput.java | 33 ++ .../infisical/sdk/resources/AuthClient.java | 44 ++- 4 files changed, 234 insertions(+), 187 deletions(-) create mode 100644 src/main/java/com/infisical/sdk/models/AwsAuthLoginInput.java diff --git a/pom.xml b/pom.xml index 655462e..75e28d0 100644 --- a/pom.xml +++ b/pom.xml @@ -70,7 +70,7 @@ org.projectlombok lombok - 1.18.30 + 1.18.42 provided @@ -171,7 +171,7 @@ org.projectlombok lombok - 1.18.30 + 1.18.42 diff --git a/src/main/java/com/infisical/sdk/api/ApiClient.java b/src/main/java/com/infisical/sdk/api/ApiClient.java index ae73e3e..504dec2 100644 --- a/src/main/java/com/infisical/sdk/api/ApiClient.java +++ b/src/main/java/com/infisical/sdk/api/ApiClient.java @@ -8,207 +8,209 @@ import java.util.Map; public class ApiClient { - private final OkHttpClient client; + private final OkHttpClient client; - private String accessToken; - private String baseUrl; + private String accessToken; + private String baseUrl; - private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); + private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); - public ApiClient(String baseUrl, String accessToken) { - this.client = new OkHttpClient(); - this.baseUrl = baseUrl; + public ApiClient(String baseUrl, String accessToken) { + this.client = new OkHttpClient(); + this.baseUrl = baseUrl; - if (accessToken != null) { - this.accessToken = accessToken; - } - - this.formatBaseUrl(); + if (accessToken != null) { + this.accessToken = accessToken; } - private void formatBaseUrl() { - // Remove trailing slash if present - if (this.baseUrl.endsWith("/")) { - this.baseUrl = this.baseUrl.substring(0, this.baseUrl.length() - 1); - } - - // Check if URL starts with protocol - if (!this.baseUrl.matches("^[a-zA-Z]+://.*")) { - this.baseUrl = "http://" + this.baseUrl; - } + this.formatBaseUrl(); + } - // Add /api if not present - if (this.baseUrl.endsWith("/api")) { - this.baseUrl = this.baseUrl.substring(0, this.baseUrl.length() - 4); - } + private void formatBaseUrl() { + // Remove trailing slash if present + if (this.baseUrl.endsWith("/")) { + this.baseUrl = this.baseUrl.substring(0, this.baseUrl.length() - 1); } - public String GetBaseUrl(){ - return this.baseUrl; + // Check if URL starts with protocol + if (!this.baseUrl.matches("^[a-zA-Z]+://.*")) { + this.baseUrl = "http://" + this.baseUrl; } - @SuppressWarnings(value = "lombok") - public OkHttpClient getClient() { - return this.client; + // Add /api if not present + if (this.baseUrl.endsWith("/api")) { + this.baseUrl = this.baseUrl.substring(0, this.baseUrl.length() - 4); } + } - private String formatErrorMessage(Response response) throws IOException { - String message = String.format("Unexpected response: %s", response); + public String GetBaseUrl() { + return this.baseUrl; + } - try (ResponseBody errorBody = response.body()) { - if (errorBody != null) { - String bodyString = errorBody.string(); - if (!bodyString.isEmpty()) { - message += String.format(" - %s", bodyString); - } - } - } + @SuppressWarnings(value = "lombok") + public OkHttpClient getClient() { + return this.client; + } - return message; - } + private String formatErrorMessage(Response response) throws IOException { + String message = String.format("Unexpected response: %s", response); - public R post(String url, T requestBody, Class responseType) throws InfisicalException { - try { - // convert request to json - var gson = new Gson(); - String jsonBody = gson.toJson(requestBody); - - // Build request - var requestBuilder = new Request.Builder() - .url(url) - .post(RequestBody.create(JSON, jsonBody)) - .header("Accept", "application/json"); - - - if (this.accessToken != null && !this.accessToken.isEmpty()) { - requestBuilder.addHeader("Authorization", "Bearer " + this.accessToken); - } - var request = requestBuilder.build(); - - Response response = client.newCall(request).execute(); - try (ResponseBody responseBody = response.body()) { - if (!response.isSuccessful()) { - throw new IOException(this.formatErrorMessage(response)); - } - - if (responseBody == null) { - throw new IOException("Response body is null"); - } - - String responseJson = responseBody.string(); - return gson.fromJson(responseJson, responseType); - } - } catch (IOException e) { - throw new InfisicalException(e); + try (ResponseBody errorBody = response.body()) { + if (errorBody != null) { + String bodyString = errorBody.string(); + if (!bodyString.isEmpty()) { + message += String.format(" - %s", bodyString); } + } } - public R get(String baseUrl, Map queryParams, Class responseType) throws InfisicalException { - try { - HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl).newBuilder(); + return message; + } + + public R post(String url, T requestBody, Class responseType) throws InfisicalException { + try { + // convert request to json + var gson = new Gson(); + String jsonBody = gson.toJson(requestBody); + + // Build request + var requestBuilder = + new Request.Builder() + .url(url) + .post(RequestBody.create(JSON, jsonBody)) + .header("Accept", "application/json"); + + if (this.accessToken != null && !this.accessToken.isEmpty()) { + requestBuilder.addHeader("Authorization", "Bearer " + this.accessToken); + } + var request = requestBuilder.build(); + + Response response = client.newCall(request).execute(); + try (ResponseBody responseBody = response.body()) { + if (!response.isSuccessful()) { + throw new IOException(this.formatErrorMessage(response)); + } + + if (responseBody == null) { + throw new IOException("Response body is null"); + } - if (queryParams != null) { - queryParams.forEach(urlBuilder::addQueryParameter); - } + String responseJson = responseBody.string(); + return gson.fromJson(responseJson, responseType); + } + } catch (IOException e) { + throw new InfisicalException(e); + } + } - var requestBuilder = new Request.Builder() - .url(urlBuilder.build()) - .get() - .header("Accept", "application/json"); + public R get(String baseUrl, Map queryParams, Class responseType) + throws InfisicalException { + try { + HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl).newBuilder(); - if (this.accessToken != null && !this.accessToken.isEmpty()) { - requestBuilder.addHeader("Authorization", "Bearer " + this.accessToken); - } + if (queryParams != null) { + queryParams.forEach(urlBuilder::addQueryParameter); + } - var request = requestBuilder.build(); + var requestBuilder = + new Request.Builder().url(urlBuilder.build()).get().header("Accept", "application/json"); + if (this.accessToken != null && !this.accessToken.isEmpty()) { + requestBuilder.addHeader("Authorization", "Bearer " + this.accessToken); + } - Response response = client.newCall(request).execute(); - try (ResponseBody responseBody = response.body()) { - if (!response.isSuccessful()) { - throw new IOException(this.formatErrorMessage(response)); - } + var request = requestBuilder.build(); - if (responseBody == null) { - throw new IOException("Response body is null"); - } + Response response = client.newCall(request).execute(); + try (ResponseBody responseBody = response.body()) { + if (!response.isSuccessful()) { + throw new IOException(this.formatErrorMessage(response)); + } - var gson = new Gson(); - String responseJson = responseBody.string(); - return gson.fromJson(responseJson, responseType); - } - } catch (IOException e) { - throw new InfisicalException(e); + if (responseBody == null) { + throw new IOException("Response body is null"); } + + var gson = new Gson(); + String responseJson = responseBody.string(); + return gson.fromJson(responseJson, responseType); + } + } catch (IOException e) { + throw new InfisicalException(e); } + } + + public R patch(String url, T requestBody, Class responseType) + throws InfisicalException { + try { + // convert request to json + var gson = new Gson(); + String jsonBody = gson.toJson(requestBody); + + // Build request + var requestBuilder = + new Request.Builder() + .url(url) + .patch(RequestBody.create(JSON, jsonBody)) + .header("Accept", "application/json"); + + if (this.accessToken != null && !this.accessToken.isEmpty()) { + requestBuilder.addHeader("Authorization", "Bearer " + this.accessToken); + } + var request = requestBuilder.build(); + + Response response = client.newCall(request).execute(); + try (ResponseBody responseBody = response.body()) { + if (!response.isSuccessful()) { + throw new IOException(this.formatErrorMessage(response)); + } - public R patch(String url, T requestBody, Class responseType) throws InfisicalException { - try { - // convert request to json - var gson = new Gson(); - String jsonBody = gson.toJson(requestBody); - - // Build request - var requestBuilder = new Request.Builder() - .url(url) - .patch(RequestBody.create(JSON, jsonBody)) - .header("Accept", "application/json"); - - if (this.accessToken != null && !this.accessToken.isEmpty()) { - requestBuilder.addHeader("Authorization", "Bearer " + this.accessToken); - } - var request = requestBuilder.build(); - - Response response = client.newCall(request).execute(); - try (ResponseBody responseBody = response.body()) { - if (!response.isSuccessful()) { - throw new IOException(this.formatErrorMessage(response)); - } - - if (responseBody == null) { - throw new IOException("Response body is null"); - } - - String responseJson = responseBody.string(); - return gson.fromJson(responseJson, responseType); - } - } catch (IOException e) { - throw new InfisicalException(e); + if (responseBody == null) { + throw new IOException("Response body is null"); } + + String responseJson = responseBody.string(); + return gson.fromJson(responseJson, responseType); + } + } catch (IOException e) { + throw new InfisicalException(e); } + } + + public R delete(String url, T requestBody, Class responseType) + throws InfisicalException { + try { + // convert request to json + var gson = new Gson(); + String jsonBody = gson.toJson(requestBody); + + // Build request + var requestBuilder = + new Request.Builder() + .url(url) + .delete(RequestBody.create(JSON, jsonBody)) + .header("Accept", "application/json"); + + if (this.accessToken != null && !this.accessToken.isEmpty()) { + requestBuilder.addHeader("Authorization", "Bearer " + this.accessToken); + } + var request = requestBuilder.build(); + + Response response = client.newCall(request).execute(); + try (ResponseBody responseBody = response.body()) { + if (!response.isSuccessful()) { + throw new IOException(this.formatErrorMessage(response)); + } - public R delete(String url, T requestBody, Class responseType) throws InfisicalException { - try { - // convert request to json - var gson = new Gson(); - String jsonBody = gson.toJson(requestBody); - - // Build request - var requestBuilder = new Request.Builder() - .url(url) - .delete(RequestBody.create(JSON, jsonBody)) - .header("Accept", "application/json"); - - if (this.accessToken != null && !this.accessToken.isEmpty()) { - requestBuilder.addHeader("Authorization", "Bearer " + this.accessToken); - } - var request = requestBuilder.build(); - - Response response = client.newCall(request).execute(); - try (ResponseBody responseBody = response.body()) { - if (!response.isSuccessful()) { - throw new IOException(this.formatErrorMessage(response)); - } - - if (responseBody == null) { - throw new IOException("Response body is null"); - } - - String responseJson = responseBody.string(); - return gson.fromJson(responseJson, responseType); - } - } catch (IOException e) { - throw new InfisicalException(e); + if (responseBody == null) { + throw new IOException("Response body is null"); } + + String responseJson = responseBody.string(); + return gson.fromJson(responseJson, responseType); + } + } catch (IOException e) { + throw new InfisicalException(e); } -} \ No newline at end of file + } +} diff --git a/src/main/java/com/infisical/sdk/models/AwsAuthLoginInput.java b/src/main/java/com/infisical/sdk/models/AwsAuthLoginInput.java new file mode 100644 index 0000000..90d033f --- /dev/null +++ b/src/main/java/com/infisical/sdk/models/AwsAuthLoginInput.java @@ -0,0 +1,33 @@ +package com.infisical.sdk.models; + +import com.infisical.sdk.util.Helper; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class AwsAuthLoginInput { + private final String identityId; + private final String iamRequestBody; + private final String iamHttpRequestMethod; + private final String iamRequestHeaders; + + public String validate() { + if (Helper.isNullOrEmpty(identityId)) { + return "Identity ID is required"; + } + + if (Helper.isNullOrEmpty(iamRequestBody)) { + return "IamRequestBody is required"; + } + + if (Helper.isNullOrEmpty(iamHttpRequestMethod)) { + return "IamHttpRequestMethod is required"; + } + + if (Helper.isNullOrEmpty(iamRequestHeaders)) { + return "IamRequestHeaders 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 73851e2..f0ece5d 100644 --- a/src/main/java/com/infisical/sdk/resources/AuthClient.java +++ b/src/main/java/com/infisical/sdk/resources/AuthClient.java @@ -1,5 +1,6 @@ package com.infisical.sdk.resources; +import com.infisical.sdk.models.AwsAuthLoginInput; import com.infisical.sdk.models.LdapAuthLoginInput; import com.infisical.sdk.models.MachineIdentityCredential; import com.infisical.sdk.util.InfisicalException; @@ -18,29 +19,40 @@ public AuthClient(ApiClient apiClient, Consumer onAuthenticate) { } public void UniversalAuthLogin(String clientId, String clientSecret) throws InfisicalException { - var params = UniversalAuthLoginInput.builder() - .clientId(clientId) - .clientSecret(clientSecret) - .build(); - - var url = String.format("%s%s", this.apiClient.GetBaseUrl(), "/api/v1/auth/universal-auth/login"); - var credential = this.apiClient.post(url, params, MachineIdentityCredential.class); - this.onAuthenticate.accept(credential.getAccessToken()); + var params = + UniversalAuthLoginInput.builder().clientId(clientId).clientSecret(clientSecret).build(); + + var url = + String.format("%s%s", this.apiClient.GetBaseUrl(), "/api/v1/auth/universal-auth/login"); + var credential = this.apiClient.post(url, params, MachineIdentityCredential.class); + this.onAuthenticate.accept(credential.getAccessToken()); } public void LdapAuthLogin(LdapAuthLoginInput input) throws InfisicalException { - var validationMsg = input.validate(); + var validationMsg = input.validate(); + + if (validationMsg != null) { + throw new InfisicalException(validationMsg); + } + + var url = String.format("%s%s", this.apiClient.GetBaseUrl(), "/api/v1/auth/ldap-auth/login"); + var credential = this.apiClient.post(url, input, MachineIdentityCredential.class); + this.onAuthenticate.accept(credential.getAccessToken()); + } + + public void AwsAuthLogin(AwsAuthLoginInput input) throws InfisicalException { + var validationMsg = input.validate(); - if (validationMsg != null) { - throw new InfisicalException(validationMsg); - } + if (validationMsg != null) { + throw new InfisicalException(validationMsg); + } - var url = String.format("%s%s", this.apiClient.GetBaseUrl(), "/api/v1/auth/ldap-auth/login"); - var credential = this.apiClient.post(url, input, MachineIdentityCredential.class); - this.onAuthenticate.accept(credential.getAccessToken()); + var url = String.format("%s%s", this.apiClient.GetBaseUrl(), "/api/v1/auth/aws-auth/login"); + var credential = this.apiClient.post(url, input, MachineIdentityCredential.class); + this.onAuthenticate.accept(credential.getAccessToken()); } public void SetAccessToken(String accessToken) { this.onAuthenticate.accept(accessToken); } -} \ No newline at end of file +} From e7707be19b0134e5af4d4280c34b6d3f32d86b84 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Thu, 2 Oct 2025 15:26:41 -0700 Subject: [PATCH 02/32] Implement aws auth provider --- .../infisical/sdk/auth/AwsAuthProvider.java | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java diff --git a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java new file mode 100644 index 0000000..90b9697 --- /dev/null +++ b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java @@ -0,0 +1,51 @@ +package com.infisical.sdk.auth; + +import com.amazonaws.DefaultRequest; +import com.amazonaws.Request; +import com.amazonaws.auth.AWS4Signer; +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.http.HttpMethodName; +import com.amazonaws.util.SdkHttpUtils; +import com.infisical.sdk.models.AwsAuthLoginInput; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; +import java.util.Map; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class AwsAuthProvider { + @Builder.Default private final String serviceName = "sts"; + @Builder.Default private final HttpMethodName methodName = HttpMethodName.POST; + @Builder.Default private final String endpointTemplate = "https://sts.%s.amazonaws.com"; + + @Builder.Default + private final Map> params = + Map.of("Action", List.of("GetCallerIdentity"), "Version", List.of("2011-06-15")); + + public AwsAuthLoginInput fromCredentials(String region, AWSCredentials credentials) + throws URISyntaxException { + + final Request request = new DefaultRequest<>(serviceName); + final String iamRequestURL = endpointTemplate.formatted(region); + request.setHttpMethod(methodName); + request.setEndpoint(new URI(iamRequestURL)); + request.setParameters(params); + final String iamRequestBody = SdkHttpUtils.encodeParameters(request); + + final AWS4Signer signer = new AWS4Signer(); + signer.setServiceName(serviceName); + signer.setRegionName(region); + signer.sign(request, credentials); + + System.out.println("Signed Request Headers:"); + request.getHeaders().forEach((key, value) -> System.out.println(key + ": " + value)); + return AwsAuthLoginInput.builder() + .iamHttpRequestMethod(methodName.name()) + .iamRequestHeaders("FIXME") + .iamRequestBody(iamRequestBody) + .build(); + } +} From 126656a964902c8b485d11f6bd0fb8cbbb568c5d Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Thu, 2 Oct 2025 15:56:23 -0700 Subject: [PATCH 03/32] Add tests --- .../sdk/auth/AwsAuthProviderTest.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java diff --git a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java new file mode 100644 index 0000000..a68a897 --- /dev/null +++ b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java @@ -0,0 +1,36 @@ +package com.infisical.sdk.auth; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.amazonaws.auth.BasicAWSCredentials; +import com.infisical.sdk.models.AwsAuthLoginInput; +import java.net.URISyntaxException; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; + +class AwsAuthProviderTest { + + @Test + void testFromCredentials() throws URISyntaxException { + final AwsAuthProvider provider = AwsAuthProvider.builder().build(); + final AwsAuthLoginInput loginInput = + provider.fromCredentials( + "us-west-2", new BasicAWSCredentials("MOCK_ACCESS_KEY", "MOCK_SECRET_KEY")); + assertEquals("POST", loginInput.getIamHttpRequestMethod()); + assertEquals( + provider.getParams(), + Arrays.stream(loginInput.getIamRequestBody().split("&")) + .map( + item -> { + final String[] parts = URLDecoder.decode(item, StandardCharsets.UTF_8).split("="); + return Map.entry(parts[0], List.of(parts[1])); + }) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); + } +} From 762e398feb53720135734e587d4faf8ecbf6ec1e Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Thu, 2 Oct 2025 16:09:36 -0700 Subject: [PATCH 04/32] Add more test cases```` --- .../infisical/sdk/auth/AwsAuthProvider.java | 29 ++++++++++++++----- .../sdk/auth/AwsAuthProviderTest.java | 8 +++++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java index 90b9697..474864a 100644 --- a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java +++ b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java @@ -6,6 +6,8 @@ import com.amazonaws.auth.AWSCredentials; import com.amazonaws.http.HttpMethodName; import com.amazonaws.util.SdkHttpUtils; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.infisical.sdk.models.AwsAuthLoginInput; import java.net.URI; import java.net.URISyntaxException; @@ -17,13 +19,17 @@ @Data @Builder public class AwsAuthProvider { + private static final ObjectMapper objectMapper = new ObjectMapper(); + @Builder.Default private final String serviceName = "sts"; @Builder.Default private final HttpMethodName methodName = HttpMethodName.POST; @Builder.Default private final String endpointTemplate = "https://sts.%s.amazonaws.com"; @Builder.Default private final Map> params = - Map.of("Action", List.of("GetCallerIdentity"), "Version", List.of("2011-06-15")); + Map.ofEntries( + Map.entry("Action", List.of("GetCallerIdentity")), + Map.entry("Version", List.of("2011-06-15"))); public AwsAuthLoginInput fromCredentials(String region, AWSCredentials credentials) throws URISyntaxException { @@ -40,12 +46,19 @@ public AwsAuthLoginInput fromCredentials(String region, AWSCredentials credentia signer.setRegionName(region); signer.sign(request, credentials); - System.out.println("Signed Request Headers:"); - request.getHeaders().forEach((key, value) -> System.out.println(key + ": " + value)); - return AwsAuthLoginInput.builder() - .iamHttpRequestMethod(methodName.name()) - .iamRequestHeaders("FIXME") - .iamRequestBody(iamRequestBody) - .build(); + final Map requestHeaders = request.getHeaders(); + // TODO: add content-type and content length? + // ref: + // https://github.com/Infisical/go-sdk/blob/3f1b7f831a883ea30a9182f86bd0173f4cf82a22/auth.go#L293-L294 + + try { + return AwsAuthLoginInput.builder() + .iamHttpRequestMethod(methodName.name()) + .iamRequestHeaders(objectMapper.writeValueAsString(requestHeaders)) + .iamRequestBody(iamRequestBody) + .build(); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } } } diff --git a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java index a68a897..90cdbae 100644 --- a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java +++ b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java @@ -32,5 +32,13 @@ void testFromCredentials() throws URISyntaxException { return Map.entry(parts[0], List.of(parts[1])); }) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); + assertEquals( + Map.ofEntries( + Map.entry("Host", "sts.us-west-2.amazonaws.com"), + Map.entry("X-Amz-Date", "20251002T230735Z"), + Map.entry( + "Authorization", + "AWS4-HMAC-SHA256 Credential=MOCK_ACCESS_KEY/20251002/us-west-2/sts/aws4_request, SignedHeaders=host;x-amz-date, Signature=09bdfc811945a6696912eb468b046029a67c3418b58588c32d88a7989dc53ed3")), + loginInput.getIamRequestHeaders()); } } From 5b63505647850330455255ff0307448edcc3cb06 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Thu, 2 Oct 2025 16:27:43 -0700 Subject: [PATCH 05/32] Fix broken tests --- .../com/infisical/sdk/auth/AwsAuthProvider.java | 6 ++++++ .../infisical/sdk/auth/AwsAuthProviderTest.java | 16 +++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java index 474864a..0652099 100644 --- a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java +++ b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java @@ -11,6 +11,7 @@ import com.infisical.sdk.models.AwsAuthLoginInput; import java.net.URI; import java.net.URISyntaxException; +import java.util.Date; import java.util.List; import java.util.Map; import lombok.Builder; @@ -31,6 +32,8 @@ public class AwsAuthProvider { Map.entry("Action", List.of("GetCallerIdentity")), Map.entry("Version", List.of("2011-06-15"))); + private final Date overrideDate; + public AwsAuthLoginInput fromCredentials(String region, AWSCredentials credentials) throws URISyntaxException { @@ -44,6 +47,9 @@ public AwsAuthLoginInput fromCredentials(String region, AWSCredentials credentia final AWS4Signer signer = new AWS4Signer(); signer.setServiceName(serviceName); signer.setRegionName(region); + if (overrideDate != null) { + signer.setOverrideDate(overrideDate); + } signer.sign(request, credentials); final Map requestHeaders = request.getHeaders(); diff --git a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java index 90cdbae..f585d81 100644 --- a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java +++ b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java @@ -3,11 +3,15 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.auth.SdkClock; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.infisical.sdk.models.AwsAuthLoginInput; import java.net.URISyntaxException; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.Date; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -15,10 +19,12 @@ import org.junit.jupiter.api.Test; class AwsAuthProviderTest { + private static final ObjectMapper objectMapper = new ObjectMapper(); @Test - void testFromCredentials() throws URISyntaxException { - final AwsAuthProvider provider = AwsAuthProvider.builder().build(); + void testFromCredentials() throws URISyntaxException, JsonProcessingException { + final AwsAuthProvider provider = + AwsAuthProvider.builder().overrideDate(new Date(1759446719)).build(); final AwsAuthLoginInput loginInput = provider.fromCredentials( "us-west-2", new BasicAWSCredentials("MOCK_ACCESS_KEY", "MOCK_SECRET_KEY")); @@ -35,10 +41,10 @@ void testFromCredentials() throws URISyntaxException { assertEquals( Map.ofEntries( Map.entry("Host", "sts.us-west-2.amazonaws.com"), - Map.entry("X-Amz-Date", "20251002T230735Z"), + Map.entry("X-Amz-Date", "19700121T084406Z"), Map.entry( "Authorization", - "AWS4-HMAC-SHA256 Credential=MOCK_ACCESS_KEY/20251002/us-west-2/sts/aws4_request, SignedHeaders=host;x-amz-date, Signature=09bdfc811945a6696912eb468b046029a67c3418b58588c32d88a7989dc53ed3")), - loginInput.getIamRequestHeaders()); + "AWS4-HMAC-SHA256 Credential=MOCK_ACCESS_KEY/19700121/us-west-2/sts/aws4_request, SignedHeaders=host;x-amz-date, Signature=f5736d3f614856326236dc15ebcfa47b4b615bc19a9da8fc24b2b767e9c50efa")), + objectMapper.readValue(loginInput.getIamRequestHeaders(), Map.class)); } } From 54cf472dfac8315079c1170a51f2c37ec0a4eb8a Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Thu, 2 Oct 2025 16:28:04 -0700 Subject: [PATCH 06/32] imports --- src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java index f585d81..b141aa7 100644 --- a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java +++ b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java @@ -3,7 +3,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import com.amazonaws.auth.BasicAWSCredentials; -import com.amazonaws.auth.SdkClock; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.infisical.sdk.models.AwsAuthLoginInput; @@ -15,7 +14,6 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; - import org.junit.jupiter.api.Test; class AwsAuthProviderTest { From 359a36f5b4647074057d6a8857ac79b57e6ded87 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Thu, 2 Oct 2025 16:53:26 -0700 Subject: [PATCH 07/32] Make base64 values instead --- .../infisical/sdk/auth/AwsAuthProvider.java | 12 +++++++++-- .../sdk/auth/AwsAuthProviderTest.java | 20 ++++++++++++++----- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java index 0652099..0a9aaab 100644 --- a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java +++ b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java @@ -11,6 +11,8 @@ import com.infisical.sdk.models.AwsAuthLoginInput; import java.net.URI; import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.util.Base64; import java.util.Date; import java.util.List; import java.util.Map; @@ -60,8 +62,14 @@ public AwsAuthLoginInput fromCredentials(String region, AWSCredentials credentia try { return AwsAuthLoginInput.builder() .iamHttpRequestMethod(methodName.name()) - .iamRequestHeaders(objectMapper.writeValueAsString(requestHeaders)) - .iamRequestBody(iamRequestBody) + .iamRequestHeaders( + Base64.getEncoder() + .encodeToString( + objectMapper + .writeValueAsString(requestHeaders) + .getBytes(StandardCharsets.UTF_8))) + .iamRequestBody( + Base64.getEncoder().encodeToString(iamRequestBody.getBytes(StandardCharsets.UTF_8))) .build(); } catch (JsonProcessingException e) { throw new RuntimeException(e); diff --git a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java index b141aa7..90e3c90 100644 --- a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java +++ b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java @@ -10,6 +10,7 @@ import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.Base64; import java.util.Date; import java.util.List; import java.util.Map; @@ -27,15 +28,24 @@ void testFromCredentials() throws URISyntaxException, JsonProcessingException { provider.fromCredentials( "us-west-2", new BasicAWSCredentials("MOCK_ACCESS_KEY", "MOCK_SECRET_KEY")); assertEquals("POST", loginInput.getIamHttpRequestMethod()); - assertEquals( - provider.getParams(), - Arrays.stream(loginInput.getIamRequestBody().split("&")) + + final String decodedBody = + new String( + Base64.getDecoder().decode(loginInput.getIamRequestBody()), StandardCharsets.UTF_8); + final Map> bodyParams = + Arrays.stream(decodedBody.split("&")) .map( item -> { final String[] parts = URLDecoder.decode(item, StandardCharsets.UTF_8).split("="); return Map.entry(parts[0], List.of(parts[1])); }) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + assertEquals(provider.getParams(), bodyParams); + + final String decodedHeaders = + new String( + Base64.getDecoder().decode(loginInput.getIamRequestHeaders()), StandardCharsets.UTF_8); + final Map actualHeaders = objectMapper.readValue(decodedHeaders, Map.class); assertEquals( Map.ofEntries( Map.entry("Host", "sts.us-west-2.amazonaws.com"), @@ -43,6 +53,6 @@ void testFromCredentials() throws URISyntaxException, JsonProcessingException { Map.entry( "Authorization", "AWS4-HMAC-SHA256 Credential=MOCK_ACCESS_KEY/19700121/us-west-2/sts/aws4_request, SignedHeaders=host;x-amz-date, Signature=f5736d3f614856326236dc15ebcfa47b4b615bc19a9da8fc24b2b767e9c50efa")), - objectMapper.readValue(loginInput.getIamRequestHeaders(), Map.class)); + actualHeaders); } } From cb1c340e051b8ea97a58924527d49993237c94e0 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Thu, 2 Oct 2025 18:11:20 -0700 Subject: [PATCH 08/32] Use v2 aws sdk instead --- .../infisical/sdk/auth/AwsAuthProvider.java | 86 ++++++++++++------- 1 file changed, 55 insertions(+), 31 deletions(-) diff --git a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java index 0a9aaab..c016b3b 100644 --- a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java +++ b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java @@ -1,34 +1,40 @@ package com.infisical.sdk.auth; -import com.amazonaws.DefaultRequest; -import com.amazonaws.Request; -import com.amazonaws.auth.AWS4Signer; -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.http.HttpMethodName; -import com.amazonaws.util.SdkHttpUtils; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.infisical.sdk.models.AwsAuthLoginInput; import java.net.URI; -import java.net.URISyntaxException; +import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.Date; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import lombok.Builder; import lombok.Data; +import lombok.NonNull; +import software.amazon.awssdk.auth.credentials.AwsCredentials; +import software.amazon.awssdk.http.ContentStreamProvider; +import software.amazon.awssdk.http.SdkHttpFullRequest; +import software.amazon.awssdk.http.SdkHttpMethod; +import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.http.auth.aws.signer.AwsV4FamilyHttpSigner; +import software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner; @Data @Builder public class AwsAuthProvider { private static final ObjectMapper objectMapper = new ObjectMapper(); - @Builder.Default private final String serviceName = "sts"; - @Builder.Default private final HttpMethodName methodName = HttpMethodName.POST; - @Builder.Default private final String endpointTemplate = "https://sts.%s.amazonaws.com"; + @NonNull @Builder.Default private final String serviceName = "sts"; + @NonNull @Builder.Default private final SdkHttpMethod httpMethod = SdkHttpMethod.POST; + @NonNull @Builder.Default private final String endpointTemplate = "https://sts.%s.amazonaws.com"; - @Builder.Default + @NonNull @Builder.Default + private final String contentType = "application/x-www-form-urlencoded; charset=utf-8"; + + @NonNull @Builder.Default private final Map> params = Map.ofEntries( Map.entry("Action", List.of("GetCallerIdentity")), @@ -36,32 +42,38 @@ public class AwsAuthProvider { private final Date overrideDate; - public AwsAuthLoginInput fromCredentials(String region, AWSCredentials credentials) - throws URISyntaxException { + public AwsAuthLoginInput fromCredentials(String region, AwsCredentials credentials) { - final Request request = new DefaultRequest<>(serviceName); - final String iamRequestURL = endpointTemplate.formatted(region); - request.setHttpMethod(methodName); - request.setEndpoint(new URI(iamRequestURL)); - request.setParameters(params); - final String iamRequestBody = SdkHttpUtils.encodeParameters(request); + final AwsV4HttpSigner signer = AwsV4HttpSigner.create(); - final AWS4Signer signer = new AWS4Signer(); - signer.setServiceName(serviceName); - signer.setRegionName(region); - if (overrideDate != null) { - signer.setOverrideDate(overrideDate); - } - signer.sign(request, credentials); + final String iamRequestURL = endpointTemplate.formatted(region); + final String iamRequestBody = encodeParameters(params); + final SdkHttpFullRequest request = + SdkHttpFullRequest.builder() + .uri(URI.create(iamRequestURL)) + .method(httpMethod) + .appendHeader("Content-Type", contentType) + .appendHeader("Content-Length", String.valueOf(iamRequestBody.length())) + .build(); - final Map requestHeaders = request.getHeaders(); - // TODO: add content-type and content length? - // ref: - // https://github.com/Infisical/go-sdk/blob/3f1b7f831a883ea30a9182f86bd0173f4cf82a22/auth.go#L293-L294 + final SdkHttpRequest signedRequest = + signer + .sign( + signingRequest -> + signingRequest + .request(request) + .identity(credentials) + .payload( + ContentStreamProvider.fromByteArray( + iamRequestBody.getBytes(StandardCharsets.UTF_8))) + .putProperty(AwsV4FamilyHttpSigner.SERVICE_SIGNING_NAME, serviceName) + .putProperty(AwsV4HttpSigner.REGION_NAME, region)) + .request(); + final Map> requestHeaders = signedRequest.headers(); try { return AwsAuthLoginInput.builder() - .iamHttpRequestMethod(methodName.name()) + .iamHttpRequestMethod(httpMethod.name()) .iamRequestHeaders( Base64.getEncoder() .encodeToString( @@ -75,4 +87,16 @@ public AwsAuthLoginInput fromCredentials(String region, AWSCredentials credentia throw new RuntimeException(e); } } + + public static String encodeParameters(Map> params) { + return params.entrySet().stream() + .flatMap(entry -> entry.getValue().stream().map(item -> Map.entry(entry.getKey(), item))) + .map( + entry -> + String.format( + "%s=%s", + URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8), + URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8))) + .collect(Collectors.joining("&")); + } } From 3a08a5807f075f67c76ea485358114cefcb83ba1 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Thu, 2 Oct 2025 18:14:12 -0700 Subject: [PATCH 09/32] Clean up a bit --- .../infisical/sdk/auth/AwsAuthProvider.java | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java index c016b3b..8047dba 100644 --- a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java +++ b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java @@ -43,9 +43,7 @@ public class AwsAuthProvider { private final Date overrideDate; public AwsAuthLoginInput fromCredentials(String region, AwsCredentials credentials) { - final AwsV4HttpSigner signer = AwsV4HttpSigner.create(); - final String iamRequestURL = endpointTemplate.formatted(region); final String iamRequestBody = encodeParameters(params); final SdkHttpFullRequest request = @@ -55,7 +53,6 @@ public AwsAuthLoginInput fromCredentials(String region, AwsCredentials credentia .appendHeader("Content-Type", contentType) .appendHeader("Content-Length", String.valueOf(iamRequestBody.length())) .build(); - final SdkHttpRequest signedRequest = signer .sign( @@ -69,23 +66,23 @@ public AwsAuthLoginInput fromCredentials(String region, AwsCredentials credentia .putProperty(AwsV4FamilyHttpSigner.SERVICE_SIGNING_NAME, serviceName) .putProperty(AwsV4HttpSigner.REGION_NAME, region)) .request(); - final Map> requestHeaders = signedRequest.headers(); + final String encodedHeader; try { - return AwsAuthLoginInput.builder() - .iamHttpRequestMethod(httpMethod.name()) - .iamRequestHeaders( - Base64.getEncoder() - .encodeToString( - objectMapper - .writeValueAsString(requestHeaders) - .getBytes(StandardCharsets.UTF_8))) - .iamRequestBody( - Base64.getEncoder().encodeToString(iamRequestBody.getBytes(StandardCharsets.UTF_8))) - .build(); + encodedHeader = + Base64.getEncoder() + .encodeToString( + objectMapper.writeValueAsString(requestHeaders).getBytes(StandardCharsets.UTF_8)); } catch (JsonProcessingException e) { throw new RuntimeException(e); } + final String encodedBody = + Base64.getEncoder().encodeToString(iamRequestBody.getBytes(StandardCharsets.UTF_8)); + return AwsAuthLoginInput.builder() + .iamHttpRequestMethod(httpMethod.name()) + .iamRequestHeaders(encodedHeader) + .iamRequestBody(encodedBody) + .build(); } public static String encodeParameters(Map> params) { From 5ba8249f99ae281fa09317c592440d113c0ebf26 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Thu, 2 Oct 2025 18:21:35 -0700 Subject: [PATCH 10/32] Flatten header --- pom.xml | 22 ++++++++++++++++++- .../infisical/sdk/auth/AwsAuthProvider.java | 5 ++++- .../sdk/auth/AwsAuthProviderTest.java | 2 +- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 75e28d0..1204096 100644 --- a/pom.xml +++ b/pom.xml @@ -55,7 +55,11 @@ 1.8.3 - + + com.fasterxml.jackson.core + jackson-core + 2.17.1 + com.fasterxml.jackson.core jackson-databind @@ -119,6 +123,22 @@ ${gson-fire-version} + + + software.amazon.awssdk + auth + 2.34.8 + + + software.amazon.awssdk + http-auth-aws + 2.34.8 + + + org.slf4j + slf4j-api + 2.0.16 + org.junit.jupiter diff --git a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java index 8047dba..d941791 100644 --- a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java +++ b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java @@ -66,7 +66,10 @@ public AwsAuthLoginInput fromCredentials(String region, AwsCredentials credentia .putProperty(AwsV4FamilyHttpSigner.SERVICE_SIGNING_NAME, serviceName) .putProperty(AwsV4HttpSigner.REGION_NAME, region)) .request(); - final Map> requestHeaders = signedRequest.headers(); + final Map requestHeaders = + signedRequest.headers().entrySet().stream() + .map(entry -> Map.entry(entry.getKey(), entry.getValue().getFirst())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); final String encodedHeader; try { encodedHeader = diff --git a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java index 90e3c90..e32d32f 100644 --- a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java +++ b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java @@ -2,7 +2,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import com.amazonaws.auth.BasicAWSCredentials; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.infisical.sdk.models.AwsAuthLoginInput; @@ -16,6 +15,7 @@ import java.util.Map; import java.util.stream.Collectors; import org.junit.jupiter.api.Test; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; class AwsAuthProviderTest { private static final ObjectMapper objectMapper = new ObjectMapper(); From 984bc71f2bee1defc234b747ec58f27615ed1b61 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Thu, 2 Oct 2025 18:42:08 -0700 Subject: [PATCH 11/32] Fixed tests --- .../infisical/sdk/auth/AwsAuthProvider.java | 32 ++++++++++++------- .../sdk/auth/AwsAuthProviderTest.java | 18 +++++++---- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java index d941791..02799fd 100644 --- a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java +++ b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java @@ -6,6 +6,9 @@ import java.net.URI; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneOffset; import java.util.Base64; import java.util.Date; import java.util.List; @@ -21,6 +24,7 @@ import software.amazon.awssdk.http.SdkHttpRequest; import software.amazon.awssdk.http.auth.aws.signer.AwsV4FamilyHttpSigner; import software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner; +import software.amazon.awssdk.http.auth.spi.signer.HttpSigner; @Data @Builder @@ -40,7 +44,7 @@ public class AwsAuthProvider { Map.entry("Action", List.of("GetCallerIdentity")), Map.entry("Version", List.of("2011-06-15"))); - private final Date overrideDate; + private final Instant overrideInstant; public AwsAuthLoginInput fromCredentials(String region, AwsCredentials credentials) { final AwsV4HttpSigner signer = AwsV4HttpSigner.create(); @@ -51,25 +55,31 @@ public AwsAuthLoginInput fromCredentials(String region, AwsCredentials credentia .uri(URI.create(iamRequestURL)) .method(httpMethod) .appendHeader("Content-Type", contentType) - .appendHeader("Content-Length", String.valueOf(iamRequestBody.length())) .build(); final SdkHttpRequest signedRequest = signer .sign( - signingRequest -> - signingRequest - .request(request) - .identity(credentials) - .payload( - ContentStreamProvider.fromByteArray( - iamRequestBody.getBytes(StandardCharsets.UTF_8))) - .putProperty(AwsV4FamilyHttpSigner.SERVICE_SIGNING_NAME, serviceName) - .putProperty(AwsV4HttpSigner.REGION_NAME, region)) + signingRequest -> { + var req = + signingRequest + .request(request) + .identity(credentials) + .payload( + ContentStreamProvider.fromByteArray( + iamRequestBody.getBytes(StandardCharsets.UTF_8))) + .putProperty(AwsV4FamilyHttpSigner.SERVICE_SIGNING_NAME, serviceName) + .putProperty(AwsV4HttpSigner.REGION_NAME, region); + if (overrideInstant != null) { + req.putProperty( + HttpSigner.SIGNING_CLOCK, Clock.fixed(overrideInstant, ZoneOffset.UTC)); + } + }) .request(); final Map requestHeaders = signedRequest.headers().entrySet().stream() .map(entry -> Map.entry(entry.getKey(), entry.getValue().getFirst())) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + requestHeaders.put("Content-Length", String.valueOf(iamRequestBody.length())); final String encodedHeader; try { encodedHeader = diff --git a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java index e32d32f..2495e4d 100644 --- a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java +++ b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java @@ -5,12 +5,11 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.infisical.sdk.models.AwsAuthLoginInput; -import java.net.URISyntaxException; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; +import java.time.Instant; import java.util.Arrays; import java.util.Base64; -import java.util.Date; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -21,12 +20,12 @@ class AwsAuthProviderTest { private static final ObjectMapper objectMapper = new ObjectMapper(); @Test - void testFromCredentials() throws URISyntaxException, JsonProcessingException { + void testFromCredentials() throws JsonProcessingException { final AwsAuthProvider provider = - AwsAuthProvider.builder().overrideDate(new Date(1759446719)).build(); + AwsAuthProvider.builder().overrideInstant(Instant.ofEpochSecond(1759446719)).build(); final AwsAuthLoginInput loginInput = provider.fromCredentials( - "us-west-2", new BasicAWSCredentials("MOCK_ACCESS_KEY", "MOCK_SECRET_KEY")); + "us-west-2", AwsBasicCredentials.create("MOCK_ACCESS_KEY", "MOCK_SECRET_KEY")); assertEquals("POST", loginInput.getIamHttpRequestMethod()); final String decodedBody = @@ -49,10 +48,15 @@ void testFromCredentials() throws URISyntaxException, JsonProcessingException { assertEquals( Map.ofEntries( Map.entry("Host", "sts.us-west-2.amazonaws.com"), - Map.entry("X-Amz-Date", "19700121T084406Z"), + Map.entry("Content-Type", "application/x-www-form-urlencoded; charset=utf-8"), + Map.entry("Content-Length", "43"), + Map.entry( + "x-amz-content-sha256", + "2e5272931159dc39306511e6dbae66f365e6748f021352ad514b568d66ebba7c"), + Map.entry("X-Amz-Date", "20251002T231159Z"), Map.entry( "Authorization", - "AWS4-HMAC-SHA256 Credential=MOCK_ACCESS_KEY/19700121/us-west-2/sts/aws4_request, SignedHeaders=host;x-amz-date, Signature=f5736d3f614856326236dc15ebcfa47b4b615bc19a9da8fc24b2b767e9c50efa")), + "AWS4-HMAC-SHA256 Credential=MOCK_ACCESS_KEY/20251002/us-west-2/sts/aws4_request, SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date, Signature=0b47d8813cf09e9f5cc7a1edce21384e8d9c11ae8cd4a1599d6f307672d4c421")), actualHeaders); } } From ba1f98b2d2b4b52d623fc9839edc95a81eda5c35 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Thu, 2 Oct 2025 18:56:06 -0700 Subject: [PATCH 12/32] Add session token --- .../com/infisical/sdk/auth/AwsAuthProvider.java | 13 ++++++++----- .../com/infisical/sdk/auth/AwsAuthProviderTest.java | 9 ++++++--- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java index 02799fd..2a1cabb 100644 --- a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java +++ b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java @@ -10,7 +10,6 @@ import java.time.Instant; import java.time.ZoneOffset; import java.util.Base64; -import java.util.Date; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -46,16 +45,20 @@ public class AwsAuthProvider { private final Instant overrideInstant; - public AwsAuthLoginInput fromCredentials(String region, AwsCredentials credentials) { + public AwsAuthLoginInput fromCredentials( + String region, AwsCredentials credentials, String sessionToken) { final AwsV4HttpSigner signer = AwsV4HttpSigner.create(); final String iamRequestURL = endpointTemplate.formatted(region); final String iamRequestBody = encodeParameters(params); - final SdkHttpFullRequest request = + final SdkHttpFullRequest.Builder requestBuilder = SdkHttpFullRequest.builder() .uri(URI.create(iamRequestURL)) .method(httpMethod) - .appendHeader("Content-Type", contentType) - .build(); + .appendHeader("Content-Type", contentType); + if (sessionToken != null) { + requestBuilder.appendHeader("X-Amz-Security-Token", sessionToken); + } + final SdkHttpFullRequest request = requestBuilder.build(); final SdkHttpRequest signedRequest = signer .sign( diff --git a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java index 2495e4d..686338b 100644 --- a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java +++ b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java @@ -25,7 +25,9 @@ void testFromCredentials() throws JsonProcessingException { AwsAuthProvider.builder().overrideInstant(Instant.ofEpochSecond(1759446719)).build(); final AwsAuthLoginInput loginInput = provider.fromCredentials( - "us-west-2", AwsBasicCredentials.create("MOCK_ACCESS_KEY", "MOCK_SECRET_KEY")); + "us-west-2", + AwsBasicCredentials.create("MOCK_ACCESS_KEY", "MOCK_SECRET_KEY"), + "MOCK_SESSION_TOKEN"); assertEquals("POST", loginInput.getIamHttpRequestMethod()); final String decodedBody = @@ -52,11 +54,12 @@ void testFromCredentials() throws JsonProcessingException { Map.entry("Content-Length", "43"), Map.entry( "x-amz-content-sha256", - "2e5272931159dc39306511e6dbae66f365e6748f021352ad514b568d66ebba7c"), + "ab821ae955788b0e33ebd34c208442ccfc2d406e2edc5e7a39bd6458fbb4f843"), + Map.entry("X-Amz-Security-Token", "MOCK_SESSION_TOKEN"), Map.entry("X-Amz-Date", "20251002T231159Z"), Map.entry( "Authorization", - "AWS4-HMAC-SHA256 Credential=MOCK_ACCESS_KEY/20251002/us-west-2/sts/aws4_request, SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date, Signature=0b47d8813cf09e9f5cc7a1edce21384e8d9c11ae8cd4a1599d6f307672d4c421")), + "AWS4-HMAC-SHA256 Credential=MOCK_ACCESS_KEY/20251002/us-west-2/sts/aws4_request, SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date;x-amz-security-token, Signature=9b1b93454bea36297168ed67a861df12d17136f47cbdf5d23b1daa0fe704742b")), actualHeaders); } } From 5819ecda8bdc4f557153548ac529dcbd41589bc1 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Thu, 2 Oct 2025 19:03:40 -0700 Subject: [PATCH 13/32] Add fromInstanceProfile --- .../infisical/sdk/auth/AwsAuthProvider.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java index 2a1cabb..3611647 100644 --- a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java +++ b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java @@ -17,6 +17,8 @@ import lombok.Data; import lombok.NonNull; import software.amazon.awssdk.auth.credentials.AwsCredentials; +import software.amazon.awssdk.auth.credentials.AwsSessionCredentials; +import software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider; import software.amazon.awssdk.http.ContentStreamProvider; import software.amazon.awssdk.http.SdkHttpFullRequest; import software.amazon.awssdk.http.SdkHttpMethod; @@ -24,6 +26,8 @@ import software.amazon.awssdk.http.auth.aws.signer.AwsV4FamilyHttpSigner; import software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner; import software.amazon.awssdk.http.auth.spi.signer.HttpSigner; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain; @Data @Builder @@ -45,6 +49,14 @@ public class AwsAuthProvider { private final Instant overrideInstant; + /** + * Create AwsAuthLoginInput from given AWS credentials. + * + * @param region region of AWS identity + * @param credentials AWS credentials for creating the login input + * @param sessionToken Session token for creating the login input + * @return the AwsAuthLoginInput created from the given credentials for exchanging access token + */ public AwsAuthLoginInput fromCredentials( String region, AwsCredentials credentials, String sessionToken) { final AwsV4HttpSigner signer = AwsV4HttpSigner.create(); @@ -101,6 +113,25 @@ public AwsAuthLoginInput fromCredentials( .build(); } + /** + * Create AwsAuthLoginInput from the instance profile in the current environment. + * + * @return the AwsAuthLoginInput created from the current instance profile for exchanging access + * token + */ + public AwsAuthLoginInput fromInstanceProfile() { + try (InstanceProfileCredentialsProvider provider = + InstanceProfileCredentialsProvider.create()) { + final AwsSessionCredentials credentials = + (AwsSessionCredentials) provider.resolveCredentials(); + final DefaultAwsRegionProviderChain regionProvider = + DefaultAwsRegionProviderChain.builder().build(); + final Region region = regionProvider.getRegion(); + final String sessionToken = credentials.sessionToken(); + return fromCredentials(region.id(), credentials, sessionToken); + } + } + public static String encodeParameters(Map> params) { return params.entrySet().stream() .flatMap(entry -> entry.getValue().stream().map(item -> Map.entry(entry.getKey(), item))) From c4b2f44bd9d73b7f39fa7b845be7bfc59376ab1f Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Thu, 2 Oct 2025 19:16:26 -0700 Subject: [PATCH 14/32] Make aws auth focus on providing params only --- .../infisical/sdk/auth/AwsAuthProvider.java | 8 +++---- .../sdk/models/AwsAuthLoginInput.java | 10 ++++----- .../sdk/models/AwsAuthParameters.java | 21 +++++++++++++++++++ .../infisical/sdk/resources/AuthClient.java | 5 ++--- .../sdk/auth/AwsAuthProviderTest.java | 4 ++-- 5 files changed, 34 insertions(+), 14 deletions(-) create mode 100644 src/main/java/com/infisical/sdk/models/AwsAuthParameters.java diff --git a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java index 3611647..211f6e8 100644 --- a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java +++ b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java @@ -2,7 +2,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.infisical.sdk.models.AwsAuthLoginInput; +import com.infisical.sdk.models.AwsAuthParameters; import java.net.URI; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; @@ -57,7 +57,7 @@ public class AwsAuthProvider { * @param sessionToken Session token for creating the login input * @return the AwsAuthLoginInput created from the given credentials for exchanging access token */ - public AwsAuthLoginInput fromCredentials( + public AwsAuthParameters fromCredentials( String region, AwsCredentials credentials, String sessionToken) { final AwsV4HttpSigner signer = AwsV4HttpSigner.create(); final String iamRequestURL = endpointTemplate.formatted(region); @@ -106,7 +106,7 @@ public AwsAuthLoginInput fromCredentials( } final String encodedBody = Base64.getEncoder().encodeToString(iamRequestBody.getBytes(StandardCharsets.UTF_8)); - return AwsAuthLoginInput.builder() + return AwsAuthParameters.builder() .iamHttpRequestMethod(httpMethod.name()) .iamRequestHeaders(encodedHeader) .iamRequestBody(encodedBody) @@ -119,7 +119,7 @@ public AwsAuthLoginInput fromCredentials( * @return the AwsAuthLoginInput created from the current instance profile for exchanging access * token */ - public AwsAuthLoginInput fromInstanceProfile() { + public AwsAuthParameters fromInstanceProfile() { try (InstanceProfileCredentialsProvider provider = InstanceProfileCredentialsProvider.create()) { final AwsSessionCredentials credentials = diff --git a/src/main/java/com/infisical/sdk/models/AwsAuthLoginInput.java b/src/main/java/com/infisical/sdk/models/AwsAuthLoginInput.java index 90d033f..c525734 100644 --- a/src/main/java/com/infisical/sdk/models/AwsAuthLoginInput.java +++ b/src/main/java/com/infisical/sdk/models/AwsAuthLoginInput.java @@ -8,19 +8,15 @@ @Builder public class AwsAuthLoginInput { private final String identityId; - private final String iamRequestBody; private final String iamHttpRequestMethod; private final String iamRequestHeaders; + private final String iamRequestBody; public String validate() { if (Helper.isNullOrEmpty(identityId)) { return "Identity ID is required"; } - if (Helper.isNullOrEmpty(iamRequestBody)) { - return "IamRequestBody is required"; - } - if (Helper.isNullOrEmpty(iamHttpRequestMethod)) { return "IamHttpRequestMethod is required"; } @@ -28,6 +24,10 @@ public String validate() { if (Helper.isNullOrEmpty(iamRequestHeaders)) { return "IamRequestHeaders is required"; } + + if (Helper.isNullOrEmpty(iamRequestBody)) { + return "IamRequestBody is required"; + } return null; } } diff --git a/src/main/java/com/infisical/sdk/models/AwsAuthParameters.java b/src/main/java/com/infisical/sdk/models/AwsAuthParameters.java new file mode 100644 index 0000000..a703691 --- /dev/null +++ b/src/main/java/com/infisical/sdk/models/AwsAuthParameters.java @@ -0,0 +1,21 @@ +package com.infisical.sdk.models; + +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class AwsAuthParameters { + private final String iamHttpRequestMethod; + private final String iamRequestHeaders; + private final String iamRequestBody; + + public AwsAuthLoginInput toLoginInput(String identityId) { + return AwsAuthLoginInput.builder() + .identityId(identityId) + .iamRequestHeaders(iamRequestHeaders) + .iamHttpRequestMethod(iamHttpRequestMethod) + .iamRequestBody(iamRequestBody) + .build(); + } +} diff --git a/src/main/java/com/infisical/sdk/resources/AuthClient.java b/src/main/java/com/infisical/sdk/resources/AuthClient.java index f0ece5d..03475f5 100644 --- a/src/main/java/com/infisical/sdk/resources/AuthClient.java +++ b/src/main/java/com/infisical/sdk/resources/AuthClient.java @@ -1,13 +1,12 @@ package com.infisical.sdk.resources; +import com.infisical.sdk.api.ApiClient; import com.infisical.sdk.models.AwsAuthLoginInput; import com.infisical.sdk.models.LdapAuthLoginInput; import com.infisical.sdk.models.MachineIdentityCredential; +import com.infisical.sdk.models.UniversalAuthLoginInput; import com.infisical.sdk.util.InfisicalException; import java.util.function.Consumer; -import com.infisical.sdk.api.ApiClient; - -import com.infisical.sdk.models.UniversalAuthLoginInput; public class AuthClient { private final ApiClient apiClient; diff --git a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java index 686338b..f49dfeb 100644 --- a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java +++ b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.infisical.sdk.models.AwsAuthLoginInput; +import com.infisical.sdk.models.AwsAuthParameters; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.time.Instant; @@ -23,7 +23,7 @@ class AwsAuthProviderTest { void testFromCredentials() throws JsonProcessingException { final AwsAuthProvider provider = AwsAuthProvider.builder().overrideInstant(Instant.ofEpochSecond(1759446719)).build(); - final AwsAuthLoginInput loginInput = + final AwsAuthParameters loginInput = provider.fromCredentials( "us-west-2", AwsBasicCredentials.create("MOCK_ACCESS_KEY", "MOCK_SECRET_KEY"), From 4cfafd231b60cfcc25d20b26e55a30ca0b8f63ef Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Thu, 2 Oct 2025 19:27:05 -0700 Subject: [PATCH 15/32] Make deps optional, revert unwanted changes --- pom.xml | 5 +- .../infisical/sdk/resources/AuthClient.java | 49 +++++++------------ 2 files changed, 23 insertions(+), 31 deletions(-) diff --git a/pom.xml b/pom.xml index 1204096..7d8e9a7 100644 --- a/pom.xml +++ b/pom.xml @@ -128,16 +128,19 @@ software.amazon.awssdk auth 2.34.8 + true software.amazon.awssdk http-auth-aws 2.34.8 + true org.slf4j slf4j-api - 2.0.16 + 2.0.16 + true diff --git a/src/main/java/com/infisical/sdk/resources/AuthClient.java b/src/main/java/com/infisical/sdk/resources/AuthClient.java index 03475f5..73851e2 100644 --- a/src/main/java/com/infisical/sdk/resources/AuthClient.java +++ b/src/main/java/com/infisical/sdk/resources/AuthClient.java @@ -1,12 +1,12 @@ package com.infisical.sdk.resources; -import com.infisical.sdk.api.ApiClient; -import com.infisical.sdk.models.AwsAuthLoginInput; import com.infisical.sdk.models.LdapAuthLoginInput; import com.infisical.sdk.models.MachineIdentityCredential; -import com.infisical.sdk.models.UniversalAuthLoginInput; import com.infisical.sdk.util.InfisicalException; import java.util.function.Consumer; +import com.infisical.sdk.api.ApiClient; + +import com.infisical.sdk.models.UniversalAuthLoginInput; public class AuthClient { private final ApiClient apiClient; @@ -18,40 +18,29 @@ public AuthClient(ApiClient apiClient, Consumer onAuthenticate) { } public void UniversalAuthLogin(String clientId, String clientSecret) throws InfisicalException { - var params = - UniversalAuthLoginInput.builder().clientId(clientId).clientSecret(clientSecret).build(); - - var url = - String.format("%s%s", this.apiClient.GetBaseUrl(), "/api/v1/auth/universal-auth/login"); - var credential = this.apiClient.post(url, params, MachineIdentityCredential.class); - this.onAuthenticate.accept(credential.getAccessToken()); + var params = UniversalAuthLoginInput.builder() + .clientId(clientId) + .clientSecret(clientSecret) + .build(); + + var url = String.format("%s%s", this.apiClient.GetBaseUrl(), "/api/v1/auth/universal-auth/login"); + var credential = this.apiClient.post(url, params, MachineIdentityCredential.class); + this.onAuthenticate.accept(credential.getAccessToken()); } public void LdapAuthLogin(LdapAuthLoginInput input) throws InfisicalException { - var validationMsg = input.validate(); - - if (validationMsg != null) { - throw new InfisicalException(validationMsg); - } - - var url = String.format("%s%s", this.apiClient.GetBaseUrl(), "/api/v1/auth/ldap-auth/login"); - var credential = this.apiClient.post(url, input, MachineIdentityCredential.class); - this.onAuthenticate.accept(credential.getAccessToken()); - } - - public void AwsAuthLogin(AwsAuthLoginInput input) throws InfisicalException { - var validationMsg = input.validate(); + var validationMsg = input.validate(); - if (validationMsg != null) { - throw new InfisicalException(validationMsg); - } + if (validationMsg != null) { + throw new InfisicalException(validationMsg); + } - var url = String.format("%s%s", this.apiClient.GetBaseUrl(), "/api/v1/auth/aws-auth/login"); - var credential = this.apiClient.post(url, input, MachineIdentityCredential.class); - this.onAuthenticate.accept(credential.getAccessToken()); + var url = String.format("%s%s", this.apiClient.GetBaseUrl(), "/api/v1/auth/ldap-auth/login"); + var credential = this.apiClient.post(url, input, MachineIdentityCredential.class); + this.onAuthenticate.accept(credential.getAccessToken()); } public void SetAccessToken(String accessToken) { this.onAuthenticate.accept(accessToken); } -} +} \ No newline at end of file From b4040691fe03acbf1aec020611e6b0b210b75dcd Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Thu, 2 Oct 2025 19:28:13 -0700 Subject: [PATCH 16/32] Revert api client --- .../java/com/infisical/sdk/api/ApiClient.java | 340 +++++++++--------- 1 file changed, 169 insertions(+), 171 deletions(-) diff --git a/src/main/java/com/infisical/sdk/api/ApiClient.java b/src/main/java/com/infisical/sdk/api/ApiClient.java index 504dec2..ae73e3e 100644 --- a/src/main/java/com/infisical/sdk/api/ApiClient.java +++ b/src/main/java/com/infisical/sdk/api/ApiClient.java @@ -8,209 +8,207 @@ import java.util.Map; public class ApiClient { - private final OkHttpClient client; + private final OkHttpClient client; - private String accessToken; - private String baseUrl; + private String accessToken; + private String baseUrl; - private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); + private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); - public ApiClient(String baseUrl, String accessToken) { - this.client = new OkHttpClient(); - this.baseUrl = baseUrl; + public ApiClient(String baseUrl, String accessToken) { + this.client = new OkHttpClient(); + this.baseUrl = baseUrl; - if (accessToken != null) { - this.accessToken = accessToken; + if (accessToken != null) { + this.accessToken = accessToken; + } + + this.formatBaseUrl(); } - this.formatBaseUrl(); - } + private void formatBaseUrl() { + // Remove trailing slash if present + if (this.baseUrl.endsWith("/")) { + this.baseUrl = this.baseUrl.substring(0, this.baseUrl.length() - 1); + } - private void formatBaseUrl() { - // Remove trailing slash if present - if (this.baseUrl.endsWith("/")) { - this.baseUrl = this.baseUrl.substring(0, this.baseUrl.length() - 1); - } + // Check if URL starts with protocol + if (!this.baseUrl.matches("^[a-zA-Z]+://.*")) { + this.baseUrl = "http://" + this.baseUrl; + } - // Check if URL starts with protocol - if (!this.baseUrl.matches("^[a-zA-Z]+://.*")) { - this.baseUrl = "http://" + this.baseUrl; + // Add /api if not present + if (this.baseUrl.endsWith("/api")) { + this.baseUrl = this.baseUrl.substring(0, this.baseUrl.length() - 4); + } } - // Add /api if not present - if (this.baseUrl.endsWith("/api")) { - this.baseUrl = this.baseUrl.substring(0, this.baseUrl.length() - 4); + public String GetBaseUrl(){ + return this.baseUrl; } - } - - public String GetBaseUrl() { - return this.baseUrl; - } - @SuppressWarnings(value = "lombok") - public OkHttpClient getClient() { - return this.client; - } + @SuppressWarnings(value = "lombok") + public OkHttpClient getClient() { + return this.client; + } - private String formatErrorMessage(Response response) throws IOException { - String message = String.format("Unexpected response: %s", response); + private String formatErrorMessage(Response response) throws IOException { + String message = String.format("Unexpected response: %s", response); - try (ResponseBody errorBody = response.body()) { - if (errorBody != null) { - String bodyString = errorBody.string(); - if (!bodyString.isEmpty()) { - message += String.format(" - %s", bodyString); + try (ResponseBody errorBody = response.body()) { + if (errorBody != null) { + String bodyString = errorBody.string(); + if (!bodyString.isEmpty()) { + message += String.format(" - %s", bodyString); + } + } } - } + + return message; } - return message; - } - - public R post(String url, T requestBody, Class responseType) throws InfisicalException { - try { - // convert request to json - var gson = new Gson(); - String jsonBody = gson.toJson(requestBody); - - // Build request - var requestBuilder = - new Request.Builder() - .url(url) - .post(RequestBody.create(JSON, jsonBody)) - .header("Accept", "application/json"); - - if (this.accessToken != null && !this.accessToken.isEmpty()) { - requestBuilder.addHeader("Authorization", "Bearer " + this.accessToken); - } - var request = requestBuilder.build(); - - Response response = client.newCall(request).execute(); - try (ResponseBody responseBody = response.body()) { - if (!response.isSuccessful()) { - throw new IOException(this.formatErrorMessage(response)); + public R post(String url, T requestBody, Class responseType) throws InfisicalException { + try { + // convert request to json + var gson = new Gson(); + String jsonBody = gson.toJson(requestBody); + + // Build request + var requestBuilder = new Request.Builder() + .url(url) + .post(RequestBody.create(JSON, jsonBody)) + .header("Accept", "application/json"); + + + if (this.accessToken != null && !this.accessToken.isEmpty()) { + requestBuilder.addHeader("Authorization", "Bearer " + this.accessToken); + } + var request = requestBuilder.build(); + + Response response = client.newCall(request).execute(); + try (ResponseBody responseBody = response.body()) { + if (!response.isSuccessful()) { + throw new IOException(this.formatErrorMessage(response)); + } + + if (responseBody == null) { + throw new IOException("Response body is null"); + } + + String responseJson = responseBody.string(); + return gson.fromJson(responseJson, responseType); + } + } catch (IOException e) { + throw new InfisicalException(e); } + } - if (responseBody == null) { - throw new IOException("Response body is null"); - } + public R get(String baseUrl, Map queryParams, Class responseType) throws InfisicalException { + try { + HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl).newBuilder(); - String responseJson = responseBody.string(); - return gson.fromJson(responseJson, responseType); - } - } catch (IOException e) { - throw new InfisicalException(e); - } - } + if (queryParams != null) { + queryParams.forEach(urlBuilder::addQueryParameter); + } - public R get(String baseUrl, Map queryParams, Class responseType) - throws InfisicalException { - try { - HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl).newBuilder(); + var requestBuilder = new Request.Builder() + .url(urlBuilder.build()) + .get() + .header("Accept", "application/json"); - if (queryParams != null) { - queryParams.forEach(urlBuilder::addQueryParameter); - } + if (this.accessToken != null && !this.accessToken.isEmpty()) { + requestBuilder.addHeader("Authorization", "Bearer " + this.accessToken); + } - var requestBuilder = - new Request.Builder().url(urlBuilder.build()).get().header("Accept", "application/json"); + var request = requestBuilder.build(); - if (this.accessToken != null && !this.accessToken.isEmpty()) { - requestBuilder.addHeader("Authorization", "Bearer " + this.accessToken); - } - var request = requestBuilder.build(); + Response response = client.newCall(request).execute(); + try (ResponseBody responseBody = response.body()) { + if (!response.isSuccessful()) { + throw new IOException(this.formatErrorMessage(response)); + } - Response response = client.newCall(request).execute(); - try (ResponseBody responseBody = response.body()) { - if (!response.isSuccessful()) { - throw new IOException(this.formatErrorMessage(response)); - } + if (responseBody == null) { + throw new IOException("Response body is null"); + } - if (responseBody == null) { - throw new IOException("Response body is null"); + var gson = new Gson(); + String responseJson = responseBody.string(); + return gson.fromJson(responseJson, responseType); + } + } catch (IOException e) { + throw new InfisicalException(e); } - - var gson = new Gson(); - String responseJson = responseBody.string(); - return gson.fromJson(responseJson, responseType); - } - } catch (IOException e) { - throw new InfisicalException(e); } - } - - public R patch(String url, T requestBody, Class responseType) - throws InfisicalException { - try { - // convert request to json - var gson = new Gson(); - String jsonBody = gson.toJson(requestBody); - - // Build request - var requestBuilder = - new Request.Builder() - .url(url) - .patch(RequestBody.create(JSON, jsonBody)) - .header("Accept", "application/json"); - - if (this.accessToken != null && !this.accessToken.isEmpty()) { - requestBuilder.addHeader("Authorization", "Bearer " + this.accessToken); - } - var request = requestBuilder.build(); - - Response response = client.newCall(request).execute(); - try (ResponseBody responseBody = response.body()) { - if (!response.isSuccessful()) { - throw new IOException(this.formatErrorMessage(response)); - } - if (responseBody == null) { - throw new IOException("Response body is null"); + public R patch(String url, T requestBody, Class responseType) throws InfisicalException { + try { + // convert request to json + var gson = new Gson(); + String jsonBody = gson.toJson(requestBody); + + // Build request + var requestBuilder = new Request.Builder() + .url(url) + .patch(RequestBody.create(JSON, jsonBody)) + .header("Accept", "application/json"); + + if (this.accessToken != null && !this.accessToken.isEmpty()) { + requestBuilder.addHeader("Authorization", "Bearer " + this.accessToken); + } + var request = requestBuilder.build(); + + Response response = client.newCall(request).execute(); + try (ResponseBody responseBody = response.body()) { + if (!response.isSuccessful()) { + throw new IOException(this.formatErrorMessage(response)); + } + + if (responseBody == null) { + throw new IOException("Response body is null"); + } + + String responseJson = responseBody.string(); + return gson.fromJson(responseJson, responseType); + } + } catch (IOException e) { + throw new InfisicalException(e); } - - String responseJson = responseBody.string(); - return gson.fromJson(responseJson, responseType); - } - } catch (IOException e) { - throw new InfisicalException(e); } - } - - public R delete(String url, T requestBody, Class responseType) - throws InfisicalException { - try { - // convert request to json - var gson = new Gson(); - String jsonBody = gson.toJson(requestBody); - - // Build request - var requestBuilder = - new Request.Builder() - .url(url) - .delete(RequestBody.create(JSON, jsonBody)) - .header("Accept", "application/json"); - - if (this.accessToken != null && !this.accessToken.isEmpty()) { - requestBuilder.addHeader("Authorization", "Bearer " + this.accessToken); - } - var request = requestBuilder.build(); - - Response response = client.newCall(request).execute(); - try (ResponseBody responseBody = response.body()) { - if (!response.isSuccessful()) { - throw new IOException(this.formatErrorMessage(response)); - } - if (responseBody == null) { - throw new IOException("Response body is null"); + public R delete(String url, T requestBody, Class responseType) throws InfisicalException { + try { + // convert request to json + var gson = new Gson(); + String jsonBody = gson.toJson(requestBody); + + // Build request + var requestBuilder = new Request.Builder() + .url(url) + .delete(RequestBody.create(JSON, jsonBody)) + .header("Accept", "application/json"); + + if (this.accessToken != null && !this.accessToken.isEmpty()) { + requestBuilder.addHeader("Authorization", "Bearer " + this.accessToken); + } + var request = requestBuilder.build(); + + Response response = client.newCall(request).execute(); + try (ResponseBody responseBody = response.body()) { + if (!response.isSuccessful()) { + throw new IOException(this.formatErrorMessage(response)); + } + + if (responseBody == null) { + throw new IOException("Response body is null"); + } + + String responseJson = responseBody.string(); + return gson.fromJson(responseJson, responseType); + } + } catch (IOException e) { + throw new InfisicalException(e); } - - String responseJson = responseBody.string(); - return gson.fromJson(responseJson, responseType); - } - } catch (IOException e) { - throw new InfisicalException(e); } - } -} +} \ No newline at end of file From 9af6271eadd76815ce350bafdfb69e4fa0932c4d Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Fri, 3 Oct 2025 08:17:56 -0700 Subject: [PATCH 17/32] Try to fix tests --- src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java index f49dfeb..9ed047d 100644 --- a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java +++ b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java @@ -54,12 +54,12 @@ void testFromCredentials() throws JsonProcessingException { Map.entry("Content-Length", "43"), Map.entry( "x-amz-content-sha256", - "ab821ae955788b0e33ebd34c208442ccfc2d406e2edc5e7a39bd6458fbb4f843"), + "2e5272931159dc39306511e6dbae66f365e6748f021352ad514b568d66ebba7c"), Map.entry("X-Amz-Security-Token", "MOCK_SESSION_TOKEN"), Map.entry("X-Amz-Date", "20251002T231159Z"), Map.entry( "Authorization", - "AWS4-HMAC-SHA256 Credential=MOCK_ACCESS_KEY/20251002/us-west-2/sts/aws4_request, SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date;x-amz-security-token, Signature=9b1b93454bea36297168ed67a861df12d17136f47cbdf5d23b1daa0fe704742b")), + "AWS4-HMAC-SHA256 Credential=MOCK_ACCESS_KEY/20251002/us-west-2/sts/aws4_request, SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date;x-amz-security-token, Signature=9f345142fb0fe12b5fcbf27516876220c37e295f104f4dae9bdf32cbd42f6bdb")), actualHeaders); } } From 39398e69007d8bd0c6bd367b6eae173dab08d6f6 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Fri, 3 Oct 2025 08:34:56 -0700 Subject: [PATCH 18/32] Fix IDE complain --- src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java index 9ed047d..d734485 100644 --- a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java +++ b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.infisical.sdk.models.AwsAuthParameters; import java.net.URLDecoder; @@ -46,7 +47,8 @@ void testFromCredentials() throws JsonProcessingException { final String decodedHeaders = new String( Base64.getDecoder().decode(loginInput.getIamRequestHeaders()), StandardCharsets.UTF_8); - final Map actualHeaders = objectMapper.readValue(decodedHeaders, Map.class); + final Map actualHeaders = + objectMapper.readValue(decodedHeaders, new TypeReference<>() {}); assertEquals( Map.ofEntries( Map.entry("Host", "sts.us-west-2.amazonaws.com"), From 33c80173562c96c91ad03fc92c1acd3960ab584f Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Fri, 3 Oct 2025 08:41:12 -0700 Subject: [PATCH 19/32] Fix broken tests --- src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java | 4 ++++ src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java index 211f6e8..0acc8e3 100644 --- a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java +++ b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java @@ -10,6 +10,7 @@ import java.time.Instant; import java.time.ZoneOffset; import java.util.Base64; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -135,6 +136,9 @@ public AwsAuthParameters fromInstanceProfile() { public static String encodeParameters(Map> params) { return params.entrySet().stream() .flatMap(entry -> entry.getValue().stream().map(item -> Map.entry(entry.getKey(), item))) + // Notice: this is not really needed for real world usage, but it makes the + // body encoded in a deterministic order, so that unit test is much eaiser + .sorted(Map.Entry.comparingByKey()) .map( entry -> String.format( diff --git a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java index d734485..85c4159 100644 --- a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java +++ b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java @@ -56,12 +56,12 @@ void testFromCredentials() throws JsonProcessingException { Map.entry("Content-Length", "43"), Map.entry( "x-amz-content-sha256", - "2e5272931159dc39306511e6dbae66f365e6748f021352ad514b568d66ebba7c"), + "ab821ae955788b0e33ebd34c208442ccfc2d406e2edc5e7a39bd6458fbb4f843"), Map.entry("X-Amz-Security-Token", "MOCK_SESSION_TOKEN"), Map.entry("X-Amz-Date", "20251002T231159Z"), Map.entry( "Authorization", - "AWS4-HMAC-SHA256 Credential=MOCK_ACCESS_KEY/20251002/us-west-2/sts/aws4_request, SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date;x-amz-security-token, Signature=9f345142fb0fe12b5fcbf27516876220c37e295f104f4dae9bdf32cbd42f6bdb")), + "AWS4-HMAC-SHA256 Credential=MOCK_ACCESS_KEY/20251002/us-west-2/sts/aws4_request, SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date;x-amz-security-token, Signature=9b1b93454bea36297168ed67a861df12d17136f47cbdf5d23b1daa0fe704742b")), actualHeaders); } } From 7be77affdb678f2e4a51c3256aa979467198fd24 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Fri, 3 Oct 2025 08:53:49 -0700 Subject: [PATCH 20/32] More test cases --- pom.xml | 7 ++++- .../sdk/auth/AwsAuthProviderTest.java | 31 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7d8e9a7..e89421b 100644 --- a/pom.xml +++ b/pom.xml @@ -149,9 +149,14 @@ 5.9.1 test + + org.junit.jupiter + junit-jupiter-params + 5.9.1 + test + - diff --git a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java index 85c4159..dcc5132 100644 --- a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java +++ b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java @@ -14,7 +14,11 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; class AwsAuthProviderTest { @@ -64,4 +68,31 @@ void testFromCredentials() throws JsonProcessingException { "AWS4-HMAC-SHA256 Credential=MOCK_ACCESS_KEY/20251002/us-west-2/sts/aws4_request, SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date;x-amz-security-token, Signature=9b1b93454bea36297168ed67a861df12d17136f47cbdf5d23b1daa0fe704742b")), actualHeaders); } + + static Stream provideTestData() { + return Stream.of( + // empty + Arguments.of(Map.of(), ""), + // simple + Arguments.of( + Map.ofEntries(Map.entry("a", List.of("123")), Map.entry("b", List.of("456"))), + "a=123&b=456"), + // sorting the key + Arguments.of( + Map.ofEntries( + Map.entry("d", List.of("3")), + Map.entry("a", List.of("0")), + Map.entry("c", List.of("2")), + Map.entry("b", List.of("1"))), + "a=0&b=1&c=2&d=3"), + Arguments.of( + Map.ofEntries(Map.entry("a", List.of("!@#$%^&*(){}[]"))), + "a=%21%40%23%24%25%5E%26*%28%29%7B%7D%5B%5D")); + } + + @ParameterizedTest + @MethodSource("provideTestData") + void testEncodeParameters(Map> params, String expected) { + assertEquals(expected, AwsAuthProvider.encodeParameters(params)); + } } From 5eca544b235f747f008430289d89a64cacafa2e7 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Fri, 3 Oct 2025 08:55:35 -0700 Subject: [PATCH 21/32] Add a bit doc --- .../java/com/infisical/sdk/auth/AwsAuthProvider.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java index 0acc8e3..7640a78 100644 --- a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java +++ b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java @@ -10,7 +10,6 @@ import java.time.Instant; import java.time.ZoneOffset; import java.util.Base64; -import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -133,11 +132,17 @@ public AwsAuthParameters fromInstanceProfile() { } } + /** + * Encode given parameters with URL encoding for the body of form posting request. + * + * @param params parameters mapping key to values to encode + * @return URL-encoded string of the parameters + */ public static String encodeParameters(Map> params) { return params.entrySet().stream() .flatMap(entry -> entry.getValue().stream().map(item -> Map.entry(entry.getKey(), item))) // Notice: this is not really needed for real world usage, but it makes the - // body encoded in a deterministic order, so that unit test is much eaiser + // body encoded in a deterministic order, so that unit test is much easier .sorted(Map.Entry.comparingByKey()) .map( entry -> From 4c10ae738a3028d655ecd4606b13d9871d442062 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Fri, 3 Oct 2025 08:56:42 -0700 Subject: [PATCH 22/32] Rename --- src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java index dcc5132..46d076c 100644 --- a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java +++ b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java @@ -69,7 +69,7 @@ void testFromCredentials() throws JsonProcessingException { actualHeaders); } - static Stream provideTestData() { + static Stream encodeParametersCases() { return Stream.of( // empty Arguments.of(Map.of(), ""), @@ -91,7 +91,7 @@ static Stream provideTestData() { } @ParameterizedTest - @MethodSource("provideTestData") + @MethodSource("encodeParametersCases") void testEncodeParameters(Map> params, String expected) { assertEquals(expected, AwsAuthProvider.encodeParameters(params)); } From df1db68bb263f526095f202d11a627289966a82c Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Fri, 3 Oct 2025 09:04:54 -0700 Subject: [PATCH 23/32] Add non null --- .../java/com/infisical/sdk/models/AwsAuthLoginInput.java | 9 +++++---- .../java/com/infisical/sdk/models/AwsAuthParameters.java | 7 ++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/infisical/sdk/models/AwsAuthLoginInput.java b/src/main/java/com/infisical/sdk/models/AwsAuthLoginInput.java index c525734..4cd6d4c 100644 --- a/src/main/java/com/infisical/sdk/models/AwsAuthLoginInput.java +++ b/src/main/java/com/infisical/sdk/models/AwsAuthLoginInput.java @@ -3,14 +3,15 @@ import com.infisical.sdk.util.Helper; import lombok.Builder; import lombok.Data; +import lombok.NonNull; @Data @Builder public class AwsAuthLoginInput { - private final String identityId; - private final String iamHttpRequestMethod; - private final String iamRequestHeaders; - private final String iamRequestBody; + @NonNull private final String identityId; + @NonNull private final String iamHttpRequestMethod; + @NonNull private final String iamRequestHeaders; + @NonNull private final String iamRequestBody; public String validate() { if (Helper.isNullOrEmpty(identityId)) { diff --git a/src/main/java/com/infisical/sdk/models/AwsAuthParameters.java b/src/main/java/com/infisical/sdk/models/AwsAuthParameters.java index a703691..88a87b3 100644 --- a/src/main/java/com/infisical/sdk/models/AwsAuthParameters.java +++ b/src/main/java/com/infisical/sdk/models/AwsAuthParameters.java @@ -2,13 +2,14 @@ import lombok.Builder; import lombok.Data; +import lombok.NonNull; @Data @Builder public class AwsAuthParameters { - private final String iamHttpRequestMethod; - private final String iamRequestHeaders; - private final String iamRequestBody; + @NonNull private final String iamHttpRequestMethod; + @NonNull private final String iamRequestHeaders; + @NonNull private final String iamRequestBody; public AwsAuthLoginInput toLoginInput(String identityId) { return AwsAuthLoginInput.builder() From ee12f13777c9980994d0bbd06387c75f9e2e4a7f Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Fri, 3 Oct 2025 09:07:44 -0700 Subject: [PATCH 24/32] Bring AwsAuthLogin changes back --- .../infisical/sdk/resources/AuthClient.java | 49 ++++++++++++------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/infisical/sdk/resources/AuthClient.java b/src/main/java/com/infisical/sdk/resources/AuthClient.java index 73851e2..03475f5 100644 --- a/src/main/java/com/infisical/sdk/resources/AuthClient.java +++ b/src/main/java/com/infisical/sdk/resources/AuthClient.java @@ -1,12 +1,12 @@ package com.infisical.sdk.resources; +import com.infisical.sdk.api.ApiClient; +import com.infisical.sdk.models.AwsAuthLoginInput; import com.infisical.sdk.models.LdapAuthLoginInput; import com.infisical.sdk.models.MachineIdentityCredential; +import com.infisical.sdk.models.UniversalAuthLoginInput; import com.infisical.sdk.util.InfisicalException; import java.util.function.Consumer; -import com.infisical.sdk.api.ApiClient; - -import com.infisical.sdk.models.UniversalAuthLoginInput; public class AuthClient { private final ApiClient apiClient; @@ -18,29 +18,40 @@ public AuthClient(ApiClient apiClient, Consumer onAuthenticate) { } public void UniversalAuthLogin(String clientId, String clientSecret) throws InfisicalException { - var params = UniversalAuthLoginInput.builder() - .clientId(clientId) - .clientSecret(clientSecret) - .build(); - - var url = String.format("%s%s", this.apiClient.GetBaseUrl(), "/api/v1/auth/universal-auth/login"); - var credential = this.apiClient.post(url, params, MachineIdentityCredential.class); - this.onAuthenticate.accept(credential.getAccessToken()); + var params = + UniversalAuthLoginInput.builder().clientId(clientId).clientSecret(clientSecret).build(); + + var url = + String.format("%s%s", this.apiClient.GetBaseUrl(), "/api/v1/auth/universal-auth/login"); + var credential = this.apiClient.post(url, params, MachineIdentityCredential.class); + this.onAuthenticate.accept(credential.getAccessToken()); } public void LdapAuthLogin(LdapAuthLoginInput input) throws InfisicalException { - var validationMsg = input.validate(); + var validationMsg = input.validate(); + + if (validationMsg != null) { + throw new InfisicalException(validationMsg); + } + + var url = String.format("%s%s", this.apiClient.GetBaseUrl(), "/api/v1/auth/ldap-auth/login"); + var credential = this.apiClient.post(url, input, MachineIdentityCredential.class); + this.onAuthenticate.accept(credential.getAccessToken()); + } + + public void AwsAuthLogin(AwsAuthLoginInput input) throws InfisicalException { + var validationMsg = input.validate(); - if (validationMsg != null) { - throw new InfisicalException(validationMsg); - } + if (validationMsg != null) { + throw new InfisicalException(validationMsg); + } - var url = String.format("%s%s", this.apiClient.GetBaseUrl(), "/api/v1/auth/ldap-auth/login"); - var credential = this.apiClient.post(url, input, MachineIdentityCredential.class); - this.onAuthenticate.accept(credential.getAccessToken()); + var url = String.format("%s%s", this.apiClient.GetBaseUrl(), "/api/v1/auth/aws-auth/login"); + var credential = this.apiClient.post(url, input, MachineIdentityCredential.class); + this.onAuthenticate.accept(credential.getAccessToken()); } public void SetAccessToken(String accessToken) { this.onAuthenticate.accept(accessToken); } -} \ No newline at end of file +} From 0b39380c3b787859c20ffab88e3f2a0131f15a57 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Fri, 3 Oct 2025 09:14:16 -0700 Subject: [PATCH 25/32] Remove unused deps --- pom.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pom.xml b/pom.xml index e89421b..af1db2b 100644 --- a/pom.xml +++ b/pom.xml @@ -130,12 +130,6 @@ 2.34.8 true - - software.amazon.awssdk - http-auth-aws - 2.34.8 - true - org.slf4j slf4j-api From 999a17acdabccb61d7e517a502c19ce469594652 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Fri, 3 Oct 2025 09:16:47 -0700 Subject: [PATCH 26/32] Add to builder arg --- src/main/java/com/infisical/sdk/models/AwsAuthLoginInput.java | 2 +- src/main/java/com/infisical/sdk/models/AwsAuthParameters.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/infisical/sdk/models/AwsAuthLoginInput.java b/src/main/java/com/infisical/sdk/models/AwsAuthLoginInput.java index 4cd6d4c..60401f9 100644 --- a/src/main/java/com/infisical/sdk/models/AwsAuthLoginInput.java +++ b/src/main/java/com/infisical/sdk/models/AwsAuthLoginInput.java @@ -6,7 +6,7 @@ import lombok.NonNull; @Data -@Builder +@Builder(toBuilder = true) public class AwsAuthLoginInput { @NonNull private final String identityId; @NonNull private final String iamHttpRequestMethod; diff --git a/src/main/java/com/infisical/sdk/models/AwsAuthParameters.java b/src/main/java/com/infisical/sdk/models/AwsAuthParameters.java index 88a87b3..5b0a511 100644 --- a/src/main/java/com/infisical/sdk/models/AwsAuthParameters.java +++ b/src/main/java/com/infisical/sdk/models/AwsAuthParameters.java @@ -5,7 +5,7 @@ import lombok.NonNull; @Data -@Builder +@Builder(toBuilder = true) public class AwsAuthParameters { @NonNull private final String iamHttpRequestMethod; @NonNull private final String iamRequestHeaders; From ca8fdf1e10c96818b184171c0178e7f7992b3929 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Fri, 3 Oct 2025 09:51:28 -0700 Subject: [PATCH 27/32] Remove duplicate log deps --- pom.xml | 6 ------ .../java/com/infisical/sdk/auth/AwsAuthProviderTest.java | 3 ++- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index af1db2b..646c55b 100644 --- a/pom.xml +++ b/pom.xml @@ -130,12 +130,6 @@ 2.34.8 true - - org.slf4j - slf4j-api - 2.0.16 - true - org.junit.jupiter diff --git a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java index 46d076c..a124a9b 100644 --- a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java +++ b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java @@ -42,7 +42,8 @@ void testFromCredentials() throws JsonProcessingException { Arrays.stream(decodedBody.split("&")) .map( item -> { - final String[] parts = URLDecoder.decode(item, StandardCharsets.UTF_8).split("="); + final String[] parts = + URLDecoder.decode(item, StandardCharsets.UTF_8).split("=", 2); return Map.entry(parts[0], List.of(parts[1])); }) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); From 59e3ea4397142b1d0c7bd0fdd1aa496be6251de4 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Fri, 3 Oct 2025 10:14:42 -0700 Subject: [PATCH 28/32] Add default provider --- src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java index 7640a78..aa6a337 100644 --- a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java +++ b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java @@ -152,4 +152,8 @@ public static String encodeParameters(Map> params) { URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8))) .collect(Collectors.joining("&")); } + + public static AwsAuthProvider defaultProvider() { + return builder().build(); + } } From e1d973fa2c8d88c10b0b1d42c234b9cc6b7ca9b9 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Fri, 3 Oct 2025 10:28:44 -0700 Subject: [PATCH 29/32] Be a bit more careful with the region value --- .../infisical/sdk/auth/AwsAuthProvider.java | 5 +++++ .../sdk/auth/AwsAuthProviderTest.java | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java index aa6a337..274c92a 100644 --- a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java +++ b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java @@ -12,6 +12,7 @@ import java.util.Base64; import java.util.List; import java.util.Map; +import java.util.regex.Pattern; import java.util.stream.Collectors; import lombok.Builder; import lombok.Data; @@ -33,6 +34,7 @@ @Builder public class AwsAuthProvider { private static final ObjectMapper objectMapper = new ObjectMapper(); + private static final Pattern REGION_REGEX = Pattern.compile("^[a-z0-9-]+$"); @NonNull @Builder.Default private final String serviceName = "sts"; @NonNull @Builder.Default private final SdkHttpMethod httpMethod = SdkHttpMethod.POST; @@ -59,6 +61,9 @@ public class AwsAuthProvider { */ public AwsAuthParameters fromCredentials( String region, AwsCredentials credentials, String sessionToken) { + if (!REGION_REGEX.matcher(region).matches()) { + throw new IllegalArgumentException("Invalid region: " + region); + } final AwsV4HttpSigner signer = AwsV4HttpSigner.create(); final String iamRequestURL = endpointTemplate.formatted(region); final String iamRequestBody = encodeParameters(params); diff --git a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java index a124a9b..a647f43 100644 --- a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java +++ b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java @@ -1,6 +1,7 @@ package com.infisical.sdk.auth; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; @@ -24,6 +25,23 @@ class AwsAuthProviderTest { private static final ObjectMapper objectMapper = new ObjectMapper(); + static Stream invalidRegionCases() { + return Stream.of(Arguments.of(""), Arguments.of("myevialdomain.com?"), Arguments.of("!@#$")); + } + + @ParameterizedTest + @MethodSource("invalidRegionCases") + void testInvalidRegions(String region) { + final AwsAuthProvider provider = AwsAuthProvider.defaultProvider(); + assertThrows( + IllegalArgumentException.class, + () -> + provider.fromCredentials( + region, + AwsBasicCredentials.create("MOCK_ACCESS_KEY", "MOCK_SECRET_KEY"), + "MOCK_SESSION_TOKEN")); + } + @Test void testFromCredentials() throws JsonProcessingException { final AwsAuthProvider provider = From 63bb17c7f36afe05b55ea47738329001daa6fef1 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Fri, 3 Oct 2025 14:36:13 -0700 Subject: [PATCH 30/32] Add overload method with just the identity id to make it more userfriendly --- src/main/java/com/infisical/sdk/resources/AuthClient.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/infisical/sdk/resources/AuthClient.java b/src/main/java/com/infisical/sdk/resources/AuthClient.java index 03475f5..bf334df 100644 --- a/src/main/java/com/infisical/sdk/resources/AuthClient.java +++ b/src/main/java/com/infisical/sdk/resources/AuthClient.java @@ -1,6 +1,7 @@ package com.infisical.sdk.resources; import com.infisical.sdk.api.ApiClient; +import com.infisical.sdk.auth.AwsAuthProvider; import com.infisical.sdk.models.AwsAuthLoginInput; import com.infisical.sdk.models.LdapAuthLoginInput; import com.infisical.sdk.models.MachineIdentityCredential; @@ -39,6 +40,10 @@ public void LdapAuthLogin(LdapAuthLoginInput input) throws InfisicalException { this.onAuthenticate.accept(credential.getAccessToken()); } + public void AwsAuthLogin(String identityId) throws InfisicalException { + AwsAuthLogin(AwsAuthProvider.defaultProvider().fromInstanceProfile().toLoginInput(identityId)); + } + public void AwsAuthLogin(AwsAuthLoginInput input) throws InfisicalException { var validationMsg = input.validate(); From 87ff9ab81806c7c0e04337379e0a355c389bd5ac Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Fri, 3 Oct 2025 14:37:59 -0700 Subject: [PATCH 31/32] Remove not needed region regex check --- .../infisical/sdk/auth/AwsAuthProvider.java | 4 ---- .../sdk/auth/AwsAuthProviderTest.java | 18 ------------------ 2 files changed, 22 deletions(-) diff --git a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java index 274c92a..bf58321 100644 --- a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java +++ b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java @@ -34,7 +34,6 @@ @Builder public class AwsAuthProvider { private static final ObjectMapper objectMapper = new ObjectMapper(); - private static final Pattern REGION_REGEX = Pattern.compile("^[a-z0-9-]+$"); @NonNull @Builder.Default private final String serviceName = "sts"; @NonNull @Builder.Default private final SdkHttpMethod httpMethod = SdkHttpMethod.POST; @@ -61,9 +60,6 @@ public class AwsAuthProvider { */ public AwsAuthParameters fromCredentials( String region, AwsCredentials credentials, String sessionToken) { - if (!REGION_REGEX.matcher(region).matches()) { - throw new IllegalArgumentException("Invalid region: " + region); - } final AwsV4HttpSigner signer = AwsV4HttpSigner.create(); final String iamRequestURL = endpointTemplate.formatted(region); final String iamRequestBody = encodeParameters(params); diff --git a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java index a647f43..a124a9b 100644 --- a/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java +++ b/src/test/java/com/infisical/sdk/auth/AwsAuthProviderTest.java @@ -1,7 +1,6 @@ package com.infisical.sdk.auth; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; @@ -25,23 +24,6 @@ class AwsAuthProviderTest { private static final ObjectMapper objectMapper = new ObjectMapper(); - static Stream invalidRegionCases() { - return Stream.of(Arguments.of(""), Arguments.of("myevialdomain.com?"), Arguments.of("!@#$")); - } - - @ParameterizedTest - @MethodSource("invalidRegionCases") - void testInvalidRegions(String region) { - final AwsAuthProvider provider = AwsAuthProvider.defaultProvider(); - assertThrows( - IllegalArgumentException.class, - () -> - provider.fromCredentials( - region, - AwsBasicCredentials.create("MOCK_ACCESS_KEY", "MOCK_SECRET_KEY"), - "MOCK_SESSION_TOKEN")); - } - @Test void testFromCredentials() throws JsonProcessingException { final AwsAuthProvider provider = From f0be8e48cd8e5c3d6e47ad987ef5a08e7d7ebe13 Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Fri, 3 Oct 2025 15:25:08 -0700 Subject: [PATCH 32/32] Remove unused import --- src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java index bf58321..aa6a337 100644 --- a/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java +++ b/src/main/java/com/infisical/sdk/auth/AwsAuthProvider.java @@ -12,7 +12,6 @@ import java.util.Base64; import java.util.List; import java.util.Map; -import java.util.regex.Pattern; import java.util.stream.Collectors; import lombok.Builder; import lombok.Data;