Community contributed extensions

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.

Getting started

To install Deadbolt, you can use the modules repository:

play install deadbolt-<required version>

Examples

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

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.

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.

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;
    }
}

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:

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.

This method will typically return the current RoleHolder.

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.

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.

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!

deadbolt.handler=controllers.MyDeadboltHandler

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

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

Connecting Deadbolt to your controllers

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

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

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.

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.

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.

// 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()
// 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()
// 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()

Securing views

Views are secured with the deadbolt.restrictedResource tag.

#{deadbolt.restrictedResource resourceKeys:['resourceA']}
    this restricts access to this content to how you deal with "resourceA".  If NOT_SPECIFIED is returned, access is denied.
#{/deadbolt.restrictedResource}
#{deadbolt.restrictedResource resourceKeys:['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}
#{deadbolt.restrictedResource resourceKeys:['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}

If you need to give supplementary information, you can use the resourceParameters argument to give a map that will be passed to your RestrictedResourcesHandler

#{deadbolt.restrictedResource resourceKeys:['resourceA'], resourceParameters:['foo':'bar']}
    this restricts access to this content to how you deal with "resourceA".  A map containing foo -> bar will also be available in the restricted resource handler.
#{/deadbolt.restrictedResource}

Static restrictions

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

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.

@Restrict

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

// This restricts access to the list method to any role holder with a role whose name is "foo"
@Restrict("foo")
public static void list()
// 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()

@Restrictions

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

// 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()
// 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([email protected]("foo"), @Restrict({"bar", "gee"})})
public static void list()

@RoleHolderPresent

The @RoleHolderPresent annotation indicates that a role holder (i.e. the user is logged in) must be present to access the resource. The annotation is inherited, so it can be declared on a controller that other controllers extend from.

// This restricts access to the list method to any role holder, regardless of roles.
@RoleHolderPresent
public static void list()
// This restricts access to the Foo controller to any role holder, regardless of roles.
@RoleHolderPresent
public class Foo extends Controller

@Unrestricted

The @Unrestricted annotation unlocks a controller or method. It is not inherited, and so needs to be declared on any controller that extends a restricted controller that you wish to be unrestricted. It takes precedence over any declared restriction.

// This unrestricts access to the list method, even if the method is in a restricted controller.
@Unrestricted
public static void list()
// This unrestricts access to the Foo controller, even if the parent controller is restricted.
@Unrestricted
public class Foo extends Controller

Note that

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.

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

If you want to restrict part of a view to any user that’s logged in, regardless of their roles, you can use the deadbolt.roleHolderPresent tag, i.e.

#{deadbolt.roleHolderPresent}
    this restricts access to this content to logged-in users
#{/deadbolt.roleHolderPresent}

Negating role names

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

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:

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}

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,

#{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}

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.

@ExternalRestrictions("admin-only")
public static void edit()
@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.

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);
    }
}

Securing views

Views are secured with the deadbolt.externalizedRestriction tag.

#{deadbolt.externalizedRestriction externalRestrictions:['admin']}
    this restricts access to this content to how you deal with "admin" in your ExternalizedRestrictionsAccessor.
#{/deadbolt.externalizedRestriction}
#{deadbolt.externalizedRestriction externalRestrictions:['admin', 'super-mega-admin']}
    this restricts access to this content to how you deal with "admin" OR "super-mega-admin" in your ExternalizedRestrictionsAccessor.
#{/deadbolt.externalizedRestriction}

Mixing annotations

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

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.

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:

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.

Content return types

Play takes HTML as the default response format when you invoke the Controller#render method. If your application typically, or even occasionally, returns another
format then you have a problem. Unless you configure your routes file to specify the response format, or do some content negotiation from the client, you’re pretty
much stuck with HTML. The impact for Deadbolt is that while your DeadboltHandler implementation has an onAccessFailure method, there’s no easy way to check what
format you’re working with.

If you want to specify a default response format for when DeadboltHandler#onAccessFailure is invoked, you can add an entry to the application.conf file to do this:

deadbolt.default-response-format=json

This will set the request.format field to whatever response type you have set, which will in turn determine which template is called when you invoke render()...html
will result in .html templates being rendered, json will result in .json templates being rendered, foo will result in .foo templates, and so on.

If you have an application that returns a mix of response types, then setting a default format may not be of much use to you. In these cases, you may want to use annotations
to give you some hints. There are two built-in annotations with Deadbolt, @JSON and @XML. You can apply these to a controller or to an individual method. When present,
the request format will be set to json or xml respectively before your DeadboltHandler’s onAccessFailure method is invoked.

Note that you can annotate a controller with @JSON but have an @XML method in there, and vice-versa.

What about when the next great format is invented? That’s where you have to do your own work. You can add your own annotations to controllers or methods and check for them
in your onAccessFailure implementation before you call render.

One important note – whichever forbidden method you invoke from onAccessFailure cannot be a redirect. The easiest way to do this is to create a 403 file in the views/errors
directory of your application for each format type, and invoke Deadbolt.forbidden() from your onAccessFailure implementation. See samples-and-tests/content-types for an example.

Ideally, the content types would be specified in the routes file but since route wildcards are both possible and extremely useful, you sometimes need to get implementation-specific.