I wanted to give a follow up on how I solved this, and thanks Jared for the
input. I ended up going pretty much with your design.
I created my own realm for dynamically generating permissions at runtime as
they are requested. To do this I created some generic typed entity and
entity loader interfaces:
public interface PermissibleEntity
and
public interface PermissibleEntityLoader <I extends Serializable, E extends
PermissibleEntity<I>>
"Security providers" can code implementations of these for specific entities
that need permissions (e.g. RailcarEntity and RailcarEntityLoader). The
loader is meant to go to the source data and get any additional data needed
for permission determination (e.g. Railcar ownership information).
Central to this is my own custom Permission object that contains the entity
object and a separate Permission object to contain the rest of the
permission request. This is almost always WildcardPermission.
Then I abstracted out the actual permission generation with an interface:
public interface EntityPermissionLoader <I extends Serializable, E extends
PermissibleEntity<I>>
AssignableAuthorizationInfo loadEntityPermissions(PrincipalCollection
principals, E entity);
AssignableAuthorizationInfo is my own interface that extends
AuthorizationInfo to allow writing to the permission collections.
I provided a Drools based implementation of this interface in the framework.
It expects to be configured with a valid KnowledgeBase that has the security
rules written in them. It inserts the loaded entity that permissions are
being requested for, the principals, and the AssignableAuthorizationInfo and
fires the rules.
An example rule looks like:
rule "GiveConsistManagerUmlerOwnerViewAllPermission"
when
$eq : PermissibleEquipmentEntity($umow : umlerCarOwner)
$user : AssignableAuthorizationInfo(hasRole("AHSICONMGR/" +
$umow))
then
$user.addStringPermission("equipment:" + $eq.getEntityId() +
":health:*:view");
end
This gives a user with the AHSICONMGR role for the company that is the
railcar owner (umow) permissions to see all health related data for that
railcar.
My custom realm uses its configured EntityPermissionLoader for any of the
isPermitted type methods.
Caching is intended for the EntityLoaders via an abstract base class and for
the EntityPermissionLoader. I cached the generated
AssignableAuthorizationInfo along with the IDs of all entities that have
been already generated. Entities know when they are expired so they can be
reloaded and the permissions regenerated. I had to do a synchronized block
on the user to make it thread safe.
I have already implemented two domains with this model (railcars and train
events). I packaged them in a separate modular maven project from my core
api. Each entity type gets a module. These are pretty small with the entity
object itself, a entity loader if needed, and a permission resolver.
Overall the core api is pretty small with a few key interfaces, some base
abstract classes, the Drools implementation for permission "loading" and my
custom realm to use it all. There are also a few classes for converting our
preauthenticated SSO user object into a shiro subject and that's it. Then
each domain only has a handful of classes to implement the key interfaces.
Then there is a centralized Drools project to contain the business rules.
My clients just need to configure the domain and its required dependencies
(usually with spring config) then call something like:
SecurityUtils.getSubject().isPermitted("equipment:BNSF1:health:repairs:view")
...
If everything is configured correctly this will:
* Parse the string via the resolver into my EntityPermission object. The
equipment token identifies the entity type. The BNSF1 token is parsed into
the entity ID using our Railcar Equipment ID Parser. The rest is created as
a wildcard permission (health:repairs:view)
* Load the railcar data for ID BNSF1 from the database
* Execute the security rules (Drools) with the loaded railcar data and the
user's roles to populate all permissions for the user related to railcar
BNSF1
* Finally do the typical permission implies calls on the user's permissions
until true is found and return the result.
The key object model looks like:
<http://shiro-user.582556.n2.nabble.com/file/n7578985/shiro-security-authorization-class-model.jpg>
--
View this message in context:
http://shiro-user.582556.n2.nabble.com/Handling-a-large-number-of-dynamic-permissions-tp7578918p7578985.html
Sent from the Shiro User mailing list archive at Nabble.com.