I have an update for everyone who was interested, which I'll present as a
twist on my first use case:
1. a "my profile" or "my settings" feature: user should be able to edit
own things but not everything in the table. administrators should be
able to edit any profile.
And here is the corresponding application code, this time with the
existing @RequiresPermissions annotation and my @Map annotation
(which could be replaced with @PermissionParam that is similar),
but no attribute expression:
public class ProfileEditor {
@RequiresPermissions("profile:edit")
public void store(@Map("profile") Profile userProfile) {
// TODO: store the changes
}
}
And here is an example configuration the administrator can edit which grants
additional permissions based on attributes:
'attributeBasedPermissions':
'profile:edit': '$user.id = $profile.userId'
Now the attribute-based control is integrated into the existing permission
evaluation:
If the user has been granted the "profile:edit" permission in the
usual way, then code will be executed. If the user does not have the
"profile:edit" permission, then:
1. attributeBasedPermissions map is checked to see if there is a
"profile:edit" key there
2. if it exists, its value is parsed to identify the variables $user and
$profile
and the context is checked to see if these variables are mapped
(the $user variable would be mapped by the authentication filter, and the
$profile variable would be mapped by the @Map annotation on the
method)
4. if they are mapped then the expression is evaluated
5. if its result is true then the user is granted the "profile:edit"
permission,
and the code will be executed.
If any of these conditions don't pan out, the user is not granted the
permission and the unauthorized access exception would be thrown as usual.
Now the attribute-based permissions are editable outside the codebase, and
they rely on
artifacts in the codebase that can easily be cataloged by static analysis,
and although the
same result can still be obtained by doing the evaluation in the code, this
way it's much
cleaner because if the developer simply annotates important input objects
with @Map
then it's possible for the administrator to grant additional access based on
runtime attributes
without changing the code.
Requirements on developers stay roughly the same - annotate methods with
@RequiresPermissions and annotate interesting inputs with @Map.
One variation that I am considering is that for methods whose parameters are
not
annotated with @Map (all existing code),
that the external configuration can itself map class names to variables, and
this
would make it possible to apply attribute-based permissions to existing code
without
even adding the new annotation. For example:
'parameterMap':
'com.example.Profile': 'profile'
This variation has two disadvantages:
First, expressions would not be
able to handle methods that accept multiple parameters of the same type, as
it
would be too tight of a binding to say $profile[0] and $profile[1] ... the
semantics
are lost compared to mappings like $currentProfile and $editedProfile.
Second, a class name would always be mapped to the same variable so generic
classes like collections cannot be mapped to anything useful.
So I'm considering the automatic class mapping as a fallback with a
restriction
that it only applies when there is just one parameter with that class, and
would
not override any parameters already annotated with @Map.
Eventually, an administrator is unlikely to be typing in the attribute
expressions
by hand. A permission editor could scan the code for annotated methods and
their
parameters and then display the available fields and some relations like
equals,
contains, etc. in a form and let the administrator point and click on
things, and
then generate the expressions from that.
--
View this message in context:
http://shiro-user.582556.n2.nabble.com/Attribute-based-access-control-tp7581093p7581162.html
Sent from the Shiro User mailing list archive at Nabble.com.