edoDev
edoDev

Reputation: 581

Custom AccessDecisionManager runs unexpectedly

I have a Spring Boot application that uses spring security for both authentication and authorisation. The authorisation phase, requires a custom AccessDecisionVoter on top of standard "authority based" authorisation. Here is my setup:

@Override
    protected void configure(HttpSecurity http) throws Exception {

        ...

        http.authorizeRequests()
                .requestMatchers(getMatcherForAuthority1Urls())
                .hasAuthority("AUTHORITY1")
        .and().authorizeRequests()
                .requestMatchers(getMatcherForAuthority2Urls())
                .hasAuthority("AUTHORITY1")
        .and().authorizeRequests()
                .requestMatchers(getMatcherForAuthorities1and2Urls())
                .hasAnyAuthority("AUTHORITY1", "AUTHORITY1");

        http.authorizeRequests()
                .regexMatchers(REGEX_PATTERN_URLS_THAT_NEED_AUTHORISATION).authenticated()
                .accessDecisionManager(myAccessDecisionManager());

        http.authorizeRequests()
                .antMatchers(LOGIN_URL).permitAll();
    }

    @Bean
    public AccessDecisionManager myAccessDecisionManager() {
        List<AccessDecisionVoter<? extends Object>> decisionVoters
                = Arrays.asList(
                new WebExpressionVoter(), // votes for authorities
                myAccessVoter);           // my custom voter

        return new UnanimousBased(decisionVoters);
    }

My expectations are that:

  1. some URLs are only accessible by users with authority1, some only by users with authority2 and some other urls may be accessed by users with either authority. This is setup in the first section of the configure() method.
  2. On some specific URLs, defined by the regex, on top of authorisation by authority, a custom AccessDecisionManager will also be executed, which includes a custom voter.

What is really happening when running the application, is that the custom voter is always executed, no matter the URL of the request. However, if I remove the initial part of the configure() method, so that no "hasAuthority()" authorisation is setup, then the custom voter only gets executed on the URLs that match the regex.

The same type of unwanted behaviour occurs if I use:

http.authorizeRequests().anyRequest().authenticated();

before setting up the custom AccessDecisionManager.

I am clearly making some mistakes in setting this up. Shouldn't the custom voter always run ONLY on the URLs that match the "REGEX_PATTERN_URLS_THAT_NEED_AUTHORISATION" pattern?

Thanks!

Upvotes: 0

Views: 624

Answers (1)

NatFar
NatFar

Reputation: 2220

The AccessDecisionManager bean that you register in .accessDecisionManager(...) is shared across your entire Spring Security application, so anytime an authorization decision must be made, the same UnanimousBased decision manager will be invoked, which in turn calls your custom voter.

So this:

authorizeRequests()
    .requestMatchers(getMatcherForAuthorities1and2Urls())
        .hasAnyAuthority("AUTHORITY1", "AUTHORITY1");

will call the same AccessDecisionManager for the "matcherForAuthorities1and2Urls" as REGEX_PATTERN_URLS_THAT_NEED_AUTHORISATION:

authorizeRequests()          
    .regexMatchers(REGEX_PATTERN_URLS_THAT_NEED_AUTHORISATION).authenticated()
        .accessDecisionManager(myAccessDecisionManager());

Similarly, including http.authorizeRequests().anyRequest().authenticated() will register a matcher that matches every request, and asks the same AccessDecisionManager to decide access.

Possible solution

Perhaps one way to achieve the desired behavior is to pass the REGEX_PATTERN_URLS_THAT_NEED_AUTHORISATION into your custom voter via its constructor and use it to create a RequestMatcher. Then, your voter can decide whether or not it will "vote" on a per-request basis:

public int vote(Authentication authentication, FilterInvocation fi,
            Collection<ConfigAttribute> attributes) {

    if(!regexPatternMatcher.matches(fi.getRequest())) {
        return ACCESS_ABSTAIN;
    }
    // ...
}

Upvotes: 1

Related Questions