SecretValueDeserializer.java
package net.morimekta.config.jackson;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import net.morimekta.config.SecretsManager;
import java.io.IOException;
import java.util.NoSuchElementException;
import java.util.Objects;
/**
* Deserializer meant to read secrets into string fields. The default behavior
* is to just read secrets from environment variables. To read secrets from the
* secrets manager, it has to be set in the deserialization context as an
* attribute:
*
* <pre><code>{@code
* objectManager
* .reader()
* .withAttribute(SecretsManager.class, secretsManager)
* .readValue(from, MyConfig.class)
* }</code></pre>
* <p>
* Or use the feature of the <code>SecretManagerDeserializer</code> that it will
* insert itself into the deserializer context when itself is deserialized.
* Example for yaml:
* <pre><code>{@code
* ---
* secretsManager:
* dir: "/my/secrets"
* mySecret: "${SECRET_NAME}"
* }</code></pre>
*/
public class SecretValueDeserializer extends StdDeserializer<String> {
public SecretValueDeserializer() {
super(String.class);
}
@Override
public String deserialize(
JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException {
if (Objects.requireNonNull(jsonParser.currentToken()) == JsonToken.VALUE_STRING) {
var value = jsonParser.getValueAsString();
try {
if (value.startsWith("${") && value.endsWith("}")) {
var manager = (SecretsManager) deserializationContext.getAttribute(SecretsManager.class);
var secretName = value.substring(2, value.length() - 1).trim();
if (manager != null) {
if (manager.exists(secretName)) {
return manager.get(secretName).getAsString();
}
}
var envValue = System.getenv(secretName);
if (envValue == null || envValue.isEmpty()) {
throw new JsonParseException(
jsonParser,
"Unknown secret '" + secretName + "'",
jsonParser.currentTokenLocation());
}
return envValue;
}
} catch (IllegalArgumentException | IllegalStateException e) {
throw new JsonParseException(
jsonParser,
e.getMessage(),
jsonParser.currentTokenLocation(),
e);
} catch (NoSuchElementException e) {
throw new JsonParseException(
jsonParser,
"Unknown secret " + value,
jsonParser.currentTokenLocation(),
e);
}
}
// Fall back to standard string parsing.
return this._parseString(jsonParser, deserializationContext, this);
}
}