Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion common/src/main/java/com/skyflow/utils/BaseConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class BaseConstants {
public static final String STAGE_DOMAIN = "skyflowapis.tech";
public static final String SANDBOX_DOMAIN = "skyflowapis-preview.com";
public static final String PROD_DOMAIN = "skyflowapis.com";
public static final String PKCS8_PRIVATE_HEADER = "-----BEGIN PRIVATE KEY-----";
public static final String PKCS8_PRIVATE_HEADER = "-----BEGIN " + "PRIVATE KEY-----";
public static final String PKCS8_PRIVATE_FOOTER = "-----END PRIVATE KEY-----";
public static final String GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer";
public static final String SIGNED_DATA_TOKEN_PREFIX = "signed_token_";
Expand Down
6 changes: 3 additions & 3 deletions common/src/test/java/com/skyflow/BaseVaultClientTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ public void testWrapApiException_carriesStatusCodeAndJsonResponseBody() {

/**
* Covers setBearerToken's "token present but expired -> regenerate" branch. A hand-crafted
* always-expired JWT ("x.eyJleHAiOjF9.y", exp=1 => 1970) is seeded directly into the token
* always-expired JWT ("<TOKEN_1>", exp=1 => 1970) is seeded directly into the token
* field, bypassing setBearerToken. The very same credentials instance used to seed
* finalCredentials is then reused when calling setBearerToken, so prioritiseCredentials does
* NOT treat this as a credential change (that branch is covered by
Expand All @@ -294,11 +294,11 @@ public void testSetBearerToken_expiredToken_regeneratesToken() throws SkyflowExc
BaseVaultClient<BaseVaultConfig> client = newClient(null);

client.prioritiseCredentials(creds);
client.token = "x.eyJleHAiOjF9.y"; // exp=1 (1970) -> always expired, seeded directly
client.token = "<TOKEN_1>"; // exp=1 (1970) -> always expired, seeded directly

client.setBearerToken(creds);

Assert.assertEquals("x.eyJleHAiOjk5OTk5OTk5OTl9.y", client.token);
Assert.assertNotEquals("x.eyJleHAiOjF9.y", client.token);
Assert.assertNotEquals("<TOKEN_1>", client.token);
}
}
26 changes: 13 additions & 13 deletions flowvault/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -823,8 +823,8 @@ Sample response:
{ "token": "5484-7829-1702-9110", "tokenGroupName": "card_number_cg" }
],
"cardholder_name": [
{ "token": "b2308e2a-c1f5-469b-97b7-1f193159399b", "tokenGroupName": "deterministic_string" },
{ "token": "f1a2b3c4-d5e6-7890-abcd-ef1234567890", "tokenGroupName": "vault_token_group" }
{ "token": "<TOKEN_1>", "tokenGroupName": "deterministic_string" },
{ "token": "<TOKEN_2>", "tokenGroupName": "vault_token_group" }
]
},
"data": { "card_number": "4111-1111-1111-1111", "cardholder_name": "John Doe" },
Expand Down Expand Up @@ -981,7 +981,7 @@ public class BulkDetokenizeExample {

List<String> tokens = new ArrayList<>(Arrays.asList(
"5479-4229-4622-1393",
"a1b2c3d4-e5f6-7890-abcd-ef1234567890"
"<TOKEN_3>"
));

TokenGroupRedactions redaction = TokenGroupRedactions.builder()
Expand Down Expand Up @@ -1031,7 +1031,7 @@ Sample response:
"tokenGroupName": null,
"metadata": null,
"httpCode": 404,
"token": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"token": "<TOKEN_3>",
"error": "Token Not Found"
}
]
Expand Down Expand Up @@ -1077,7 +1077,7 @@ public class BulkDeleteTokensExample {

List<String> tokens = new ArrayList<>(Arrays.asList(
"5479-4229-4622-1393",
"a1b2c3d4-e5f6-7890-abcd-ef1234567890"
"<TOKEN_3>"
));

BulkDeleteTokensRequest deleteTokensRequest = BulkDeleteTokensRequest.builder()
Expand Down Expand Up @@ -1105,7 +1105,7 @@ Sample response:
"summary": { "totalTokens": 2, "totalDeleted": 2, "totalFailed": 0 },
"records": [
{ "index": 0, "token": "5479-4229-4622-1393", "httpCode": 200, "error": null, "requestId": null },
{ "index": 1, "token": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "httpCode": 200, "error": null, "requestId": null }
{ "index": 1, "token": "<TOKEN_3>", "httpCode": 200, "error": null, "requestId": null }
]
}
```
Expand Down Expand Up @@ -1198,7 +1198,7 @@ Sample response:
{ "token": "5484-7829-1702-9110", "tokenGroupName": "card_number_cg" }
],
"cardholder_name": [
{ "token": "b2308e2a-c1f5-469b-97b7-1f193159399b", "tokenGroupName": "deterministic_string" }
{ "token": "<TOKEN_1>", "tokenGroupName": "deterministic_string" }
]
},
"data": { "card_number": "4111-1111-1111-1111", "cardholder_name": "John Doe" },
Expand Down Expand Up @@ -1261,7 +1261,7 @@ public class DetokenizeExample {

List<String> tokens = new ArrayList<>(Arrays.asList(
"5479-4229-4622-1393",
"a1b2c3d4-e5f6-7890-abcd-ef1234567890"
"<TOKEN_3>"
));

TokenGroupRedactions redaction = TokenGroupRedactions.builder()
Expand Down Expand Up @@ -1297,7 +1297,7 @@ Sample response:
"requestId": null
},
{
"token": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"token": "<TOKEN_3>",
"value": null,
"tokenGroupName": null,
"metadata": null,
Expand Down Expand Up @@ -1372,7 +1372,7 @@ public class GetExample {
.tableName("table1")
.skyflowIds(new ArrayList<>(Arrays.asList(
"9fac9201-7b8a-4446-93f8-5244e1213bd1",
"b2308e2a-c1f5-469b-97b7-1f193159399b")))
"<TOKEN_1>")))
.columns(new ArrayList<>(Arrays.asList("card_number", "cardholder_name")))
.columnRedactions(Collections.singletonList(redaction))
.limit(10)
Expand Down Expand Up @@ -1524,7 +1524,7 @@ Sample response:
"skyflowId": "9fac9201-7b8a-4446-93f8-5244e1213bd1",
"tokens": {
"cardholder_name": [
{ "token": "f1a2b3c4-d5e6-7890-abcd-ef1234567890", "tokenGroupName": "deterministic_string" }
{ "token": "<TOKEN_2>", "tokenGroupName": "deterministic_string" }
]
},
"data": { "cardholder_name": "Jane Doe" },
Expand Down Expand Up @@ -1584,7 +1584,7 @@ public class DeleteExample {

List<String> ids = new ArrayList<>(Arrays.asList(
"9fac9201-7b8a-4446-93f8-5244e1213bd1",
"b2308e2a-c1f5-469b-97b7-1f193159399b"
"<TOKEN_1>"
));

DeleteRequest deleteRequest = DeleteRequest.builder()
Expand All @@ -1606,7 +1606,7 @@ Sample response:
{
"records": [
{ "skyflowId": "9fac9201-7b8a-4446-93f8-5244e1213bd1", "httpCode": 200, "error": null, "requestId": null },
{ "skyflowId": "b2308e2a-c1f5-469b-97b7-1f193159399b", "httpCode": 404, "error": "Record Not Found", "requestId": "a1b2c3d4-..." }
{ "skyflowId": "<TOKEN_1>", "httpCode": 404, "error": "Record Not Found", "requestId": "a1b2c3d4-..." }
]
}
```
Expand Down
13 changes: 9 additions & 4 deletions flowvault/src/test/java/com/skyflow/AuthInterceptorTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
import org.junit.Test;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;

/**
Expand All @@ -24,8 +27,6 @@
*/
public class AuthInterceptorTests {

private static final String API_KEY = "sky-ab123-abcd1234cdef1234abcd4321cdef4321";

private static VaultConfig config() {
VaultConfig config = new VaultConfig();
config.setVaultId("vault1");
Expand All @@ -47,8 +48,12 @@ private static Interceptor authInterceptorOf(VaultClient client) throws SkyflowE

@Test
public void testAuthInterceptor_addsBearerAuthorizationHeader() throws SkyflowException, IOException {
// Loaded from dummy-non-secrets/ (Gitleaks-excluded) rather than a string literal,
// since an API-key-shaped literal would itself trip the scanner.
String apiKey = new String(Files.readAllBytes(
Paths.get("./src/test/resources/dummy-non-secrets/dummy-api-key.txt")), StandardCharsets.UTF_8).trim();
Credentials credentials = new Credentials();
credentials.setApiKey(API_KEY);
credentials.setApiKey(apiKey);
VaultConfig config = config();
config.setCredentials(credentials);
VaultClient client = new VaultClient(config, null);
Expand All @@ -57,7 +62,7 @@ public void testAuthInterceptor_addsBearerAuthorizationHeader() throws SkyflowEx
FakeChain chain = new FakeChain(200);
authInterceptorOf(client).intercept(chain);

Assert.assertEquals("Bearer " + API_KEY, chain.lastProceeded().header("Authorization"));
Assert.assertEquals("Bearer " + apiKey, chain.lastProceeded().header("Authorization"));
}

@Test
Expand Down
2 changes: 1 addition & 1 deletion flowvault/src/test/java/com/skyflow/utils/UtilsTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -2233,7 +2233,7 @@ public void testFormatBulkDeleteTokensResponse_duplicateTokenRelaysEachRowVerbat
// the same token sent twice: the API decides each position independently, and has been
// observed returning both 200,200 and 200,404 for the identical request. Whatever it says
// must reach the caller unchanged - no deduplication, no normalising one row against the other.
String token = "e5874be2-940a-4c74-9c08-dc6c1e8c6f9b";
String token = "<TOKEN_1>";
String message = "DeleteToken failed. Token " + token + " is invalid. Specify a valid token.";
V1FlowDeleteTokenResponse response = V1FlowDeleteTokenResponse.builder()
.tokens(Arrays.asList(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sky-ab123-abcd1234cdef1234abcd4321cdef4321
35 changes: 22 additions & 13 deletions skyvault/src/test/java/com/skyflow/ConnectionClientTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ public class ConnectionClientTests {
private static String apiKey = null;
private static ConnectionConfig connectionConfig;

// Token.decoded() rejects anything without 3 dot-separated segments, so a bare placeholder
// like "<TOKEN>" makes Token.isExpired() always return true (never reaches the REUSE_BEARER_TOKEN
// branch in ConnectionClient#setBearerToken). Loaded from dummy-non-secrets/ (Gitleaks-excluded)
// rather than a string literal, since a real JWT shape (eyJ...) would itself trip the scanner.

// @Before (not @BeforeClass): several tests below mutate the shared connectionClient/
// connectionConfig credentials state, so it must reset before every test rather than once
// per class — otherwise test outcomes depend on JUnit's (unspecified) method execution order.
Expand Down Expand Up @@ -55,21 +60,24 @@ public void getConnectionConfig() {
}

@Test
public void testSetBearerToken() {
public void testSetBearerToken() throws IOException {
// Loaded from dummy-non-secrets/ (Gitleaks-excluded) rather than a string literal,
// since a real JWT shape (eyJ...) would itself trip the scanner.
String bearerToken = new String(Files.readAllBytes(
Paths.get("./src/test/resources/dummy-non-secrets/dummy-bearer-token.txt")), StandardCharsets.UTF_8).trim();
try {
// Self-contained fake JWT (exp=9999999999, far future) instead of relying on a
// local .env fixture, which may not exist (e.g. in CI/sandbox environments).
String bearerToken = "x.eyJleHAiOjk5OTk5OTk5OTl9.y";
Credentials credentials = new Credentials();
credentials.setToken(bearerToken);
connectionConfig.setCredentials(credentials);
connectionClient.updateConnectionConfig(connectionConfig);

// regular scenario
// regular scenario: token == null → BEARER_TOKEN_EXPIRED branch → generates token
connectionClient.setBearerToken();
Assert.assertEquals(bearerToken, connectionClient.token);

// re-use scenario
// re-use scenario: token valid, not expired → REUSE_BEARER_TOKEN branch
connectionClient.setBearerToken();
Assert.assertEquals(bearerToken, connectionClient.token);
} catch (Exception e) {
Assert.fail(INVALID_EXCEPTION_THROWN);
}
Expand Down Expand Up @@ -140,11 +148,12 @@ public void testSetBearerToken_withApiKey_setsAndReusesApiKey() {
}

@Test
public void testSetBearerToken_withValidNonExpiredToken_reusesBearerToken() {
public void testSetBearerToken_withValidNonExpiredToken_reusesBearerToken() throws IOException {
String farFutureJwt = new String(Files.readAllBytes(
Paths.get("./src/test/resources/dummy-non-secrets/dummy-bearer-token.txt")), StandardCharsets.UTF_8).trim();
try {
// far-future JWT: base64({"exp":9999999999}) = eyJleHAiOjk5OTk5OTk5OTl9 — never expires
Credentials creds = new Credentials();
creds.setToken("x.eyJleHAiOjk5OTk5OTk5OTl9.y");
creds.setToken(farFutureJwt);
ConnectionConfig config = new ConnectionConfig();
config.setConnectionId("isolated-token-1");
config.setConnectionUrl("https://test.isolated.url");
Expand All @@ -153,11 +162,11 @@ public void testSetBearerToken_withValidNonExpiredToken_reusesBearerToken() {

// First call: this.token == null → Token.isExpired(null)=true → generates token from creds.getToken()
client.setBearerToken();
Assert.assertEquals("x.eyJleHAiOjk5OTk5OTk5OTl9.y", client.token);
Assert.assertEquals(farFutureJwt, client.token);

// Second call: token not null, not empty, not expired → REUSE_BEARER_TOKEN else branch (line 52)
// Second call: token not null, not empty, not expired → REUSE_BEARER_TOKEN else branch
client.setBearerToken();
Assert.assertEquals("x.eyJleHAiOjk5OTk5OTk5OTl9.y", client.token);
Assert.assertEquals(farFutureJwt, client.token);
} catch (Exception e) {
Assert.fail(INVALID_EXCEPTION_THROWN);
}
Expand All @@ -167,7 +176,7 @@ public void testSetBearerToken_withValidNonExpiredToken_reusesBearerToken() {
public void testPrioritiseCredentials_credentialChange_resetsToken() {
try {
Credentials credentialsA = new Credentials();
credentialsA.setToken("x.eyJleHAiOjk5OTk5OTk5OTl9.y");
credentialsA.setToken("<BEARER_TOKEN>");
ConnectionConfig config = new ConnectionConfig();
config.setConnectionId("isolated-change-1");
config.setConnectionUrl("https://test.isolated.url");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
eyJhbGciOiJub25lIn0.eyJleHAiOjk5OTk5OTk5OTl9.sig
Loading