Spring Security (Acegi) and user Groups (vs. Roles) - grails

We're developing an app (using Grails Spring Security (formerly Acegi)) in which we'll have thousands of users that span 10-15 discreet user types. In the current system, each user type equates to a "group", and the specific roles and permissions are tied to the group. The user gets all their "roles" from the group.
For example, we might have two user groups:
CLOWN: roles = ride_clown_car, toot_horn, receive_applause
ACROBAT: roles = do_flip, walk_tightrope, receive_applause
We have three users, one assigned to the CLOWN group, one assigned to the ACROBAT group, and one assigned to both (has union of CLOWN and ACROBAT roles).
If we change permissions, we do so at the group level. For example, if we add a swing_on_trapeze permission to the ACROBAT group, all acrobats will automatically inherit it.
In Grails terms, the permissions on the controllers would still be at the role level. So an action with #Secured (['toot_horn']) would allow users in the CLOWN group but not in the ACROBAT group. #Secured (['receive_applause']) would allow both CLOWNS and ACROBATS.
How would I do this in Spring Security given the two-tiered nature of the model (user, role)? Do I need to implement my own custom authentication to collect roles based via groups?
Thanks!

You should be using the new Spring Security Core plugin since the Acegi plugin isn't being developed and is basically deprecated.
But either way, both plugins just expect that there's something like a getAuthorities() method in your user class that returns role instances. In a scenario like this where the user has many groups, just collect all of the groups' roles:
class User {
...
def getAllRoles() {
Set allRoles = []
groups.each { allRoles.addAll it.roles }
allRoles
}
}
This assumes that you have a many-to-many between User and Group:
static hasMany = [groups: Group]
and Group has a many-to-many with Role:
static hasMany = [roles: Role]
To use this set the 'relationalAuthorities' property to 'allRoles' in SecurityConfig.groovy so it uses that instead of the many-to-many between user and role:
relationalAuthorities='allRoles'
There's no configuration required for the Spring Security core plugin since it depends on an application-defined getAuthorities method already, so just use something like this in your User class:
Set<Role> getAuthorities() {
Set allRoles = []
groups.each { allRoles.addAll it.roles }
allRoles
}

Related

How can I store the hierarchic Authority Class in the Person Class?

my question is: (How?) can I store my "authority" for my user in the user class in Grails with the Spring Security Plugin?
I've given the following SQL structure:
User table:
...
varchar(255): username
...
int(2) rank: (refers to Rank table)
Rank table:
int(11): id
varchar(25): rank_name
Can I configure my domain classes, to work with that structure instead of the typical "User, Authority, UserAuthority many to many" schema?
The rank (id) is hierarchial, that means a user with the rank 4 got all "authorities" from a user with rank 3 etc.
EDIT:
I simply replaced in the User Domain, the getAuthorities method with the following:
Set<Permission> getAuthorities() {
Authority.findAllById(this.rank)
// instead of: UserAuthority.findAllByUser(this)*.permission
}
Look at GormUserDetailsService to see how authorites are loaded.
There's an authoritiesPropertyName configuration for springsecurity core, which specifies the property name of user domain which should return the authorities, the default value is 'authorities' -- you can modify this to return the authorities based on your domain model.
If this does not satisfy your requirement then You can create a custom user details service too.
And override the original user details service by defining a bean with name userDetailsService in your conf/spring/resources.groovy

Grails - Mapping and Data Binding multiple collections of the same Domain Class

I have a domain class (let's call it security) and it needs three "one to many" relationships with another domain class (let's call it role). The three relationships are "current", "previous", and "new".
Basically I'm hoping to do something like this (in reality it's more complicated, but this will do to illustrate things):
class Security
{
static hasMany = [current: Role, previous: Role, new: Role]
Set current
Set previous
Set new
}
class Role
{
static belongsTo = [security: Security]
String name
}
Unfortunately something like this doesn't work since the Role domain class maps to only one table and that one table has only one column for the Security class' ID.
Is this possible without creating three separate Role classes and instead just map one class to multiple tables?
I'm also looking at the possibility of simply including a flag in the Role class to signal whether the role is current, previous, or new and only having one "one to many" relationship in the Security class. However, this approach is causing issues with data binding, since my HTML form would need to send two pieces of information for each role (the name property and also the type). Since these roles are listed in a HTML select statement I can only send one piece of information (the name).
If you do not depend on the belongsTo in Role (you have to remove it) you can use joinTable:
class Security {
static hasMany = [current: Role, previous: Role, next: Role]
static mapping = {
current joinTable: [name:'current']
previous joinTable: [name:'previous']
next joinTable: [name:'next']
}
}
You could use Grails mappedBy, just like below, if creating 3 fields inside the Role is not an issue:
class Security {
static hasMany = [current: Role, previous: Role, new: Role]
static mappedBy = [current: 'securityForCurrent',
previous: 'securityForPrevious', new: 'securityForNew']
}
class Role {
String name
Security securityForCurrent
Security securityForPrevious
Security securityForNew
}

Spring Security in Grails providing authorization

A suggestion here would be deeply appreciated.
We use Spring Security domain object security to lock down access to specific objects in our Grails application. We have extended permissions as well. We use normal #PreAuthorize annotation to control access to the domain objects depending up the privileges assigned to a specific user.
In this architecture, I want for one user to be able to invite a second user to join a group. To do this, the second user receives an invitation from the first user to join a group with specific privileges. The second user can choose to accept or reject the invitation.
Given that the domain object belongs to the first user, not the second user, how could I grant access to the second user to the domain object with the privileges offered?
I have tried to hack a solution using #PreAuthorize("permitAll") on a service method just to see if that would work. While not ask secure as granting an object the privilege to set privileges to a domain object, it at least would get me going. But no joy, I continue to get "accessDenied for class" errors, presumably because the current user is the second user, not the domain object owner.
Do I need to work through the SpEL suggestion here? I barely understand how the bean resolver works unfortunately.
Edit: Even when I verify the invitation is valid at invocation Spring ACL throws an exception when the first ACE for the new user is attempted to be inserted. The exception occurs in void setPermissions(ObjectIdentity oid, recipient, Collection permissions) at the insertAce call:
void setPermissions(ObjectIdentity oid, recipient, Collection permissions) {
Sid sid = createSid(recipient)
MutableAcl acl
try {
acl = aclService.readAclById(oid)
}
catch (NotFoundException e) {
acl = aclService.createAcl(oid)
}
int deleted = 0
acl.entries.eachWithIndex { AccessControlEntry ace, int i ->
if (ace.sid == sid) {
acl.deleteAce(i-deleted)
deleted++
}
}
permissions.each {
acl.insertAce(acl.entries.size(), it, sid, true)
}
aclService.updateAcl acl
}
I assume that the access is denied because the current user (who did not exist when the invitation was issued) does not have the rights to set permissions on an object owned by another user.
You can use your Role class not only for generic roles (Admin,User, etc.), but for application specific ones as well. Simply allow the user to create a Role for a resource and then allow their invitees to be granted that role. Spring Security comes with a handy ifAnyGranted() method, which accepts a comma-delimited String of role names. At a resource entry-point simply ensure that a particular role is granted:
class Conversation{
Role role
}
class ConversationController{
def enterConversation(){
// obtain conversation instance
if(!SpringSecurityUtils.ifAnyGranted(conversationInstance.role.authority){response.sendError(401)}
}
}
The answer turned out to be using RunAs. For Grails, I did this as follows:
Create a Role specific for allowing invited users to act as an administrator in order to access a protected object. In Bootstrap, ensure this role is loaded into the SecRole domain:
def invitedRole = SecRole.findByAuthority('ROLE_RUN_AS_INVITED_USER')
if (!invitedRole) {
invitedRole = new SecRole()
invitedRole.authority = "ROLE_RUN_AS_INVITED_USER"
invitedRole.save(failOnError:true, flush:true)
}
Ensure in config.groovy that the role can change the target object:grails.plugins.springsecurity.acl.authority.changeAclDetails = 'ROLE_RUN_AS_INVITED_USER'
Enable RunAs in config.groovy
grails.plugins.springsecurity.useRunAs = true
grails.plugins.springsecurity.runAs.key = [key]
annotate the service method
#PreAuthorize([check that this is indeed an invited user])
#Secured(['ROLE_USER', 'RUN_AS_INVITED_USER'])
and it all works. The trick was to make the ROLE_RUN_AS_INVITED_USER able to change acl's.
Reference:
http://static.springsource.org/spring-security/site/docs/3.0.x/reference/runas.html
http://grails-plugins.github.io/grails-spring-security-acl/docs/manual/guide/2.%20Usage.html#2.5%20Run-As%20Authentication%20Replacement

Spring Security User Roles Per Organization

In my application I have a top level entity called Organization. The relationship between User and Organization is many-to-many.
Because of this I could have the following scenario:
UserA has role ROLE_ADMIN for OrganizationA
UserA has role ROLE_USER for OrganizationB
I need to ensure that when UserA accesses resources for OrganizationB he is not doing it as an ADMIN. So I need an additional check that the user has the correct roles at the organization level. Is there anything built into Spring Security that allows for this? If not, does anyone know what the best way would be to about solving this?
UPDATE: A bit more information...
A User logs in and chooses which org they want to work with. That is stored in the session. Beyond that, URLs are locked down with the Secured annotation. What that means is that if UserA were to log in and select OrgA, they should be able to access /admin/user/create however, if they log in and choose OrgB they should not have access to that URL.
The long way is to add additional checks in every method where this matters. So call some service method that says "ok, you're an admin for OrgA but not for OrgB and you're logged in using OrgB, so deny this request".
I'm hoping for a more grails / spring-security way of handling this.
You can probably do this by using a custom AccessDecisionVoter. The vote method will supply you with the "configuration attributes" for the resource (method or URL), which will typically be the required roles, and you can obtain the current user's roles/authorities either directly from the Authentication object, or by reading the current org and selecting the appropriate roles for the user.
I'm assuming that you have some way of differentiating the user's roles, based on the org they've selected.
Essentially, you'd be writing an extended version of the standard RoleVoter, which takes the organization into account.
I think I'm little late here but this is what worked for me:
When an organization is selected, you can set a new Authentication object with new roles in your session(The previous Authentication object gets invalidated). Something like this:
#RequestMapping(value = "/org-a")
String orgA(HttpServletRequest request) {
request.getSession().setAttribute("org", "org-a")
Organization org = new Organization("org-a")
reloadRolesForAuthenticatedUser(org)
....
}
private void reloadRolesForAuthenticatedUser(Organization org) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication()
List<String> newRoles = getRoles(auth.getPrincipal().getUsername(), org)
List<GrantedAuthority> authorities = getAuthorities(newRoles)
Authentication newAuth = new UsernamePasswordAuthenticationToken(auth.getPrincipal(),auth.getCredentials(),authorities)
SecurityContextHolder.getContext().setAuthentication(newAuth)
}
private List<GrantedAuthority> getAuthorities(List<String> roles) {
List<GrantedAuthority> auths = new ArrayList<GrantedAuthority>()
if (!roles.isEmpty()) {
for (String r : roles) {
auths.add(new SimpleGrantedAuthority(r))
}
}
return auths
}

Grails Spring Security SecUser

can i create two different types of secuser's such as secuser and enduser ,SecRole and EndRole
where my secuser and secrole will be given for admins of my hospital and doctors and enduser,endrole will be given to endusers of my hospital project
I have secuser and secrole tables with me but when i created enduser and endrole with s2-quickstart command am able to get the domain classes and i didn't overide my login and logout controllers now am not able to create a enduser object endrole object in my boot strap
class BootStrap {
def springSecurityService
def init = { servletContext ->
/*
def userRole = EndRole.findByAuthority('ROLE_USER') ?: new EndRole(authority: 'ROLE_USER').save(failOnError: true)
def endadminUser = EndUser.findByUsername('endadmin') ?: new EndUser(
username: 'endadmin',
password:'endadmin',enabled: true).save(failOnError: true)
if (!endadminUser.authorities.contains(userRole)) {
EndUserEndRole.create endadminUser, userRole
}
*/
def x= new EndRole(authority: 'ROLE_USER')
println(" new fresh "+x.authority)
}
def destroy = {
}
}
You can have as many types of user as you want, but to support that you will need a custom UserDetailsService. This is a common thing to do, so there's a section in the docs for it; see section "11 Custom UserDetailsService" in http://grails-plugins.github.com/grails-spring-security-core/docs/manual/
I'm not sure why you think you need more than one role class though. Just create an instance of SecRole with a different role name for admins and doctors, e.g. new SecRole(authority: 'ROLE_DOCTOR').save(), new SecRole(authority: 'ROLE_ADMIN').save().
You probably don't need different user classes at all (at least not for security - you may need to support different attributes for non-security reasons). Just create SecUser instances and grant them whatever roles (with EndUserEndRole.create) they need, i.e. ROLE_DOCTOR or ROLE_ADMIN.
Do yourself a favor and read the plugin documentation, but also the Spring Security documentation at http://static.springsource.org/spring-security/site/docs/3.0.x/reference/springsecurity.html - this stuff is way too important to implement if you're not comfortable with how to secure a web site.

Resources