Announcement Announcement Module
Collapse
No announcement yet.
Programmatic access to http and intercept-url elements Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Programmatic access to http and intercept-url elements

    Is there any way I can access the intercept-url elements from the security schema programmatically?


    Code:
    <security:http>
            <security:intercept-url pattern="/**" access="ROLE_USER" />
    <security:/http>
    // Johan

  • #2
    Are you looking for access to the xml or the objects it creates? If it is the XML look at the spring-security-config project. If it is the objects it creates you can do this by using using the suggestion on the FAQ. Note that this describes how to modify the bean, but you can also just access bean(s) and modify another bean if desired.

    Comment


    • #3
      Thanks for your reply. The end goal is to extract the pattern and access attributes, preferably by poking the objects maintaining this information, but also by accessing the schema itself if the former fails (does spring/spring-security offer any useful tools to manually parse the schema, or should I look to a domparser?).

      I already tried to retrieve some of the beans created by the HttpConfigurationBuilder, such as FilterChainProxy. Although it does give me the roles it doesn't provide the url patterns associated with the role.

      As a complement to using the postProcessor, I can probably access all beans by

      Code:
      FilterChainProxy filterChain = appContext.getBean(FilterChainProxy.class);

      Comment


      • #4
        It may be worth trying to understand why you are trying to do this too (i.e. there may be another way of doing it).

        Originally posted by Toxic View Post
        Thanks for your reply. The end goal is to extract the pattern and access attributes, preferably by poking the objects maintaining this information, but also by accessing the schema itself if the former fails (does spring/spring-security offer any useful tools to manually parse the schema, or should I look to a domparser?).
        Look at the spring-security-config project which provides the namespace handlers for the security namespace.

        Originally posted by Toxic View Post
        I already tried to retrieve some of the beans created by the HttpConfigurationBuilder, such as FilterChainProxy. Although it does give me the roles it doesn't provide the url patterns associated with the role.
        Curious that you were able to get the roles and not the URL patterns as I would have thought it would be the other way around. A mapping of the url to the role can be found in the FilterSecurityInterceptor's FilterInvocationSecurityMetadataSource (i.e. DefaultFilterInvocationSecurityMetadataSource). Note that the implementation details change quite a bit at 3.1

        Originally posted by Toxic View Post
        As a complement to using the postProcessor, I can probably access all beans by

        Code:
        FilterChainProxy filterChain = appContext.getBean(FilterChainProxy.class);
        This along with autowiring can work if the bean you are trying to access is not an anonymous inner bean.

        Comment


        • #5
          Originally posted by rwinch View Post
          It may be worth trying to understand why you are trying to do this too (i.e. there may be another way of doing it).
          There usually is. I want to use rolemapping for authority, but also to determine visibility. I.e., a user with ROLE_USER should only be able to see in a rendered html-menu the jsp pages he is authorized to see. I figuered I would reuse the role mapping already in place to avoid duplicate configurations.


          Originally posted by rwinch View Post
          Look at the spring-security-config project which provides the namespace handlers for the security namespace.
          Interesting, this would be beneficial in a situation were I must parse the schema.


          Originally posted by rwinch View Post
          Curious that you were able to get the roles and not the URL patterns as I would have thought it would be the other way around. A mapping of the url to the role can be found in the FilterSecurityInterceptor's FilterInvocationSecurityMetadataSource (i.e. DefaultFilterInvocationSecurityMetadataSource). Note that the implementation details change quite a bit at 3.1
          I'm currently looking at 3.0. Interestingly FilterSecurityInterceptor doesn't seem to provide a complete mapping. The FilterInvocationSecurityMetadataSource seems to contain all available roles though.


          The other option is I see next to parsing the XML manually, is to actually use something like the PropertyPlaceholderConfigurer to populate the intercept-url elements, and access the configurer programmatically instead. Neither approach seems perfect though Btw, I must say I appreciate the effort you put in to help me on a weekend and everything.



          [EDIT]: Well, it seems DefaultFilterInvocationSecurityMetadataSource contains both role and url mapping, but only exposes the former. One really hack'ish option here would be to access the private member variable requestMap using reflection. Separation of concerns can surely be a b**** some times.


          Code:
              for (FilterChainProxy filterChain : ctx.getBeansOfType(FilterChainProxy.class).values()) {
                      for (Map.Entry<String, List<Filter>> entry : filterChain.getFilterChainMap().entrySet()) {
                          for (Filter filter : entry.getValue()) {
                              if (filter instanceof FilterSecurityInterceptor) {
                                  FilterSecurityInterceptor interceptor = (FilterSecurityInterceptor) filter;
                                  // hack away to extract the private variable requestmap
                              }
                          }
                      }
              }
          To further increase the ugliness, what we actually get is Map<RequestMatcher, Collection<ConfigAttribute>>, where we must again use some magic to extract the url-patterns. *sigh*. I'm starting to feel this approach is far inferior .... to most anything.
          Last edited by Toxic; Jan 29th, 2011, 01:32 PM.

          Comment


          • #6
            Originally posted by Toxic View Post
            There usually is. I want to use rolemapping for authority, but also to determine visibility. I.e., a user with ROLE_USER should only be able to see in a rendered html-menu the jsp pages he is authorized to see. I figuered I would reuse the role mapping already in place to avoid duplicate configurations.
            There are a few options for this. The first is using the provided authorize jsp tag. The second is to @Autowire an instance of WebInvocationPrivilegeEvaluator.

            Comment


            • #7
              Good thinking, the WebInvocationPrivilegeEvaluator will suffice to meet my end goal. The downside is that I must perform the matching at request-time rather than building up the url-to-role structure in advance. Not too bad of a tradeoff, at least not for the price of avoiding hacking around the framework too much. Thanks for your suggestion.

              Comment


              • #8
                I'm not clear what you mean about having to perform the match at request time.

                You always have to check the request and WebInvocationPrivilegeEvaluator allows you to directly query the web-authorization mapping that is already defined within Spring Security.

                Comment


                • #9
                  Originally posted by Luke Taylor View Post
                  I'm not clear what you mean about having to perform the match at request time.

                  You always have to check the request and WebInvocationPrivilegeEvaluator allows you to directly query the web-authorization mapping that is already defined within Spring Security.
                  True in the general sence, but in my case it could be optimized further by knowing all the mappings in advance.

                  Say I have a list of pages {"index.html", "admin.html",.... } which is known at compile-time. This list is delivered to the view (using model map) which generates a HTML menu from it.
                  However, each time I need to render a page I must use the WebInvocationPrivilegeEvaluator to extract only those pages from the list that the user is actually authorized to see, and then render the menu based on those pages.


                  Instead, I would've prefered to retrieve all pages accessible by the role of the current user directly:

                  Code:
                  @Controller
                  public class AController {
                      // Maps the list of pages each role is authorized to see
                      Map<String, List<String>> roleToPages;
                  
                      @RequestMapping(..)
                      public void controllerRoutine(Map model) {
                          model.put("menu_pages, roleToPages.get(CURRENT_ROLE));
                      }
                  }
                  Last edited by Toxic; Jan 30th, 2011, 07:59 AM.

                  Comment


                  • #10
                    Ok, I see. It seems that you are assuming a single role for the user, is that correct?

                    You should still be able to do that. If you know the pages and all the possible roles, then you can just call the WebInvocationPrivilegeEvaluator on initialization. Create a dummy authentication object with each role:

                    Code:
                    Map<String, List<String>> roleToPages = ...;
                    WebInvocationPrivilegeEvaluator wipe = ...
                    List<String> possibleRoles = ...
                    List<String> pages = ...
                    
                    for (String role : possibleRoles) {
                        for (String page : pages) {
                            if (wipe.isAllowed(page, new DummyAuthentication(role))) {
                                List<String> rolePages = roleToPages.get(role);
                                if (rolePages == null) {
                                    rolesPages = new ArrayList<String>();
                                    roleToPages.put(role, rolePages);
                                }
                                rolesPages.add(page);
                            } 
                        }
                    }
                    or something similar.

                    Comment


                    • #11
                      Originally posted by Luke Taylor View Post
                      Ok, I see. It seems that you are assuming a single role for the user, is that correct?
                      No that was just a flaw in my quick example, but that's just a minor change anyhow.


                      Originally posted by Luke Taylor View Post
                      You should still be able to do that. If you know the pages and all the possible roles, then you can just call the WebInvocationPrivilegeEvaluator on initialization. Create a dummy authentication object with each role:
                      Yes, assuming I know the roles. From previous tries I noticed those can be extraced from the FilterSecurityInterceptor.



                      Although this does seem to work as I intend, it would probably be nice as a request-for-feature to have the intercept-url elements exposed in more direct way. Perhaps by a HttpElementConfigurationBean which corresponds to the <security:http /> element.

                      Comment


                    • #12
                      Were you ever able to find a way to get the request maps? WebInvocationPrivilegeEvaluator is the only class we need however the variable requestMaps containing the URLs is not exposed from a getter. The alternative is to post process the bean and set WebInvocationPrivilegeEvaluator with a custom class extending Spring's that exposes a getter.

                      This ended up being my implementation to get access to the URL patterns:

                      Code:
                      /**
                       * Manages the URLs that users have access to.
                       */
                      @Service
                      public class UrlAuthenticationService {
                      
                          private static final Logger log = LoggerFactory.getLogger(UrlAuthenticationService.class);
                      
                          private static final String REQUEST_MAP = "requestMap";
                      
                          @Autowired
                          private WebInvocationPrivilegeEvaluator webInvocationPrivilegeEvaluator;
                      
                          @Autowired
                          private FilterSecurityInterceptor filterSecurityInterceptor;
                      
                          /**
                           * Retrieves a list of URL patterns that the current user can access.
                           *
                           * @return the URLs in the order they have been defined for pattern matching; empty list if none found
                           */
                          List<String> getCurrentUserPermittedUrlPatterns() {
                              List<String> allowedUrls = new LinkedList<>();
                      
                              List<String> urlPatterns = getUrlPatterns();
                              if (CollectionUtils.isNotEmpty(urlPatterns)) {
                                  Authentication authentication = getAuthentication();
                                  for (String urlPattern : urlPatterns) {
                                      boolean allowed = webInvocationPrivilegeEvaluator.isAllowed(urlPattern, authentication);
                                      if (allowed) {
                                          allowedUrls.add(urlPattern);
                                      }
                                  }
                              }
                      
                              return allowedUrls;
                          }
                      
                          /**
                           * Retrieves the URL patterns found in the Spring configuration.
                           *
                           * @return
                           */
                          private List<String> getUrlPatterns() {
                              List<String> uris = new LinkedList<>();
                              FilterInvocationSecurityMetadataSource securityMetadataSource = filterSecurityInterceptor.getSecurityMetadataSource();
                      
                              try {
                                  // use reflection to access the url patterns from the Spring config since Spring has not exposed this as a variable
                                  Map<RequestMatcher, Collection<ConfigAttribute>> requestMap = (Map<RequestMatcher, Collection<ConfigAttribute>>) FieldUtils
                                      .readField(securityMetadataSource, REQUEST_MAP, true);
                                  Set<RequestMatcher> requestMatchers = requestMap.keySet();
                                  for (RequestMatcher requestMatcher : requestMatchers) {
                      
                                      // get the URL pattern based on the type of matcher present
                                      if (requestMatcher instanceof AntPathRequestMatcher) {
                      
                                          AntPathRequestMatcher antPathRequestMatcher = (AntPathRequestMatcher) requestMatcher;
                                          String pattern = antPathRequestMatcher.getPattern();
                      
                                          log.debug(String.format("Adding pattern %s from requestMatcher %s", pattern,
                                              AntPathRequestMatcher.class.getSimpleName()));
                      
                                          uris.add(pattern);
                                      } else if (requestMatcher instanceof AnyRequestMatcher) {
                                          log.debug(String.format("Ignoring request matcher %s", requestMatcher.getClass().getSimpleName()));
                                      } else {
                                          String msg = String.format(
                                              "The requestMatcher %s used in the Spring config's <http request-matcher> element is not supported",
                                              requestMatcher.getClass().getSimpleName(), requestMatcher.getClass().getSimpleName());
                                          throw new UnsupportedOperationException(msg);
                                      }
                                  }
                              } catch (IllegalArgumentException | IllegalAccessException ex) {
                                  throw new RuntimeException("Failed to read the url patterns found in the security configuration", ex);
                      
                              }
                              return uris;
                          }
                      
                          /**
                           * @return the authentication used to evaluate URLs
                           */
                          protected Authentication getAuthentication() {
                              return SecurityUtil.getAuthentication();
                          }
                      
                      }
                      Last edited by dukethrash; Apr 10th, 2014, 03:45 PM.

                      Comment

                      Working...
                      X