I'm attempting to implement fine grain access control while still taking advantage of Spring data rest.
I'm working on securing a CrudRepository
so users can only modify or insert data that belongs to them. I'm making use of @PreAuthorize
/@PostAuthorize
and @PreFilter
/@PostFilter
to lock access down to the current principal.
So far my repository looks like this.
public interface MyRepository extends CrudRepository<MyObject, Integer> {
@PreAuthorize("#entity.userId == principal.id")
@Override
<S extends MyObject> S save(S entity);
@PreFilter("filterObject.userId === principal.id")
@Override
<S extends MyObject> Iterable<S> save(Iterable<S> entities);
@PostAuthorize("returnObject.userId == principal.id")
@Override
MyObject findOne(Integer integer);
@PostFilter("filterObject.userId == principal.id")
@Override
Iterable<MyObject> findAll();
}
While this is a bit tedious, it does seem to accomplish what I'm after. (If anyone knows a better way, feel free to let me know!)
Where I'm running into problems is with delete()
, count()
and exists()
@Override
long count();
@Override
void delete(Integer integer);
@Override
void delete(MyObject entity);
@Override
void deleteAll();
@Override
boolean exists(Integer integer);
These methods either take an Integer
ID parameter or none at all. It seems like I would have to first select the entity with the input ID and then perform the auth check.
Is this type of authorization possible within the repository?
Thanks
Edit:
Thanks to ksokol this seems to be working now.
I added a new bean to a @Configuration
class
@Bean
public EvaluationContextExtension securityExtension() {
return new SecurityEvaluationContextExtensionImpl();
}
This bean extends EvaluationContextExtensionSupport
and overrides getRootObject
to return a SecurityExpressionRoot
that holds my custom principal.
public class SecurityEvaluationContextExtensionImpl extends EvaluationContextExtensionSupport {
@Override
public String getExtensionId() {
return "security";
}
@Override
public Object getRootObject() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return new SecurityExpressionRoot(authentication){};
}
}
Can also be achieved by implementing your checks in your custom Spring repository event handlers. See @HandleBeforeCreate
, @HandleBeforeUpdate
, @HandleBeforeDelete
.
Alternatively, you can use permission-based expressions, e.g. with ACL or your custom ones, you can write @PreAuthorize("hasPermission(#id, 'MyObject', 'DELETE')")
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With