From 15f514f87743ad338632942b2c575c423e69b85f Mon Sep 17 00:00:00 2001 From: Stefan Kalscheuer Date: Mon, 8 Sep 2025 10:25:39 +0200 Subject: [PATCH] add token_bound_cidrs field to AppRoleSecret model (#110) --- CHANGELOG.md | 1 + .../jvault/connector/model/AppRoleSecret.java | 39 +++++++++++++++++-- .../connector/model/AppRoleSecretTest.java | 20 +++++++++- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 883e5de..3f60db8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ### Improvements * Extract API paths into a utility class (#108) * Encode user-provided URL parts (#109) +* Add `token_bound_cidrs` field to `AppRoleSecret` model (#110) ### Fix * Prevent potential off-by-1 error in internal `mapOf()` helper (#107) diff --git a/src/main/java/de/stklcode/jvault/connector/model/AppRoleSecret.java b/src/main/java/de/stklcode/jvault/connector/model/AppRoleSecret.java index e6dec23..5f1292c 100644 --- a/src/main/java/de/stklcode/jvault/connector/model/AppRoleSecret.java +++ b/src/main/java/de/stklcode/jvault/connector/model/AppRoleSecret.java @@ -32,7 +32,7 @@ import java.util.Objects; */ @JsonIgnoreProperties(ignoreUnknown = true) public final class AppRoleSecret implements Serializable { - private static final long serialVersionUID = -3401074170145792641L; + private static final long serialVersionUID = 3079272087137299819L; @JsonProperty("secret_id") @JsonInclude(JsonInclude.Include.NON_NULL) @@ -47,6 +47,8 @@ public final class AppRoleSecret implements Serializable { private List cidrList; + private List tokenBoundCidrs; + @JsonProperty(value = "creation_time", access = JsonProperty.Access.WRITE_ONLY) private String creationTime; @@ -137,6 +139,36 @@ public final class AppRoleSecret implements Serializable { return String.join(",", cidrList); } + /** + * @return list of bound CIDR subnets of associated tokens + * @since 1.5.3 + */ + public List getTokenBoundCidrs() { + return tokenBoundCidrs; + } + + /** + * @param boundCidrList list of subnets in CIDR notation to bind role to + * @since 1.5.3 + */ + @JsonSetter("token_bound_cidrs") + public void setTokenBoundCidrs(final List boundCidrList) { + this.tokenBoundCidrs = boundCidrList; + } + + /** + * @return list of subnets in CIDR notation as comma-separated {@link String} + * @since 1.5.3 + */ + @JsonGetter("token_bound_cidrs") + @JsonInclude(JsonInclude.Include.NON_EMPTY) + public String getTokenBoundCidrsString() { + if (tokenBoundCidrs == null || tokenBoundCidrs.isEmpty()) { + return ""; + } + return String.join(",", tokenBoundCidrs); + } + /** * @return Creation time */ @@ -184,6 +216,7 @@ public final class AppRoleSecret implements Serializable { Objects.equals(accessor, that.accessor) && Objects.equals(metadata, that.metadata) && Objects.equals(cidrList, that.cidrList) && + Objects.equals(tokenBoundCidrs, that.tokenBoundCidrs) && Objects.equals(creationTime, that.creationTime) && Objects.equals(expirationTime, that.expirationTime) && Objects.equals(lastUpdatedTime, that.lastUpdatedTime) && @@ -193,7 +226,7 @@ public final class AppRoleSecret implements Serializable { @Override public int hashCode() { - return Objects.hash(id, accessor, metadata, cidrList, creationTime, expirationTime, lastUpdatedTime, numUses, - ttl); + return Objects.hash(id, accessor, metadata, cidrList, tokenBoundCidrs, creationTime, expirationTime, + lastUpdatedTime, numUses, ttl); } } diff --git a/src/test/java/de/stklcode/jvault/connector/model/AppRoleSecretTest.java b/src/test/java/de/stklcode/jvault/connector/model/AppRoleSecretTest.java index fb3d34b..1a5df64 100644 --- a/src/test/java/de/stklcode/jvault/connector/model/AppRoleSecretTest.java +++ b/src/test/java/de/stklcode/jvault/connector/model/AppRoleSecretTest.java @@ -39,6 +39,7 @@ class AppRoleSecretTest extends AbstractModelTest { "number", 1337 ); private static final List TEST_CIDR = List.of("203.0.113.0/24", "198.51.100.0/24"); + private static final List TEST_TOKEN_CIDR = List.of("192.0.2.0/24", "198.51.100.0/24"); AppRoleSecretTest() { super(AppRoleSecret.class); @@ -61,6 +62,8 @@ class AppRoleSecretTest extends AbstractModelTest { assertNull(secret.getMetadata()); assertNull(secret.getCidrList()); assertEquals("", secret.getCidrListString()); + assertNull(secret.getTokenBoundCidrs()); + assertEquals("", secret.getTokenBoundCidrsString()); assertNull(secret.getCreationTime()); assertNull(secret.getExpirationTime()); assertNull(secret.getLastUpdatedTime()); @@ -74,6 +77,8 @@ class AppRoleSecretTest extends AbstractModelTest { assertNull(secret.getMetadata()); assertNull(secret.getCidrList()); assertEquals("", secret.getCidrListString()); + assertNull(secret.getTokenBoundCidrs()); + assertEquals("", secret.getTokenBoundCidrsString()); assertNull(secret.getCreationTime()); assertNull(secret.getExpirationTime()); assertNull(secret.getLastUpdatedTime()); @@ -87,6 +92,8 @@ class AppRoleSecretTest extends AbstractModelTest { assertEquals(TEST_META, secret.getMetadata()); assertEquals(TEST_CIDR, secret.getCidrList()); assertEquals(String.join(",", TEST_CIDR), secret.getCidrListString()); + assertNull(secret.getTokenBoundCidrs()); + assertEquals("", secret.getTokenBoundCidrsString()); assertNull(secret.getCreationTime()); assertNull(secret.getExpirationTime()); assertNull(secret.getLastUpdatedTime()); @@ -108,6 +115,15 @@ class AppRoleSecretTest extends AbstractModelTest { secret.setCidrList(null); assertNull(secret.getCidrList()); assertEquals("", secret.getCidrListString()); + + assertNull(secret.getTokenBoundCidrs()); + assertEquals("", secret.getTokenBoundCidrsString()); + secret.setTokenBoundCidrs(TEST_TOKEN_CIDR); + assertEquals(TEST_TOKEN_CIDR, secret.getTokenBoundCidrs()); + assertEquals(String.join(",", TEST_TOKEN_CIDR), secret.getTokenBoundCidrsString()); + secret.setTokenBoundCidrs(null); + assertNull(secret.getTokenBoundCidrs()); + assertEquals("", secret.getTokenBoundCidrsString()); } /** @@ -159,7 +175,8 @@ class AppRoleSecretTest extends AbstractModelTest { // Those fields should be deserialized from JSON though. String secretJson4 = "{\"secret_id\":\"abc123\",\"metadata\":{\"number\":1337,\"foo\":\"bar\"}," + - "\"cidr_list\":[\"203.0.113.0/24\",\"198.51.100.0/24\"],\"secret_id_accessor\":\"TEST_ACCESSOR\"," + + "\"cidr_list\":[\"203.0.113.0/24\",\"198.51.100.0/24\"],\"cidr_list\":[\"192.0.2.0/24\",\"198.51.100.0/24\"]," + + "\"secret_id_accessor\":\"TEST_ACCESSOR\"," + "\"creation_time\":\"TEST_CREATION\",\"expiration_time\":\"TEST_EXPIRATION\"," + "\"last_updated_time\":\"TEST_LASTUPDATE\",\"secret_id_num_uses\":678,\"secret_id_ttl\":12345}"; secret2 = assertDoesNotThrow(() -> objectMapper.readValue(secretJson4, AppRoleSecret.class), "Deserialization failed"); @@ -181,6 +198,7 @@ class AppRoleSecretTest extends AbstractModelTest { private static String commaSeparatedToList(String json) { return json.replaceAll("\"cidr_list\":\"([^\"]*)\"", "\"cidr_list\":[$1]") + .replaceAll("\"token_bound_cidrs\":\"([^\"]*)\"", "\"token_bound_cidrs\":[$1]") .replaceAll("(\\d+\\.\\d+\\.\\d+\\.\\d+/\\d+)", "\"$1\""); } }