After having watched the presentation, followed along by setting up a sample Spring Security application with the presenter, I am still left with one underlining problem and that is how to handle the granular security requirements of my original post.
I can easily write my own service that handles returning my own custom UserDetails object and generating a list of rights based on the roles assigned within my system. What I have to be careful though is that these rights reflect the right domain object they refer to because users will be able to perform different operations on different domain objects.
For example, user crancran has the rights of create, display, and search however I don't have the right to cancel a purchase order. Then in my service, I could easily annotate my methods like:
Code:
public class PurchaseOrderService {
@PreAuthorize("hasPermission(#po, 'create')")
public PurchaseOrder create(PurchaseOrder po) {
// business logic
}
@PreAuthorize("hasPermission(#po, 'display')")
public PurchaseOrder lookup(Long poNumber) {
// business logic
}
@PreAuthorize("hasPermission(#po, 'cancel')")
public void cancel(PurchaseOrder po) {
// business logic
}
@PreAuthorize("hasPermission(#pocriteria, 'search')")
public List<PurchaseOrder> search(PoSearchCriteria pocriteria) {
// business logic
}
}
The permissions evaluator would do a check on the target object for its instanceof type, then based on the permission object value, I would need to filter my user's granted authorizations by the module or domain object type to see whether the user can perform the requested operation of create, display, cancel, or search.
But the security requirements go a step farther and imply a restriction that I can only perform these operations on purchase order documents within a specific company or division of my organization. So I can only search for purchase orders, create, or display those within division A; however another user may have the same roles but they can do the same operations but for a different division or a combination of division A and division B.
For create, display, and cancel, it would be as simple as checking to see whether the division on the purchase order was within the allowed divisions from my role. If not, I return false.
The search portion is what throws me ...
The search form allows the user to specify a global range view against domain objects, in this case the purchase order. The user can select to have the results search across their entire organization or within a specific division, region or even at a specific facility level. But when the role specifies that I can only search purchase orders within division A; how can I handle this?
One option is to use the @PostFilter annotation and when the results are returned, I check the division for each and if its not within division A; then I remove it from the collection. But this is highly inefficient.
Is there a better way that I can force this restriction check ahead of time so that when my Hibernate query fires, it would only return objects within that division? if the user chose region or facility, the results would be even more granular based on those values; however if the user choose across entire organization but their visibility was only for division A; then only division A records would be queried.
What I don't want to see however, is that I have placed my security checks inside this Permissions evaluator class and yet in my service, I must impose this additional granular check. If there is a way I could put it inside the permissions evaluator or within a security class rather than the service, this would be ideal. I want to keep my security stuff out if the service and self contained if at all possible.
Thoughts?