Announcement Announcement Module
Collapse
No announcement yet.
Custom Role-Permission URL handling is impossible in 3.1 Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Custom Role-Permission URL handling is impossible in 3.1

    I just spent the last month crawling through Spring Security 3.1. The more I wanted to customise how authentication was handled, the worse things got. And then I hit this.

    I have a simple ROLE-->permissions authorisation model. That is to say, a ROLE_manager would have several permissions assigned to it: PERM_create, PERM_delete, PERM_update, and PERM_read, whereas ROLE_reader would only have PERM_read assigned to it. Pretty simple.

    For @PreAuthorize annotations, I was able to override the standard behaviour of the namespace configuration with a combination of a custom AccessDecisionManager injected with a custom RoleVoter, which operate on custom GrantedAuthority objects. The custom GrantedAuthority objects would contain a single Role object.


    Code:
    <bean id="almanacAccessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
        <constructor-arg name="decisionVoters">
            <list>
                <bean id="webExpressionVoter" class="org.springframework.security.web.access.expression.WebExpressionVoter" />
                <bean id="authenticatedVoter" class="org.springframework.security.access.vote.AuthenticatedVoter" />
                <bean id="almanacRoleVoter" class="com.facets.w101.almanac.spring.sec.AlmanacRoleVoter">
                    <constructor-arg name="masterRoleShelf" ref="masterRoleShelf" />
                </bean>
            </list>
        </constructor-arg>
    </bean>

    Code:
    <http name="httpSiteMap"
          auto-config="false"
          use-expressions="true"
          entry-point-ref="loginUrlAuthenticationEntryPoint"
          access-decision-manager-ref="almanacAccessDecisionManager" >
        <custom-filter ref="almanacUsrPwdAuthProcFilter" position="FORM_LOGIN_FILTER" />
        <custom-filter ref="almanacAnonAuthFilter" position="ANONYMOUS_FILTER"/>
        <anonymous enabled="false" />
        ...
    So, in my custom RoleVoter, I was now able to receive a PreAuthorise declaration like this:

    Code:
    @PreAuthorize("hasRole('PERM_read')")
    ... and the role voter searched through the authenticated user's roles to see if any of them contained the PERM_read permission. With that done, I turned to the namespace intercept-url entries. It looks to me that there is no way to customize the way the WebExpressionVoter handles the "hasRole" expressions.

    I found that SecurityExpressionRoot is the class that handles the final role comparison, and someone decided to make all the relevant methods either final or private. The first place you would implement the same custom logic for URL security would be the hasRole and hasAnyRole methods. They are final. The next place you would rig this would be the getAuthoritySet method. That's private. How about a custom RoleHierarchy? My custom GrantedAuthority class encapsulates a single Role object, since Granted Authority can only return a single String. Well, because RoleHierarchy has to return Collection<? extends GrantedAuthority>, the only thing I could really do is to modify my GrantedAuthority to represent both my Role and Permission classes, which breaks the "assign only roles to authenticated entities" convention and would probably cause problems later.

    I can't/won't override the DefaultWebSecurityExpressionHandler because there is too much functionality that I would have to re-implement or rewrite, and the same goes even more for the WebExpressionVoter. That I can't just override the SecurityExpressionRoot (and inject it via a custom AbstractSecurityExpressionHandler) is annoying to say the least after getting this far.

    What are my options here? Do I just forgo fine-grained control for URLs? Is there a way to do this that I haven't found yet? Anyone got any ideas?
    Last edited by ogradyjd; Feb 18th, 2012, 06:45 PM.

  • #2
    Have you considered using "Group Authorities" as described in http://static.springsource.org/sprin...ix-schema.html ? That would allow you to assign a group (manager) with the authorities "PERM_create, PERM_delete, PERM_update, and PERM_read" and another group reader with the authority "PERM_read"

    Then in your code use:

    Code:
    @PreAuthorize("hasRole('PERM_read')")
    And not have to muck with custom voters.

    Comment


    • #3
      Not so much...

      From what I'm reading from your link, I would need to implement the database schema as described. The text hints that you could use your own schema:

      Remember that these tables are only required if you are using the provided JDBC UserDetailsService implementation. If you write your own or choose to implement AuthenticationProvider without a UserDetailsService, then you have complete freedom over how you store the data, as long as the interface contract is satisfied.
      ...but give no clues as to how you would do that. As it is, I'm using a database schema that I cannot change to meet the structure described, and as for figuring out how to use my own schema - I'm getting kind of tired of the docs hinting at something in one chapter, and having to put together disparate information mentioned briefly in several other chapters in order to figure out how to do it. It is literally easier to runtime-debug the source code to figure out how to make Spring Security work when you do anything other than the hello-world configuration.</endRant>

      What I really don't understand here, and this is for the almighty Spring Devs, is why there isn't a single unified Role evaluator interface that you can implement, and then inject into all the places Roles are checked. It's like the URL modules and the method modules were written by completely separate teams with no collaboration. With a single interface with the "hasRole", "hasRoles", and probably permission methods as well defined in it, changing the behaviour of both URL and method protectors could be done with one class injected into both modules - into the default RoleVoter and SecurityExpressionRoot. In my search for an answer to my question, I've found that the problems with customizing Role evaluation have existed for at least three years, so why this should still be a problem with such a simple fix available puzzles me.

      Comment


      • #4
        There's better documentation in the javadocs regarding groups at http://static.springsource.org/sprin...bcDaoImpl.html. No, you don't have to use their schema.

        That said, I might not be sending you in the direction you want to go.

        Comment


        • #5
          Thanks for the tip - it looks good, and if I were starting from scratch, I might have tried to use the JdbcDaoImpl class. As it is, it looks that I just won't be able to use fine-grained role-based permissions on URLs, unless there are any other hooks out there I don't know about.

          Comment

          Working...
          X