grails with spring-security: manually log out selected users - grails

As a application administrator I would like to be able to log off any user, for example, after setting the flag "enabled = false" to the selected user. Is it possible in spring-security?
I should add that my application allows the use of "remember Me" for users.
I'm using:
grails 2.2.1
plugin spring-security-core 1.2.7.3
Settings spring-security-core (config.groovy):
grails.plugins.springsecurity.useHttpSessionEventP ublisher = true
grails.plugins.springsecurity.useSessionFixationPr evention = true
grails.plugins.springsecurity.userLookup.userDomai nClassName = 'com.app.User'
grails.plugins.springsecurity.userLookup.authority JoinClassName = 'com.app.UserRole'
grails.plugins.springsecurity.authority.className = 'com.app.Role'
grails.plugins.springsecurity.userLookup.usernameP ropertyName = 'email'
grails.plugins.springsecurity.securityConfigType = "Requestmap"
grails.plugins.springsecurity.rejectIfNoRule = true
grails.plugins.springsecurity.requestMap.className ='com.app.Requestmap'
grails.plugins.springsecurity.requestMap.urlField= 'url'
grails.plugins.springsecurity.requestMap.configAtt ributeField='configAttribute'
grails.plugins.springsecurity.rememberMe.cookieNam e = 'remember_me'
grails.plugins.springsecurity.cacheUsers = false
grails.plugins.springsecurity.rememberMe.tokenVali ditySeconds=604800
Has anyone had a similar problem may be?
thank you in advance :)

Setting the UserDetails.enabled flag to false should cause a DisabledException to be thrown on the user's next secure request. This can either send the user to the default authfail handler, or you can configure an exception handler in your UrlMappings to send the user to a custom controller or action.
If you're not using the enabled flag anywhere else in your application, you can direct the DisabledException to an action which clears the authentication session (and rememberMe), then resets the enabled flag.
Another possible way would be to create a custom filter and inject it into the spring security filter chain in your Bootstrap.
Both the url exception mapping and the filter configuration are described in the Spring Security Plugin Documentation

Thanks Codelark, I'm reading about filters and try to use them :)
Unfortunately, when I set enabled = false for the selected user application does not redirect it to "/login /authfail".
Best of all, the changes (enabled = false) are visible to the user profile but the lack of the desired effect.
I would add, I tried to set "expireNow" a user session:
def expireSession(User user) {
def orginalUser = springSecurityService?.principal.username
springSecurityService?.reauthenticate(user?.email) //modified user np: test#app.com
springSecurityService?.reauthenticate(orginalUser) //admin np: admin#app.com
sessionRegistry.getAllPrincipals()?.each { princ ->
sessionRegistry.getAllSessions(princ, false);
if(princ?.username?.equals(user?.email)) { //killing sessions only for user (test#app.com)
sessionRegistry.getAllSessions(princ, false)?.each { sess ->
sess.expireNow()
}
}//if
}//each
}//expireSession
Namely sessionRegistry really gets active sessions for each user, but by calling:
sess.expireNow()
The result is that calling expireSession (user) for the same user again, the session is no longer visible. Which is understandable because it has expired.
But in spite of expired user session. He may continue to work in the application. The application does not log you off

Related

Manually logout another user in grails

In my application I need to programmatically logout a user, not the current one. I was able to expire its session but this does not force a logout for that specific user. So my question is, how can I force a logout on the user's next request? This is my code now:
sessionRegistry.getAllPrincipals().each {
principal = it
sessionRegistry.getAllSessions(it, false).each {
if (principal.username == userSec.username) {
it.expireNow()
}
}
}
I have this in my resources.groovy:
sessionRegistry(SessionRegistryImpl)
sessionAuthenticationStrategy(ConcurrentSessionControlStrategy, sessionRegistry) {
maximumSessions = -1
}
concurrentSessionFilter(ConcurrentSessionFilter){
sessionRegistry = sessionRegistry
expiredUrl = '/login/concurrentSession'
}
Using the session mechanismus it is not possible. You have to use a storage medium where you can keep the users. This medium can be either an in-RAM singleton, like ConcurrentHashMap instance, or a DB, depending on your clustering architecture.
Then upon each call to springSecurityService.currentUser (e.g. in Spring Security core plugin), you have to check if the sessionRegistry contains that user, and if not, invalidate the session or the user

ldap.rememberMe.usernameMapper.userDnBase (multiple instances of?) (searchSubtree search capability)

I have a Grails application that is successfully using the latest spring-security-core:2.0-RC4 and spring-security-ldap:2.0-RC2. Users can login perfectly using
grails.plugin.springsecurity.ldap.search.base setting for LDAP login authentication.
There is a different setting for the rememberMe userDnBase (mapper) and that setting is:
grails.plugin.springsecurity.ldap.rememberMe.usernameMapper.userDnBase
The LDAP authentication grails.plugin.springsecurity.ldap.search.base is set to ou=people,dc=sitcudy,dc=edu. As mentioned above - the logins work fine because there is a property called searchSubtree that I have set to true. Unfortunately, the searchSubtree setting does not hold true and carry through consistently within the 'remember-me' portion of the code (.ldap.rememberMe)*. The remember-me portion of the code uses a map for the base DN, grails.plugin.springsecurity.ldap.rememberMe.usernameMapper.userDnBase
so I put in a string in the config.groovy file (the same as for the authentication piece) to map to the base DN of ou=people,dc=sitcudy,dc=edu.... which gets mapped to the DN for the LDAP user look up upon returning to the application for persistence cookie login.
Here's where my problem comes in, most users are segregated into different DIT's in our LDAP system. For example, some uses are in ou=staff,ou=people,dc=sitcudy,dc=edu while other users are in ou=students,ou=people,dc=sitcudy,dc=edu therefore, because of the remember me mapping, upon returning to the application, once verifying the cookie, the code tries to bind users in this format, uid=reuben_marcus,ou=people,dc=sitcudy,dc=edu which doesn't exist. What does exist is uid=reuben_marcus,ou=staff,ou=people,dc=sitcudy,dc=edu therefore the cookie is destroyed and the login (IS_AUTHENTICATED_REMEMBERED) never occurs.
If I change grails.plugin.springsecurity.ldap.rememberMe.usernameMapper.userDnBase
to ou=staff,ou=people,dc=sitcudy,dc=edu the remember me functionality works perfect for all staff members, but it doesn't work for all other people - students, faculty etc.
The main setting in question below for me in this issue is:
grails.plugin.springsecurity.ldap.rememberMe.usernameMapper.userDnBase
Since this is just a mapping and there isn't allowance for multiple userDNBases or searchSubtree search.. How is the ‘remember-me’ code supposed to find users that do not fall into this base DN setting...??
I wonder if I'm doing something wrong or if this is a feature request to have the ‘remember me’ code have options for multiple mapping userDNBases or allow it to have a searchSubtree search capability.
Relevant settings from my config.groovy:
grails.plugin.springsecurity.ldap.mapper.roleAttributes = 'sitPriRole,uid'
grails.plugin.springsecurity.ldap.context.managerDn = 'uid=SPS_bind,ou=People,dc=sitcudy,dc=edu'
grails.plugin.springsecurity.ldap.context.managerPassword = 'xxx'
grails.plugin.springsecurity.ldap.context.server = 'ldap://ds01.sitcudy.edu:389'
grails.plugin.springsecurity.ldap.authorities.groupSearchBase ='ou=Groups,dc=sitcudy,dc=edu'
grails.plugin.springsecurity.ldap.search.base = 'ou=People,dc=sitcudy,dc=edu'
grails.plugin.springsecurity.ldap.search.searchSubtree = true
grails.plugin.springsecurity.ldap.auth.hideUserNotFoundExceptions = false
grails.plugin.springsecurity.ldap.search.attributesToReturn = ['uid', 'sitPriRole', 'mail', 'displayName']
grails.plugin.springsecurity.providerNames = ['ldapAuthProvider', 'anonymousAuthenticationProvider', 'rememberMeAuthenticationProvider']
grails.plugin.springsecurity.ldap.authorities.retrieveGroupRoles = false
grails.plugin.springsecurity.ldap.authorities.retrieveDatabaseRoles = false
grails.plugin.springsecurity.password.algorithm = 'SHA-256'
grails.plugin.springsecurity.rememberMe.persistent = true
grails.plugin.springsecurity.rememberMe.persistentToken.domainClassName = 'od.PersistentLogin'
// role-specific LDAP config
// grails.plugin.springsecurity.ldap.useRememberMe = true
grails.plugin.springsecurity.ldap.rememberMe.detailsManager.attributesToRetrieve = null
grails.plugin.springsecurity.ldap.rememberMe.detailsManager.groupMemberAttributeName = 'uniquemember'
grails.plugin.springsecurity.ldap.rememberMe.detailsManager.groupRoleAttributeName = 'cn'
grails.plugin.springsecurity.ldap.rememberMe.detailsManager.groupSearchBase = 'ou=Groups,dc=sitcudy,dc=edu'
grails.plugin.springsecurity.ldap.rememberMe.detailsManager.passwordAttributeName = 'userPassword'
grails.plugin.springsecurity.ldap.rememberMe.usernameMapper.userDnBase = 'ou=People,dc=sitcudy,dc=edu'
grails.plugin.springsecurity.ldap.rememberMe.usernameMapper.usernameAttribute = 'uid'
This problem was mentioned here: Grails - Spring security plugin ldap: remember me not working
I found a workaround for this by registering custom TokenBasedRememberMeServices bean in resources.groovy.
I didn't use persistent logins functionality available in grails-spring-security-ldap plugin, because I found it incompatible with my Active Directory tree layout. Most probably, this could be customized by extending LdapUserDetailsManager but in my situation I found it unnecessary to store token in database.
I used regular spring security remember me cookie option but without storing user password in the cookie. I extended the following methods from TokenBasedRememberMeServices
makeTokenSignature - make token signature without password field
processAutoLoginCookie- if cookie exists, then retrieve username from cookie token and fetch ldap user details (I had to write my own method retrieveUserFromLdap() explained later)
onLoginSuccess - this gets triggered when user logs in with remember-me option checked. Here, I'm removing password and saving token signature to cookie.
To fetch user details and roles from LDAP it might depend on specific implementation but my method looks like this:
static protected UserDetails retrieveUserFromLdap(String username) {
def ldapUserSearch = Holders.applicationContext.getBean('ldapUserSearch')
def userContextMapper = Holders.applicationContext.getBean('ldapUserDetailsMapper')
def authoritiesPopulator = Holders.applicationContext.getBean('ldapAuthoritiesPopulator')
def userContext = ldapUserSearch.searchForUser(username)
def userAuthorities = authoritiesPopulator.getGrantedAuthorities(userContext,username)
userContextMapper.mapUserFromContext(userContext,username,userAuthorities)
}

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 use a shiro native session in a grails web application?

Currently, I am using the default HttpSession object in both controllers and gsp pages:
In controllers:
...
session.mykey = anObject; // adding an object to session
...
if (session.otherkey) { // performing some checking
In GSPs:
...
<g:if test="${session.mykey}">
...
I'd like to have a "remember me" functionality. Shiro has already it built in. However, as far as I understood, in order to do it I have to use the shiro native session mode (in Config.groovy: security.shiro.session.mode="native"). By default, it persists the session state, so objects will remain in the session as far as the cookie expires or the user logs off.
Is my understanding right?
Then i will have to change my controllers to this:
def shiroSession = SecurityUtils.subject.session
shiroSession.setAttribute("mykey",anObject)
....
if (shiroSession.getAttribute("otherkey") ){
And my views to this:
<g:if test="${SecurityUtils.subject.session.getAttribute('mykey')}">
So, my questions are:
Is that right?
Can't I just use the previous way to access the session?
Do I have to turn off the default http session in some configuration?
I gave up keeping objects in the session persistently (until cookie expires). Here is what i did:
In the login method in the controller:
if (! session.currentProfile){
Subject currentUser = SecurityUtils.getSubject()
if (currentUser.isRemembered()){
boolean success = configureSession(session, currentUser.getPrincipal())
if (success){
...
}
}
....
}
The first "if" checks whether the session has the object i need.
The configureSession method puts in the session all information I need.

symfony sfGuardUser hasCrendential live after update

I'm using symfony 1.4 and the sfGuardDoctrinePlugin, I've got it installed and setup fine but I have the following problem:
If I login as an admin and update the permissions for a user, that user must logout then login again before having the newly added credential/permission.
Is there a way around this?
I'm not sure how easy this would be to fix. When a user logs in I think their credentials are added to their session attributes there and then. So when the admin updates their credentials their session still holds the old credentials. This means any call to hasCredential isn't "live".
Thanks
This would add extra queries to each and every request to your application. You could force update of the credentials by $user->getSfGuardUser()->refresh(true), which would reload the entity and all its relations (and thus its permissions).
Thanks for your answer, I've modified the processForm function of the sfGuardUser module's actions class.
If I login and change my own permissions, the session is updated there and then.
My problem is that if I edit the user permissions of another user, I would need to edit their session data. To solve this I enabled database sessions, so I now have sessions saving there instead of to file. So my next problem is how to isolate the session for the other user.
The sessions database has the following columns: sess_id, sess_data, sess_time.
sess_data is serialized and that is what I would need to update.
But I think symfony updates the session ids quite often and it would be hard to always isolate the correct session for the other user.
I think that it would also be slow to try and unserialize, check user_id then reserialize the data. I would need a user_id column I think.
I know this is an old question, but I recently had this same problem and it took me way longer than it should have to find the answer (which was posted in Symfony's code snippet section). Paste this function in your myUser class and all problems go away:
/**
* Overridden method that actually reads the permission from DB
* instead of relying on data present when the user logs in.
*
* #param string permission name
*
* #return boolean true if the user has credential
*/
public function hasCredential($permission_name)
{
if (!$this->isAuthenticated()) {
return false;
}
$gu = $this->getGuardUser();
$groups = $gu->getGroups();
$permissions = $gu->getPermissions();
$permission_names = array();
foreach($permissions as $permission) {
$permission_names[] = $permission->getName();
}
foreach($groups as $group) {
$group_permissions = $group->getPermissions();
foreach($group_permissions as $group_permission) {
$permission_names = array_merge($permission_names, array($group_permission->getName()));
}
}
$permission_names = array_unique($permission_names);
return (in_array($permission_name, $permission_names)) ? true : false;
}

Resources