Use multiple ClientAuthentiation with spring-vault - spring-boot

We have an application using spring-vault. It authenticates to Vault using an AppRole. We use the token we get from that operation to read and write secrets. The configuration for the VaultEndpoint and AppRoleAuthentication are auto-configured from a properties file.
Code looks like this:
#Autowired
private ApplicationContext context;
#Autowired
private VaultOperations vault;
private Logger logger = LoggerFactory.getLogger(VaultFacade.class);
public VaultFacadeImpl() {
logger.debug("Creating VaultFacade with autowired context");
context = new AnnotationConfigApplicationContext(VaultConfig.class);
vault = context.getBean(VaultTemplate.class);
//vault variable ready to use with vault.read or vault.write
//in our VaultFacadeImpl
}
I would like to keep autowire capabilities, but also support two other ClientAuthentication implementations:
The existing TokenAuthentication
A custom ClientAuthentication implementation (LDAP auth backend)
The end result would be having two authentication mechanism available at the same time. Some operations would be carried out with the application's credentials (AppRole in Vault), others with the user's credentials (LDAP in Vault).
I think I can create multiple AbstractVaultConfiguration classes, each returning a different ClientAuthentication derivative. But how can I create a VaultTemplate for configuration class?

If you want to have an additional VaultTemplate bean, then you need to configure and declare the bean yourself. You can keep the foundation provided by AbstractVaultConfiguration. Your config could look like:
#Configuration
public class CustomConfiguration {
#Bean
public VaultTemplate ldapAuthVaultTemplate(ClientFactoryWrapper clientHttpRequestFactoryWrapper,
ThreadPoolTaskScheduler threadPoolTaskScheduler) {
return new VaultTemplate(…,
clientHttpRequestFactoryWrapper.getClientHttpRequestFactory(),
ldapSessionManager(threadPoolTaskScheduler));
}
#Bean
public SessionManager ldapSessionManager(ThreadPoolTaskScheduler threadPoolTaskScheduler) {
ClientAuthentication clientAuthentication = new MyLdapClientAuthentication(…);
return new LifecycleAwareSessionManager(clientAuthentication,
threadPoolTaskScheduler,
…);
}
}
On the client side (using the second VaultTemplate) you need to make sure to look up the appropriate instance. Spring doesn't limit you to a bean per type but allows registration of multiple beans of the same type.

Related

Programmatic RedissonClient in Spring boot project

I am trying to implement Hibernate second level caching in a Spring boot project using Redisson.
I have followed this blog as a reference
https://pavankjadda.medium.com/implement-hibernate-2nd-level-cache-with-redis-spring-boot-and-spring-data-jpa-7cdbf5632883
Also i am trying to initialize the RedissionClient programmatically and not through declaratively /through a config file
Created a spring bean to be initialized which should create the RedissonClient instance.
#Configuration
#Lazy(value = false)
public class RedissonConfig {
#Bean
public RedissonClient redissionClient() {
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
return Redisson.create(config);
}
}
However this bean is never intialized and i get the following error while application startup.
Caused by: org.hibernate.cache.CacheException: Unable to locate Redisson configuration
at org.redisson.hibernate.RedissonRegionFactory.createRedissonClient(RedissonRegionFactory.java:107) ~[redisson-hibernate-53-3.12.1.jar:3.12.1]
at org.redisson.hibernate.RedissonRegionFactory.prepareForUse(RedissonRegionFactory.java:83) ~[redisson-hibernate-53-3.12.1.jar:3.12.1]
It seems Spring boot Hibernate still trying to load the Redisson config through a config file.
is it possible to load the Redission config in spring boot programmatically ?
Best Regards,
Saurav
I just did exactly this, here is how:
you need a custom RegionFactory that is similar to the JndiRedissonRegionFactory but gets its RedissonClient injected somehow.
an instance of this Class, fully configured, is put into the hibernate-properties map. Hibernates internal code is flexible: if the value of hibernate.cache.region.factory_class is a string it is treated as a FQDN. If it is an instance of Class<?>, it will be instantiated. If it is an Object, it will be used.
Spring offers a rather simple way to customize hibernate properties with a bean:
#AutoConfiguration(after = RedissonAutoConfiguration.class, before = JpaAutoConfiguration.class)
#ConditionalOnProperty("spring.jpa.properties.hibernate.cache.use_second_level_cache")
public class HibernateCacheAutoConfiguration {
#Bean
public HibernatePropertiesCustomizer setRegionFactory(RedissonClient redisson) {
return hibernateProperties -> hibernateProperties.put(AvailableSettings.CACHE_REGION_FACTORY, new SpringBootRedissonRegionFactory(redisson));
}
}
My RegionFactory is really simple:
#AllArgsConstructor
public class SpringBootRedissonRegionFactory extends RedissonRegionFactory {
private RedissonClient redissonClient;
#Override
protected RedissonClient createRedissonClient(Map properties) {
return redissonClient;
}
#Override
protected void releaseFromUse() {
}
}
I used the redisson-starter to get a RedissonClient, hence the reference to RedissonAutoConfiguration, but you could just create an instance by hand.
It is possible, but then you need to provide a custom implementation of RegionFactory to Hibernate, which can extends RedissonRegionFactory but uses your own client instance.

Delaying Dependency Injection in Spring

I'm writing an app that talks to one database, obtains credentials for other databases, and connects to the others. It does this using a DataSource and EntityManagerFactory constructed at runtime.
If I want to use Spring Data Repositories, I think I'd need to Autowire them, and therefore they must be Spring Beans.
How can I use Spring Data if I don't have a constructed DataSource until after I run a query against the first database?
I believe that conditional bean creation is your answer. check here.
Also, you have to get the bean after you make sure the conditions are met. check here.
#Component
public class RuntimeBeanBuilder {
#Autowired
private ApplicationContext applicationContext;
public MyObject load(String beanName, MyObject myObject) {
ConfigurableApplicationContext configContext = (ConfigurableApplicationContext) applicationContext;
SingletonBeanRegistry beanRegistry = configContext.getBeanFactory();
if (beanRegistry.containsSingleton(beanName)) {
return beanRegistry.getSingleton(beanName);
} else {
beanRegistry.registerSingleton(beanName, myObject);
return beanRegistry.getSingleton(beanName);
}
}
}
#Service
public MyService{
//inject your builder and create or load beans
#Autowired
private RuntimeBeanBuilder builder;
//do something
}
So, define a bean for your Spring Data Repository and set its condition to be met when the other database credentials are fetched.
And then, reloading the bean using the RuntimeBeanBuilder in your service will get you the bean because now its condition is met.

What is the best way to inject a singleton service into a JAX-RS/Jersey resource?

For example, what if several resource endpoints need access to some message bus to handle requests? Surely there is some way to register a singleton service class and inject it into the resources when the service class itself is NOT a resource but used by the resources.
All of the examples I've seen with providers or custom HK2 bindings refer to resources.
The closest thing I found to what I'm looking for was with this question:
Trouble creating a simple singleton class in Jersey 2 using built-in Jersey dependency injection
What is the best JAX-RS/Jersey way of doing this?
Note that the programmatic way would be most useful, I'm not using an xml file to configure the server.
If your platform supports EJB, you could use the #Singleton EJB (javax.ejb package, not javax.inject), and inject it on your resources with the #EJB annotation. Singleton EJB have also outofthebox concurrency access control.
On plain Jersey, you can use CDI application context. Declare the service class with an #ApplicationScoped annotation and inject it on your resources with #Inject. CDI will only instantiate one bean.
If you cannot annotate the service class, you can create a method that provides your service implementation an annotate it with #Produces and #ApplicationScoped.
#Produces
#ApplicationScoped
public MyService produceService() {
// instantiate your service client
}
And then use it on your resources, with:
#Inject
private MyService
Answer credit goes to #areus the answer provided here.
However, I'm providing my own answer so that I can share the code.
The Service Bean
#Singleton
public final class MyServiceBean
{
private static final AtomicInteger INSTANCES = new AtomicInteger();
private final AtomicInteger calls = new AtomicInteger();
public MyServiceBean()
{
INSTANCES.incrementAndGet();
}
public String getMessage()
{
return String.format("MyServiceBean{INSTANCES=%d, CALLED=%d}", INSTANCES.get(), calls.incrementAndGet());
}
}
The Resource Class
#Path("/messages")
public final class MyResource
{
#Inject
private MyServiceBean bean;
#GET
#Produces(MediaType.TEXT_PLAIN)
public Response handle()
{
return Response.ok(this.bean.getMessage())
.type(MediaType.TEXT_PLAIN_TYPE)
.build();
}
}
HK2 Binder
public final class MyServiceBeanBinder extends AbstractBinder
{
#Override
protected void configure()
{
bind(MyServiceBean.class).to(MyServiceBean.class).in(Singleton.class);
}
}
Then just register the binder and the resource like so:
final ResourceConfig config = new ResourceConfig();
config.register(MyResource.class);
config.register(new MyServiceBeanBinder());
Starting the server and hitting the resource multiple times yields:
MyServiceBean{INSTANCES=1, CALLED=1}
MyServiceBean{INSTANCES=1, CALLED=2}
MyServiceBean{INSTANCES=1, CALLED=3}
MyServiceBean{INSTANCES=1, CALLED=4}
MyServiceBean{INSTANCES=1, CALLED=5}

how override spring framework beans?

I want to customize some of the codes of OAuth authorization server provided by spring security. the code responsible for generating /oauth/authorize is a bean named AuthorizationEndpoint. in AuthorizationServerEndpointsConfiguration class the following code creates a bean of AuthorizationEndpoint class:
#Bean
public AuthorizationEndpoint authorizationEndpoint() throws Exception {
AuthorizationEndpoint authorizationEndpoint = new AuthorizationEndpoint();
FrameworkEndpointHandlerMapping mapping = getEndpointsConfigurer().getFrameworkEndpointHandlerMapping();
authorizationEndpoint.setUserApprovalPage(extractPath(mapping, "/oauth/confirm_access"));
authorizationEndpoint.setProviderExceptionHandler(exceptionTranslator());
authorizationEndpoint.setErrorPage(extractPath(mapping, "/oauth/error"));
authorizationEndpoint.setTokenGranter(tokenGranter());
authorizationEndpoint.setClientDetailsService(clientDetailsService);
authorizationEndpoint.setAuthorizationCodeServices(authorizationCodeServices());
authorizationEndpoint.setOAuth2RequestFactory(oauth2RequestFactory());
authorizationEndpoint.setOAuth2RequestValidator(oauth2RequestValidator());
authorizationEndpoint.setUserApprovalHandler(userApprovalHandler());
return authorizationEndpoint;
}
I want to override it by a new custom bean. I have created a class which extends AuthorizationEndpoint. for now I have pasted the same code inside this new class.
public class AuthorizationEndpointCustom extends AuthorizationEndpoint {
creating the bean:
#Autowired
private ClientDetailsService clientDetailsService;
#Autowired
AuthorizationServerEndpointsConfiguration asec;
#Bean
// #Order(value = Ordered.LOWEST_PRECEDENCE)
#Primary
public AuthorizationEndpoint authorizationEndpoint () {
AuthorizationEndpointCustom authorizationEndpoint = new AuthorizationEndpointCustom();
FrameworkEndpointHandlerMapping mapping = asec.getEndpointsConfigurer().getFrameworkEndpointHandlerMapping();
authorizationEndpoint.setUserApprovalPage(extractPath(mapping, "/oauth/confirm_access"));
authorizationEndpoint.setProviderExceptionHandler(asec.getEndpointsConfigurer().getExceptionTranslator());
authorizationEndpoint.setErrorPage(extractPath(mapping, "/oauth/error"));
authorizationEndpoint.setTokenGranter(asec.getEndpointsConfigurer().getTokenGranter());
authorizationEndpoint.setClientDetailsService(clientDetailsService);
authorizationEndpoint.setAuthorizationCodeServices(asec.getEndpointsConfigurer().getAuthorizationCodeServices());
authorizationEndpoint.setOAuth2RequestFactory(asec.getEndpointsConfigurer().getOAuth2RequestFactory());
authorizationEndpoint.setOAuth2RequestValidator(asec.getEndpointsConfigurer().getOAuth2RequestValidator());
authorizationEndpoint.setUserApprovalHandler(asec.getEndpointsConfigurer().getUserApprovalHandler());
return authorizationEndpoint;
}
private String extractPath(FrameworkEndpointHandlerMapping mapping, String page) {
String path = mapping.getPath(page);
if (path.contains(":")) {
return path;
}
return "forward:" + path;
}
when I try to create a bean of this new class I encounter the following error:
APPLICATION FAILED TO START
Description:
The bean 'authorizationEndpoint', defined in
org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration,
could not be registered. A bean with that name has already been
defined in class path resource
[com/example/demo/AuthorizationServerConfig.class] and overriding is
disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting
spring.main.allow-bean-definition-overriding=true
the error goes away by adding the suggested config to application.properties. but the new bean does not replace the framework bean. in another part of my code I accessed the AuthorizationEndpoint from applicationContext. I called the .getClass() of this object and it is the same bean from the framework:
"org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint"
how can I force spring to use my bean?
You need a Configuration class
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class AppConfig {
#Bean
public AuthorizationEndpoint authorizationEndpoint() {
if(...) return new AuthorizationEndpoint();
else return new AuthorizationEndpointCustom();
}
}
I red an article about overriding beans and it seems so messy and unpredictable. read here
it's best to avoid doing so. The solution to disable framework bean lies in excluding the configuration class which creates it. but this means we have to implement the hole thing ourselves.
#SpringBootApplication(exclude=<AuthorizationServerEndpointsConfiguration>.class)
but the solution to overriding the framework endpoints is much easier.all we have to do is create a controller with mapping for /oauth/authorize
Customizing the UI Most of the Authorization Server endpoints are used
primarily by machines, but there are a couple of resource that need a
UI and those are the GET for /oauth/confirm_access and the HTML
response from /oauth/error. They are provided using whitelabel
implementations in the framework, so most real-world instances of the
Authorization Server will want to provide their own so they can
control the styling and content. All you need to do is provide a
Spring MVC controller with #RequestMappings for those endpoints, and
the framework defaults will take a lower priority in the dispatcher.
In the /oauth/confirm_access endpoint you can expect an
AuthorizationRequest bound to the session carrying all the data needed
to seek approval from the user (the default implementation is
WhitelabelApprovalEndpoint so look there for a starting point to
copy). You can grab all the data from that request and render it
however you like, and then all the user needs to do is POST back to
/oauth/authorize with information about approving or denying the
grant. The request parameters are passed directly to a
UserApprovalHandler in the AuthorizationEndpoint so you can interpret
the data more or less as you please. The default UserApprovalHandler
depends on whether or not you have supplied an ApprovalStore in your
AuthorizationServerEndpointsConfigurer (in which case it is an
ApprovalStoreUserApprovalHandler) or not (in which case it is a
TokenStoreUserApprovalHandler). The standard approval handlers accept
the following:
read more here.
there is another question related to this subject: read here

How to perform RunAs using method security with Spring MVC 3.2 and Spring Security 3.1

I have a web application with Spring MVC 3.2 and Spring Security 3.1
I'm using roles base security and have implemented UserDetailsService and UserDetails to provide GrantedAuthority.
I've enabled global method security with jsr250-annotations
Everything upto here is working as expected with signed in user method access restricted to the declared roles.
I have a further requirement to run certain methods called during application initialisation as a special user with a 'system role' ideally along the lines of JavaEE RunAs.
I'm not sure how to do this in Spring Security.
Should I be trying to create a PreAuthenticatedAuthenticationToken with some made up values and a 'system role' authority.
I could then do something likeSecurityContextHolder.getContext().setAuthentication(token);
when initialising the application.
Alternatively should I be trying to use the RunAsManager. It sounds like what I need but I have not found any simple examples of how I actually could use it.
I'm fairly new to Spring Security and I'm unsure of the best way to proceed.
When my application starts
I run a post construct method in my spring bean to create a special user in memory with a system role.
This user object implements the org.springframework.security.core.userdetails.UserDetails interface.
I then use the user to create a security token org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
The token is then set in the Security Context.
#Service
#Transactional(readOnly = true)
public class ApplicationConfiguration{
#Inject
MyService myService;
#PostConstruct
#Transactional(readOnly = false)
public void init(){
// ######## Application Starting #######"
// Create a user that meets the contract of the Spring UserDetails interface
UserAccountImpl sysAcc = new UserAccountImpl("system", "system", "system");
UserRole role = new UserRole(Role.SYSTEM_ROLE);
role.addUserPermission(Permission.SYSTEM);
sysAcc.addUserRole(role);
UserDetailsAdapter userDetails = new UserDetailsAdapter(sysAcc);
// Create a token and set the security context
PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken( userDetails, userDetails.getPassword(), userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(token);
// Now call service method with roles allowed
myService.initialiseSystem();
}
}
....
public interface MyService {
#RolesAllowed(SYSTEM)
public void initialiseSystem();
}
Do you really need to attach a role to the said app initialization? Why not just extract the code that needs to be run during initialization like so:
public interface Service {
#Secured("hasRole('USER')")
void service();
}
public class DefaultService implements Service {
#Override
public void service() {
doService();
}
public void doService() {
// Implementation here
}
}
...
public class AppInitializer {
#Autowired
private DefaultService service;
public void init() {
service.doService();
}
}
I believe that in this case a good solution for you would be to use the Spring Security OAuth because allow you have a greater integration to custom rules for access via tokens.
http://projects.spring.io/spring-security-oauth/

Resources