Announcement Announcement Module
Collapse
No announcement yet.
Configuration of Spring Security 3.0M1 Expression Handler - bug? Page Title Module
Move Remove Collapse
This topic is closed
X
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • #16
    As stated in previous post I basically had to go the route of duplicating the following classes b/c their visibility is package currently.

    MethodSecurityExpressionRoot
    MethodSecurityEvaluationContext
    DenyAllPermissionEvaluator

    And then extended SecurityExpressionRoot with my own customized one which has my new customized check (i.e. hasVisibility(Object) )

    And then implemented the MethodSecurityExpressionHandler basically coping the DefaultMethodSecurityExpressionHandler code into mine (b/c I need to use a different SecurityExpressionRoot)

    This does seem like a lot of unnecessary work to accomplish extending the the MethodSecurityExpressionHandler (would be easier if others were public classes). But as pointed out in previous post the need to implement a MethodSecurityExpressionHandler is necessary to use a different SecurityExpressionRoot.

    What would be AMAZING would be full SpEL support in the method security approach in which it will look up my particular bean and invoke them method directly on it returning a boolean for access.

    So I would have
    Code:
    @PostAuthorize("#somebean.hasVisibility(returnObject)")
    public Object findByRandomCriteriaField(String fieldValue);

    Comment


    • #17
      Originally posted by pmularien View Post
      A couple points that made it hard to do what I wanted:
      • WebSecurityExpressionRoot is package protected. This meant that I couldn't extend it (and retain the hasIpAddress functionality) and had to instead extend SecurityExpressionRoot. It would be convenient if this class were public.
      • DefaultWebSecurityExpressionHandler directly instantiates WebSecurityExpressionRoot. This meant that I couldn't substitute my own implementation of SecurityExpressionRoot, but instead had to write my own WebSecurityExpressionHandler just to resolve this dependency to my custom class. It would be great if the SecurityExpressionRoot implementation were an injectable dependency.
      • I don't know if this is possible with the way Spring 3 EL contexts are implemented, but it would be nice if, instead of requiring me to subclass SecurityExpressionRoot, SecurityExpressionRoot could be provided with a Collection (List?) of classes that provide additonal methods that could be bound into the EL context. This way I wouldn't have to touch any of these classes directly to expose custom EL methods used for access control.
      Hope that clarifies things. The more I dig into Spring Sec 3, the more I appreciate the great OO design that you and Ben have done. I hope this input helps!
      I had to do all the same things and agree having either the ability to specify a list of various classes that provide additional custom security checking or full context aware Spring SpEL access as I mentioned in my previous post.

      Comment


      • #18
        I'm working on the same topic but on the "web" side, and as soon as I come up with a solution, I will post here the steps too.

        Anyway, I really think this stuff is hot and actually having this kind of flexibility can boost development time. I think we should give Luke and everyone else involved in the design and development of SpringSecurity the time to think carefully about this stuff. I would definitely consider this a killer feature of SpringSecurity (once it's implemented the correct and flexible way), and I do really look forward to see improvements on this area (maybe v3.1 or v3.5?).

        Great job and thank you (never enough)

        Originally posted by split3 View Post
        I had to do all the same things and agree having either the ability to specify a list of various classes that provide additional custom security checking or full context aware Spring SpEL access as I mentioned in my previous post.
        I second this one. I have a bean that already "does everything" (return a nice boolean that is my condition) but, in order to use it, I have to do all this round-trip. To access the bean through EL would be awesome.
        Last edited by namero999; Dec 2nd, 2009, 12:52 PM. Reason: addition

        Comment


        • #19
          Originally posted by namero999 View Post
          Anyway, I really think this stuff is hot and actually having this kind of flexibility can boost development time. I think we should give Luke and everyone else involved in the design and development of SpringSecurity the time to think carefully about this stuff. I would definitely consider this a killer feature of SpringSecurity (once it's implemented the correct and flexible way), and I do really look forward to see improvements on this area (maybe v3.1 or v3.5?).

          Great job and thank you (never enough)
          Oh I agree 100% the new stuff in Spring Security 3 is amazingly well done, and most definitely cut down our existing development time for sure. I don't mind having to implement a few extra classes myself to accomplish what I needed to as it was pretty straight forward once I started digging in the source.

          Great job guys and keep up the amazing work.

          Daniel

          Comment


          • #20
            So after some playing I managed to get this working. I write the steps here for future references.

            Unfortunately this required some tweaking to the xml schema definition.

            First, I "enabled" the expression-handler element inside of the http element and configured it.

            spring-security-3.0.xsd
            Code:
            <xs:element name="expression-handler">
                <xs:complexType>
                    <xs:attributeGroup ref="security:ref"/>
                </xs:complexType>
            </xs:element>
            security.xml
            Code:
            <beans:bean id="myHandler" class="com.MyHandler" />
            <http use-expressions="true">
                <expression-handler ref="myHandler"/>
            </http>
            After that, I implemented a custom WebSecurityExpressionHandler (MyHandler) and extended WebSecurityExpressionRoot, referenced by the handler.

            I wrote my security checks inside of the custom WebSecurityExpressionRoot, and I was finally able to have a syntax like:

            Code:
            <sec:authorize access="myCustomCheck(...)"> ... </sec:authorize>
            But of course this was no more than a proof of concept, I don't like forking and tweaking this way (even if it's very fun and instructive). Let's wait for this feature to be addressed in future versions of Security!

            Comment


            • #21
              I got stuck in another little problem, that is accessing model attributes from expressions.

              As an example, I have a controller that simply does

              Code:
              model.addAttribute("accountId", accountId);
              and a JSP with

              Code:
              <security:authorize access="check(accountId, principal.id)">ok</security:authorize>
              But of course the evaluation of the expression fails because of "accountId". Is it possible to access model attributes from expressions in any way?

              Thanks.

              Comment


              • #22
                Originally posted by namero999 View Post
                So after some playing I managed to get this working. I write the steps here for future references.

                Unfortunately this required some tweaking to the xml schema definition.

                First, I "enabled" the expression-handler element inside of the http element and configured it.

                spring-security-3.0.xsd
                Code:
                <xs:element name="expression-handler">
                    <xs:complexType>
                        <xs:attributeGroup ref="security:ref"/>
                    </xs:complexType>
                </xs:element>
                security.xml
                Code:
                <beans:bean id="myHandler" class="com.MyHandler" />
                <http use-expressions="true">
                    <expression-handler ref="myHandler"/>
                </http>
                After that, I implemented a custom WebSecurityExpressionHandler (MyHandler) and extended WebSecurityExpressionRoot, referenced by the handler.

                I wrote my security checks inside of the custom WebSecurityExpressionRoot, and I was finally able to have a syntax like:

                Code:
                <sec:authorize access="myCustomCheck(...)"> ... </sec:authorize>
                But of course this was no more than a proof of concept, I don't like forking and tweaking this way (even if it's very fun and instructive). Let's wait for this feature to be addressed in future versions of Security!
                Just in case someone arrives here and needs an easier and cleaner way of doing this, I will post here how I managed to do this.
                In your spring security config file:
                Code:
                <security:http auto-config="true" use-expressions="true" access-decision-manager-ref="accessDecisionManager">
                	...
                </security:http>
                
                <bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
                	<property name="decisionVoters">
                		<list>
                			<bean class="org.springframework.security.web.access.expression.WebExpressionVoter">
                				<property name="expressionHandler">
                					<bean class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler">
                						<property name="roleHierarchy" ref="roleHierarchy" />
                					</bean>
                				</property>
                			</bean>
                		</list>
                	</property>
                </bean>
                
                <bean id="roleHierarchy" class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
                	<property name="hierarchy">
                		<value>
                			ROLE_A > ROLE_B
                			ROLE_B > ROLE_AUTHENTICATED
                			ROLE_AUTHENTICATED >
                			ROLE_UNAUTHENTICATED
                		</value>
                	</property>
                </bean>
                I used this to add my role hierarchy, but you can just replace the DefaultWebSecurityExpressionHandler with your custom implementation like you did in your example and you are ready to go.
                Hope this helps even it this post is a bit old now, but this post was the best information I could find about these topics, so I think it's not such a bad idea to update it.


                ***** UPDATED *****
                The config above is not completely right. See following posts. I just copy the right config here in case someone missed the discussion originated by this post
                Code:
                <!-- This must go before the http element in order to be used by security:authorize tags using the access attribute -->
                <bean id="expressionHandler" class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler">
                	<property name="roleHierarchy" ref="roleHierarchy" />
                </bean>
                
                <security:http auto-config="true" use-expressions="true" access-decision-manager-ref="accessDecisionManager">
                	...
                </security:http>
                
                <!-- security:authorize tags using the url attribute will delegate to this accessDecisionManager -->
                <bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
                	<property name="decisionVoters">
                		<list>
                			<bean class="org.springframework.security.web.access.expression.WebExpressionVoter">
                				<property name="expressionHandler" ref="expressionHandler" />
                			</bean>
                		</list>
                	</property>
                </bean>
                
                <bean id="roleHierarchy" class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
                	<property name="hierarchy">
                		<value>
                			ROLE_A > ROLE_B
                			ROLE_B > ROLE_AUTHENTICATED
                			ROLE_AUTHENTICATED >
                			ROLE_UNAUTHENTICATED
                		</value>
                	</property>
                </bean>
                Last edited by triqui; Jun 22nd, 2010, 04:17 AM. Reason: Uncomplete solution

                Comment


                • #23
                  Originally posted by triqui

                  Code:
                  <security:http auto-config="true" use-expressions="true" access-decision-manager-ref="accessDecisionManager">
                  	...
                  </security:http>
                  
                  <bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
                  	<property name="decisionVoters">
                  		<list>
                  			<bean class="org.springframework.security.web.access.expression.WebExpressionVoter">
                  				<property name="expressionHandler">
                  					<bean class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler">
                  						<property name="roleHierarchy" ref="roleHierarchy" />
                  					</bean>
                  				</property>
                  			</bean>
                  		</list>
                  	</property>
                  </bean>
                  
                  <bean id="roleHierarchy" class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
                  	<property name="hierarchy">
                  		<value>
                  			ROLE_A > ROLE_B
                  			ROLE_B > ROLE_AUTHENTICATED
                  			ROLE_AUTHENTICATED >
                  			ROLE_UNAUTHENTICATED
                  		</value>
                  	</property>
                  </bean>
                  I tried this out under 3.0.2 but it didn't work. Using exactly the same config with a custom roleHierarchy (mine is database aware), I couldn't force the security:authorize tag to use my roleHierarchy. The reason is that FilterInvocationSecurityMetadataSourceParser still registers its own ExpressionHandler because expression-handler is not supported by the schema for the http element(I don't prefer to tweak it). That's the point that pmularien already made when starting this topic. See:

                  Code:
                          if (useExpressions) {
                              Element expressionHandlerElt = DomUtils.getChildElementByTagName(elt, Elements.EXPRESSION_HANDLER);
                              String expressionHandlerRef = expressionHandlerElt == null ? null : expressionHandlerElt.getAttribute("ref");
                  
                              if (StringUtils.hasText(expressionHandlerRef)) {
                                  logger.info("Using bean '" + expressionHandlerRef + "' as web SecurityExpressionHandler implementation");
                              } else {
                                  BeanDefinition expressionHandler = BeanDefinitionBuilder.rootBeanDefinition(DefaultWebSecurityExpressionHandler.class).getBeanDefinition();
                                  expressionHandlerRef = pc.getReaderContext().generateBeanName(expressionHandler);
                                  pc.registerBeanComponent(new BeanComponentDefinition(expressionHandler, expressionHandlerRef));
                              }
                  @triqui: if you found a way to let it work with your own roleHierarchy then i'm very curious to know how you did it.

                  So question remains: what should I do to wire my custom roleHierarchy in the namespace's expressionhandler (or my own expressionhandler)...

                  Comment


                  • #24
                    It's strange. It's working for me. It's been some time since I debugged it, but I think I remember that even if you are right and the parser loads a default handler, it's overwritten latter on, but I can't remember where.
                    I can tell you it's been working for me since I posted that. I'll try to have a look at it again and tell where the custom handler is linked in.

                    Comment


                    • #25
                      Reading again your post, I think that maybe you are missing something.
                      As you said "expression-handler is not supported by the schema for the http element" but access-decision-manager is, and you can configure the accessDecisionManager with your custom expression handler. Just like I wrote in my first post.
                      security:authorize tags are processed by the access decision manager configured in the http element.
                      Are you sure you included the access-decision-manager-ref in your http?
                      Code:
                      <security:http auto-config="true" use-expressions="true" access-decision-manager-ref="accessDecisionManager">

                      Comment


                      • #26
                        Triqui,

                        Fact is that both handlers are loaded in the application context, the one from the FISMSParser and the one that I configured. Mine has the roleHierarchy correctly wired, but the default one from the parser has a null roleHierarchy.

                        In AuthorizeTag this code then simply takes the 0-th index from the ExpressionHandlers array:

                        Code:
                            WebSecurityExpressionHandler getExpressionHandler() throws JspException {
                                ServletContext servletContext = pageContext.getServletContext();
                                ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
                                Map<String, WebSecurityExpressionHandler> expressionHdlrs = ctx.getBeansOfType(WebSecurityExpressionHandler.class);
                        
                                if (expressionHdlrs.size() == 0) {
                                    throw new JspException("No visible WebSecurityExpressionHandler instance could be found in the application " +
                                            "context. There must be at least one in order to support expressions in JSP 'authorize' tags.");
                                }
                        
                                return (WebSecurityExpressionHandler) expressionHdlrs.values().toArray()[0];
                            }
                        which in my case was the wrong one (the default ExpressionHandler). That's why I'm stuck. If I could force the default ExpressionHandler to use my roleHierarchy, that would be OK for me too. But I can't see how to do this...

                        Comment


                        • #27
                          Yes, my <security:http> element refers to my custom access decision manager, exactly like you did in your first post

                          Comment


                          • #28
                            Ok.
                            Just checked again and everything seems ok.

                            Could you debug this and see what happens.

                            org.springframework.security.config.http.HttpConfi gurationBuilder
                            (Read my comments on the right side)
                            Code:
                                void createFilterSecurityInterceptor(BeanReference authManager) {
                                    boolean useExpressions = FilterInvocationSecurityMetadataSourceParser.isUseExpressions(httpElt);
                                    BeanDefinition securityMds = FilterInvocationSecurityMetadataSourceParser.createSecurityMetadataSource(interceptUrls, httpElt, pc);                       // This line creates the default expression-handler
                            
                                    RootBeanDefinition accessDecisionMgr;            // The next lines create the default accessDecisionManager
                                    ManagedList<BeanDefinition> voters =  new ManagedList<BeanDefinition>(2);
                            
                                    if (useExpressions) {
                                        voters.add(new RootBeanDefinition(WebExpressionVoter.class));      // This line tells the default accessDecisionManager to use the DefaultWebExpressionHandler, but we don't care since we will be using a different acccessDecisionManager
                                    } else {
                                        voters.add(new RootBeanDefinition(RoleVoter.class));
                                        voters.add(new RootBeanDefinition(AuthenticatedVoter.class));
                                    }
                                    accessDecisionMgr = new RootBeanDefinition(AffirmativeBased.class);
                                    accessDecisionMgr.getPropertyValues().addPropertyValue("decisionVoters", voters);
                                    accessDecisionMgr.setSource(pc.extractSource(httpElt));
                            
                                    // Set up the access manager reference for http
                                    String accessManagerId = httpElt.getAttribute(ATT_ACCESS_MGR);
                            
                                    if (!StringUtils.hasText(accessManagerId)) {          // This block should be skipped since we are declaring a customized accessDecisionManager
                                        accessManagerId = pc.getReaderContext().generateBeanName(accessDecisionMgr);  // Put a breakpoint here and make sure this block never executes
                                        pc.registerBeanComponent(new BeanComponentDefinition(accessDecisionMgr, accessManagerId));
                                    }
                            
                                    BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(FilterSecurityInterceptor.class);
                            
                                    builder.addPropertyReference("accessDecisionManager", accessManagerId);  // This line tells the builder to use our custom accessDecissionManager and forget the one already created
                            ...
                            ...
                            You have seen how it's built. If everything seems to be working fine. You should then check what happens when the accessDecisionManager is invoked.

                            org.springframework.security.access.vote.AbstractA ccessDecisionManager
                            Code:
                                public List<AccessDecisionVoter> getDecisionVoters() {
                                    return this.decisionVoters;  // Put a breakpoint here
                                }
                            If everything is working properly, you should have only one voter, the WebExpressionVoter with your custom expression handler:

                            What I have when I stop here is my role hierarchy implementaion in
                            decisionVoters.elementData[0].expressionHandler.roleHierarchy.

                            Which is what tells the security:authorize tags whether to allow something or not.

                            Double check your config you must have something wrong!

                            Comment


                            • #29
                              Originally posted by triqui
                              What I have when I stop here is my role hierarchy implementaion in
                              decisionVoters.elementData[0].expressionHandler.roleHierarchy.

                              Which is what tells the security:authorize tags whether to allow something or not.
                              Triqui,

                              Thank you for the thorough explanation. From what I see in the AuthorizeTag when using the access attribute, the voters aren't used at all:
                              Code:
                                  private int authorizeUsingAccessExpression(Authentication currentUser) throws JspException {
                                      // Get web expression
                                      WebSecurityExpressionHandler handler = getExpressionHandler();
                              
                                      Expression accessExpression;
                                      try {
                                          accessExpression = handler.getExpressionParser().parseExpression(access);
                              
                                      } catch (ParseException e) {
                                          throw new JspException(e);
                                      }
                              
                                      FilterInvocation f = new FilterInvocation(pageContext.getRequest(), pageContext.getResponse(), DUMMY_CHAIN);
                              
                                      if (ExpressionUtils.evaluateAsBoolean(accessExpression, handler.createEvaluationContext(currentUser, f))) {
                                          return EVAL_BODY_INCLUDE;
                                      } //This condition always returns false in my particular case 
                              because my custom RoleHierarchy isn't considered, so the tag doesn't render its content.
                              
                                      return SKIP_BODY;
                                  }
                              The voters are used by the FilterSecurityInterceptor. I followed your debug instructions and everything works as expected. So that part seems OK. But the authorize tag doesn't seem to care about that and picks the WebExpressionHandlers right from application context. Do I see it wrong or is this a bug??

                              Comment


                              • #30
                                OK, I've found a solution from the FAQ.

                                Originally posted by gbs
                                If I could force the default ExpressionHandler to use my roleHierarchy, that would be OK for me too. But I can't see how to do this...
                                You can do this with a bean postprocessor of course.

                                Anyhow, I still wonder why the AuthorizeTag simply picks the first ExpressionHandler from the application context... Shouldn't this happen in a slightly more fine-tuned way?

                                Comment

                                Working...
                                X