Search code examples
springspring-securityspring-security-acl

Is Spring Security ACL supposed to be usable with WebSecurityExpressionHandler?


I followed this guide to set up security using ACLs, which works fine. After that i also tried to implement ACLs in WebSecurityConfigurerAdapter. So i set up a bean which would create a DefaultWebSecurityExpressionHandler:

    @Bean
    public DefaultWebSecurityExpressionHandler webExpressionHandler(AclPermissionEvaluator aclPermissionEvaluator) {
        final DefaultWebSecurityExpressionHandler webSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
        AclPermissionEvaluator permissionEvaluator = aclPermissionEvaluator();
        webSecurityExpressionHandler.setPermissionEvaluator(permissionEvaluator);
        return webSecurityExpressionHandler;
    }

And i would apply it to HttpSecurity in my WebSecurityConfigurerAdapter:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable()
        .authorizeRequests()
        .expressionHandler(webExpressionHandlerWithACL)
        .antMatchers(HttpMethod.PUT, "/api/user/users/{ID}").access("isAuthenticated() and hasPermission(#ID, 'xxx.xxx.xxx.xxx.xxx.UserDto', 'write')")
        .anyRequest().authenticated()
        .and()
        .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
        .and()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    http.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);
}

But this just does not work. This exactly same expression works in a @PreAuthorize tag, but here it just does not. I debugged it and found out that the exact point of failure is in the JdbcAclServiceImpl class provided by Spring in the following method:

@Override
public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids)
        throws NotFoundException {
    Map<ObjectIdentity, Acl> result = this.lookupStrategy.readAclsById(objects, sids);
    // Check every requested object identity was found (throw NotFoundException if
    // needed)
    for (ObjectIdentity oid : objects) {
        if (!result.containsKey(oid)) {
            throw new NotFoundException("Unable to find ACL information for object identity '" + oid + "'");
        }
    }
    return result;
}

Even though the provided parameters are the same as if i used the @PreAuthorize tag, the result.containsKey(oid) just CANNOT find the acl in the even though i can see it with the debugger and i see that the oid should match it.

So my question is: Is Spring Security ACL even supposed to work in order to secure routes, or is it only to be used to prottect methods?

btw, i am using Spring boot 2.5.5


Solution

  • I found the problem. The class AclPermissionEvaluator has a method public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) that gets called. If i use it as a webExpressionHandler, then Serializable targetId is allways a String. But for this to work, it needs to be a Long. So one solution is to extend AclPermissionEvaluator and override the method in question so that it converts the String into a Long and then it works. Dont forget to then acttually use the custom AclPermissionEvaluator.