0

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.

Community
  • 1
  • 1
Mike T
  • 405
  • 9
  • 19
  • So @PreAuthorize does not really authorize all the way down to the ACL's. To access a domain object protected by ACL requires that the current user has access privileges. So either I completely hack the ACE insertion or I work out how to grantPermission in this special case. – Mike T Aug 24 '13 at 00:22
  • So can I use RunAs Authentication replacement? Looks like it will do the job. http://static.springsource.org/spring-security/site/docs/3.0.x/reference/runas.html – Mike T Aug 26 '13 at 13:58
  • Well, it should work but it doesn't. I created a new role RUN_AS_INVITED_USER in the db, enabled RUnAss with grails.plugins.springsecurity.useRunAs = true, set grails.plugins.springsecurity.acl.authority.changeAclDetails = 'ROLE_RUN_AS_INVITED_USER' then annotated the method with @Secured(['ROLE_USER', 'RUN_AS_INVITED_USER']) but still no joy. Even worse, when I query the current roles in the method with springSecurityService.getPrincipal().getAuthorities() I don't see my new role set. – Mike T Aug 27 '13 at 23:02

2 Answers2

0

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)}
    }
}
Anatoly
  • 456
  • 5
  • 13
  • Thank you, but I am uncertain if this does what I need. While I can check to ensure that an object (the invitation, since the user may not yet be registered on the service) at resource entry point, I find that I am still getting an exception when setting the ACL permissions. – Mike T Aug 22 '13 at 17:19
  • Since acceptance of invitation is an event that may or may not occur in the future, there is a whole separate use-case scenario that needs to be properly handled outside of authentication cycle. You can take a look at Email Confirmation Plugin for an sample implementation of the "confirmation" part, which includes a scheduling domain class, a quartz job to handle timeouts, and uses an event listener API to react the various scenario outcomes. In either case, you definitely want a registered user before granting them a role. Hope that help. – Anatoly Aug 22 '13 at 20:53
  • The issue is that I am not granting a new user a role, I am granting a new user access to a domain object. – Mike T Aug 23 '13 at 23:56
0

The answer turned out to be using RunAs. For Grails, I did this as follows:

  1. 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) }

  2. Ensure in config.groovy that the role can change the target object:grails.plugins.springsecurity.acl.authority.changeAclDetails = 'ROLE_RUN_AS_INVITED_USER'

  3. Enable RunAs in config.groovy

    grails.plugins.springsecurity.useRunAs = true grails.plugins.springsecurity.runAs.key = [key]

  4. 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

Mike T
  • 405
  • 9
  • 19