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

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/

Related

Overriding a class defined in Spring Boot \ Spring Security

We are in the process of migrating a legacy application to Spring Boot. In order to continue with testing until we have assigned roles to users, I would like to override the following:
class: SecurityContextHolderAwareRequestWrapper
method: public boolean isUserInRole(String role)
I have created a new class which extends SecurityContextHolderAwareRequestWrapper and overrides isUserInRole(), as follows:
#Component
public class MySecurityContextHolderAwareRequestWrapper extends org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper {
public MySecurityContextHolderAwareRequestWrapper(HttpServletRequest request,
AuthenticationTrustResolver trustResolver, String rolePrefix) {
super(request, trustResolver, rolePrefix);
}
#Override
public boolean isUserInRole(String role) {
return true;
}
When the application is run, the new bean does not take the place of the existing SecurityContextHolderAwareRequestWrapper class. This is clear because when the new class is instantiated, the constructor is not injected with the beans being injected into SecurityContextHolderAwareRequestWrapper. The application fails to start because parameters of type AuthenticationTrustResolver and String to the new class MySecurityContextHolderAwareRequestWrappercould could not be found
What is the correct way to override SecurityContextHolderAwareRequestWrapper, or for that matter any class in the Spring Boot framework?
Thanks
The SecurityContextHolderAwareRequestWrapper class is ultimately used by the SecurityContextHolderAwareRequestFilter configured with http.servletApi(). Some information about this feature is available in the Spring Security reference docs.
This feature shields you from direct dependence on Spring Security and provides very high level integration with Spring Security through the Servlet API. You cannot directly influence the class used to wrap the request.
However, if you wish to temporarily modify the result of role checks, you can influence what roles are available in the Authentication object during authentication itself. See info in the docs on GrantedAuthority, and note that you will want to customize roles during authentication by providing a custom UserDetailsService.

How to pass attribute to Spring Controller in a stateless application?

I am currently implementing a SAML SSO solution in my application where in my SAMLUserDetailsService, I am loading my user
#Service
public class SAMLUserDetailsServiceImpl implements SAMLUserDetailsService {
#Override
public Object loadUserBySAML(SAMLCredential credential) throws UsernameNotFoundException {
return new User(credential.getNameID().getValue());
}
}
I am then using a SavedRequestAwareAuthenticationSuccessHandler to redirect user to a landing controller upon successful authentication.
#Bean
public AuthenticationSuccessHandler successRedirectHandler() {
SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler =
new SavedRequestAwareAuthenticationSuccessHandler();
successRedirectHandler.setDefaultTargetUrl("/landing");
return successRedirectHandler;
}
Controller:
#RequestMapping("/landing")
public ResponseEntity landing(User user) {
return ResponseEntity.ok(user.getLoginName());
}
Is there a way to pass the User object to my controller. I noticed that this is usually done using a HandlerMethodArgumentResolver but since my application is stateless and does not use sessions, is there a way to achieve this using another way please?
You don't need injection for this. Use following instead:
SecurityContextHolder.getContext().getAuthentication().getName()
or
SecurityContextHolder.getContext().getAuthentication().getPrincipal()
In the latter case check the type of what getPrincipal() returned. It can be String, it can be UserDetails. If latter, cast it to UserDetails and call getUsername().

How to use InMemoryUserDetailsManager post autoconfiguration?

In my Spring Boot based app - for the sake of simplicity - I rely on the Spring Web Security auto-configuration of the InMemoryUserDetailsManager via the UserDetailsServiceAutoConfiguration.
For this purpose I've just set up a single user via properties(.yml) as follow:
spring.security.user:
name: whatever
password: xxx
roles: USER
I'd just like to subsequently add extra users I might read from another source (properties or so - exact values not known at compile time), by calling the appropriate method on InMemoryUserDetailsManager i.e. createUser(UserDetails user) (inherited from interface UserDetailsManager). Basically in a #Configuration-like bean of mine:
#Autowired // Spring will inject its newly created InMemoryUserDetailsManager
private UserDetailsManager userDetailsManager;
// Maybe some annotation like #PostConstruct maybe? but should be conditioned to the existence of the above UserDetailsManager - can I do this?
public void loadMyUsers() {
...
Collection<UserDetails> users; // my list of "users"/UserDetails
users.stream().forEach(userDetailsManager::createUser);
}
I've seen this question: How can I add users to the inMemoryAuthentication builder after it has been built? but unfortunately answers lead to other directions that I'm expecting i.e. either by providing a REST endpoint to add users (1st answer), or by "hardcoding" the add of users (2nd answer).
Is it possible to do this?
You can use ApplicationRunner or CommandLineRunner to execute some codes once Spring Boot has started and create users in these runner.
Code wise , It looks like:
#SpringBootApplication
public class MyApplication {
#Autowired
private UserDetailsManager userDetailsManager;
#Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return args -> {
User user=new User("Foo", "password", ......);
userDetailsManager.createUser(user);
};
}
public static void main(String[] args) {
SpringApplication.run(DangerApplication.class, args);
}
}

Use multiple ClientAuthentiation with spring-vault

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.

Spring alternative for Factory

May be its a duplicate, Please feel free to tag... I am a newbie to Spring.
I am implementing a UserService for getting user details from different vendors,
So My class Structure is
Interface UserService ->> UserServiceA, UserServiceB
Which user service to use depends upon a field called provider. my code will look something like
public interface ExternalUserService {
ExternalUserDTO getUserDetail(String username);
}
Implementations -
public class GoogleUserService implements ExternalUserService{
#Override
public ExternalUserDTO getUserDetail(String username) {
return user;
}
}
public class FacebookUserService implements ExternalUserService{
#Override
public ExternalUserDTO getUserDetail(String username) {
return user;
}
}
I want to use it in my code in this fashion, I dont know if this is possible, but giving a try to see if its possible
public class ExternalUserManager(String provider) {
String provider;
#Autowired
ExternalUserService service; //This is supposed to come from some factory, dont know how to get it in spring context.
public void doSomething(String username) {
System.out.println(service.getUserDetail(username));
}
}
Had it been in conventional java programming, I would have created a Factory called UserServiceFactory, which would have made the things straight.
Can someone please help me on how much it is possible with spring, and if its possible, then how can I achieve it? We use Spring boot, so no xml config.
You can use a #Bean annotated method with scope 'prototype' as a factory.
Spring will call this method anytime this bean is injected somewhere.
import org.springframework.beans.factory.config.BeanDefinition;
...
#Bean
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public ExternalUserService externalUserService(UserServiceFactory factory,UserProviderResolver resolver) {
.. create the user service depending on resolver.getProvider()
}
The UserServiceFactory is used to create the specific service depending on the provider name, as you already described.
Create a class UserProviderResolver whith a method getProvider() that returns the provider name for the current request or user.
You can #Autowire the HttpRequest in the UserProviderResolver to get access to the current request.

Resources