model: eliminate double-mapping of generic data in response classes

Explicitly declare mapping of the "data" field in response classes.
Therefore, the JSON setter setData() is no longer used. SecretResponse
is split into subclasses for secret with and without metadata.
This commit is contained in:
Stefan Kalscheuer 2021-10-25 20:40:05 +02:00
parent 021421a54c
commit 4f3cb4b330
Signed by: stefan
GPG Key ID: 3887EC2A53B55430
26 changed files with 338 additions and 412 deletions

View File

@ -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

View File

@ -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

View File

@ -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<String, Object> data) throws InvalidResponseException {
var mapper = new ObjectMapper();
try {
/* null empty strings on list objects */
Map<String, Object> 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
*/

View File

@ -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<String, Object> data) throws InvalidResponseException {
var mapper = new ObjectMapper();
try {
/* null empty strings on list objects */
Map<String, Object> 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
*/

View File

@ -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<String, AuthMethod> supportedMethods;
/**
@ -45,19 +44,6 @@ public final class AuthMethodsResponse extends VaultDataResponse {
this.supportedMethods = new HashMap<>();
}
@Override
public void setData(final Map<String, Object> data) throws InvalidResponseException {
var mapper = new ObjectMapper();
for (Map.Entry<String, Object> 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
*/

View File

@ -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<String, Object> 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<String, Object> 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<String, Object> data) {
this.data = data;
}
/**
* @return Raw data
*/
public Map<String, Object> 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);
}
}

View File

@ -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;
/**

View File

@ -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<String, Serializable> 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);
}
}

View File

@ -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<String, Object> 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.
*

View File

@ -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<String, Serializable> data;
@Override
public final Map<String, Serializable> 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);
}
}

View File

@ -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<String, Object> data;
@Override
public void setData(final Map<String, Object> data) {
this.data = data;
}
@JsonProperty("data")
private Map<String, Serializable> data;
/**
* @return Raw data {@link Map}
*/
public Map<String, Object> getData() {
public Map<String, Serializable> getData() {
return data;
}

View File

@ -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<String> 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<String, Object> data) throws InvalidResponseException {
try {
this.keys = (List<String>) 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<String> 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);
}
}

View File

@ -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<String, Object> data;
private VersionMetadata metadata;
@Override
public final void setData(final Map<String, Object> 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<String, Object>) 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<String, Object> getData() {
if (data == null) {
return Collections.emptyMap();
}
return data;
}
public abstract Map<String, Serializable> 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 <T> Class to parse response
* @param <C> Class to parse response
* @return Parsed object or {@code null} if absent
* @throws InvalidResponseException on parsing error
* @since 0.4.0
*/
public final <T> T get(final String key, final Class<T> type) throws InvalidResponseException {
public final <C> C get(final String key, final Class<C> 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);
}
}

View File

@ -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<String, Object> 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.
*

View File

@ -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<String, Object> 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) {

View File

@ -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<String, Object> 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
*/

View File

@ -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<String, Object> data) throws InvalidResponseException;
/**
* @return Lease ID
*/

View File

@ -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<String> keys;
public List<String> 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);
}
}

View File

@ -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<String, Serializable> data;
@JsonProperty("metadata")
private VersionMetadata metadata;
public Map<String, Serializable> 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);
}
}

View File

@ -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<String, Object> 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"
);
}
/**

View File

@ -63,8 +63,6 @@ class AuthMethodsResponseTest {
" }\n" +
"}";
private static final Map<String, Object> 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"
);
}
/**

View File

@ -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<String, Object> 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");
}
/**

View File

@ -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<String, Object> 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");
}

View File

@ -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<String> KEYS = Arrays.asList(KEY1, KEY2);
private static final Map<String, Object> 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<String, Object> 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

View File

@ -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<String, Object> 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) {

View File

@ -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<String, Object> 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"
);
}
/**