Allow encoded properties in application.yml
main.class
public static void main(final String[] args) {
SpringApplication application=new SpringApplication(IdpUiApplication.class);
application.addInitializers(new PropertyDecodingContextInitializer());
application.run(args);
}
PropertyDecodingContextInitializer.class
package com.ahold.api.idp.ui;
import java.io.UnsupportedEncodingException;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Base64.Decoder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
@Component
@Slf4j
public class PropertyDecodingContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final Pattern base64Pattern = Pattern.compile("base64\\((.*?)\\)");
/**
* Handle every property and check if it is base64 encoded. overwrite the value with the decoed value.
*/
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
log.debug("initialize: scanning for encoded properties");
ConfigurableEnvironment environment = applicationContext.getEnvironment();
for (PropertySource<?> propertySource : environment.getPropertySources()) {
Map<String, Object> propertyOverrides = new LinkedHashMap<>();
decodeProperties(propertySource, propertyOverrides);
if (!propertyOverrides.isEmpty()) {
PropertySource<?> decodedProperties = new MapPropertySource("decoded " + propertySource.getName(),
propertyOverrides);
environment.getPropertySources().addBefore(propertySource.getName(), decodedProperties);
}
}
}
/**
* Decode all encoded properties and put them in the overrides map. Also the
* original properties without encoding are added to the map.
*
* @param source - source of the properties
* @param propertyOverrides - resulting map of decoded properties
*/
private void decodeProperties(PropertySource<?> source, Map<String, Object> propertyOverrides) {
if (source instanceof EnumerablePropertySource) {
EnumerablePropertySource<?> enumerablePropertySource = (EnumerablePropertySource<?>) source;
for (String key : enumerablePropertySource.getPropertyNames()) {
Object rawValue = source.getProperty(key);
if (rawValue instanceof String) {
// Only strings can represent a base64 encoded value
String decodedValue = decodeBase64InString((String) rawValue);
propertyOverrides.put(key, decodedValue);
}
}
}
}
/**
* @param input - possible base64 code
* @return decoded string
*/
private String decodeBase64InString(String input) {
if (StringUtils.isBlank(input)) return input;
StringBuffer output = new StringBuffer();
Matcher matcher = base64Pattern.matcher(input);
while (matcher.find()) {
String replacement = decodeBase64(matcher.group(1));
matcher.appendReplacement(output, replacement);
}
matcher.appendTail(output);
return output.toString();
}
/**
* @param encoded - base64 value
* @return decoded value
*/
private String decodeBase64(String encoded) {
try {
Decoder base64Decoder = Base64.getDecoder();
byte[] decodedData = base64Decoder.decode(encoded);
String decodedString = new String(decodedData, "UTF-8");
return decodedString;
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}