-1

I am trying to use an expression-based check for an user ID path variable, so users can only access resources that belong to them. It is pretty clearly described in the Spring documentation. But I cannot access the bean, with the error that a String is provided.

This is my security filter chain and the bean:

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
                .cors()
                    .and()
                .csrf().disable()
                .authorizeHttpRequests()
                    .antMatchers(WHITELIST_URLS).permitAll()
                    .antMatchers("/api/**").authenticated()
                    .antMatchers("/api/**/users/{userId}/**").access("@userSecurity.checkUserId(authentication,#userId)")
                    .and()
                .oauth2Login(oauth2login ->
                        oauth2login.loginPage("/oauth2/authorization/api-client-oidc"))
                .oauth2Client(Customizer.withDefaults())
                .build();
    }

    public static class UserSecurityConfig extends WebSecurityConfiguration {

        @Bean("userSecurity")
        private boolean checkUserId(Authentication authentication, String userId) {
            return authentication.getPrincipal().equals(userId);
        }
    }

Error:

Required type: AuthorizationManager 
<org.springframework.security.web.access.intercept.RequestAuthorizationContext>

Provided: String

I also have been trying to use an AuthorizationDecision (as lambda expression) but could not access the path variable.

Is the spring documentation wrong on this one? Been searching for quiet a while, but mostly found the same thing as in the Spring documentation.

Actually, I would like to manage this globally in the config and not on each mapping in the controllers by using the @PreAuthorize annotation.

Edit:

I have been unsuccessffuly trying to solve this using something like:

.access((authentication, object) -> 
new AuthorizationDecision(object.getRequest().getServletPath().contains(
                            authentication.get().getName())))

or

.access((authentication, object) -> 
new AuthorizationDecision(authentication.get().getPrincipal().equals(
                        object.getVariables().get("#userId"))))
dur
  • 15,689
  • 25
  • 79
  • 125
s4timuen
  • 1
  • 4
  • 1
    I figured it out. The first code example from my edit works but must be called before the other two matchers. Idk why, I guess this is because the path is more specific. – s4timuen Aug 29 '22 at 12:17

1 Answers1

0

I figured it out, the following example works. The more specific matcher has to be called first, otherwise it will not work.

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        return http
                .cors()
                .and()
                .csrf()
                .disable()
                .authorizeHttpRequests()
                .antMatchers("/api/v1/users/{userId}/**")
                .access((authentication, object) -> new AuthorizationDecision(
                        object.getRequest().getServletPath().contains(
                                authentication.get().getName())
                )) 
                .antMatchers(WHITELIST_URLS)
                .permitAll()
                .antMatchers("/api/v1/**")
                .authenticated()
                .and()
                .oauth2Login(oauth2login ->
                        oauth2login.loginPage("/oauth2/authorization/api-client-oidc"))
                .oauth2Client(Customizer.withDefaults())
                .build();
    }
s4timuen
  • 1
  • 4