Hi, I've been using Shiro for a couple of years now in various projects with
annotations and the wildcard permission syntax. Now I have a new requirement
for ABAC, and I think it should be possible to add this capability to Shiro.
I have a proposal below, and wanted to get some comments. If there's
interest, I would develop it myself and contribute the code &
documentation.
Here are some use cases:
1. a "my profile" or "my settings" feature: user should be able to edit own
things but not everything in the table.
2. project or organization based permissions: user should be able to read
documents in a project while user is associated with the project or team,
possibly combined with a general "can read documents" permission.
3. clearance level: user has a "can read documents" permission but this
needs to be matched with the user's clearance level and the document's
clearance level.
I see attribute-based access control as being a nice complement and
orthogonal to the permission-based access control. A given method could be
annotated with either one or both together to achieve the right level of
control.
My proposal is to add a new annotation @RequiresAttributes and to let its
value be a boolean expression to be evaluated. Of course the expression
will need to be able to refer to the current user, the object in question,
and maybe even other things like the time of day, the https client
certificate, or whatever. Getting these bits of info into the context of the
expression language would be a combination of three things: 1) the realm
can set some context when the user is authenticated based on information
available at that time (user attributes, connection attributes, etc.) , 2)
there could be generic "attribute injectors" like for calendar/time, system
properties, or other things that are neither user nor object in question;
3) a new @Map annotation that can be applied to method parameters in order
to give them a specific name in the expression.
Also there is a special case when we might want to apply attribute-based
permissions on the return value of a method. For this we have another
annotation @RequiresAttributesOnReturn which would be applied with around
advice, calling proceed() and then checking permissions using the return
value of the original method bound to a special variable $return.
Both @RequiresAttributes and @RequiresAttributesOnReturn would have
corresponding methods that could be called directly from anywhere in the
code in order to perform the same evaluation without annotations.
Special variable $this would be available to expressions annotated on
instance methods.
Variables and methods of objects would be accessible via dot notation, with
automatic use of bean-style "getter" methods when they exist.
In all the following examples, $user is a variable bound by the
authentication realm to a User object with methods String getId() and
Integer getClearanceLevel().
Example of use case #1:
public class ProfileEditor {
// all users implicitly have permission to edit their own profile,
// so don't need RequiresPermissions, but we do need to check
// that the profile belongs to this user
@RequiresAttributes("$user.id = $profile.userId")
public void store(@Map("profile") Profile userProfile) {
// TODO: store the changes
}
}
Example of use case #2:
public class Project {
List<String> getMemberIds() {
// TODO: return list of member ids in this project
}
// the user has role "project manager" with a "settings:edit" permission
// BUT... it's only for this project, not for all projects!
// also special variable $this refers to enclosing class instance
@RequiresPermissions("settings:edit")
@RequiresAttributes("$this.memberIds.contains($user.id)")
public void editSettings(String key, String newValue) {
// TODO: change the setting
}
}
Example of use case #3:
public class ClassifiedDocumentRepository {
@RequiresPermissions("documents:store")
@RequiresAttributes("$user.clearanceLevel >= $document.clearanceLevel")
public void storeDocument(@Map("document") Document doc) {
// TODO: store the document
}
// applying access control on return value here with special variable
$return
@RequiresPermissions("documents:retrieve")
@RequiresAttributesOnReturn("$user.clearanceLevel >=
$return.clearanceLevel")
public Document retrieveDocument(String documentId) {
// TODO: find the document and its metadata
return document;
}
}
--
View this message in context:
http://shiro-user.582556.n2.nabble.com/Attribute-based-access-control-tp7581093.html
Sent from the Shiro User mailing list archive at Nabble.com.