views:

428

answers:

2

Hi,

I'm using the annotations provided by the Spring Security (AKA Acegi) plugin. I have controller actions annotated with

@Secured(['ROLE_ADMIN', 'ROLE_USER'])

To indicate that they should be available to administrators and regular users. But now I need to indicate that an action is available to administrators and unregistered users. Is it possible to use annotations to indicate a user without any role, i.e. unregistered?

Thanks, Don

+2  A: 

The token for that is IS_AUTHENTICATED_ANONYMOUSLY which means anyone whether logged in or not. IS_AUTHENTICATED_REMEMBERED means anyone logged in with a remember-me cookie or via explicit login, and IS_AUTHENTICATED_FULLY means logged in via explicit login (not using cookie).

If you annotate your action with

@Secured(['IS_AUTHENTICATED_ANONYMOUSLY'])

then it'll allow anyone to access that action. You can combine these special tokens with roles, so for example to allow admin-only but force a username/password login even if the user has a remember-me cookie you'd use

@Secured(['ROLE_ADMIN', 'IS_AUTHENTICATED_FULLY'])
Burt Beckwith
Thanks for the response. What I want to express using annotations is "not logged in", rather than "whether logged in or not", is that possible?
Don
+2  A: 

Here's a solution that requires that you not be logged in, or that you have ROLE_ADMIN. You need a custom voter that processes a new 'IS_NOT_AUTHENTICATED' token:

package com.burtbeckwith.grails.springsecurity

import org.springframework.security.Authentication
import org.springframework.security.AuthenticationTrustResolverImpl
import org.springframework.security.ConfigAttribute
import org.springframework.security.ConfigAttributeDefinition
import org.springframework.security.vote.AccessDecisionVoter

class NotLoggedInVoter implements AccessDecisionVoter {

   private authenticationTrustResolver = new AuthenticationTrustResolverImpl()

   int vote(Authentication authentication, object, ConfigAttributeDefinition config) {
      for (configAttribute in config.configAttributes) {
         if (supports(configAttribute)) {
            if (authenticationTrustResolver.isAnonymous(authentication)) {
               // allowed if not logged in
               return ACCESS_GRANTED
            }
            for (authority in authentication.authorities) {
               if ('ROLE_ADMIN' == authority.authority) {
                  // allowed if logged in as an admin
                  return ACCESS_GRANTED
               }
            }
         }
      }

      return ACCESS_DENIED
   }

   boolean supports(ConfigAttribute attribute) {
      'IS_NOT_AUTHENTICATED' == attribute?.attribute
   }

   boolean supports(Class clazz) {
      true
   }
}

Register this as a bean in resources.groovy:

beans = {
   notLoggedInVoter(com.burtbeckwith.grails.springsecurity.NotLoggedInVoter)
}

and add it to the voters list in SecurityConfig.groovy by setting the 'decisionVoterNames' property:

decisionVoterNames = ['notLoggedInVoter', 'authenticatedVoter', 'roleVoter']

and annotate your controller action with this:

@Secured(['IS_NOT_AUTHENTICATED'])

and it'll only allow non-authenticated users and authenticated users with ROLE_ADMIN.

Burt Beckwith
I think it would be better from a design point of view to change the meaning of IS_NOT_AUTHENTICATED from (admin or not authenticaed) to simply (not authenticated). Presumably then you could combine this token with any combination of ROLE_ tokens.Presumably this change could be made by removing the last 'for' loop in `NotLoggedInVoter.vote`
Don
By the way, great job, I wasn't expecting this level of help.
Don