diff --git a/CHANGELOG.md b/CHANGELOG.md index 49ee8c3..8f25399 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * Add support for `wrap_info` in data response models * Dependency updates * model and response classes implement `Serializable` (#57) +* split `SercretResponse` into `PlainSecretResponse` and `MetaSecretResponse` subclasses (common API unchanged) ### Test * Tested against Vault 1.10.0 diff --git a/src/main/java/de/stklcode/jvault/connector/HTTPVaultConnector.java b/src/main/java/de/stklcode/jvault/connector/HTTPVaultConnector.java index feccf1f..6b5687c 100644 --- a/src/main/java/de/stklcode/jvault/connector/HTTPVaultConnector.java +++ b/src/main/java/de/stklcode/jvault/connector/HTTPVaultConnector.java @@ -411,7 +411,7 @@ public class HTTPVaultConnector implements VaultConnector { public final SecretResponse read(final String key) throws VaultConnectorException { requireAuth(); /* Issue request and parse secret response */ - return request.get(key, emptyMap(), token, SecretResponse.class); + return request.get(key, emptyMap(), token, PlainSecretResponse.class); } @Override @@ -423,7 +423,7 @@ public class HTTPVaultConnector implements VaultConnector { args.put("version", version.toString()); } - return request.get(mount + PATH_DATA + key, args, token, SecretResponse.class); + return request.get(mount + PATH_DATA + key, args, token, MetaSecretResponse.class); } @Override diff --git a/src/main/java/de/stklcode/jvault/connector/model/response/AppRoleResponse.java b/src/main/java/de/stklcode/jvault/connector/model/response/AppRoleResponse.java index b13dd85..862dc96 100644 --- a/src/main/java/de/stklcode/jvault/connector/model/response/AppRoleResponse.java +++ b/src/main/java/de/stklcode/jvault/connector/model/response/AppRoleResponse.java @@ -17,13 +17,9 @@ package de.stklcode.jvault.connector.model.response; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.databind.ObjectMapper; -import de.stklcode.jvault.connector.exception.InvalidResponseException; +import com.fasterxml.jackson.annotation.JsonProperty; import de.stklcode.jvault.connector.model.AppRole; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; import java.util.Objects; /** @@ -34,27 +30,11 @@ import java.util.Objects; */ @JsonIgnoreProperties(ignoreUnknown = true) public final class AppRoleResponse extends VaultDataResponse { - private static final long serialVersionUID = -7817890935652200399L; + private static final long serialVersionUID = -6536422219633829177L; + @JsonProperty("data") private AppRole role; - @Override - public void setData(final Map data) throws InvalidResponseException { - var mapper = new ObjectMapper(); - try { - /* null empty strings on list objects */ - Map filteredData = new HashMap<>(data.size(), 1); - data.forEach((k, v) -> { - if (!(v instanceof String && ((String) v).isEmpty())) { - filteredData.put(k, v); - } - }); - this.role = mapper.readValue(mapper.writeValueAsString(filteredData), AppRole.class); - } catch (IOException e) { - throw new InvalidResponseException("Failed deserializing response", e); - } - } - /** * @return The role */ diff --git a/src/main/java/de/stklcode/jvault/connector/model/response/AppRoleSecretResponse.java b/src/main/java/de/stklcode/jvault/connector/model/response/AppRoleSecretResponse.java index 5c5f9be..9f71e0e 100644 --- a/src/main/java/de/stklcode/jvault/connector/model/response/AppRoleSecretResponse.java +++ b/src/main/java/de/stklcode/jvault/connector/model/response/AppRoleSecretResponse.java @@ -17,13 +17,9 @@ package de.stklcode.jvault.connector.model.response; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.databind.ObjectMapper; -import de.stklcode.jvault.connector.exception.InvalidResponseException; +import com.fasterxml.jackson.annotation.JsonProperty; import de.stklcode.jvault.connector.model.AppRoleSecret; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; import java.util.Objects; /** @@ -34,27 +30,11 @@ import java.util.Objects; */ @JsonIgnoreProperties(ignoreUnknown = true) public final class AppRoleSecretResponse extends VaultDataResponse { - private static final long serialVersionUID = 7511563325431032667L; + private static final long serialVersionUID = -2484103304072370585L; + @JsonProperty("data") private AppRoleSecret secret; - @Override - public void setData(final Map data) throws InvalidResponseException { - var mapper = new ObjectMapper(); - try { - /* null empty strings on list objects */ - Map filteredData = new HashMap<>(data.size(), 1); - data.forEach((k, v) -> { - if (!(v instanceof String && ((String) v).isEmpty())) { - filteredData.put(k, v); - } - }); - this.secret = mapper.readValue(mapper.writeValueAsString(filteredData), AppRoleSecret.class); - } catch (IOException e) { - throw new InvalidResponseException("Failed deserializing response", e); - } - } - /** * @return The secret */ diff --git a/src/main/java/de/stklcode/jvault/connector/model/response/AuthMethodsResponse.java b/src/main/java/de/stklcode/jvault/connector/model/response/AuthMethodsResponse.java index 5c88fde..72e6885 100644 --- a/src/main/java/de/stklcode/jvault/connector/model/response/AuthMethodsResponse.java +++ b/src/main/java/de/stklcode/jvault/connector/model/response/AuthMethodsResponse.java @@ -17,11 +17,9 @@ package de.stklcode.jvault.connector.model.response; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.databind.ObjectMapper; -import de.stklcode.jvault.connector.exception.InvalidResponseException; +import com.fasterxml.jackson.annotation.JsonProperty; import de.stklcode.jvault.connector.model.response.embedded.AuthMethod; -import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -34,8 +32,9 @@ import java.util.Objects; */ @JsonIgnoreProperties(ignoreUnknown = true) public final class AuthMethodsResponse extends VaultDataResponse { - private static final long serialVersionUID = 5521702564857621352L; + private static final long serialVersionUID = -1802724129533405375L; + @JsonProperty("data") private Map supportedMethods; /** @@ -45,19 +44,6 @@ public final class AuthMethodsResponse extends VaultDataResponse { this.supportedMethods = new HashMap<>(); } - @Override - public void setData(final Map data) throws InvalidResponseException { - var mapper = new ObjectMapper(); - for (Map.Entry entry : data.entrySet()) { - try { - this.supportedMethods.put(entry.getKey(), - mapper.readValue(mapper.writeValueAsString(entry.getValue()), AuthMethod.class)); - } catch (IOException e) { - throw new InvalidResponseException(); - } - } - } - /** * @return Supported authentication methods */ diff --git a/src/main/java/de/stklcode/jvault/connector/model/response/AuthResponse.java b/src/main/java/de/stklcode/jvault/connector/model/response/AuthResponse.java index eb8a7f6..f843581 100644 --- a/src/main/java/de/stklcode/jvault/connector/model/response/AuthResponse.java +++ b/src/main/java/de/stklcode/jvault/connector/model/response/AuthResponse.java @@ -18,12 +18,8 @@ package de.stklcode.jvault.connector.model.response; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.ObjectMapper; -import de.stklcode.jvault.connector.exception.InvalidResponseException; import de.stklcode.jvault.connector.model.response.embedded.AuthData; -import java.io.IOException; -import java.util.Map; import java.util.Objects; /** @@ -34,39 +30,10 @@ import java.util.Objects; */ @JsonIgnoreProperties(ignoreUnknown = true) public final class AuthResponse extends VaultDataResponse { - private static final long serialVersionUID = -6728387061352164781L; + private static final long serialVersionUID = 1628851361067456715L; - private Map data; - - private AuthData auth; - - /** - * Set authentication data. The input will be mapped to the {@link AuthData} model. - * - * @param auth Raw authentication data - * @throws InvalidResponseException on mapping errors - */ @JsonProperty("auth") - public void setAuth(final Map auth) throws InvalidResponseException { - var mapper = new ObjectMapper(); - try { - this.auth = mapper.readValue(mapper.writeValueAsString(auth), AuthData.class); - } catch (IOException e) { - throw new InvalidResponseException("Failed deserializing response", e); - } - } - - @Override - public void setData(final Map data) { - this.data = data; - } - - /** - * @return Raw data - */ - public Map getData() { - return data; - } + private AuthData auth; /** * @return Authentication data @@ -83,11 +50,11 @@ public final class AuthResponse extends VaultDataResponse { return false; } AuthResponse that = (AuthResponse) o; - return Objects.equals(data, that.data) && Objects.equals(auth, that.auth); + return Objects.equals(auth, that.auth); } @Override public int hashCode() { - return Objects.hash(super.hashCode(), data, auth); + return Objects.hash(super.hashCode(), auth); } } diff --git a/src/main/java/de/stklcode/jvault/connector/model/response/CredentialsResponse.java b/src/main/java/de/stklcode/jvault/connector/model/response/CredentialsResponse.java index 861e12c..9e31275 100644 --- a/src/main/java/de/stklcode/jvault/connector/model/response/CredentialsResponse.java +++ b/src/main/java/de/stklcode/jvault/connector/model/response/CredentialsResponse.java @@ -25,7 +25,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; * @since 0.5.0 */ @JsonIgnoreProperties(ignoreUnknown = true) -public final class CredentialsResponse extends SecretResponse { +public final class CredentialsResponse extends PlainSecretResponse { private static final long serialVersionUID = -1439692963299045425L; /** diff --git a/src/main/java/de/stklcode/jvault/connector/model/response/MetaSecretResponse.java b/src/main/java/de/stklcode/jvault/connector/model/response/MetaSecretResponse.java new file mode 100644 index 0000000..3d88992 --- /dev/null +++ b/src/main/java/de/stklcode/jvault/connector/model/response/MetaSecretResponse.java @@ -0,0 +1,75 @@ +/* + * Copyright 2016-2021 Stefan Kalscheuer + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.stklcode.jvault.connector.model.response; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import de.stklcode.jvault.connector.model.response.embedded.SecretWrapper; +import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Map; +import java.util.Objects; + +/** + * Vault response for secret responses with metadata. + * + * @author Stefan Kalscheuer + * @since 1.1 abstract + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class MetaSecretResponse extends SecretResponse { + private static final long serialVersionUID = -1076542846391240162L; + + @JsonProperty("data") + private SecretWrapper secret; + + @Override + public final Map getData() { + if (secret != null) { + return secret.getData(); + } else { + return Collections.emptyMap(); + } + } + + @Override + public final VersionMetadata getMetadata() { + if (secret != null) { + return secret.getMetadata(); + } else { + return null; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o == null || getClass() != o.getClass() || !super.equals(o)) { + return false; + } + MetaSecretResponse that = (MetaSecretResponse) o; + return Objects.equals(secret, that.secret); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), secret); + } +} diff --git a/src/main/java/de/stklcode/jvault/connector/model/response/MetadataResponse.java b/src/main/java/de/stklcode/jvault/connector/model/response/MetadataResponse.java index 7948334..3ae1103 100644 --- a/src/main/java/de/stklcode/jvault/connector/model/response/MetadataResponse.java +++ b/src/main/java/de/stklcode/jvault/connector/model/response/MetadataResponse.java @@ -17,14 +17,12 @@ package de.stklcode.jvault.connector.model.response; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.databind.ObjectMapper; -import de.stklcode.jvault.connector.exception.InvalidResponseException; +import com.fasterxml.jackson.annotation.JsonProperty; import de.stklcode.jvault.connector.model.response.embedded.SecretMetadata; -import java.io.IOException; -import java.util.Map; import java.util.Objects; + /** * Vault response for secret metadata (KV v2). * @@ -33,20 +31,11 @@ import java.util.Objects; */ @JsonIgnoreProperties(ignoreUnknown = true) public class MetadataResponse extends VaultDataResponse { - private static final long serialVersionUID = 3407081728744500975L; + private static final long serialVersionUID = -3679762333630984679L; + @JsonProperty("data") private SecretMetadata metadata; - @Override - public final void setData(final Map data) throws InvalidResponseException { - var mapper = new ObjectMapper(); - try { - this.metadata = mapper.readValue(mapper.writeValueAsString(data), SecretMetadata.class); - } catch (IOException e) { - throw new InvalidResponseException("Failed deserializing response", e); - } - } - /** * Get the actual metadata. * diff --git a/src/main/java/de/stklcode/jvault/connector/model/response/PlainSecretResponse.java b/src/main/java/de/stklcode/jvault/connector/model/response/PlainSecretResponse.java new file mode 100644 index 0000000..096475e --- /dev/null +++ b/src/main/java/de/stklcode/jvault/connector/model/response/PlainSecretResponse.java @@ -0,0 +1,66 @@ +/* + * Copyright 2016-2021 Stefan Kalscheuer + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.stklcode.jvault.connector.model.response; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Map; +import java.util.Objects; + +/** + * Vault response for plain secret responses. + * + * @author Stefan Kalscheuer + * @since 1.1 abstract + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class PlainSecretResponse extends SecretResponse { + private static final long serialVersionUID = 3010138542437913023L; + + @JsonProperty("data") + private Map data; + + @Override + public final Map getData() { + return Objects.requireNonNullElseGet(data, Collections::emptyMap); + } + + @Override + public final VersionMetadata getMetadata() { + return null; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o == null || getClass() != o.getClass() || !super.equals(o)) { + return false; + } + PlainSecretResponse that = (PlainSecretResponse) o; + return Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), data); + } +} diff --git a/src/main/java/de/stklcode/jvault/connector/model/response/RawDataResponse.java b/src/main/java/de/stklcode/jvault/connector/model/response/RawDataResponse.java index afbdab9..9af0466 100644 --- a/src/main/java/de/stklcode/jvault/connector/model/response/RawDataResponse.java +++ b/src/main/java/de/stklcode/jvault/connector/model/response/RawDataResponse.java @@ -17,7 +17,9 @@ package de.stklcode.jvault.connector.model.response; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.io.Serializable; import java.util.Map; import java.util.Objects; @@ -29,19 +31,15 @@ import java.util.Objects; */ @JsonIgnoreProperties(ignoreUnknown = true) public final class RawDataResponse extends VaultDataResponse { - private static final long serialVersionUID = -5494734676257709074L; + private static final long serialVersionUID = -319727427792124071L; - private Map data; - - @Override - public void setData(final Map data) { - this.data = data; - } + @JsonProperty("data") + private Map data; /** * @return Raw data {@link Map} */ - public Map getData() { + public Map getData() { return data; } diff --git a/src/main/java/de/stklcode/jvault/connector/model/response/SecretListResponse.java b/src/main/java/de/stklcode/jvault/connector/model/response/SecretListResponse.java index 5800177..94446bb 100644 --- a/src/main/java/de/stklcode/jvault/connector/model/response/SecretListResponse.java +++ b/src/main/java/de/stklcode/jvault/connector/model/response/SecretListResponse.java @@ -18,10 +18,10 @@ package de.stklcode.jvault.connector.model.response; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; -import de.stklcode.jvault.connector.exception.InvalidResponseException; +import de.stklcode.jvault.connector.model.response.embedded.SecretListWrapper; +import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Objects; /** @@ -32,29 +32,19 @@ import java.util.Objects; */ @JsonIgnoreProperties(ignoreUnknown = true) public final class SecretListResponse extends VaultDataResponse { - private static final long serialVersionUID = -5279146643326713976L; - private List keys; - - /** - * Set data. Extracts list of keys from raw response data. - * - * @param data Raw data - */ + private static final long serialVersionUID = 8597121175002967213L; @JsonProperty("data") - public void setData(final Map data) throws InvalidResponseException { - try { - this.keys = (List) data.get("keys"); - } catch (ClassCastException e) { - throw new InvalidResponseException("Keys could not be parsed from data.", e); - } - } + private SecretListWrapper data; /** * @return List of secret keys */ public List getKeys() { - return keys; + if (data == null) { + return Collections.emptyList(); + } + return Objects.requireNonNullElseGet(data.getKeys(), Collections::emptyList); } @Override @@ -65,11 +55,11 @@ public final class SecretListResponse extends VaultDataResponse { return false; } SecretListResponse that = (SecretListResponse) o; - return Objects.equals(keys, that.keys); + return Objects.equals(data, that.data); } @Override public int hashCode() { - return Objects.hash(super.hashCode(), keys); + return Objects.hash(super.hashCode(), data); } } diff --git a/src/main/java/de/stklcode/jvault/connector/model/response/SecretResponse.java b/src/main/java/de/stklcode/jvault/connector/model/response/SecretResponse.java index ba24a94..9e159bd 100644 --- a/src/main/java/de/stklcode/jvault/connector/model/response/SecretResponse.java +++ b/src/main/java/de/stklcode/jvault/connector/model/response/SecretResponse.java @@ -22,57 +22,28 @@ import de.stklcode.jvault.connector.exception.InvalidResponseException; import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata; import java.io.IOException; -import java.util.Collections; +import java.io.Serializable; import java.util.Map; -import java.util.Objects; /** * Vault response for secret request. * * @author Stefan Kalscheuer * @since 0.1 + * @since 1.1 abstract */ @JsonIgnoreProperties(ignoreUnknown = true) -public class SecretResponse extends VaultDataResponse { - private static final long serialVersionUID = -8215178956885015265L; - - private static final String KEY_DATA = "data"; - private static final String KEY_METADATA = "metadata"; - - private Map data; - private VersionMetadata metadata; - - @Override - public final void setData(final Map data) throws InvalidResponseException { - if (data.size() == 2 - && data.containsKey(KEY_DATA) && data.get(KEY_DATA) instanceof Map - && data.containsKey(KEY_METADATA) && data.get(KEY_METADATA) instanceof Map) { - var mapper = new ObjectMapper(); - try { - // This is apparently a KV v2 value. - this.data = (Map) data.get(KEY_DATA); - this.metadata = mapper.readValue(mapper.writeValueAsString(data.get(KEY_METADATA)), VersionMetadata.class); - } catch (ClassCastException | IOException e) { - throw new InvalidResponseException("Failed deserializing response", e); - } - } else { - // For KV v1 without metadata just store the data map. - this.data = data; - } - } +public abstract class SecretResponse extends VaultDataResponse { + private static final long serialVersionUID = 5198088815871692951L; /** * Get complete data object. * * @return data map * @since 0.4.0 + * @since 1.1 Serializable map value. */ - public final Map getData() { - if (data == null) { - return Collections.emptyMap(); - } - return data; - } + public abstract Map getData(); /** * Get secret metadata. This is only available for KV v2 secrets. @@ -80,9 +51,7 @@ public class SecretResponse extends VaultDataResponse { * @return Metadata of the secret. * @since 0.8 */ - public final VersionMetadata getMetadata() { - return metadata; - } + public abstract VersionMetadata getMetadata(); /** * Get a single value for given key. @@ -92,9 +61,6 @@ public class SecretResponse extends VaultDataResponse { * @since 0.4.0 */ public final Object get(final String key) { - if (data == null) { - return null; - } return getData().get(key); } @@ -103,12 +69,12 @@ public class SecretResponse extends VaultDataResponse { * * @param key the key * @param type Class to parse response - * @param Class to parse response + * @param Class to parse response * @return Parsed object or {@code null} if absent * @throws InvalidResponseException on parsing error * @since 0.4.0 */ - public final T get(final String key, final Class type) throws InvalidResponseException { + public final C get(final String key, final Class type) throws InvalidResponseException { try { Object rawValue = get(key); if (rawValue == null) { @@ -119,20 +85,4 @@ public class SecretResponse extends VaultDataResponse { throw new InvalidResponseException("Unable to parse response payload: " + e.getMessage()); } } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } else if (o == null || getClass() != o.getClass() || !super.equals(o)) { - return false; - } - SecretResponse that = (SecretResponse) o; - return Objects.equals(data, that.data) && Objects.equals(metadata, that.metadata); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), data, metadata); - } } diff --git a/src/main/java/de/stklcode/jvault/connector/model/response/SecretVersionResponse.java b/src/main/java/de/stklcode/jvault/connector/model/response/SecretVersionResponse.java index f426f2e..de876cc 100644 --- a/src/main/java/de/stklcode/jvault/connector/model/response/SecretVersionResponse.java +++ b/src/main/java/de/stklcode/jvault/connector/model/response/SecretVersionResponse.java @@ -17,12 +17,9 @@ package de.stklcode.jvault.connector.model.response; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.databind.ObjectMapper; -import de.stklcode.jvault.connector.exception.InvalidResponseException; +import com.fasterxml.jackson.annotation.JsonProperty; import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata; -import java.io.IOException; -import java.util.Map; import java.util.Objects; /** @@ -33,20 +30,11 @@ import java.util.Objects; */ @JsonIgnoreProperties(ignoreUnknown = true) public class SecretVersionResponse extends VaultDataResponse { - private static final long serialVersionUID = -6681638207727120184L; + private static final long serialVersionUID = 2748635005258576174L; + @JsonProperty("data") private VersionMetadata metadata; - @Override - public final void setData(final Map data) throws InvalidResponseException { - var mapper = new ObjectMapper(); - try { - this.metadata = mapper.readValue(mapper.writeValueAsString(data), VersionMetadata.class); - } catch (IOException e) { - throw new InvalidResponseException("Failed deserializing response", e); - } - } - /** * Get the actual metadata. * diff --git a/src/main/java/de/stklcode/jvault/connector/model/response/TokenResponse.java b/src/main/java/de/stklcode/jvault/connector/model/response/TokenResponse.java index 6720269..3242783 100644 --- a/src/main/java/de/stklcode/jvault/connector/model/response/TokenResponse.java +++ b/src/main/java/de/stklcode/jvault/connector/model/response/TokenResponse.java @@ -18,12 +18,8 @@ package de.stklcode.jvault.connector.model.response; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.ObjectMapper; -import de.stklcode.jvault.connector.exception.InvalidResponseException; import de.stklcode.jvault.connector.model.response.embedded.TokenData; -import java.io.IOException; -import java.util.Map; import java.util.Objects; /** @@ -34,29 +30,14 @@ import java.util.Objects; */ @JsonIgnoreProperties(ignoreUnknown = true) public final class TokenResponse extends VaultDataResponse { - private static final long serialVersionUID = 2248288114849229479L; + private static final long serialVersionUID = -4053126653764241197L; + @JsonProperty("data") private TokenData data; @JsonProperty("auth") private Boolean auth; - /** - * Set data. Parses response data map to {@link TokenData}. - * - * @param data Raw response data - * @throws InvalidResponseException on parsing errors - */ - @Override - public void setData(final Map data) throws InvalidResponseException { - var mapper = new ObjectMapper(); - try { - this.data = mapper.readValue(mapper.writeValueAsString(data), TokenData.class); - } catch (IOException e) { - throw new InvalidResponseException("Failed deserializing response", e); - } - } - /** * @return Token data */ @@ -64,6 +45,13 @@ public final class TokenResponse extends VaultDataResponse { return data; } + /** + * @return Auth data + */ + public Boolean getAuth() { + return auth; + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/src/main/java/de/stklcode/jvault/connector/model/response/TokenRoleResponse.java b/src/main/java/de/stklcode/jvault/connector/model/response/TokenRoleResponse.java index 871545f..1e60166 100644 --- a/src/main/java/de/stklcode/jvault/connector/model/response/TokenRoleResponse.java +++ b/src/main/java/de/stklcode/jvault/connector/model/response/TokenRoleResponse.java @@ -17,13 +17,10 @@ package de.stklcode.jvault.connector.model.response; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.databind.ObjectMapper; -import de.stklcode.jvault.connector.exception.InvalidResponseException; +import com.fasterxml.jackson.annotation.JsonProperty; import de.stklcode.jvault.connector.model.TokenRole; import de.stklcode.jvault.connector.model.response.embedded.TokenData; -import java.io.IOException; -import java.util.Map; import java.util.Objects; /** @@ -34,26 +31,11 @@ import java.util.Objects; */ @JsonIgnoreProperties(ignoreUnknown = true) public final class TokenRoleResponse extends VaultDataResponse { - private static final long serialVersionUID = -6622498881812517596L; + private static final long serialVersionUID = 5265363857731948626L; + @JsonProperty("data") private TokenRole data; - /** - * Set data. Parses response data map to {@link TokenRole}. - * - * @param data Raw response data - * @throws InvalidResponseException on parsing errors - */ - @Override - public void setData(final Map data) throws InvalidResponseException { - var mapper = new ObjectMapper(); - try { - this.data = mapper.readValue(mapper.writeValueAsString(data), TokenRole.class); - } catch (IOException e) { - throw new InvalidResponseException("Failed deserializing response", e); - } - } - /** * @return TokenRole data */ diff --git a/src/main/java/de/stklcode/jvault/connector/model/response/VaultDataResponse.java b/src/main/java/de/stklcode/jvault/connector/model/response/VaultDataResponse.java index 068ec8c..2e0f874 100644 --- a/src/main/java/de/stklcode/jvault/connector/model/response/VaultDataResponse.java +++ b/src/main/java/de/stklcode/jvault/connector/model/response/VaultDataResponse.java @@ -17,11 +17,9 @@ package de.stklcode.jvault.connector.model.response; import com.fasterxml.jackson.annotation.JsonProperty; -import de.stklcode.jvault.connector.exception.InvalidResponseException; import de.stklcode.jvault.connector.model.response.embedded.WrapInfo; import java.util.List; -import java.util.Map; import java.util.Objects; /** @@ -48,15 +46,6 @@ public abstract class VaultDataResponse implements VaultResponse { @JsonProperty("wrap_info") private WrapInfo wrapInfo; - /** - * Set data. To be implemented in the specific subclasses, as data can be of arbitrary structure. - * - * @param data Raw response data - * @throws InvalidResponseException on parsing errors - */ - @JsonProperty("data") - public abstract void setData(final Map data) throws InvalidResponseException; - /** * @return Lease ID */ diff --git a/src/main/java/de/stklcode/jvault/connector/model/response/embedded/SecretListWrapper.java b/src/main/java/de/stklcode/jvault/connector/model/response/embedded/SecretListWrapper.java new file mode 100644 index 0000000..8afa0a4 --- /dev/null +++ b/src/main/java/de/stklcode/jvault/connector/model/response/embedded/SecretListWrapper.java @@ -0,0 +1,42 @@ +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; + +/** + * Wrapper object for secret key lists. + * + * @author Stefan Kalscheuer + * @since 1.1 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class SecretListWrapper implements Serializable { + + private static final long serialVersionUID = -8777605197063766125L; + @JsonProperty("keys") + private List keys; + + public List getKeys() { + return keys; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o == null || getClass() != o.getClass()) { + return false; + } + SecretListWrapper that = (SecretListWrapper) o; + return Objects.equals(keys, that.keys); + } + + @Override + public int hashCode() { + return Objects.hash(keys); + } +} diff --git a/src/main/java/de/stklcode/jvault/connector/model/response/embedded/SecretWrapper.java b/src/main/java/de/stklcode/jvault/connector/model/response/embedded/SecretWrapper.java new file mode 100644 index 0000000..842a11c --- /dev/null +++ b/src/main/java/de/stklcode/jvault/connector/model/response/embedded/SecretWrapper.java @@ -0,0 +1,49 @@ +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.Map; +import java.util.Objects; + +/** + * Wrapper object for secret data and metadata. + * + * @author Stefan Kalscheuer + * @since 1.1 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class SecretWrapper implements Serializable { + private static final long serialVersionUID = 8600413181758893378L; + + @JsonProperty("data") + private Map data; + + @JsonProperty("metadata") + private VersionMetadata metadata; + + public Map getData() { + return data; + } + + public VersionMetadata getMetadata() { + return metadata; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o == null || getClass() != o.getClass()) { + return false; + } + SecretWrapper that = (SecretWrapper) o; + return Objects.equals(data, that.data) && Objects.equals(metadata, that.metadata); + } + + @Override + public int hashCode() { + return Objects.hash(data, metadata); + } +} diff --git a/src/test/java/de/stklcode/jvault/connector/model/response/AppRoleResponseTest.java b/src/test/java/de/stklcode/jvault/connector/model/response/AppRoleResponseTest.java index caf87c3..2f0ae8a 100644 --- a/src/test/java/de/stklcode/jvault/connector/model/response/AppRoleResponseTest.java +++ b/src/test/java/de/stklcode/jvault/connector/model/response/AppRoleResponseTest.java @@ -17,13 +17,11 @@ package de.stklcode.jvault.connector.model.response; import com.fasterxml.jackson.databind.ObjectMapper; -import de.stklcode.jvault.connector.exception.InvalidResponseException; import de.stklcode.jvault.connector.model.AppRole; import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.jupiter.api.Test; import java.util.List; -import java.util.Map; import static org.junit.jupiter.api.Assertions.*; @@ -63,8 +61,6 @@ class AppRoleResponseTest { " \"lease_id\": \"\"\n" + "}"; - private static final Map INVALID_DATA = Map.of("token_policies", "fancy-policy"); - /** * Test getter, setter and get-methods for response data. */ @@ -73,13 +69,6 @@ class AppRoleResponseTest { // Create empty Object. AppRoleResponse res = new AppRoleResponse(); assertNull(res.getRole(), "Initial data should be empty"); - - // Parsing invalid auth data map should fail. - assertThrows( - InvalidResponseException.class, - () -> res.setData(INVALID_DATA), - "Parsing invalid data succeeded" - ); } /** 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 50a3c13..6786b9e 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 @@ -63,8 +63,6 @@ class AuthMethodsResponseTest { " }\n" + "}"; - private static final Map INVALID_DATA = Map.of("dummy/", new Dummy()); - /** * Test getter, setter and get-methods for response data. */ @@ -73,13 +71,6 @@ class AuthMethodsResponseTest { // Create empty Object. AuthMethodsResponse res = new AuthMethodsResponse(); assertEquals(Collections.emptyMap(), res.getSupportedMethods(), "Initial method map should be empty"); - - // Parsing invalid data map should fail. - assertThrows( - InvalidResponseException.class, - () -> res.setData(INVALID_DATA), - "Parsing invalid data succeeded" - ); } /** diff --git a/src/test/java/de/stklcode/jvault/connector/model/response/AuthResponseTest.java b/src/test/java/de/stklcode/jvault/connector/model/response/AuthResponseTest.java index d7342d0..62a59d1 100644 --- a/src/test/java/de/stklcode/jvault/connector/model/response/AuthResponseTest.java +++ b/src/test/java/de/stklcode/jvault/connector/model/response/AuthResponseTest.java @@ -17,7 +17,6 @@ package de.stklcode.jvault.connector.model.response; import com.fasterxml.jackson.databind.ObjectMapper; -import de.stklcode.jvault.connector.exception.InvalidResponseException; import de.stklcode.jvault.connector.model.response.embedded.AuthData; import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.jupiter.api.Test; @@ -69,8 +68,6 @@ class AuthResponseTest { " }\n" + "}"; - private static final Map INVALID_AUTH_DATA = Map.of("policies", "fancy-policy"); - /** * Test getter, setter and get-methods for response data. */ @@ -78,18 +75,8 @@ class AuthResponseTest { void getDataRoundtrip() { // Create empty Object. AuthResponse res = new AuthResponse(); - assertNull(res.getData(), "Initial data should be empty"); - - // Parsing invalid auth data map should fail. - assertThrows( - InvalidResponseException.class, - () -> res.setAuth(INVALID_AUTH_DATA), - "Parsing invalid auth data succeeded" - ); - - // Data method should be agnostic. - res.setData(INVALID_AUTH_DATA); - assertEquals(INVALID_AUTH_DATA, res.getData(), "Data not passed through"); + // TODO +// assertNull(res.getData(), "Initial data should be empty"); } /** diff --git a/src/test/java/de/stklcode/jvault/connector/model/response/CredentialsResponseTest.java b/src/test/java/de/stklcode/jvault/connector/model/response/CredentialsResponseTest.java index 1446f55..957fd4b 100644 --- a/src/test/java/de/stklcode/jvault/connector/model/response/CredentialsResponseTest.java +++ b/src/test/java/de/stklcode/jvault/connector/model/response/CredentialsResponseTest.java @@ -16,14 +16,12 @@ package de.stklcode.jvault.connector.model.response; +import com.fasterxml.jackson.databind.ObjectMapper; import de.stklcode.jvault.connector.exception.InvalidResponseException; import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.jupiter.api.Test; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.*; /** * JUnit Test for {@link CredentialsResponse} model. @@ -34,10 +32,17 @@ import static org.junit.jupiter.api.Assertions.assertNull; class CredentialsResponseTest { private static final String VAL_USER = "testUserName"; private static final String VAL_PASS = "5up3r5ecr3tP455"; - private static final Map DATA = Map.of( - "username", VAL_USER, - "password", VAL_PASS - ); + private static final String JSON = "{\n" + + " \"request_id\": \"68315073-6658-e3ff-2da7-67939fb91bbd\",\n" + + " \"lease_id\": \"\",\n" + + " \"lease_duration\": 2764800,\n" + + " \"renewable\": false,\n" + + " \"data\": {\n" + + " \"username\": \"" + VAL_USER + "\",\n" + + " \"password\": \"" + VAL_PASS + "\"\n" + + " },\n" + + " \"warnings\": null\n" + + "}"; /** * Test getter, setter and get-methods for response data. @@ -51,8 +56,10 @@ class CredentialsResponseTest { assertNull(res.getUsername(), "Username not present in data map should not return anything"); assertNull(res.getPassword(), "Password not present in data map should not return anything"); - // Fill data map. - res.setData(DATA); + res = assertDoesNotThrow( + () -> new ObjectMapper().readValue(JSON, CredentialsResponse.class), + "Deserialization of CredentialsResponse failed" + ); assertEquals(VAL_USER, res.getUsername(), "Incorrect username"); assertEquals(VAL_PASS, res.getPassword(), "Incorrect password"); } diff --git a/src/test/java/de/stklcode/jvault/connector/model/response/SecretListResponseTest.java b/src/test/java/de/stklcode/jvault/connector/model/response/SecretListResponseTest.java index f664939..0348ddf 100644 --- a/src/test/java/de/stklcode/jvault/connector/model/response/SecretListResponseTest.java +++ b/src/test/java/de/stklcode/jvault/connector/model/response/SecretListResponseTest.java @@ -16,16 +16,14 @@ package de.stklcode.jvault.connector.model.response; -import de.stklcode.jvault.connector.exception.InvalidResponseException; +import com.fasterxml.jackson.databind.ObjectMapper; import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.jupiter.api.Test; -import java.util.Arrays; import java.util.List; -import java.util.Map; -import java.util.Set; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * JUnit Test for {@link SecretListResponse} model. @@ -36,33 +34,30 @@ import static org.junit.jupiter.api.Assertions.*; class SecretListResponseTest { private static final String KEY1 = "key1"; private static final String KEY2 = "key-2"; - private static final List KEYS = Arrays.asList(KEY1, KEY2); - private static final Map DATA = Map.of("keys", KEYS); + private static final String JSON = "{\n" + + " \"auth\": null,\n" + + " \"data\": {\n" + + " \"keys\": [" + + " \"" + KEY1 + "\",\n" + + " \"" + KEY2 + "\"\n" + + " ]\n" + + " },\n" + + " \"lease_duration\": 2764800,\n" + + " \"lease_id\": \"\",\n" + + " \"renewable\": false\n" + + "}"; /** - * Test getter, setter and get-methods for response data. - * - * @throws InvalidResponseException Should not occur + * Test JSON deserialization and key getter. */ @Test - void getKeysTest() throws InvalidResponseException { - // Create empty Object. - SecretListResponse res = new SecretListResponse(); - assertNull(res.getKeys(), "Keys should be null without initialization"); - - // Provoke internal ClassCastException. - Map invalidData = Map.of("keys", "some string"); - assertThrows( - InvalidResponseException.class, - () -> res.setData(invalidData), - "Setting incorrect class succeeded" + void getKeysTest() { + SecretListResponse res = assertDoesNotThrow( + () -> new ObjectMapper().readValue(JSON, SecretListResponse.class), + "SecretListResponse deserialization failed" ); - // Fill correct data. - res.setData(DATA); - assertNotNull(res.getKeys(), "Keys should be filled here"); - assertEquals(2, res.getKeys().size(), "Unexpected number of keys"); - assertTrue(res.getKeys().containsAll(Set.of(KEY1, KEY2)), "Unexpected keys"); + assertEquals(List.of(KEY1, KEY2), res.getKeys(), "Unexpected secret keys"); } @Test diff --git a/src/test/java/de/stklcode/jvault/connector/model/response/SecretResponseTest.java b/src/test/java/de/stklcode/jvault/connector/model/response/SecretResponseTest.java index cfa4be7..ccd4de8 100644 --- a/src/test/java/de/stklcode/jvault/connector/model/response/SecretResponseTest.java +++ b/src/test/java/de/stklcode/jvault/connector/model/response/SecretResponseTest.java @@ -17,13 +17,10 @@ package de.stklcode.jvault.connector.model.response; import com.fasterxml.jackson.databind.ObjectMapper; -import de.stklcode.jvault.connector.exception.InvalidResponseException; import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.jupiter.api.Test; import java.util.List; -import java.util.Map; -import java.util.Set; import static org.junit.jupiter.api.Assertions.*; @@ -34,20 +31,6 @@ import static org.junit.jupiter.api.Assertions.*; * @since 0.6.2 */ class SecretResponseTest { - private static final String KEY_UNKNOWN = "unknown"; - private static final String KEY_STRING = "test1"; - private static final String VAL_STRING = "testvalue"; - private static final String KEY_INTEGER = "test2"; - private static final Integer VAL_INTEGER = 42; - private static final String KEY_LIST = "list"; - private static final String VAL_LIST = "[\"first\",\"second\"]"; - - private static final Map DATA = Map.of( - KEY_STRING, VAL_STRING, - KEY_INTEGER, VAL_INTEGER, - KEY_LIST, VAL_LIST - ); - private static final String SECRET_REQUEST_ID = "68315073-6658-e3ff-2da7-67939fb91bbd"; private static final String SECRET_LEASE_ID = ""; private static final Integer SECRET_LEASE_DURATION = 2764800; @@ -109,58 +92,20 @@ class SecretResponseTest { " \"warnings\": " + SECRET_WARNINGS + "\n" + "}"; - /** - * Test getter, setter and get-methods for response data. - * - * @throws InvalidResponseException Should not occur - */ - @Test - @SuppressWarnings("unchecked") - void getDataRoundtrip() throws InvalidResponseException { - // Create empty Object. - SecretResponse res = new SecretResponse(); - assertNotNull(res.getData(), "Initial data should be Map"); - assertTrue(res.getData().isEmpty(), "Initial data should be empty"); - assertNull(res.get(KEY_STRING), "Getter should return NULL on empty data map"); - - // Fill data map. - res.setData(DATA); - assertEquals(DATA, res.getData(), "Data setter/getter not transparent"); - assertEquals(DATA.size(), res.getData().keySet().size(), "Data size modified"); - assertTrue(res.getData().keySet().containsAll(Set.of(KEY_STRING, KEY_INTEGER, KEY_LIST)), "Data keys not passed correctly"); - assertEquals(VAL_STRING, res.get(KEY_STRING), "Data values not passed correctly"); - assertEquals(VAL_INTEGER, res.get(KEY_INTEGER), "Data values not passed correctly"); - assertNull(res.get(KEY_UNKNOWN), "Non-Null returned on unknown key"); - - // Try explicit JSON conversion. - final List list = res.get(KEY_LIST, List.class); - assertNotNull(list, "JSON parsing of list failed"); - assertEquals(2, list.size(), "JSON parsing of list returned incorrect size"); - assertTrue(list.containsAll(List.of("first", "second")), "JSON parsing of list returned incorrect elements"); - assertNull(res.get(KEY_UNKNOWN, Object.class), "Non-Null returned on unknown key"); - - // Requesting invalid class should result in Exception. - assertThrows( - InvalidResponseException.class, - () -> res.get(KEY_LIST, Double.class), - "JSON parsing to incorrect type succeeded" - ); - } - /** * Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation). */ @Test void jsonRoundtrip() { SecretResponse res = assertDoesNotThrow( - () -> new ObjectMapper().readValue(SECRET_JSON, SecretResponse.class), + () -> new ObjectMapper().readValue(SECRET_JSON, PlainSecretResponse.class), "SecretResponse deserialization failed" ); assertSecretData(res); // KV v2 secret. res = assertDoesNotThrow( - () -> new ObjectMapper().readValue(SECRET_JSON_V2, SecretResponse.class), + () -> new ObjectMapper().readValue(SECRET_JSON_V2, MetaSecretResponse.class), "SecretResponse deserialization failed" ); assertSecretData(res); @@ -174,7 +119,7 @@ class SecretResponseTest { // Deleted KV v2 secret. res = assertDoesNotThrow( - () -> new ObjectMapper().readValue(SECRET_JSON_V2_2, SecretResponse.class), + () -> new ObjectMapper().readValue(SECRET_JSON_V2_2, MetaSecretResponse.class), "SecretResponse deserialization failed" ); assertSecretData(res); @@ -190,6 +135,8 @@ class SecretResponseTest { @Test void testEqualsHashcode() { EqualsVerifier.simple().forClass(SecretResponse.class).verify(); + EqualsVerifier.simple().forClass(PlainSecretResponse.class).verify(); + EqualsVerifier.simple().forClass(MetaSecretResponse.class).verify(); } private void assertSecretData(SecretResponse res) { diff --git a/src/test/java/de/stklcode/jvault/connector/model/response/TokenResponseTest.java b/src/test/java/de/stklcode/jvault/connector/model/response/TokenResponseTest.java index 6c5f45a..f173d7d 100644 --- a/src/test/java/de/stklcode/jvault/connector/model/response/TokenResponseTest.java +++ b/src/test/java/de/stklcode/jvault/connector/model/response/TokenResponseTest.java @@ -17,7 +17,6 @@ package de.stklcode.jvault.connector.model.response; import com.fasterxml.jackson.databind.ObjectMapper; -import de.stklcode.jvault.connector.exception.InvalidResponseException; import de.stklcode.jvault.connector.model.response.embedded.TokenData; import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.jupiter.api.Test; @@ -89,8 +88,6 @@ class TokenResponseTest { " \"auth\": null\n" + "}"; - private static final Map INVALID_TOKEN_DATA = Map.of("num_uses", "fourtytwo"); - /** * Test getter, setter and get-methods for response data. */ @@ -99,13 +96,6 @@ class TokenResponseTest { // Create empty Object. TokenResponse res = new TokenResponse(); assertNull(res.getData(), "Initial data should be empty"); - - // Parsing invalid data map should fail. - assertThrows( - InvalidResponseException.class, - () -> res.setData(INVALID_TOKEN_DATA), - "Parsing invalid token data succeeded" - ); } /**