Skip to content

SEC-2334: Expose createDefaultDecisionVoters method in UrlAuthorizationConfigurer, ExpressionUrlAuthorizationConfigurer, GlobalMethodSecurityConfiguration #2559

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
spring-projects-issues opened this issue Sep 18, 2013 · 4 comments
Assignees
Labels
in: config An issue in spring-security-config status: declined A suggestion or change that we don't feel we should currently apply type: enhancement A general enhancement type: jira An issue that was migrated from JIRA

Comments

@spring-projects-issues
Copy link

Nick Williams (Migrated from SEC-2334) said:

Currently, UrlAuthorizationConfigurer, ExpressionUrlAuthorizationConfigurer, and GlobalMethodSecurityConfiguration provide ways for configuring a custom AccessDecisionManager (either by calling a method, as in the first two cases, or overriding a method, as in the latter case). For all of the standard AccessDecisionManager implementations, you want to construct them with a List of AccessDecisionVoter implementations. Then, some AccessDecisionVoter implementations require either a SecurityExpressionHandler or PreInvocationAuthorizationAdvice to function properly.

So, taking the ExpressionUrlAuthorizationConfigurer case as an example (the UrlAuthorizationConfigurer case is extremely similar), let's take a look at what's involved to simply switch from the AffirmativeBased decision manager to the ConsensusBased decision manager:

    @Override
    protected void configure(HttpSecurity security) throws Exception {
        List<AccessDecisionVoter> decisionVoters = new ArrayList<AccessDecisionVoter>();
        WebExpressionVoter expressionVoter = new WebExpressionVoter();
        expressionVoter.setExpressionHandler(expressionHandler);
        decisionVoters.add(new DefaultWebSecurityExpressionHandler());

        security
                .authorizeRequests()
                    .accessDecisionManager(new ConsensusBased(decisionVoters))
        ...
    }

The obvious problem here is that the first four lines of code in this method are duplicated and unnecessary. They are identical to code found in ExpressionUrlAuthorizationConfigurer. Now AbstractInterceptUrlConfigurer defines a package-private, abstract method getDecisionVoters that UrlAuthorizationConfigurer and ExpressionUrlAuthorizationConfigurer implement. How does this configuration method change if we make getDecisionVoters public and take advantage of it?

    @Override
    protected void configure(HttpSecurity security) throws Exception
    {
        security
                .authorizeRequests()
                    .accessDecisionManager(new ConsensusBased(
                            security.authorizeRequests().getDecisionVoters()
                    ))
        ...
    }

This code is much cleaner and takes advantage of the default, standard expression handler and voters while using a different decision manager. For clarity, it might make sense to rename getDecisionVoters to something like createDefaultDecisionVoters, but you get the idea.

Next let's take a look at GlobalMethodSecurityConfiguration. Assume you want to do the same thing with it: just change from the AffirmativeBased to the ConsensusBased decision manager. As of now:

@Configuration
@EnableGlobalMethodSecurity(...)
public static class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
    @Override
    protected AccessDecisionManager accessDecisionManager() {
        List<AccessDecisionVoter> decisionVoters = new ArrayList<>();
        ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
        expressionAdvice.setExpressionHandler(getExpressionHandler());
        if(prePostEnabled()) {
            decisionVoters.add(new PreInvocationAuthorizationAdviceVoter(
                    expressionAdvice));
        }
        if(jsr250Enabled()) {
            decisionVoters.add(new Jsr250Voter());
        }
        decisionVoters.add(new RoleVoter());
        decisionVoters.add(new AuthenticatedVoter());
        return new ConsensusBased(decisionVoters);
    }
}

Once again, we've duplicated a lot of code unnecessarily. Additionally, prePostEnabled() and jsr250Enabled() have private access and can't be called from here. Arguably you could eliminate this last problem by only adding the voters that your @EnableGlobalMethodSecurity config requires, but still. It's definitely not clean, and the only way to get it "right" is to have some knowledge of the Spring Security source code. Now let's look at this same change using the (currently non-existent) createDefaultDecisionVoters method:

@Configuration
@EnableGlobalMethodSecurity(...)
public static class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
    @Override
    protected AccessDecisionManager accessDecisionManager() {
        return new ConsensusBased(createDefaultDecisionVoters());
    }
}

WOW. Much cleaner. Adding the createDefaultDecisionVoters() here also provides us with another capability. If you want, you can create the default voters and add just your one custom voter to them, still creating the default decision manager (or a custom decision manager--it's up to you):

@Configuration
@EnableGlobalMethodSecurity(...)
public static class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
    @Override
    protected List<AccessDecisionVoter> createDefaultDecisionVoters() {
        List<AccessDecisionVoter> list = super.createDefaultDecisionVoters();
        list.add(new MyCustomDecisionVoter());
        return list;
    }
}

Finally, in both of these cases, exposing a getDecisionVoters/createDefaultDecisionVoters method ensures that if any bug fixes are made to the default implementations, or if any new default voters are added, the user gets this update on the next upgrade.

Given these use cases and compelling arguments :-), I propose the following:

Rename AbstractInterceptUrlConfigurer#getDecisionVoters to createDefaultDecisionVoters and make it public.

Create a GlobalMethodSecurityConfiguration#getDecisionVoters method, move the voter creation to it, and make it protected.

It's possible I missed other configuration classes with similar design, in which case I implicitly make analogous proposals for those classes as well.

@spring-projects-issues spring-projects-issues added in: config An issue in spring-security-config Open type: enhancement A general enhancement type: jira An issue that was migrated from JIRA labels Feb 5, 2016
@jgoldhammer
Copy link
Contributor

I am also struggling with that... This would be nice to see it one of the next versions...

Thanks,
JEns

@mskluev
Copy link

mskluev commented Sep 13, 2016

+1
Spent all day trying to figure out how to add a custom voter to an application already using @EnableGlobalMethodSecurity . The AccessDecisionManager / Voter construct makes it look so easy at first. I felt like I must be doing something wrong having to copy/paste a bunch of code from a spring source code file. At least I found this issue which helped reaffirm I am not crazy.

Thanks!

@rwinch rwinch removed the Open label May 3, 2019
@okohub
Copy link
Contributor

okohub commented Aug 16, 2021

@rwinch @eleftherias is this issue open to contribute? I would like to add this.

@jzheaux
Copy link
Contributor

jzheaux commented Aug 20, 2021

Hi, @okohub. I think given #9290, we'll want to expose this using a custom authorization manager.

@jzheaux jzheaux closed this as completed Aug 20, 2021
@jzheaux jzheaux added the status: declined A suggestion or change that we don't feel we should currently apply label Aug 20, 2021
@jzheaux jzheaux self-assigned this Aug 20, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: config An issue in spring-security-config status: declined A suggestion or change that we don't feel we should currently apply type: enhancement A general enhancement type: jira An issue that was migrated from JIRA
Projects
None yet
Development

No branches or pull requests

6 participants