package com.ulisesbocchio.jasyptspringboot.configuration; import com.ulisesbocchio.jasyptspringboot.EncryptablePropertyResolver; import com.ulisesbocchio.jasyptspringboot.EncryptablePropertySource; import com.ulisesbocchio.jasyptspringboot.InterceptionMode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.aop.support.AopUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.core.Ordered; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertySource; import java.util.stream.StreamSupport; import static com.ulisesbocchio.jasyptspringboot.EncryptablePropertySourceConverter.instantiatePropertySource; import static com.ulisesbocchio.jasyptspringboot.EncryptablePropertySourceConverter.proxyPropertySource; import static com.ulisesbocchio.jasyptspringboot.configuration.EncryptablePropertyResolverConfiguration.RESOLVER_BEAN_PLACEHOLDER; import static java.util.stream.Collectors.toList; /** * <p>{@link BeanFactoryPostProcessor} that wraps all {@link PropertySource} defined in the {@link Environment} * with {@link com.ulisesbocchio.jasyptspringboot.wrapper.EncryptablePropertySourceWrapper} and defines a default {@link * EncryptablePropertyResolver} for decrypting properties * that can be configured through the same properties it wraps.</p> * <p> * <p>It takes the lowest precedence so it does not interfere with Spring Boot's own post processors</p> * * @author Ulises Bocchio */ public class EnableEncryptablePropertiesBeanFactoryPostProcessor implements BeanFactoryPostProcessor, ApplicationListener<ApplicationEvent>, Ordered { private static final Logger LOG = LoggerFactory.getLogger(EnableEncryptablePropertiesBeanFactoryPostProcessor.class); private ConfigurableEnvironment environment; private InterceptionMode interceptionMode; public EnableEncryptablePropertiesBeanFactoryPostProcessor() { this.interceptionMode = InterceptionMode.PROXY; } public EnableEncryptablePropertiesBeanFactoryPostProcessor(ConfigurableEnvironment environment, InterceptionMode interceptionMode) { this.environment = environment; this.interceptionMode = interceptionMode; } private <T> PropertySource<T> makeEncryptable(PropertySource<T> propertySource, ConfigurableListableBeanFactory registry) { EncryptablePropertyResolver resolver = registry.getBean(environment.resolveRequiredPlaceholders(RESOLVER_BEAN_PLACEHOLDER), EncryptablePropertyResolver.class); PropertySource<T> encryptablePropertySource = interceptionMode == InterceptionMode.PROXY ? proxyPropertySource(propertySource, resolver) : instantiatePropertySource(propertySource, resolver); LOG.info("Converting PropertySource {} [{}] to {}", propertySource.getName(), propertySource.getClass().getName(), AopUtils.isAopProxy(encryptablePropertySource) ? "AOP Proxy" : encryptablePropertySource.getClass().getSimpleName()); return encryptablePropertySource; } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { LOG.info("Post-processing PropertySource instances"); MutablePropertySources propSources = environment.getPropertySources(); StreamSupport.stream(propSources.spliterator(), false) .filter(ps -> !(ps instanceof EncryptablePropertySource)) .map(s -> makeEncryptable(s, beanFactory)) .collect(toList()) .forEach(ps -> propSources.replace(ps.getName(), ps)); } @Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE; } @Override public void onApplicationEvent(ApplicationEvent event) { LOG.debug("Application Event Raised: {}", event.getClass().getSimpleName()); } }