I saw this forum post: http://www.jsecurity.org/node/1025
It got me thinking... I run into this problem a lot - where the permission you want enforced is based off of the domain object itself. For example, I just did a lot of work where your permission is decided based on whether or not you are a leader or owner of the group you are editing.
For example, a group has a collection of leaders on it. Now sure - I could add individual permission objects to every person who gets added as a leader "group:123456:editMember,editPendingMembers,editInfo" - for example. This was the answer given by Les on the previously mentioned thread. That works, and that's how you implement it in JSecurity today.
But that approach has a couple of disadvantages:
So - I was just generally wondering about ideas for having security checks based on domain objects that JSecurity could somehow support.
Idea #1 - Use Custom Annotations
@GroupLeaderRequired
public void myMethod() {..}
Then you define extend AuthorizingAnnotationMethodInterceptor and add the method interceptor to your AOP spring config.
Pros: Currently supported, not too difficult
Cons: Currently annotations are very Spring-specific and there's no programmatic or URL-based way to do the checking. I can't say subject.isPermitted( "groupLeader:123" ), /group/*=groupLeader or whatever...
Idea #2 - Standard annotation that compares an argument to the current user's principal
@PrincipalRequirement( "arg0.owner.id" )
@PrincipalRequirement( "arg0.leaders.id" )
We could have some principal matching support that checks to see if the principal of the current user is contained in a property of the argument passed in. In the previous example, since owner is a bean, JSecurity just checks the ID property of the owner. In the second example, leaders is a collection, so JSecurity loops through the collection and sees if the ID property of any leader object in the collection matches the current user's principal.
Obviously the syntax would have to be thought out more - but you get the gist of this idea...
Pros: Supports the case where a method argument contains the principal.
Cons: Again, annotation is spring-specific. We could easily make a programmatic implementation (but is that even useful). It'd be tough to do something like this with URLs - since the URL doesn't have the domain object. Doesn't handle a lot of other domain cases, like where the domain-authorization info isn't contained in a method argument or isn't represented as a "principal reference"
Idea #3 - Standard annotation that supports retrieving external domain objects and doing expression evaluations
@DomainRequirement( "arg0.leaders.id = $principal" )
/group/edit*=domain[group(groupId).leaders.id = $principal]
Ok - so this one builds on Idea #2 in that it adds an expression language that would have certain tokens that JSecurity understands (like $principal). Obviously this would be very complex, but potentially awesome.
The URL example ties into our current system. "domain" is the name of the filter. "group" would be a hook that the user would have to define - for example I could provide JSecurity's domain stuff with a way to look up a group based on a groupId. The (groupId) that comes after the word group indicates that the filter should pass the request parameter "groupId" value into the method to get back the domain object.
Here's some pseudo code of how a user might register: (just to get the idea across, maybe not how we actually do it)
securityManager.registerDomainLookup( "group", new DomainLookupTemplate() {
Object lookupDomainObject( Object... args ) {
String groupId = (String) args[0];
return groupManager.getGroup( groupId );
} );
Or we could have it configured in web.xml like this:
domainLookups
group=com.myapp.GroupDomainLookupHandler
OR
group=spring:groupDomainLookupHandlerBeanId
Pros: This would be awesome and extremely powerful. You could even do things like domain[group(groupId) = groupMembership(contactId,groupId).group] - two lookups that are compared against each other.
Cons: Would be pretty complicated to implement the expression language. You have to write code, etc. for it anyway. Perhaps we can simplify....
Idea #4 - Standard annotation that supports calling a custom callback function to evaluate domain-based authorization
@DomainRequirement( value="groupLeader", args="group.id" )
/group/edit*=domain[groupLeader(groupId)]
So, this is similar to the last one, but instead of an expression language, the callback function would actually just say whether or not the user is authorized.
The first example shows the groupLeader requirement being called with the group arguments ID value.
The second example shows the groupLeader requirement being called with a request parameter value.
In this idea, the "groupLeader" text is just parsed in order to lookup a DomainRequirementHandler object. Then the only other complexity is figuring out the arguments, which would vary based on whether it was URL-based or annotation based.
So, the actual registering could actually look just like the examples in IDEA #3, but instead of returning object, it would return a boolean that indicates whether or not the user is allowed to perform the action (access the URL, invoke the method)
Pros: Still pretty powerful - once you write a DomainRequirementHandler you can then use it for both URL or annotation based processing. You could even do something like subject.checkDomainRequirement( "groupLeader", group.getId() ); So we could tie it into all three ways of checking permissions: annotation, URL, and programmatic.
Cons: Still fairly complicated to implement, but probably my favorite idea. I can't think of too much bad about it =)
Wrap Up
I think having some sort of domain-based authorization would definitely set JSecurity apart from the crowd. It does muddy the waters a bit on where you store your permission information (in web.xml, in the database, in annotations) - but at the same time, I think that it reflects the reality that a lot of systems do have permission information embedded in their domain objects.
I can't think of any other security framework out there that supports this. I'd love in my app if I could configure all of my security requirements using JSecurity, even the security that is based on domain object info, not roles/permissions.
So - this was pretty much a brain dump of my ideas related to domain-oriented authorization. What do you think?