Grails pre-authenticated scenario - grails

I'm trying to configure a Grails app in a pre-authenticated scenario, using Spring Security Core.
So I wrote a custom authentication filter:
class MyAuthenticationFilter extends AbstractPreAuthenticatedProcessingFilter {
protected getPreAuthenticatedPrincipal(request) { "my_username" }
protected getPreAuthenticatedCredentials(request) { "N/A" }
}
added it to Spring:
beans = {
myAuthenticationFilter(MyAuthenticationFilter) {
authenticationManager = ref('authenticationManager')
checkForPrincipalChanges = true
}
}
and registered it in BootStrap, with position PRE_AUTH_FILTER:
class BootStrap {
def init = { servletContext ->
SpringSecurityUtils.clientRegisterFilter('myAuthenticationFilter',
SecurityFilterPosition.PRE_AUTH_FILTER.order)
}
}
Config.groovy only has the standard User, Role, and UserRole class names, plus some staticRules.
Inside the filter, I tried returning all sorts of things from its getPreAuthenticatedPrincipal() method: the user id, the user id as a string, its username, the User object itself… I can see that my filter is being called for each request (which is what I want, having set checkForPrincipalChanges = true) no matter what I return from it, the current user remains anonymous: springSecurityService.principal is still __grails.anonymous.user__
What do I need to change in my setup, to be able to authenticate my users, with their existing groups and roles? I don't want to write an additional authentication provider, I'm fine with Grails's standard daoAuthenticationProvider. Do I need to return something specific from my filter? Do I need to setup some other Spring classes?

I solved it. I'm documenting it here in case somebody else needs to do the same.
Basically the missing piece was to configure a new PreAuthenticatedAuthenticationProvider as the only provider. No additional code needed, just define it in resources:
myAuthenticationProvider(PreAuthenticatedAuthenticationProvider) {
preAuthenticatedUserDetailsService = ref('authenticationUserDetailsService')
}
and set it as the only provider in Config:
grails.plugin.pringsecurity.providerNames = ['myAuthenticationProvider']
Then, from the authentication filter, just return the username of the pre-authenticated user, or null if none (the anonymous user.)

Related

Grail's Spring security: Use email instead of username to switch users in SwitchUserFilter

I have a modified User class in my spring security which use attribute email instead of username. Now I want to a functionality to switch users, so an admin can login seamlessly as a particular user without logging out. I came across this Link , which shows there is a switchUserFilter to achieve this. So I tried to get it working by passing j_username as email,but it gets redirected to a blank page and the user does not switch.
I have tried all these things but still could not figure out a way around it:
1) Added to Config.groovy:
grails.plugins.springsecurity.userLookup.usernamePropertyName='email'
2) Create a method in User class getUserName() to return email.
P.S: I looked into the source code of springSecurity switchUserFilter(link)and came across this code on line 209:
protected Authentication attemptSwitchUser(HttpServletRequest request)
throws AuthenticationException {
UsernamePasswordAuthenticationToken targetUserRequest;
String username = request.getParameter(usernameParameter);
But I am not sure if that is the issue and do not want to make changes in the plugin.
The usernameParameter property of the SwitchUserFilter is set to username by default. That does seem to be part of your problem.
The SwitchUserFilter has a method named setUsernameParameter() that allows you to change this default. It seems the filter is a bean, so you might be able to do something like this in grails-app/conf/spring/Config.groovy
import org.springframework.security.web.authentication.switchuser.SwitchUserFilter
beans = {
switchUserFilter {
usernameParameter = 'email'
}
}
Or maybe something like this in grails-app/config/BootStrap.groovy
def switchUserFilter
def init = { servletContext ->
switchUserFilter.usernameParameter = 'email'
}
Finally found the solution: Add this to the config.groovy file
grails.plugin.springsecurity.userLookup.usernamePropertyName = 'email'
grails.plugin.springsecurity.useSwitchUserFilter = true
grails.plugin.springsecurity.controllerAnnotations.staticRules = [
....
'/j_spring_security_switch_user': ['ROLE_SWITCH_USER', 'isFullyAuthenticated()'],
'/j_spring_security_exit_user': ['isFullyAuthenticated()'],
'/public/**': ['permitAll']
.....
]
2) Then create a Role ROLE_SWITCH_USER in bootstrap.groovy
def switchUserRole = Role.findByAuthority('ROLE_SWITCH_USER') ?: new Role(authority: 'ROLE_SWITCH_USER').save(flush: true, failOnError: true)
And assign it to a super user
3) Then follow the instruction(here) to update the view to add a switch button

Spring Security - Preauthentication - Authorization and Set Roles Defined in DB

The authentication for our application happens through siteminder agent but the authorization is controlled through our application.
I am using org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter to check the header. I have also defined UserDetailsService to load the user details.
I also need to set the role for the user so that I can use spring security tag libraries and other spring methods to check the role and show the options.
How do I implement this?
I have tried the below statements in my user details service implementation, but doesn't seem to work.
Authentication auth = new UsernamePasswordAuthenticationToken(user, null, roles);
SecurityContextHolder.getContext().setAuthentication(auth);
I have also read about AbstractPreAuthenticatedProcessingFilter class but looks like this is may not be useful for this purpose.
Any help on this issue will be very helpful.
Thanks!
I was trying to set the roles(using the statements in my question) in the UserDetailsService
implementation and it was not working.
Solution-1:
I have written a sub class PreAuthenticatedAuthenticationProvider and overridden the authenticate method as below :
public class CustomPreAuthenticatedAuthenticationProvider extends PreAuthenticatedAuthenticationProvider {
#Autowired
DBConfig dbConfig;
#Override
public Authentication authenticate(Authentication authentication)throws AuthenticationException {
Authentication auth = super.authenticate(authentication);
User user = (User)auth.getPrincipal();
Set<Role> roles = new HashSet<Role>();
String[] rolesArray = dbConfig.getRoles(user.getAccessLevel());
for(String role: rolesArray){
Role r = new Role();
r.setName(role);
roles.add(r);
}
user.setRoles(roles);
auth = new UsernamePasswordAuthenticationToken(user, null, roles);
return auth;
}
}
Solution-2 :
I tried setting the roles in the controller (the home page after authentication) and it worked. But looks like Solution-1 is a standard solution.

Spring Security Rest

I'm having a set of Sping Data Repositories which are all exposed over Rest by using Spring-data-rest project. Now I want to secure the HTTP, so that only registered users can access the http://localhost:8080/rest/ So for this purpose I add #Secured(value = { "ROLE_ADMIN" }) to all the repositories and I also enable the security by specifying the
#EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true, prePostEnabled = true)
So now what happens is I go to the rest and it's all good - i'm asked to authenticate. Next thing I do is I go to my website (which uses all the repositories to access the database) but my request fails with
nested exception is org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
which is correct because i'm browsing my website as anonymous user.
So my question is: is there a way to provide method authentication for the REST layer only? To me it sounds like a new annotation is needed (something like #EnableRestGlobalMethodSecurity or #EnableRestSecurity)
I don't know if this will solve your problem, however I managed to get something similar, working for me by creating an event handler for my specific repository, and then used the #PreAuthorize annotation to check for permissions, say on beforeCreate. For example:
#RepositoryEventHandler(Account.class)
public class AccountEventHandler {
private final Logger logger = LoggerFactory.getLogger(getClass());
#PreAuthorize("isAuthenticated() and (hasRole('ROLE_USER'))")
#HandleBeforeCreate
public void beforeAccountCreate(Account account) {
logger.debug(String.format("In before create for account '%s'", account.getName()));
}
#PreAuthorize("isAuthenticated() and (hasRole('ROLE_ADMIN'))")
#HandleBeforeSave
public void beforeAccountUpdate(Account account) {
logger.debug(String.format("In before update for account '%s'", account.getName()));
//Don't need to add anything to this method, the #PreAuthorize does the job.
}
}

Grails Spring Security: Logging in with a target URL skips post authentication workflow

In my grails app I have customized the post authorization workflow by writing a custom auth success handler (in resources.groovy) as shown below.
authenticationSuccessHandler (MyAuthSuccessHandler) {
def conf = SpringSecurityUtils.securityConfig
requestCache = ref('requestCache')
defaultTargetUrl = conf.successHandler.defaultTargetUrl
alwaysUseDefaultTargetUrl = conf.successHandler.alwaysUseDefault
targetUrlParameter = conf.successHandler.targetUrlParameter
useReferer = conf.successHandler.useReferer
redirectStrategy = ref('redirectStrategy')
superAdminUrl = "/admin/processSuperAdminLogin"
adminUrl = "/admin/processAdminLogin"
userUrl = "/admin/processUserLogin"
}
As you can from the last three lines in the closure above, depending on the Role granted to the logging in User I am redirecting her to separate actions within the AdminController where a custom UserSessionBean is created and stored in the session.
It works fine for a regular login case which in my app is like so:
User comes to the app via either http://localhost:8080/my-app/ OR http://localhost:8080/my-app/login/auth
She enters her valid login id and password and proceeds.
The app internally accesses MyAuthSuccessHandler which redirects to AdminController considering the Role granted to this User.
The UserSessionBean is created and stored it in the session
User is taken to the app home page
I have also written a custom MyUserDetailsService by extending GormUserDetailsService which is correctly accessed in the above flow.
PROBLEM SCENARIO:
Consider a user directly accessing a protected resource (in this case the controller is secured with #Secured annotation) within the app.
User clicks http://localhost:8080/my-app/inbox/index
App redirects her to http://localhost:8080/my-app/login/auth
User enters her valid login id and password
User is taken to http://localhost:8080/my-app/inbox/index
The MyAuthSuccessHandler is skipped entirely in this process and hence my UserSessionBean is not created leading to errors upon further use in places where the UserSessionBean is accessed.
QUESTIONS:
In the problem scenario, does the app skip the MyAuthSuccessHandler because there is a target URL for it to redirect to upon login?
Can we force the process to always pass through MyAuthSuccessHandler even with the target URL present?
If the answer to 2 is no, is there an alternative as to how and where the UserSessionBean can still be created?
You can implement a customized eventListener to handle the post-login process, without disrupting the original user requested url.
In config.groovy, insert a config item:
grails.plugins.springsecurity.useSecurityEventListener = true
In you resources.groovy, add a bean like this:
import com.yourapp.auth.LoginEventListener
beans = {
loginEventListener(LoginEventListener)
}
And create a eventListener in src/groovy like this:
package com.yourapp.auth
import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent
import org.springframework.web.context.request.RequestContextHolder as RCH
class LoginEventListener implements
ApplicationListener<InteractiveAuthenticationSuccessEvent> {
//deal with successful login
void onApplicationEvent(InteractiveAuthenticationSuccessEvent event) {
User.withTransaction {
def user = User.findByUsername(event.authentication.principal.username)
def adminRole = Role.findByAuthority('ROLE_ADMIN')
def userRole = Role.findByAuthority('ROLE_USER')
def session = RCH.currentRequestAttributes().session //get httpSession
session.user = user
if(user.authorities.contains(adminRole)){
processAdminLogin()
}
else if(user.authorities.contains(userRole)){
processUserLogin()
}
}
}
private void processAdminLogin(){ //move admin/processAdminLogin here
.....
}
private void processUserLogin(){ //move admin/processUserLogin here
.....
}
}
Done.
1) Yes, because it is an "on-demand" log in.
2) Yes, you can set it to always use default. The spring security plugin has a setting for it "successHandler.alwaysUseDefault" change that to true it defaults to false.
Also if you need more details check out the spring docs look for the Setting a Default Post-Login Destination section.
3) If you want to still create the user session bean and then redirect to the original URL you have two options create the bean in an earlier filter or expose the needed data via a custom UserDetailsService. Personally I would go the route of a custom details service.

How to implement permission-based access control in Grails?

I want to implement permission-based access control as discussed in this post.
I am not familiar with the implementation - is there any detailed example on how to implement this from start to finish?
Have you seen the Spring security plugin? Main docs are here
As in this post you could also consider the Shiro plugin as well. I find the Spring security plugin a simpler but the Shiro one does have some advantages as discussed in the post.
I have just solved this (mostly) so I post it here for reference. This solution works on Grails 2.2.4 and Spring Security 2.0 RC
1) Security Domain Model
You model your security domain classes as described in the article, so you will have these domains in the end:
- Permission
- Role
- RolePermission
- User
- UserRole
2) Querying authorities for users
You make sure that your User class returns Permissions instead of Roles as authorities in the getAuthorities() method:
/**
* Gets authorities for the user.
*
* It will return all of the Permissions of the User assigned by the Roles
* which the User has
*/
Set<Permission> getAuthorities() {
Set allPermissions = []
// Collect all Roles of the User
UserRole.findAllByUser(this).each { userRole ->
// Collect all Permissions from the Role
Role role = userRole.role
// Returning the collected permissions
RolePermission.findAllByRole(role).each { rp ->
allPermissions.add(rp.permission)
}
}
return allPermissions
}
3) Spring Security Config
I have this in my Config.groovy for Spring Security configuration (non-relevant parts omitted):
grails {
plugin {
springsecurity {
...
userLookup {
userDomainClassName = 'limes.security.User'
}
authority {
nameField = 'name'
className = 'limes.security.Permission'
}
...
}
}
}
One important highlight is the authority.nameField which MUST conform with your Permission class. The name attribute is called 'name' in my model (and the article).
Naturally, you set your Permission class as the authority.className in order to make it fit with the return values of User.getAuthorities().
4) Using in security expressions
The solution above does not solve the limitation of the Grails Spring Security plugin that you can use only authority name starting with "ROLE_".
So, if you want to call your permissions like "PERM_PERMISSION1", than you have to write EL expressions for checks everywhere (notybly on controller #Secured annotations and static url rules).
So instead of
#Secured(["PERM_PERMISSION1"])
You write
#Secured(["hasRole('PERM_PERMISSION1')"])

Resources