My sentiments exactly. There are a lot of SNACs (shiny new ACronyms) popping up every few months that “some other framework” may have that are “missing” from Shiro, like many variants of **BAC (which are very much too vaguely defined, and not in any way standard) All of this can easily be solved by a few lines of custom code (in custom realm etc.) You can’t possibly chase all those vague acronyms and succeed.
Saying that, I am not opposed to adding some sort of additional abstraction layer for **BAC, but it has to be well defined. Here is what I see as viable options: - Write an implementation yourself on top of Shiro, get traction in the wild, and then contribute it to Shiro, if still makes sense. - Point out specific code / features / documentation in another framework that implements your feature that has traction in the industry, i.e. has proven useful in the wild, and propose inclusion of such specific functionality in Shiro. > On Jul 7, 2016, at 3:02 AM, Richard Bradley <[email protected]> > wrote: > > Could you explain what the benefit of this is over just implementing those > restrictions in plain old Java code directly? > > Each of the use cases you have given could be translated into plain old Java > code more or less 1-1 and the resulting code would be: > 1. simpler > 2. more maintainable > 3. more accessible to newcomers (no new language needs to be learnt) > 4. benefit from IDE support and static analysis support > 5. likely more reliable (there are fewer moving parts; no AOP framework is > needed etc.; no chance of bugs in the implementation of @RequiresAttributes > or misunderstandings in its usage) > 6. easier to test > > The only downside I can see is that it might be slightly harder to statically > verify that a developer has remembered to address security (in the proposed > version you might be able to write a unit tests that asserts that all > relevant methods have a non-empty "@RequiresAttributes" annotation). > > Here are the examples from below rewritten in POJ: > > Assume the following helper code is imported in each case: > > public class AuthenticationHelper { > public static void assertAuth(bool ok) { > if (!ok) { > throw new AuthenticationException("Insufficient permissions"); > } > } > > public static User currentUser() { > // create a domain-specific User class from Shiro's > SecurityUtils.getSubject() > } > } > > 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 > public void store(Profile userProfile) { > assertAuth(currentUser().id == userProfile.userId); > > // 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! > @RequiresPermissions("settings:edit") > public void editSettings(String key, String newValue) { > assertAuth(getMemberIds().contains(currentUser().id)); > > // TODO: change the setting > } > > } > > Example of use case #3: > > public class ClassifiedDocumentRepository { > > @RequiresPermissions("documents:store") > public void storeDocument(@Map("document") Document doc) { > assertAuth(currentUser().clearanceLevel >= document.clearanceLevel); > // TODO: store the document > } > > // applying access control on return value here with special variable > $return > @RequiresPermissions("documents:retrieve") > public Document retrieveDocument(String documentId) { > // TODO: find the document and its metadata > > Document document = ... > > assertAuth(currentUser().clearanceLevel >= document.clearanceLevel); > return document; > } > > > } > > Best, > > > Rich > > > > -----Original Message----- > From: jbuhacoff [mailto:[email protected]] > Sent: 07 July 2016 05:19 > To: [email protected] > Subject: Attribute-based access control > > 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. > > Richard Bradley > Tel : 020 7485 7500 ext 3230 | Fax : 020 7485 7575 > > softwire > Sunday Times Best Small Companies - UK top 25 six years running > Web : www.softwire.com<http://www.softwire.com/> | Follow us on Twitter : > @SoftwireUK<https://twitter.com/SoftwireUK> > Addr : 110 Highgate Studios, 53-79 Highgate Road, London NW5 1TL > Softwire Technology Limited. Registered in England no. 3824658. Registered > Office : Gallery Court, 28 Arcadia Avenue, Finchley, London. N3 2FG >
