One of the core functions that Nimble brings to any host application is access control to functionality or resources provided by the host application. Nimble utilizes the excellent Apache Shiro system for much of the access control process. Apache Shiro is a powerful and flexible open-source security framework that cleanly handles authentication, authorization, enterprise session management and cryptography.
Several pieces of information on this page are licensed from the Apache Shiro project under the Apache 2.0 license and re-printed here with some modification for clarity around the Nimble use-cases.
Important access control domain objects
grails.plugins.nimble.core.UserBase
This represents an individual user within the system. A specific user object may be granted access to host application functionality or resources by directly assigning them a permission. We recommend you only assign permissions directly to users when the permission will only ever apply to one user or to an extremely small subset of users (even then though we'd probably still user a role).
grails.plugins.nimble.core.Group
A group and a role are closely related in terms of security, they are both a collection of users and can have permissions assigned directly to them, many texts use the concepts interchangeably. We differentiate the two by considering a group as representing something that is physically based. For example Users who are all part of the same work team would be represented as a group.
As well as directly assigning permissions to groups you can also assign a role to a group. For example the work group we've defined are all responsible for looking after the source code of product XYZ, therefore they are considered to be programmers and should all obtain the role of programmer. By adding this role to their work group instead of each user individually we've significantly cut down on management overhead. As folks join and leave the work group the programmer role will automatically be assigned and removed from their permission list.
grails.plugins.nimble.core.Role
As mentioned roles and groups are closely related. We differentiate a role by considering a role as representing a virtual concept. From the example above we consider programmer to be a virtual concept whose permissions apply to many users (and as shown groups of users). You might have a role for after-hours-access or quality-inspector pretty much anything that is of use in the deployment scenario that has more then one user as a member can be a role.
grails.plugins.nimble.core.Permission
A Permission represents the ability to perform an action or access a resource. A Permission is the most granular, or atomic, unit in the security policy stack supported by Nimble.
It is important to understand a Permission instance only represents functionality or access - it does not grant it. Granting access to host application functionality or a particular resource is done by assigning permissions to users, roles or groups.
Most typical security systems are role-based in nature, where a role represents common behavior for certain user types. For a dynamic security model such as that provided by Nimble, where roles can be created and deleted at runtime, you can't hard-code role names in your code. In this environment, roles themselves aren't very useful. What matters is what permissions are assigned to these roles.
Under this paradigm, permissions are immutable and reflect the host application's raw functionality (opening files, accessing a web URL, creating users, etc). This is what allows the Nimble security policy to be dynamic, because Permission classes represent raw functionality and only change when the host application's source code changes. Permissions are immutable at runtime - they represent 'what' the system can do. Roles, users, and groups are the 'who' of the application. Determining 'who' can do 'what' then becomes a simple exercise of associating Permissions to roles, users, and groups via either the Nimble UI or programatically in your host application.
Supported Permissions
grails.plugins.nimble.auth.WildcardPermission
This is currently directly extended from the Shrio WildcardPermission object.
A WildcardPermission is a very flexible permission construct supporting multiple levels of permission matching.
Simple Usage
In the simplest form, WildcardPermission can be used as a simple permission string. You could grant a user an "editNewsletter" permission and then check to see if the user has the editNewsletter permission by calling
subject.isPermitted("editNewsletter")
The simple permission string may work for simple applications, but it requires you to have permissions like "viewNewsletter", "deleteNewsletter", "createNewsletter", etc. You can also grant a user "*" permissions using the wildcard character (giving this class its name), which means they have all permissions. But using this approach there's no way to just say a user has "all newsletter permissions".
Multiple Levels
WildcardPermission also supports the concept of multiple levels. For example, you could restructure the previous simple example by granting a user the permission "newsletter:edit". The colon in this example is a special character used by the WildcardPermission that delimits the next token in the permission. This is the format that Nimble promotes by default and is the format exposed by the Nimble management UI.
In this example, the first token is the domain that is being operated on and the second token is the action being performed. Each level can contain multiple values. So you could simply grant a user the permission "newsletter:view,edit,create" which gives them access to perform view, edit, and create actions on newsletters. Then you could check to see if the user has the "newsletter:create" permission by calling
subject.isPermitted("newsletter:create")
In addition to granting multiple permissions via a single string, you can grant all permission for a particular level. So if you wanted to grant a user all actions in the newsletter domain, you could simply give them "newsletter:*". Now, any permission check for "newsletter:XXX" will return true. It is also possible to use the wildcard token at the domain level (or both): so you could grant a user the "view" action across all domains "*:view".
Instance-level Access Control
Another common usage of the WildcardPermission is to model instance-level access control lists. In this scenario you use three tokens - the first is the domain, the second is the action, and the third is the instance you are acting on.
So for example you could grant a user "newsletter:edit:12,13,18". In this example, the third token is the host applications ID for the newsletter. That would allow the user to edit newsletters 12, 13, and 18. This is an extremely powerful way to express permissions, since you can now say things like "newsletter:*:13" (grant a user all actions for newsletter 13), "newsletter:view,create,edit:*" (allow the user to view, create, or edit any newsletter), or "newsletter:*:* (allow the user to perform any action on any newsletter).
To perform checks against these instance-level permissions, the application should include the instance ID in the permission check like so:
subject.isPermitted( "newsletter:edit:13" )
grails.plugins.nimble.auth.AllPermission
This is currently directly extended from the Shrio AllPermission object.
An all AllPermission instance is one that always implies any other permission; that is, its implies method
always returns true.
You should be very careful about the users, roles, and/or groups to which this permission is assigned since those respective entities will have the ability to do anything.
As such, an instance of this class is typically only assigned only to "root" or "administrator" users or roles.- By the host application making a direct call to subject.isPermitted(), subject.hasAllRoles() or subject.hasRole()
- By a grails filter ( grails-app/conf/*Filters.groovy ) invoking accessControl when a request matches a configured controller, action. Internally this also calls subject.isPermitted(), subject.hasAllRoles() or subject.hasRole() depending on requirements.
At this point grails.plugins.nimble.core.LocalizedRealm is invoked. It supplies corresponding methods for subject.isPermitted(), subject.hasAllRoles() and subject.hasRole().
Both hasAllRoles and hasRole work in the same manner. First this user object of the logged in user is checked to determine if the required role is directly assigned to it. If this process fails all groups the user is a member of are checked to see if they are assigned the role. If through this process it is determined the user has the role (or all the roles) a value of true is returned. If this is not the case false is returned.
isPermitted undertakes the same functionality, first checking to see if the permission is directly assigned to the user, then if it is assigned to any of the roles which are assigned to the User. Finally permissions assigned to the groups of which the user is a member and any roles which are assigned to those groups are checked. Each check is made using this Shiro provided 'implies' mechanism. The entire process is short circuit in nature, as soon as a permission is found associated with the user which implies the required permission true is returned. If all possibilities are exhausted the response will be false.
All of these checks are made against the local database associated with the host application. For this reason if you're using an external mechanism to manage group or role membership you must updated the host application database when this changes. Alternatively (and probbably far easier) is to have the local cache updated when the user logs in. It is envisaged that the LDAP plugin when available will be able to undertake this as many larger organizations already have much of this data stored in corporate directories. Of course with groups and roles being stored locally host application administrators are able to create and manage additional local roles and groups that apply only to application and do concern the organization as a whole.