I had similar thoughts in these threads:
http://forum.springsource.org/showthread.php?t=79002
http://forum.springsource.org/showthread.php?t=79371
I share Wolfram's and andrews' concerns:
(I also like jeduan's notion that the REST-Ful urls shouldn't necessarily have to contain the user's id just to return the logged-in user's objects. I haven't adopted that, but would like to.)Quote:
That's good as far as it goes, but I think what Wolfram is asking is how do you allow the user to go to this URL:
http://hostname/appname/shoppingcart/29
... where that's the user's own shopping cart, but not to this URL:
http://hostname/appname/shoppingcart/30
... where that's someone else's shopping cart.
My solution to general object-level authorization requires some initial setup and would benefit from Roo doing some of these things for us. I fear I won't explain this well enough, but here's what I have done (experimentally - I'm not completely convinced yet):
1) Use an AppUser object that implements UsersDetails.
2) Note that the "owner" property of each of my entities is an object of type AppUser, which implements UserDetails as per #1 above. So each object is persisted with an owner (or AppUser) id column.
3) Use a BaseEntity interface and an AbstractBaseEntity abstract implementation to include id, version, and owner properties. Make every Roo entity extend AbstractBaseEntity. (I would also add dateCreated and dateModified, and possibly modifiedBy to include cases where the object can be and was modified by someone other than the owner.)
4) Include "Authentication auth" in the method signature of each controller method. (This currently requires me to push in my controller methods. I would be thrilled if Roo did this in its managed controllers.)
With the above, I can:
5) Write aspectj advice for each controller method to check if a requested object is "owned" by the currently logged-in user (auth.getPrincipal().getId()==baseEntity.getOwner( ).getId())
I think Roo could handle 1,2,3, and 4, and the developer could write the aspects with his own logic. A common aspect pointcut/advice would seem to be, as it is for me, something like:
The benefit of using the BaseEntity interface is that a single joinpoint+advice block could be written to cover each "type" of controller method (update, delete, persist, find, findAll, findSome). This also allows for custom coding beyond what would be available using @PreAuthorize methods. Additionally, this creates security at the controller method level, leaving the developer to use all of the entity methods and/or service methods freely in administrative code.Code:@Before("execution(* com.mycompany.web.*.*.update(Authentication, BasePersistableObject+, BindingResult, ModelMap)) && args(auth, object, result, model)")
public void update(Authentication auth, BasePersistableObject object, BindingResult result, ModelMap model) {
AppUser u = (AppUser) auth.getPrincipal();
if(u.getId().longValue()!=object.getOwner().getId().longValue())
{
throw new IllegalArgumentException("access denied");
}
}
One potential problem I can see is that the generated Controller method signatures are already complex, and it would not be unreasonable to expect Roo to change how it does its method signatures in controllers. If that changed from one version of Roo to another, all of the user-written aspects would break.
