diff --git a/CHANGELOG.md b/CHANGELOG.md index e4c5983..0832a1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Parse timestamps as `ZonedDateTime` instead of `String` representation * Remove redundant `java.base` requirement from _module-info.java_ (#69) * Close Java HTTP Client when running on Java 21 or later (#70) +* Extend `AuthMethod` data model ### Dependencies * Updated Jackson to 2.16.0 diff --git a/src/main/java/de/stklcode/jvault/connector/model/response/embedded/AuthMethod.java b/src/main/java/de/stklcode/jvault/connector/model/response/embedded/AuthMethod.java index 2d5d410..12422d1 100644 --- a/src/main/java/de/stklcode/jvault/connector/model/response/embedded/AuthMethod.java +++ b/src/main/java/de/stklcode/jvault/connector/model/response/embedded/AuthMethod.java @@ -42,11 +42,14 @@ public final class AuthMethod implements Serializable { @JsonProperty("accessor") private String accessor; + @JsonProperty("deprecation_status") + private String deprecationStatus; + @JsonProperty("description") private String description; @JsonProperty("config") - private Map config; + private MountConfig config; @JsonProperty("external_entropy_access") private boolean externalEntropyAccess; @@ -54,6 +57,18 @@ public final class AuthMethod implements Serializable { @JsonProperty("local") private boolean local; + @JsonProperty("options") + private Map options; + + @JsonProperty("plugin_version") + private String pluginVersion; + + @JsonProperty("running_plugin_version") + private String runningPluginVersion; + + @JsonProperty("running_sha256") + private String runningSha256; + @JsonProperty("seal_wrap") private boolean sealWrap; @@ -91,6 +106,14 @@ public String getAccessor() { return accessor; } + /** + * @return Deprecation status + * @since 1.2 + */ + public String getDeprecationStatus() { + return deprecationStatus; + } + /** * @return Description */ @@ -100,8 +123,10 @@ public String getDescription() { /** * @return Configuration data + * @since 0.2 + * @since 1.2 Returns {@link MountConfig} instead of {@link Map} */ - public Map getConfig() { + public MountConfig getConfig() { return config; } @@ -120,6 +145,38 @@ public boolean isLocal() { return local; } + /** + * @return Options + * @since 1.2 + */ + public Map getOptions() { + return options; + } + + /** + * @return Plugin version + * @since 1.2 + */ + public String getPluginVersion() { + return pluginVersion; + } + + /** + * @return Running plugin version + * @since 1.2 + */ + public String getRunningPluginVersion() { + return runningPluginVersion; + } + + /** + * @return Running SHA256 + * @since 1.2 + */ + public String getRunningSha256() { + return runningSha256; + } + /** * @return Seal wrapping enabled * @since 1.1 @@ -150,13 +207,19 @@ public boolean equals(Object o) { sealWrap == that.sealWrap && Objects.equals(rawType, that.rawType) && Objects.equals(accessor, that.accessor) && + Objects.equals(deprecationStatus, that.deprecationStatus) && Objects.equals(description, that.description) && Objects.equals(config, that.config) && + Objects.equals(options, that.options) && + Objects.equals(pluginVersion, that.pluginVersion) && + Objects.equals(runningPluginVersion, that.runningPluginVersion) && + Objects.equals(runningSha256, that.runningSha256) && Objects.equals(uuid, that.uuid); } @Override public int hashCode() { - return Objects.hash(type, rawType, accessor, description, config, externalEntropyAccess, local, sealWrap, uuid); + return Objects.hash(type, rawType, accessor, deprecationStatus, description, config, externalEntropyAccess, + local, options, pluginVersion, runningPluginVersion, runningSha256, sealWrap, uuid); } } diff --git a/src/main/java/de/stklcode/jvault/connector/model/response/embedded/MountConfig.java b/src/main/java/de/stklcode/jvault/connector/model/response/embedded/MountConfig.java new file mode 100644 index 0000000..ab40e57 --- /dev/null +++ b/src/main/java/de/stklcode/jvault/connector/model/response/embedded/MountConfig.java @@ -0,0 +1,168 @@ +package de.stklcode.jvault.connector.model.response.embedded; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.io.Serializable; +import java.util.List; +import java.util.Objects; + +/** + * Embedded mount config output. + * + * @author Stefan Kalscheuer + * @since 1.2 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class MountConfig implements Serializable { + private static final long serialVersionUID = -8653909672663717792L; + + @JsonProperty("default_lease_ttl") + private Integer defaultLeaseTtl; + + @JsonProperty("max_lease_ttl") + private Integer maxLeaseTtl; + + @JsonProperty("force_no_cache") + private Boolean forceNoCache; + + @JsonProperty("token_type") + private String tokenType; + + @JsonProperty("audit_non_hmac_request_keys") + private List auditNonHmacRequestKeys; + + @JsonProperty("audit_non_hmac_response_keys") + private List auditNonHmacResponseKeys; + + @JsonProperty("listing_visibility") + private String listingVisibility; + + @JsonProperty("passthrough_request_headers") + private List passthroughRequestHeaders; + + @JsonProperty("allowed_response_headers") + private List allowedResponseHeaders; + + @JsonProperty("allowed_managed_keys") + private List allowedManagedKeys; + + @JsonProperty("delegated_auth_accessors") + private List delegatedAuthAccessors; + + @JsonProperty("user_lockout_config") + private UserLockoutConfig userLockoutConfig; + + /** + * @return Default lease TTL + */ + public Integer getDefaultLeaseTtl() { + return defaultLeaseTtl; + } + + /** + * @return Maximum lease TTL + */ + public Integer getMaxLeaseTtl() { + return maxLeaseTtl; + } + + /** + * @return Force no cache? + */ + public Boolean getForceNoCache() { + return forceNoCache; + } + + /** + * @return Token type + */ + public String getTokenType() { + return tokenType; + } + + /** + * @return Audit non HMAC request keys + */ + public List getAuditNonHmacRequestKeys() { + return auditNonHmacRequestKeys; + } + + /** + * @return Audit non HMAC response keys + */ + public List getAuditNonHmacResponseKeys() { + return auditNonHmacResponseKeys; + } + + /** + * @return Listing visibility + */ + public String getListingVisibility() { + return listingVisibility; + } + + /** + * @return Passthrough request headers + */ + public List getPassthroughRequestHeaders() { + return passthroughRequestHeaders; + } + + /** + * @return Allowed response headers + */ + public List getAllowedResponseHeaders() { + return allowedResponseHeaders; + } + + /** + * @return Allowed managed keys + */ + public List getAllowedManagedKeys() { + return allowedManagedKeys; + } + + /** + * @return Delegated auth accessors + */ + public List getDelegatedAuthAccessors() { + return delegatedAuthAccessors; + } + + /** + * @return User lockout config + */ + public UserLockoutConfig getUserLockoutConfig() { + return userLockoutConfig; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o == null || getClass() != o.getClass()) { + return false; + } + MountConfig that = (MountConfig) o; + return Objects.equals(defaultLeaseTtl, that.defaultLeaseTtl) && + Objects.equals(maxLeaseTtl, that.maxLeaseTtl) && + Objects.equals(forceNoCache, that.forceNoCache) && + Objects.equals(tokenType, that.tokenType) && + Objects.equals(auditNonHmacRequestKeys, that.auditNonHmacRequestKeys) && + Objects.equals(auditNonHmacResponseKeys, that.auditNonHmacResponseKeys) && + Objects.equals(listingVisibility, that.listingVisibility) && + Objects.equals(passthroughRequestHeaders, that.passthroughRequestHeaders) && + Objects.equals(allowedResponseHeaders, that.allowedResponseHeaders) && + Objects.equals(allowedManagedKeys, that.allowedManagedKeys) && + Objects.equals(delegatedAuthAccessors, that.delegatedAuthAccessors) && + Objects.equals(userLockoutConfig, that.userLockoutConfig); + } + + @Override + public int hashCode() { + return Objects.hash(defaultLeaseTtl, maxLeaseTtl, forceNoCache, tokenType, auditNonHmacRequestKeys, + auditNonHmacResponseKeys, listingVisibility, passthroughRequestHeaders, allowedResponseHeaders, + allowedManagedKeys, delegatedAuthAccessors, userLockoutConfig); + } +} diff --git a/src/main/java/de/stklcode/jvault/connector/model/response/embedded/UserLockoutConfig.java b/src/main/java/de/stklcode/jvault/connector/model/response/embedded/UserLockoutConfig.java new file mode 100644 index 0000000..225fc4b --- /dev/null +++ b/src/main/java/de/stklcode/jvault/connector/model/response/embedded/UserLockoutConfig.java @@ -0,0 +1,77 @@ +package de.stklcode.jvault.connector.model.response.embedded; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.io.Serializable; +import java.util.Objects; + +/** + * Embedded user lockout config output. + * + * @author Stefan Kalscheuer + * @since 1.2 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class UserLockoutConfig implements Serializable { + private static final long serialVersionUID = -8051060041593140550L; + + @JsonProperty("lockout_threshold") + private Integer lockoutThreshold; + + @JsonProperty("lockout_duration") + private Integer lockoutDuration; + + @JsonProperty("lockout_counter_reset_duration") + private Integer lockoutCounterResetDuration; + + @JsonProperty("lockout_disable") + private Boolean lockoutDisable; + + /** + * @return Lockout threshold + */ + public Integer getLockoutThreshold() { + return lockoutThreshold; + } + + /** + * @return Lockout duration + */ + public Integer getLockoutDuration() { + return lockoutDuration; + } + + /** + * @return Lockout counter reset duration + */ + public Integer getLockoutCounterResetDuration() { + return lockoutCounterResetDuration; + } + + /** + * @return Lockout disabled? + */ + public Boolean getLockoutDisable() { + return lockoutDisable; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o == null || getClass() != o.getClass()) { + return false; + } + UserLockoutConfig that = (UserLockoutConfig) o; + return Objects.equals(lockoutThreshold, that.lockoutThreshold) && + Objects.equals(lockoutDuration, that.lockoutDuration) && + Objects.equals(lockoutCounterResetDuration, that.lockoutCounterResetDuration) && + Objects.equals(lockoutDisable, that.lockoutDisable); + } + + @Override + public int hashCode() { + return Objects.hash(lockoutThreshold, lockoutDuration, lockoutCounterResetDuration, lockoutDisable); + } +} diff --git a/src/test/java/de/stklcode/jvault/connector/model/response/AuthMethodsResponseTest.java b/src/test/java/de/stklcode/jvault/connector/model/response/AuthMethodsResponseTest.java index acdd829..890b9c2 100644 --- a/src/test/java/de/stklcode/jvault/connector/model/response/AuthMethodsResponseTest.java +++ b/src/test/java/de/stklcode/jvault/connector/model/response/AuthMethodsResponseTest.java @@ -46,7 +46,10 @@ class AuthMethodsResponseTest extends AbstractModelTest { private static final String TK_ACCESSOR = "auth_token_ac0dd95a"; private static final String TK_DESCR = "token based credentials"; private static final Integer TK_LEASE_TTL = 0; + private static final Boolean TK_FORCE_NO_CACHE = false; private static final Integer TK_MAX_LEASE_TTL = 0; + private static final String TK_TOKEN_TYPE = "default-service"; + private static final String TK_RUNNING_PLUGIN_VERSION = "v1.15.3+builtin.vault"; private static final String RES_JSON = "{\n" + " \"data\": {" + @@ -62,9 +65,15 @@ class AuthMethodsResponseTest extends AbstractModelTest { " \"" + TK_PATH + "\": {\n" + " \"config\": {\n" + " \"default_lease_ttl\": " + TK_LEASE_TTL + ",\n" + - " \"max_lease_ttl\": " + TK_MAX_LEASE_TTL + "\n" + + " \"force_no_cache\": " + TK_FORCE_NO_CACHE + ",\n" + + " \"max_lease_ttl\": " + TK_MAX_LEASE_TTL + ",\n" + + " \"token_type\": \"" + TK_TOKEN_TYPE + "\"\n" + " },\n" + " \"description\": \"" + TK_DESCR + "\",\n" + + " \"options\": null,\n" + + " \"plugin_version\": \"\",\n" + + " \"running_plugin_version\": \"" + TK_RUNNING_PLUGIN_VERSION + "\",\n" + + " \"running_sha256\": \"\",\n" + " \"type\": \"" + TK_TYPE + "\",\n" + " \"uuid\": \"" + TK_UUID + "\",\n" + " \"accessor\": \"" + TK_ACCESSOR + "\",\n" + @@ -137,15 +146,16 @@ void jsonRoundtrip() { assertTrue(method.isLocal(), "Unexpected local flag for Token"); assertFalse(method.isExternalEntropyAccess(), "Unexpected external entropy flag for Token"); assertFalse(method.isSealWrap(), "Unexpected seal wrap flag for GitHub"); + assertEquals("", method.getPluginVersion(), "Unexpected plugin version"); + assertEquals(TK_RUNNING_PLUGIN_VERSION, method.getRunningPluginVersion(), "Unexpected running plugin version"); + assertEquals("", method.getRunningSha256(), "Unexpected running SHA256"); assertNotNull(method.getConfig(), "Missing config for Token"); - assertEquals( - Map.of( - "default_lease_ttl", TK_LEASE_TTL.toString(), - "max_lease_ttl", TK_MAX_LEASE_TTL.toString() - ), - method.getConfig(), - "Unexpected config for Token" - ); + assertEquals(TK_LEASE_TTL, method.getConfig().getDefaultLeaseTtl(), "Unexpected default TTL"); + assertEquals(TK_MAX_LEASE_TTL, method.getConfig().getMaxLeaseTtl(), "Unexpected max TTL"); + assertEquals(TK_FORCE_NO_CACHE, method.getConfig().getForceNoCache(), "Unexpected force no cache flag"); + assertEquals(TK_TOKEN_TYPE, method.getConfig().getTokenType(), "Unexpected token type"); + + assertNull(method.getOptions(), "Unexpected options"); } } diff --git a/src/test/java/de/stklcode/jvault/connector/model/response/embedded/MountConfigTest.java b/src/test/java/de/stklcode/jvault/connector/model/response/embedded/MountConfigTest.java new file mode 100644 index 0000000..68660be --- /dev/null +++ b/src/test/java/de/stklcode/jvault/connector/model/response/embedded/MountConfigTest.java @@ -0,0 +1,103 @@ +package de.stklcode.jvault.connector.model.response.embedded; + +import com.fasterxml.jackson.core.JsonProcessingException; +import de.stklcode.jvault.connector.model.AbstractModelTest; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Unit test for {@link MountConfig}. + * + * @author Stefan Kalscheuer + */ +class MountConfigTest extends AbstractModelTest { + private static final Integer DEFAULT_LEASE_TTL = 1800; + private static final Integer MAX_LEASE_TTL = 3600; + private static final Boolean FORCE_NO_CACHE = false; + private static final String TOKEN_TYPE = "default-service"; + private static final String AUDIT_NON_HMAC_REQ_KEYS_1 = "req1"; + private static final String AUDIT_NON_HMAC_REQ_KEYS_2 = "req2"; + private static final String AUDIT_NON_HMAC_RES_KEYS_1 = "res1"; + private static final String AUDIT_NON_HMAC_RES_KEYS_2 = "res2"; + private static final String LISTING_VISIBILITY = "unauth"; + private static final String PT_REQ_HEADER_1 = "prh1"; + private static final String PT_REQ_HEADER_2 = "prh2"; + private static final String ALLOWED_RES_HEADER_1 = "arh1"; + private static final String ALLOWED_RES_HEADER_2 = "arh2"; + private static final String ALLOWED_MANAGED_KEY_1 = "amk1"; + private static final String ALLOWED_MANAGED_KEY_2 = "amk2"; + private static final String DEL_AUTH_ACCESSOR_1 = "daa1"; + private static final String DEL_AUTH_ACCESSOR_2 = "daa2"; + private static final Integer LOCKOUT_THRESH = 7200; + private static final Integer LOCKOUT_DURATION = 86400; + private static final Integer LOCKOUT_CNT_RESET_DURATION = 43200; + private static final Boolean LOCKOUT_DISABLE = false; + + private static final String RES_JSON = "{\n" + + " \"default_lease_ttl\": " + DEFAULT_LEASE_TTL + ",\n" + + " \"force_no_cache\": " + FORCE_NO_CACHE + ",\n" + + " \"max_lease_ttl\": " + MAX_LEASE_TTL + ",\n" + + " \"token_type\": \"" + TOKEN_TYPE + "\",\n" + + " \"audit_non_hmac_request_keys\": [\"" + AUDIT_NON_HMAC_REQ_KEYS_1 + "\", \"" + AUDIT_NON_HMAC_REQ_KEYS_2 + "\"],\n" + + " \"audit_non_hmac_response_keys\": [\"" + AUDIT_NON_HMAC_RES_KEYS_1 + "\", \"" + AUDIT_NON_HMAC_RES_KEYS_2 + "\"],\n" + + " \"listing_visibility\": \"" + LISTING_VISIBILITY + "\",\n" + + " \"passthrough_request_headers\": [\"" + PT_REQ_HEADER_1 + "\", \"" + PT_REQ_HEADER_2 + "\"],\n" + + " \"allowed_response_headers\": [\"" + ALLOWED_RES_HEADER_1 + "\", \"" + ALLOWED_RES_HEADER_2 + "\"],\n" + + " \"allowed_managed_keys\": [\"" + ALLOWED_MANAGED_KEY_1 + "\", \"" + ALLOWED_MANAGED_KEY_2 + "\"],\n" + + " \"delegated_auth_accessors\": [\"" + DEL_AUTH_ACCESSOR_1 + "\", \"" + DEL_AUTH_ACCESSOR_2 + "\"],\n" + + " \"user_lockout_config\": {\n" + + " \"lockout_threshold\": " + LOCKOUT_THRESH + ",\n" + + " \"lockout_duration\": " + LOCKOUT_DURATION + ",\n" + + " \"lockout_counter_reset_duration\": " + LOCKOUT_CNT_RESET_DURATION + ",\n" + + " \"lockout_disable\": " + LOCKOUT_DISABLE + "\n" + + " }\n" + + "}"; + + MountConfigTest() { + super(MountConfig.class); + } + + @Override + protected MountConfig createFull() { + try { + return objectMapper.readValue(RES_JSON, MountConfig.class); + } catch (JsonProcessingException e) { + fail("Creation of full model instance failed", e); + return null; + } + } + + /** + * Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation). + */ + @Test + void jsonRoundtrip() { + MountConfig mountConfig = assertDoesNotThrow( + () -> objectMapper.readValue(RES_JSON, MountConfig.class), + "MountConfig deserialization failed" + ); + assertNotNull(mountConfig, "Parsed response is NULL"); + + // Verify data. + assertEquals(DEFAULT_LEASE_TTL, mountConfig.getDefaultLeaseTtl(), "Unexpected default lease TTL"); + assertEquals(MAX_LEASE_TTL, mountConfig.getMaxLeaseTtl(), "Unexpected max lease TTL"); + assertEquals(FORCE_NO_CACHE, mountConfig.getForceNoCache(), "Unexpected force no cache"); + assertEquals(TOKEN_TYPE, mountConfig.getTokenType(), "Unexpected token type"); + assertEquals(List.of(AUDIT_NON_HMAC_REQ_KEYS_1, AUDIT_NON_HMAC_REQ_KEYS_2), mountConfig.getAuditNonHmacRequestKeys(), "Unexpected audit no HMAC request keys"); + assertEquals(List.of(AUDIT_NON_HMAC_RES_KEYS_1, AUDIT_NON_HMAC_RES_KEYS_2), mountConfig.getAuditNonHmacResponseKeys(), "Unexpected audit no HMAC response keys"); + assertEquals(LISTING_VISIBILITY, mountConfig.getListingVisibility(), "Unexpected listing visibility"); + assertEquals(List.of(PT_REQ_HEADER_1, PT_REQ_HEADER_2), mountConfig.getPassthroughRequestHeaders(), "Unexpected passthrough request headers"); + assertEquals(List.of(ALLOWED_RES_HEADER_1, ALLOWED_RES_HEADER_2), mountConfig.getAllowedResponseHeaders(), "Unexpected allowed response headers"); + assertEquals(List.of(ALLOWED_MANAGED_KEY_1, ALLOWED_MANAGED_KEY_2), mountConfig.getAllowedManagedKeys(), "Unexpected allowed managed keys"); + assertEquals(List.of(DEL_AUTH_ACCESSOR_1, DEL_AUTH_ACCESSOR_2), mountConfig.getDelegatedAuthAccessors(), "Unexpected delegate auth accessors"); + assertNotNull(mountConfig.getUserLockoutConfig(), "Missing user lockout config"); + var ulc = mountConfig.getUserLockoutConfig(); + assertEquals(LOCKOUT_THRESH, ulc.getLockoutThreshold(), "Unexpected lockout threshold"); + assertEquals(LOCKOUT_DURATION, ulc.getLockoutDuration(), "Unexpected lockout duration"); + assertEquals(LOCKOUT_CNT_RESET_DURATION, ulc.getLockoutCounterResetDuration(), "Unexpected lockout counter reset duration"); + assertEquals(LOCKOUT_DISABLE, ulc.getLockoutDisable(), "Unexpected lockout disable"); + } +}