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
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
.