h1. Deadbolt

Deadbolt is an authorisation mechanism for defining access rights to certain controller methods or parts of a view using a simple AND/OR/NOT syntax.  It is based on the original Secure module that comes with the Play! framework.

Note that Deadbolt doesn't provide authentication!  You can still use the existing Secure module alongside Deadbolt to provide authentication, and in cases where authentication is handled outside your app you can just hook up the authorisation mechanism to whatever auth system is used.


h3. Getting started

To install Deadbolt, you can use the modules repository:

bc. play install deadbolt-<required version>


h3. Examples

Check the samples-and-tests folder for examples of all the code required below.


h3. Identifying the current user

In order to know if access to a view or controller is authorised, you need to know who the current user is.  Because various applications will have their own concept of a user, Deadbolt defines the models.deadbolt.RoleHolder interface.  This interface should be implemented by any class type that represents an object that will try to access a controller method or view.

bc. package models;
import models.deadbolt.Role;
import models.deadbolt.RoleHolder;
import java.util.Arrays;
import java.util.List;
public class MyRoleHolder implements RoleHolder
{
    public List<? extends Role> getRoles()
    {
        return Arrays.asList(new MyRole("foo"),
                             new MyRole("bar"),
                             new MyRole("restricted"));
    }
}

Once a RoleHolder has been implemented, it can return a list of models.deadbolt.Role objects to indicate which roles a RoleHolder has.  Again, Role is an interface and so should not intrude on your application's model architecture.

bc. package models;
import models.deadbolt.Role;
public class MyRole implements Role
{
    private String name;
    public MyRole(String name)
    {
        this.name = name;
    }
    public String getRoleName()
    {
        return name;
    }
}

h3. Hooking Deadbolt into your application

To provide the information needed for the runtime security, you need to implement the controllers.deadbolt.DeadboltHandler interface; an abstract version - controllers.deadbolt.AbstractDeadboltHandler - is also available for convenience when you aren't using externalised restrictions.

The DeadboltHandler interface defines four methods:

* void beforeRoleCheck()

This method will be invoked prior to checking the authorisation of the current RoleHolder.  This forms the basis for hooking into authentication, i.e. in this method you can check if the user is actually logged in, and redirect accordingly if not.

* RoleHolder getRoleHolder()

This method will typically return the current RoleHolder.

* void onAccessFailure(String controllerClassName)

This method is invoked when a restricted resource is accessed without permission.  This method may log something; invoke a controller class and set the current user back on the path of not messing around with the address bar.

* ExternalizedRestrictionsAccessor getExternalizedRestrictionsAccessor()

If your application externalized restrictions, e.g. in a database or XML file, you will need to provide a way to access them by implementing an ExternalizedRestrictionsAccessor.  That implementation can then be passed into Deadbolt by returning it from this method.  If you don't use external restrictions in your app, you can safely return null from this method.

* RestrictedResourcesHandler getRestrictedResourcesHandler()

If your application supports dynamic restrictions, you will need to provide a way to evaluate them by implementing a RestrictedResourcesHandler.  That implementation can then be passed into Deadbolt by returning it from this method.  If you don't use dynamic restrictions in your app, you can safely return null from this method.

Once you've implemented DeadboltHandler, you can declare it in your application.conf file.  Don't forget a no-args constructor is required!

bc. deadbolt.handler=controllers.MyDeadboltHandler

If you want to have different DeadboltHandlers used in different contexts, the standard Play mechanism is used:

bc. %production.deadbolt.handler=controllers.ProductionDeadboltHandler
%test.deadbolt.handler=controllers.TestDeadboltHandler

h3. Connecting Deadbolt to your controllers

Controllers are secured using the @With annotation to ensure method calls are checked against the current user.

bc. @With(Deadbolt.class)
public class MyController extends Controller


h2. Dynamic restrictions

Dynamic restrictions are completely arbitrary, and work by naming a resource - class, method or view - and then passing that name to a handler implemented by you.  You can then use this name to check roles belonging to the current user, the roles belonging to a group the current user is in, the time of day...it's completely arbitrary.  Dynamic restrictions take precedence over static restrictions.

h3. RestrictedResourcesHandler

RestrictedResourceHandlers are used to determine access to a resource.  DeadboltHandler has a getRestrictedResourcesHandler() method from which you can return a handler - or one of several handlers, again based on arbitrary reasons - which will be passed the name of the resource. From that point on, it's up to you to decide the access.  Access requests can have one of three results - ALLOWED, DENIED or NOT_SPECIFIED.  The first two are self-explanatory, whereas the third is a bit more complicated.  You may decide that if you can't determine if access to a resource - for example, your database might not map the current user to the resource - then access should be denied.  However, you could return NOT_SPECIFIED which - if the RestrictedResource staticFallback parameter is true - would make Deadbolt drop back to static Deadbolt restrictions.

h3. Securing controllers

The @RestrictedResource annotation is used to mark a source as having dynamic security.  It takes a mandatory name value, which should be unique if you want fine-grained control, and an optional boolean to indicate if integration with static restrictions should be allowed.  The RestrictedResource annotation can be set at the class or method level.

bc. // This restricts access to the list method according to how your RestrictedResourcesHandler deals with the name "foo".  If NOT_SPECIFIED is returned, access is denied.
@RestrictedResource(name = {"foo"})
public static void list()

bc. // This restricts access to the list method according to how your RestrictedResourcesHandler deals with the name "foo".  If NOT_SPECIFIED is returned, access is allowed.
@RestrictedResource(name = {"foo"}, staticFallback = true)
public static void list()

bc. // This restricts access to the list method according to how your RestrictedResourcesHandler deals with the name "foo".  If NOT_SPECIFIED is returned, access is determined by the Restrict annotation.
@RestrictedResource(name = {"foo"}, staticFallback = true)
@Restrict("foo")
public static void list()


h3. Securing views

Views are secured with the deadbolt.restrictedResource tag.

bc. #{deadbolt.restrictedResource resourceKey:['resourceA']}
    this restricts access to this content to how you deal with "resourceA".  If NOT_SPECIFIED is returned, access is denied.
#{/deadbolt.restrictedResource}

bc. #{deadbolt.restrictedResource resourceKey:['resourceA', 'someMetaInfo'], allowUnspecified:false}
    this restricts access to this content to how you deal with "resourceA", possibly informed by someMetaInfo.  If NOT_SPECIFIED is returned, access is denied.
#{/deadbolt.restrictedResource}

bc. #{deadbolt.restrictedResource resourceKey:['resourceA'], allowUnspecified:true}
    this restricts access to this content to how you deal with "resourceA".  If NOT_SPECIFIED is returned, access is allowed.
#{/deadbolt.restrictedResource}


h2. Static restrictions

Static restrictions work by specifying hard-coded information into your controllers and views.

h3. Securing controllers

Rolenames are matched against result of the Role.getRoleName() method of the roles return from RoleHolder#getRoles().

There are two annotations available to define access to a controller method - @Restrict, and @Restrictions.

h3. @Restrict

The @Restrict annotation defines a set of role names that are ANDed together.  RoleHolders with matching roles are allowed access.

bc. // This restricts access to the list method to any role holder with a role whose name is "foo"
@Restrict("foo")
public static void list()

bc. // This restricts access to the list method to any role holder with both "foo" and a "bar" roles.
@Restrict({"foo", "bar"})
public static void list()


h3. @Restrictions

The @Restrictions annotation defines a set of @Restricts that are ORed together.

bc. // This restricts access to the list method to any role holder with a role whose name is "foo".  This is the equivalent to just @Restrict("foo")
@Restrictions(@Restrict("foo"))
public static void list()

bc. // This restricts access to the list method to any role holder with the "foo" role, or roles whose names are "bar" AND "gee".  Note that a role holder with a "bar" OR a "gee" role will be denied access.
@Restrictions({@Restrict("foo"), @Restrict({"bar", "gee"})})
public static void list()


h3. Securing views

Views are secured with the deadbolt.restrict tag.  This is slightly simpler than the annotations used by controllers, since the syntax is always that of @Restrictions, i.e. ORed role groups in the top level, ANDed roles groups in the inner level:  RoleHolders with matching roles are allowed access.

bc. #{deadbolt.restrict roles:[['foo']]}
    this restricts access to this content to role holders with the "foo" role
#{/deadbolt.restrict}

bc. #{deadbolt.restrict roles:[['foo', 'bar']]}
    this restricts access to this content to role holders with the "foo" AND 'bar' roles
#{/deadbolt.restrict}

bc. #{deadbolt.restrict roles:[['foo'], ['bar']]}
    this restricts access to this content to role holders with the "foo" OR 'bar' role
#{/deadbolt.restrict}


h3. Negating role names

By prefixing a rolename with ! you state the method or view is inaccessible to any role holder with that role.

bc. This restricts access to the list method to any role holder with a "foo" role but no "bar" role.  If a "bar" role is present, access is denied.
@Restrict({"foo", "!bar"})
public static void list()


The same applies in views:

bc. This restricts view rendering for the content to any role holder with a "foo" role but no "bar" role.  If a "bar" role is present, rendering is skipped.
#{deadbolt.restrict roles:[['foo', '!bar']]}
    ...
#{/deadbolt.restrict}

h3. Combining regular and negated role names in views

By nesting regular and negated role names in a view, you can have very fine-grained control over what is displayed.  For example,

bc. #{deadbolt.restrict roles:[['foo', 'bar', 'gee']]}
    something
    #{deadbolt.restrict roles:[['!bar']]}
        something only visible to role holders who aren't "bar"s
    #{/deadbolt.restrict}
    something else
#{/deadbolt.restrict}


h3. Externalised restrictions

Deadbolt allows you to annotation controller classes and methods with named restriction trees.  It's debatable whether this can
be classed as a static restriction, but since it works along the same lines as the truly static mechanisms I'm not going to split hairs.

bc. @ExternalRestrictions("admin-only")
public static void edit()

bc. @ExternalRestrictions({"admin", "super-mega-admin"})
public static void edit()

This annotation maps to an externalised restriction - again a combination of ANDs, ORs and NOTs - and so allows you to
define standard security schemes than can be changed at the e.g. database level.

Each name passed to the annotation, e.g. admin and super-mega-admin in the example above, maps to an ExternalizedRestrictions
instance.  The results of these instances are OR'd together.  Each ExternalizedRestrictions instance has a list of ExternalizedRestriction
instances - each of these instances is also OR'd together.  Finally, each ExternalizedRestriction has a list of role names - these are ANDed together, and also support role name
negation.

Below is an example implementation of an ExternalizedRestrictionsAccessor.

bc. import controllers.deadbolt.ExternalizedRestrictionsAccessor;
import models.deadbolt.ExternalizedRestrictions;
import java.util.HashMap;
import java.util.Map;
public class MyExternalizedRestrictionsAccessor implements ExternalizedRestrictionsAccessor
{
    private final Map<String, ExternalizedRestrictions> restrictions = new HashMap<String, ExternalizedRestrictions>();
    public MyExternalizedRestrictionsAccessor()
    {
        restrictions.put("standard",
                         new MyExternalizedRestrictions(new MyExternalizedRestriction("foo"),
                                                        new MyExternalizedRestriction("bar")));
        restrictions.put("one-role-missing",
                         new MyExternalizedRestrictions(new MyExternalizedRestriction("foo"),
                                                        new MyExternalizedRestriction("rab")));
        restrictions.put("admin",
                         new MyExternalizedRestrictions(new MyExternalizedRestriction("admin")));
        restrictions.put("exclude-restricted",
                         new MyExternalizedRestrictions(new MyExternalizedRestriction("!restricted")));
    }
    public ExternalizedRestrictions getExternalizedRestrictions(String name)
    {
        return restrictions.get(name);
    }
}


h3. Mixing annotations

The annotations detailed above can be applied in combination to controller classes and fields.  The evaluation is ANDed and the order is
* Restrict
* Restrictions
* ExternalRestrictions


h3. Integrating with authentication systems

For an example of how to integrate Deadbolt with an authentication module, please take a look at the integration-with-secure app in samples-and-tests - this uses the standard Secure module to illustrate the integration.

h3. Caching the RoleHolder on a per-request basis

It's fairly unlikely that the roles of a role holder will change within the request scope of that role holder (and if it did, you'd have a
pretty non-deterministic application!).  Since it's possible that you may get the role holder from the database on every call to
DeadboltHandler#getRoleHolder unless you're doing some caching, you can improve your performance a lot by performing per-request caching of the
role holder.

To enable this, add the following to your application.conf file:

bc. deadbolt.cache-user-per-request=true

If the property is not present or set to something other than true, the per-request caching will be disabled.  This is the default behaviour.