Deadbolt 2

Deadbolt 2
Deadbolt 2
Buy on Leanpub

Table of Contents

1. Deadbolt 2

1.1 About the author

Steve Chaloner has been a software developer, consultant and mentor since 1999. He specialises in Java and Scala, but believes in using the right tool for the job. The right tool for him, for web-based applications, is the Play framework.

He is the author of several open-source projects, the most successful of which is Deadbolt, an authorization system for Play.

In 2011, he was selected as one of the Expert Reviewers for the Play! Framework Cookbook, along with the creator of Play and one of its oldest contributors. Since then, he has been an expert reviewer for four other books on Play, covering Java, Scala and reactive programming.

In 2012, Steve co-founded the Belgian Play Framework User Group with Ben Verbeken. This later merged with BeScala, the Belgian Scala User Group.

Steve blogs at http://www.objectify.be and tweets at https://twitter.com/steve_objectify.

1.2 About the book

This book was written in Markdown, and its source files are available on GitHub at https://github.com/schaloner/deadbolt-2-guide.

2. Introduction

At the date of writing this, the small code experiment that turned into Deadbolt is nearly five years old. In that time, it has grown in capabilities, power and - hopefully - usefulness. It has certainly reached the point where a few badly-written lines of text can adequately cover its features and use. Once I factored in that it now has support for Java and Scala, and that its new architecture allows for easy extension with other languages, it became clear that a short booklet (or booklet-like document) was required.

I hope this document (booklet-like or otherwise) will prove useful as you integrate Deadbolt 2 into your application.

2.1 History

Back in September 2010, I was embarking on my first project using the Play! Framework (for fans of unnecessary detail, it was version 1.03.2, back when it still had an exclamation mark) and discovering the Secure module it shipped with was unsuitable for the required authorization. As a result, Deadbolt 1.0 was written to provide AND/OR/NOT support for roles. Sometime later, dynamic rule support was added and other new features would be released as use cases and bug reports cropped up.

The user guide for Deadbolt 1 - which I can still highly recommend if you need authorization support in your Play 1 apps - starts with this:

Deadbolt is an authorization 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 authorization mechanism to whatever auth system is used.

How much of this still holds true for Deadbolt 2? More than 50% and less than 100%, give or take.

  • Deadbolt is still used for authorization
  • It can control access to controllers
  • It can control access to templates
  • The capabilities have expanded beyond the original role-based static checks
  • Deadbolt 2 is based on Deadbolt 1, so it’s related to the old Secure module in spirit, if not in implementation
  • You can (or should be able to) combine Deadbolt 2 with any authentication system

Deadbolt 2 v1.0 was released at roughly the same time as Play 2.0, and was essentially the logic of Deadbolt 1 exposed in the Play 2 style. Nine months after that initial release - nine months, I should add, of woefully inadequate Scala support - I re-designed the architecture to a more modular approach, and made a few small changes to the API to remove anachronistic elements.

There was now a core module written in Java, and separate idiomatic modules for Java and Scala. This is slightly different to the architecture of Play 2 itself, where the core and the Scala API are co-located. By the time v2.5 was released, I had deprecated the core - I felt it forced Java classes into the Scala API, leading to the need for Scala-to-Java type conversions. The Java and Scala versions of Deadbolt are now united in philosophy and capability, but no longer by any common code.

2.2 Java versus Scala

I have tried my best, within the constraints of both languages and my knowledge of them, to give equal capabilities to each version of Deadbolt 2. Scala is generally detailed after Java in this book for two reasons.

The first reason is the alphabet.

The second is that by writing the Scala section last, I have a chance to increase my knowledge of a language that is frequently beautiful and occasionally a little bit like an ice-cream headache.

2.3 Versions

The examples in this book are for Play 2.5, and use the following versions of Deadbolt

  • "be.objectify" %% "deadbolt-java" % "2.5.0"
  • "be.objectify" %% "deadbolt-scala" % "2.5.1"

2.4 Target audience

This book works on the following assumptions:

  • you have a working knowledge of Play
  • you know Java or Scala (or both)

If you want to learn Play, the Play website is the best place to start.

There are also several books that you might want to check out.

2.5 Acknowledgements

Nothing which attempts to be useful can gestate in a vacuum. Many people donated their time, knowledge and critical capabilities to help make this book more useful.

  • Peter Hilton (@PeterHilton) - software developer, speaker, writer, author of “Play for Scala” and beer lover.
  • Francis De Brabandere (@somatik) - software developer, technology enthusiast, start-up founder and beer lover.
  • Daryl Greensill (@BigClumsyOaf) - todo: check one-line write-up

2.6 Feedback

Any and all feedback of this is greatly appreciated. You can send feedback using…

3. Root concepts

Deadbolt is centered around a single idea - constraining access to a resource to a specific group of users. I’ve had several e-mails from developers who have thought that Deadbolt had a “restrict from” approach, and therefore could not understand why the authorization was failing so spectacularly; to forestall future questions about this, I want to make it completely clear - Deadbolt uses “restrict to”. For example, a controller action annotated with @Restrict(@Group("foo")) would only allow users with the “foo” role to access the method.

Two mechanisms are provided to declare these constraints - one at the template level and another at the controller level. In each case, there are differences between how these are applied in Java and Scala applications, so specific details will be provided in later chapters. The capabilities of each version are roughly similar, taking into account the idiosyncrasies of each language.

3.1 Template-level constraints

For a Play application that uses server-side rendering, Deadbolt provides several template tags that will include or remove template content at the point of rendering.

A couple of basic use cases are

  • Only displayed a “Log in” link if there is no user present
  • Even if a user is logged in, only display an “Administration” link if the user has administrative privileges

However, it is extremely important to note that using these tags will only give you a cleaner UI, one that is better tailored to the user’s privileges. It will not secure your server-side code in any way except - possibly - obscurity. Server-side routes can be invoked from outside of templates by changing the URL in the browser’s address bar, using command-line tools such as cURL and many other ways.

If you have seen the original Dawn Of The Dead (Romero, 1978), you may remember the protagonists concealing the entrance to their living quarters using a panel of painted hardboard. There are no additional defensive layers behind this concealment. When a zombified protagonist breaks through the hardboard, knowing there is something he wants on the other side, all security is gone. Minutes later, there’s blood everywhere and the survivors have to flee. If you haven’t seen it, apologies for the spoiler.

Template security is like painted hardboard - the features it offers are certainly nice to have, but a further level of defensive depth is required. For this, you need controller action security - otherwise, the zombies will get you.

3.2 Controller-level restrictions

The controller layer is most vulnerable part of your application to external attack, because that is the part that is visible to whichever networks it is on. Attack in this sense may be a conscious attack on your system, or inadvertant damage caused by unauthorized users who are otherwise authenticated in your system. Deadbolt can help with both of these scenarios in the same way, by limiting the capabilities of any given user at the application level.

Controller authorization blocks or allows access to an action. Whereas template restrictions are essentially a boolean evaluation - “if user satisfies these conditions, then…”, controller authorization is quite a bit more powerful. Specifically, while an authorized result is generated from your application code, unauthorized results can be customised as required; you can return any status code you like along with any content you like. If you’re feeling particularly nasty, why not send a 302 redirect to a not-suitable-for-work website? If you want to, the option is there.

3.3 Core entities

Deadbolt has three interfaces which can be used to represent authorization entities in your application - Subject, Role and Permission.

Subject

A subject represents an authorizable entity - in other words, a user or account. A subject embodies four pieces of information - the fact a user is (or isn’t) authenticated, that user’s identity, and that user’s roles and permissions.

Depending on the application you’re building, you may always have a subject present - call it Guest, for example. This subject may have a very restricted set of roles and/or permissions. Alternatively, you may require explicit authentication for some or all of the application, in which case it’s possible no subject is present.

Fun fact - Subject was originally known as RoleHolder, but this swiftly became an anacronysm as Deadbolt gained capabilities beyond checking roles. As of Deadbolt 2.0, RoleHolder became ´Subject´.

Role

A Role is essentially a wrapper around a string. It is the primary entity for the Restrict and Restrictions constraints. Role A is equal to Role B, even if they are different objects, if they have exactly the same name. Role names should be case-aware, so “Admin” is not the same as “admin”.

As Role is an interface/trait, it can be implemented as a class or an enum.

If you do not require roles in your application, you do not need to implement this interface - just return an empty list from Subject#getRoles.

Permission

A Permission, just like a Role, is essentially a wrapper around a string. It is the primary entity for Pattern constraints, and has different interpretations depending on the type of pattern constraint that is being applied to it. For example, a PatternType.EQUALITY test would perform a case-sensitive comparison between a user’s permissions and the test value. A PatternType.REGEX would assess a user’s permissions in the context of regular expressions, and so on.

As Permission is an interface/trait, it can be implemented as a class or an enum.

If you do not require permissions in your application, you do not need to implement this interface - just return an empty list from Subject#getPermissions.

3.4 Hooks

There are two hooks that can be used to integrate Deadbolt into your application - DeadboltHandler and DynamicResourceHandler. In the Java version, these are represented by interfaces; in the Scala version, they are traits. There are some small differences between them caused by design differences with the Java and Scala APIs themselves, so exact breakdowns of these types will be covered in the language-specific sections. For now, it’s enough to remind ourselves of where we are working in terms of a HTTP request.

A HTTP request has a life cycle. At a high level, it is

  • Sent
  • Received
  • Processed
  • Answered

The processed point is where our web applications live. In a sense, this high-level life cycle is repeated here, as the request is sent from the container into the application, received by the app, processed and answered. Controller constraints occur at the point where the container (the Play server, in this case) hands the request over to the application; templates work during the processing phase as a response body is rendered. Both places are where any DeadboltHandler and DynamicResourceHandler instances are active.

3.5 Static and dynamic constraints

Deadbolt supports two categories of constraint - static and dynamic. This is a little like saying two types of animal exist - cats and not-cats - so read on. This bit is important.

A static constraint is one that requires no further effort on the part of the developer, because the necessary information can be determined from the DeadboltHandler and the Subject. For example, the “is a subject present?” constraint can be answered by requesting the subject from the handler; similarly, group membership can be determined by requesting a subject’s roles and testing them for the required values. Simply put, a static constraint is one in which the developer specifies what they want and Deadbolt handles the how based on existing information.

Dynamic constraints, on the other hand, require work by the developer. They embody arbitrary logic, and have total freedom as long as they eventually return a result. For example, you may have a business rule that a user on subscription plan x can only make y calls to an API within z amount of time - this could be implemented as a dynamic constraint.Dynamic constraints are exposed via the DynamicResourceHandler (in retrospect, I probably should have called it DynamicConstraintHandler…) and will be discussed in detail in the language-specific chapters.

One small but pertinent fact regarding dynamic constraints is they don’t necessarily require subjects; if you write a constraint that does not need a subject, the presence or absence of a subject is irrelevent. If your boss, in a bizarre act of caprice, decides that no-one can have access to the accounting system on a Tuesday, the constraint would essentially be if (day == tuesday)...; the subject is not needed and so not used.

I Deadbolt for Java

4. Using Deadbolt 2 with Play 2 Java projects

Deadbolt 2 for Java provides an idiomatic API for dealing with Java controllers and templates rendered from Java controllers in Play applications. It takes advantage of the features such as access to the HTTP context to give access to the current request and session, and the annotation-driven interceptor support.

4.1 The Deadbolt Handler

For any module - or framework - to be useable, it must provide a mechanism by which it can be hooked into your application. For Deadbolt, the central hook is the be.objectify.deadbolt.java.DeadboltHandler interface. The four methods defined by this interface are crucial to Deadbolt - for example, DeadboltHandler#getSubject gets the current subject (if any), whereas DeadboltHandler#onAccessFailure is used to generate a response when authorization fails. There is also a default method called handlerName which will be discussed in the section on handler caches.

DeadboltHandler implementations should be stateless.

For each method, a CompletionStage is returned - this is Java 8 interface for a future. If the future may complete to have an empty value, e.g. calling getSubject when no subject is present, the return type is CompletionStage<Optional>.

An application may have one or more handler implementations, which will be explored when we discuss the HandlerCache. Despite the use of the definite article in the section title, you can have as many Deadbolt handlers in your app as you wish. These can be specified on a per-constraint basis, allowing tight contol over your authorization configuration if required.

AbstractDeadboltHandler

Deadbolt provides an abstract implementation of DeadboltHandler, called - in time-honoured fashion - AbstractDeadboltHandler. This provides the following behaviour:

  • No pre-authorization operations are carried out; a future containg an empty option is returned.
  • No subject is present; a future containing an empty option is returned.
  • No dynamic resource handler is present; as you can probably guess by now, a future containing an empty option is returned.
  • Authorization failure gives the standard Play unauthorized template rendered into a HTTP 401 response.

AbstractDeadboltHandler takes an instance of be.objectify.deadbolt.java.ExecutionContextProvider in its constructor. When using methods such as CompletableFuture#supplyAsync, this should be used to provide the executor necessary to ensure cross-thread access to the context.

Enable cross-thread access to the context
1 public CompletionStage<Result> onAuthFailure(final Http.Context context,
2                                              final Optional<String> content) {
3     final ExecutionContext executionContext = executionContextProvider.get();
4     final ExecutionContextExecutor executor = HttpExecution.fromThread(executionContext);
5     return CompletableFuture.supplyAsync(unauthorized::render,
6                                          executor)
7                             .thenApplyAsync(Results::unauthorized,
8                                             executor);
9 }

Performing pre-constraint operations

Before a constraint is applied, the CompletionStage<Optional<Result>> beforeAuthCheck(Http.Context context) method of the current handler is invoked. If the resulting promise completes to a non-empty Optional, the target action is not invoked and instead the result of beforeAuthCheck is used for the HTTP response; if the resulting promise completes to an empty Optional the action is invoked with the Deadbolt constraint applied to it.

You may want to use this method to test if a subject is present. However, it’s important to be aware that this approach will actually shortcut the use of some annotations and result in inconsistent behaviour. The simplest example is the “subject not present” constraint, which requires no subject to be present for the action to be authorized. If you pre-emptively check for a subject in beforeAuthCheck and fail if one isn’t present, this means the “subject not present” check will fail! It’s recommended to use the constraints to drive this behaviour, and handle the consequences of failure in the onAccessFailure method.

If you don’t want to carry out any action before authorization constraints are applied, just return a completed future containng an empty option. Alternatively, extend AbstractDeadboltHandler and do not override this method.

Obtaining the subject

To get the current subject, the CompletionStage<Optional<Subject>> getSubject(Http.Context context) method is invoked. Returning an empty Optional indicates there is no subject present - this is a completely valid scenario, typically indicating no successful authentication has yet taken place.

By default, subjects are not cached and so this method is called every time the subject is required. If you have multiple constraints, for example if you’re using a lot of template constraints, this can become quite expensive. If you want the subject to be cached on a per-request basis, add deadbolt.java.cache-user=true to your configuration. Once this is done, caching will be handled automatically and this method will then only be called when no subject is already cached.

How the subject is obtained depends largely on how you authenticate subjects. You may simply get some identifier from a cookie and use it to look up a subject in the database; alternatively, your authentication library may provide support for access to subjects.

Dealing with authorization failure

When authorization fails, the CompletionStage<Result> onAccessFailure(Http.Context context, String content) method is used to obtain a result for the HTTP response. The result required from the CompletionStage returned from this method is a regular play.mvc.Result, so it can be anything you chose. You might want to return a 403 forbidden, redirect to a location accessible to everyone, etc.

Dealing with dynamic constraints

Dynamic constraints, which are Dynamic and Pattern.CUSTOM constraints, are dealt with by implementations of DynamicResourceHandler; this will be explored in a later chapter. For now, it’s enough to say CompletionStage<Optional<DynamicResourceHandler>> getDynamicResourceHandler(Http.Context context) is invoked when a dynamic constraint is used.

If you don’t have any dynamic constraints, just return a completed future containng an empty option. Alternatively, extend AbstractDeadboltHandler and do not override this method.

4.2 Expose your DeadboltHandlers with a HandlerCache

Unlike earlier versions of Deadbolt, in which handlers were declared in application.conf and created reflectively, Deadbolt now uses dependency injection to achieve the same functionality in a type-safe and more flexible manner. Various components of Deadbolt, which will be explored in later chapters, require an instance of be.objectify.deadbolt.java.cache.HandlerCache - however, no such implementations are provided.

Instead, you need to implement your own version. This has two requirements:

  • You have a get() method which returns the application-wide default handler
  • You have an apply(String handlerKey) method which returns a named handler

NB One interesting (and annoying) quirk is the way in which template and controller constraints obtain the default handler. The template constraints are written in Scala, so the HandlerCache#get() method can be used. Controllers, on the other hand, are configured via annotations and it’s not possible to have a default value of null for an annotation value and so the standard handler name defined by be.objectify.deadbolt.java.ConfigKeys.DEFAULT_HANDLER_KEY; is used. In the example below, HandlerKeys is an class that defines the handler names used in the application. To make sure we use the necessary default handler key, we re-use the ConfigKeys constant here.

Centralising the names of handlers
 1 public enum HandlerKeys
 2 {
 3     DEFAULT(ConfigKeys.DEFAULT_HANDLER_KEY),
 4     ALT("altHandler");
 5 
 6     public final String key;
 7 
 8     private HandlerKeys(final String key)
 9     {
10         this.key = key;
11     }
12 }

Here’s one possible implementation, using hard-coded handlers. defaultHandler is kept separate to make the get method more efficient, instead up looking it up in the ´handlers` map every time.

Defining hard-coded handlers
 1 @Singleton
 2 public class MyHandlerCache implements HandlerCache {
 3 
 4     private final Map<String, DeadboltHandler> handlers = new HashMap<>();
 5 
 6     private final DeadboltHandler defaultHandler = new MyDeadboltHandler();
 7 
 8     // handler keys is an application-specific enum
 9     public MyHandlerCache() {
10         // See below regarding the default handler
11         handlers.put(HandlerKeys.DEFAULT.key, defaultHandler);
12         handlers.put(HandlerKeys.ALT.key, new MyAlternativeDeadboltHandler());
13     }
14 
15     @Override
16     public DeadboltHandler apply(final String key) {
17         return handlers.get(key);
18     }
19 
20     @Override
21     public DeadboltHandler get() {
22         return defaultHandler;
23     }
24 }

Ideally, handler implementations should be self-sufficient. To this end, the DeadboltHandler interface contains a default method called handlerName. By default, this returns the class name. To make it more useful, implement the method and specific the handler name in it.

Overriding the handler name
1 public class MyDeadboltHandler implements DeadboltHandler {
2     
3     // ...
4 
5     public String handlerName() {
6         return HandlerKeys.DEFAULT.key;
7     }
8 }

This means we can change the previous implementation to the following.

Using the handlerName method to map handlers
 1 @Singleton
 2 public class MyHandlerCache implements HandlerCache {
 3 
 4     private final Map<String, DeadboltHandler> handlers = new HashMap<>();
 5 
 6     private final DeadboltHandler defaultHandler = new MyDeadboltHandler();
 7 
 8     // handler keys is an application-specific enum
 9     public MyHandlerCache() {
10         handlers.put(defaultHandler.handlerName(), defaultHandler);
11         final DeadboltHandler altHandler = new MyAlternativeDeadboltHandler();
12         handlers.put(altHandler.handlerName(), altHandler);
13     }
14 
15     // ...
16 }

Finally, create a small module which binds your implementation.

Binding your HandlerCache implementation
 1 package com.example.modules
 2 
 3 import be.objectify.deadbolt.java.cache.HandlerCache;
 4 import play.api.Configuration;
 5 import play.api.Environment;
 6 import play.api.inject.Binding;
 7 import play.api.inject.Module;
 8 import scala.collection.Seq;
 9 import security.MyHandlerCache;
10 
11 import javax.inject.Singleton;
12 
13 public class CustomDeadboltHook extends Module {
14     @Override
15     public Seq<Binding<?>> bindings(final Environment environment,
16                                     final Configuration configuration) {
17         return seq(bind(HandlerCache.class).to(MyHandlerCache.class).in(Singleton.class));
18     }
19 }

Using dependency injection with handlers

Both of these examples would work fine, but ignore the wide-spread and recommended use of dependency injection in Play 2.5. You may find it more useful to inject handlers into the handler cache, especially if those handlers themselves rely on injected components. As we’ve already seen, it’s possible to have multiple handler implementations so we also need a way to distinguish between implementations during injection.

The trick here is to qualify the handlers. This example will use the Guice approach; if you’re using a different DI framework, you need to check if it has support for a similar mechanism. If your application has a single handler implementation, you can skip this step and just declare the binding and inject the instance in the normal way.

Binding annotations for multiple handlers
 1 import com.google.inject.BindingAnnotation;
 2 
 3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.Target;
 7 
 8 public class HandlerQualifiers
 9 {
10     @Retention(RetentionPolicy.RUNTIME)
11     @Target({ElementType.TYPE, ElementType.PARAMETER})
12     @BindingAnnotation
13     public @interface MainHandler {}
14 
15     @Retention(RetentionPolicy.RUNTIME)
16     @Target({ElementType.TYPE, ElementType.PARAMETER})
17     @BindingAnnotation
18     public @interface AltHandler {}
19 }

These annotations can then be used to tag specific implementations in a way that can be accessed during the injection phase. For example, MyDeadboltHandler now has the following declaration; MyAlternativeDeadboltHandler has a similar declaration with the appropriate annotation.

Use annotations to label implementations
1 @HandlerQualifiers.MainHandler
2 public class MyDeadboltHandler implements DeadboltHandler

The ´bindings´ method of the module needs to be updated to be aware of the handler implementations.

Use annotations to label implementations
 1 public Seq<Binding<?>> bindings(final Environment environment,
 2                                     final Configuration configuration) {
 3     return seq(bind(DeadboltHandler.class)
 4                         .qualifiedWith(HandlerQualifiers.MainHandler.class)
 5                         .to(MyDeadboltHandler.class)
 6                         .in(Singleton.class),
 7                bind(DeadboltHandler.class)
 8                         .qualifiedWith(HandlerQualifiers.AltHandler.class)
 9                         .to(MyAlternativeDeadboltHandler.class)
10                         .in(Singleton.class),
11                bind(HandlerCache.class).to(MyHandlerCache.class).in(Singleton.class));
12 }

Now, it’s just a question of injecting the qualified handlers into the HandlerCache.

Injecting handlers into the cache
 1 @Singleton
 2 public class MyHandlerCache implements HandlerCache {
 3 
 4     private final Map<String, DeadboltHandler> handlers = new HashMap<>();
 5 
 6     private final DeadboltHandler defaultHandler;
 7 
 8     @Inject
 9     public MyHandlerCache(@HandlerQualifiers.MainHandler 
10                                 final DeadboltHandler defaultHandler,
11                           @HandlerQualifiers.AltHandler final DeadboltHandler altHandler) {
12         this.defaultHandler = defaultHandler;
13 
14         handlers.put(defaultHandler.handlerName(), defaultHandler);
15         handlers.put(altHandler.handlerName(), altHandler);
16     }
17 
18     // ...
19 }

4.3 application.conf

Declare the necessary modules

Both be.objectify.deadbolt.java.DeadboltModule and your custom bindings module must be declared in the configuration.

Binding your HandlerCache implementation
1 play {
2   modules {
3     enabled += be.objectify.deadbolt.java.DeadboltModule
4     enabled += com.example.modules.CustomDeadboltHook
5   }
6 }

Tweaking Deadbolt

Deadbolt Java-specific configuration lives in the deadbolt.java namespace.

  • deadbolt.java.view-timeout
    • The millisecond timeout applied to blocking calls when rendering templates. Defaults to 1000ms.
  • deadbolt.java.cache-user
    • A flag to indicate if the subject should be cached on a per-request basis. This defaults to false, but setting it to true may result in significant performance gains if you’re using template constraints.

Personally, I prefer the HOCON (Human-Optimized Config Object Notation) syntax supported by Play, so I would recommend the following:

Tweaking the default settings
1 deadbolt {
2   java {
3     cache-user=true
4     view-timeout=500
5   }
6 }

JPA

After all the effort I made to ensure Deadbolt is as non-blocking as possible, JPA emerged from the mist to bite me on the ass; entity managers, it seems, do not like the kind of multi-threaded usage implicit in Play’s asynchronous behaviour.

Luckily, a solution is at hand. Less luckily, it’s blocking.

To address this, you can put Deadbolt into blocking mode - this ensures all DB calls made in the Deadbolt layer are made from the same thread; this has performance implications, but it’s unavoidable with JPA.

To switch to blocking mode, set deadbolt.java.blocking to true in your configuration.

The default timeout is 1000 milliseconds - to change this, use deadbolt.java.blocking-timeout in your configuration.

This example configuration puts Deadbolt in blocking mode, with a timeout of 2500 milliseconds:

Surrendering to the whims of JPA
1 deadbolt {
2   java {
3     blocking=true
4     blocking-timeout=2500
5   }
6 }

5. Java controller constraints

If you like annotations in Java code, you’re in for a treat. If you don’t, this may be a good time to consider the Scala version. Actually, that’s not strictly true - it also turns out that if you take long enough to write a book, you add new features that allow constraints to be applied during routing; we’ll get to that later, I just didn’t want you to panic at the thought of yet more annotations in your code.

One very important point to bear in mind is the order in which Play evaluates annotations. Annotations applied to a method are applied to annotations applied to a class. This can lead to situations where Deadbolt method constraints deny access because information from a class constraint. See the section on deferring method-level interceptors for a solution to this.

As with the previous chapter, here is a a breakdown of all the Java annotation-driven interceptors available in Deadbolt Java, with parameters, usages and tips and tricks. Anywhere you see ec.current(), this is obtaining a java.util.concurrent.Executor from the HttpExecutionContext which has been injected into the controller. To avoid repeating it in every example, assume that each example method exists in a controller similar to the following.

Injecting the controller with an execution context
 1 import java.util.concurrent.Executor;
 2 import javax.inject.Inject;
 3 import play.libs.concurrent.HttpExecutionContext;
 4 
 5 public class FooController extends Controller
 6 {
 7     private final HttpExecutionContext ec;
 8 
 9     @Inject
10     public void FooController(final HttpExecutionContext ec)
11     {
12         this.ec = ec;
13     }
14 
15     // ...other methods
16 }

Static constraints

Static constraints, are implemented entirely within Deadbolt because it can finds all the information needed to determine authorization automatically. For example, if a constraint requires two roles, “foo” and “bar” to be present, the logic behind the Restrict constraint knows it just needs to check the roles of the current subject.

The static constraints available are

  • SubjectPresent
  • SubjectNotPresent
  • Restrict
  • RoleBasedPermissions
  • Pattern - when using EQUALITY or REGEX

Dynamic constraints

Dynamic constraints are, as far as Deadbolt is concerned, completely arbitrary; they’re handled by implementations of DynamicResourceHandler.

The dynamic constraints available are

  • Dynamic
  • Pattern - when using CUSTOM

Anything else?

Now you mention it, you can combine all of the above in arbitrary compositions to define constraint trees. These can be applied using the Composite annotation.

5.1 SubjectPresent

SubjectPresent is one of the simplest constraints offered by Deadbolt. It checks if there is a subject present, by invoking DeadboltHandler#getSubject and allows access if the result is an Optional containing a value.

@SubjectPresent can be used at the class or method level.

Parameter Type Default Notes
content String ”” A hint to indicate the content expected in the
      response. This value will be passed to
      DeadboltHandler#onAccessFailure. The value
      of this parameter is completely arbitrary.
handlerKey String “defaultHandler” The name of a handler in the HandlerCache
deferred boolean false If true, the interceptor will not be applied
      until a DeadboltDeferred annotation is
      encountered.
forceBeforeAuthCheck false false By default, the beforeAuthCheck method of the
      DeadboltHandler is not invoked before this
      constraint is applied, because implementations
      of beforeAuthCheck that return a non-empty
      Option when no subject is present can short-cut
      this constraint.
Require a subject for all actions in a controller
 1 @SubjectPresent
 2 public class MyController extends Controller
 3 {
 4     public CompletionStage<Result> index()
 5     {
 6         // this method will not be invoked unless there is a subject present
 7         ...
 8     }
 9 
10     public CompletionStage<Result> search()
11     {
12         // this method will not be invoked unless there is a subject present
13         ...
14     }
15 }
Require a subject for specific actions in a controller
 1 // Deny access to a single method of a controller unless there is a user present
 2 public class MyController extends Controller
 3 {
 4     public CompletionStage<Result> index()
 5     {
 6         // this method is accessible to anyone
 7         ...
 8     }
 9 
10     @SubjectPresent
11     public CompletionStage<Result> search()
12     {
13         // this method will not be invoked unless there is a subject present
14         ...
15     }
16 }

5.2 SubjectNotPresent

SubjectNotPresent is the opposite in functionality of SubjectPresent. It checks if there is a subject present, by invoking DeadboltHandler#getSubject and allows access only if the result is an empty Optional.

@SubjectNotPresent can be used at the class or method level.

Parameter Type Default Notes
content String ”” A hint to indicate the content expected in
      the response. This value will be passed
      to DeadboltHandler#onAccessFailure. The
      value of this parameter is completely arbitrary.
handlerKey String “defaultHandler” The name of a handler in the HandlerCache
deferred boolean false If true, the interceptor will not be applied
      until a DeadboltDeferred annotation is
      encountered.
forceBeforeAuthCheck false false By default, the beforeAuthCheck method of the
      DeadboltHandler is not invoked before this
      constraint is applied, because implementations
      of beforeAuthCheck that return a non-empty
      Option when no subject is present can short-cut
      this constraint.
Require NO subject for all actions in a controller
 1 @SubjectNotPresent
 2 public class MyController extends Controller
 3 {
 4     public CompletionStage<Result> index()
 5     {
 6         // this method will not be invoked if there is a subject present
 7         ...
 8     }
 9 
10     public CompletionStage<Result> search()
11     {
12         // this method will not be invoked if there is a subject present
13         ...
14     }
15 }
Require NO subject for specific actions in a controller
 1 // Deny access to a single method of a controller if there is a user present
 2 public class MyController extends Controller
 3 {
 4     public CompletionStage<Result> index()
 5     {
 6         // this method is accessible to anyone
 7         ...
 8     }
 9 
10     @SubjectNotPresent
11     public CompletionStage<Result> search()
12     {
13         // this method will not be invoked unless there is not a subject present
14         ...
15     }
16 }

5.3 Restrict

The Restrict constraint requires that a) there is a subject present, and b) the subject has ALL the roles specified in the at least one of the Groups in the constraint. The key thing to remember about Restrict is that it ANDs together the role names within a group and ORs between groups.

Notation

The role names specified in the annotation can take two forms.

  1. Exact form - the subject must have a role whose name matches the required role exactly. For example, for a constraint @Restrict("foo") the Subject must have a Role whose name is “foo”.
  2. Negated form - if the required role starts starts with a !, the constraint is negated. For example, for a constraint @Restrict("!foo") the Subject must not have a Role whose name is “foo”.

@Restrict can be used at the class or method level.

Parameter Type Default Notes
value Group[]   For each Group, the roles that must (or in the
      case of negation, must not) be held by the
      Subject. When the restriction is applied, the
      Group instances are OR’d together.
content String ”” A hint to indicate the content expected in
      the response. This value will be passed
      to DeadboltHandler#onAccessFailure. The
      value of this parameter is completely arbitrary.
handlerKey String “defaultHandler” The name of a handler in the HandlerCache
deferred boolean false If true, the interceptor will not be applied
      until a DeadboltDeferred annotation is
      encountered.
Both roles are required for all actions in a controller
 1 @Restrict(@Group{"editor", "viewer"})
 2 public class MyController extends Controller
 3 {
 4     public CompletionStage<Result> index()
 5     {
 6         // this method will not be invoked unless the subject has editor and viewer roles
 7         ...
 8     }
 9 
10     public CompletionStage<Result> search()
11     {
12         // this method will not be invoked unless the subject has editor and viewer roles
13         ...
14     }
15 }

Example 2

Each action has different role requirements
 1 public class MyController extends Controller
 2 {
 3     @Restrict(@Group("editor"))
 4     public CompletionStage<Result> edit()
 5     {
 6         // this method will not be invoked unless the subject has editor role
 7         ...
 8     }
 9 
10     @Restrict(@Group("view"))
11     public CompletionStage<Result> view()
12     {
13         // this method will not be invoked unless the subject has viewer role
14         ...
15     }
16 }
Negated roles
 1 @Restrict(@Group({"editor", "!viewer"}))
 2 public class MyController extends Controller
 3 {
 4     public CompletionStage<Result> edit()
 5     {
 6         // this method will not be invoked unless the subject has editor role AND does NOT\
 7  have the viewer role
 8         ...
 9     }
10 
11     public CompletionStage<Result> view()
12     {
13         // this method will not be invoked unless the subject has editor role AND does NOT\
14  have the viewer role
15         ...
16     }
17 }
Require the ‘editor’ OR ‘viewer’ roles
 1 @Restrict({@Group("editor"), @Group("viewer")})
 2 public class MyController extends Controller
 3 {
 4     public CompletionStage<Result> index()
 5     {
 6         // this method will not be invoked unless the subject has editor or viewer roles
 7         ...
 8     }
 9 
10     public CompletionStage<Result> search()
11     {
12         // this method will not be invoked unless the subject has editor or viewer roles
13         ...
14     }
15 }
Require the subject to have (customer AND viewer) OR (support AND viewer) roles
 1 @Restrict({@Group({"customer", "viewer"}), @Group({"support", "viewer"})})
 2 public class MyController extends Controller
 3 {
 4     public CompletionStage<Result> index()
 5     {
 6         // this method will not be invoked unless the subject has customer and viewer,
 7         // or support and viewer roles
 8         ...
 9     }
10 }
Require the subject to have (customer AND NOT viewer) OR (support AND NOT viewer) roles
 1 @Restrict({@Group("customer", "!viewer"), @Group("support", "!viewer")})
 2 public class MyController extends Controller
 3 {
 4     public CompletionStage<Result> index()
 5     {
 6         // this method will not be invoked unless the subject has customer but not viewer \
 7 roles,
 8         // or support but not viewer roles
 9         ...
10     }
11 }

5.4 RoleBasedPermissions

Roles and permissions are separate concepts. Roles do not imply permissions, and vice versa.

Roles are inherently tied to Restrict , so when @Restrict is used it uses Subject#getRoles and looks if the subject has the required role. If multiple (i.e. AND) or alternative (i.e. OR) relationships are defined - let’s say @Restrict(@Group({"admin", "printer"})), in which a subject must have the (admin AND printer) roles - that is also handled. This is done through a straightforward comparison of the required role name and the name of the roles the subject has, obtained via Role#getName.

Permissions are inherently tied to Pattern, and allow for greater granularity. The Pattern constraint uses Subject#getPermissions and matches e.g. @Pattern("admin.printer") against the subject’s permissions.

Let’s assume a company that has various departments, e.g. IT support, public relations, etc, and take two subjects.

Subject A has been assigned the following:

  • Roles
  • admin
  • pr
  • Permissions
  • admin.pr.blog.post.create
  • admin.pr.blog.post.delete
  • admin.pr.blog.post.update

Subject B has been assigned the following:

  • Roles
  • admin
  • it
  • Permissions
  • admin.it.printer
  • admin.it.ldap
  • admin.it.router

A controller action annotated with @Restrict(@Group("admin")) would be accessible to both subject A and subject B, even though they are admins in different departments. To restrict the action to admins of the IT department, you would need to use @Restrict(@Group({"admin", "it"})).

A controller action annotated with @Pattern("admin.*") would be accessible to both subject A and subject B, even though they are admins in different departments. To restrict the action to admins of the IT department, you would need to use @Pattern("admin.it.*").

However - and here’s the important bit - removing the admin role from a user would deny them access to actions marked with @Restrict(@Group("admin")) but still allow them access to actions marked with @Pattern("admin.*"). If you only use roles or only use permissions, this isn’t an issue. If you do use both, it can lead to inconsistencies and indeterminate authorizations.

Role-based permissions are a way of combining the two, by associating permissions with roles using DeadboltHandler#getPermissionsForRole.

  • an action is constrained to a role specified using e.g. @RoleBasedPermissions("admin")
  • the Deadbolt handler is used to get the permissions associated with the admin role, using DeadboltHandler#getPermissionsForRole
  • the permissions explicitly obtained from Subject#getPermissions must provide a match.

This is similar to using @Pattern, except that you can only constraint access with a single permission this way, whereas @RoleBasedPermissions allows you to constraint access with any of the permissions linked to a role. Let’s assume that a role foo gives these permissions:

  • admin.pr.blog.post.create
  • admin.pr.blog.post.delete
  • admin.pr.blog.post.update
  • admin.pr.twitter.post

Using @RoleBasedPermissions("foo"), you can constrain access to a subject with any of those permissions. However, if you want to have an access to an action constrained to subjects with admin.pr.twitter permissions, you can target a subset of subjects with the foo role using @Pattern("admin.pr.twitter.*"). In practice, you may not want to mix the different approaches but the possibility is there.

This still doesn’t provide a mechanism for assigning those permissions to a subject when a role is assigned, because this is more properly something that should be handled by the application itself. This could be done by injecting the handler cache into your user management code, which can then be used to obtain the default handler, and when a subject is given a role, use DeadboltHandler#getPermissionsForRole to find out which permissions to assign the subject at the same time. This make a small assumption: even if you have multiple handlers, the result of DeadboltHandler#getPermissionsForRole would always be the same for any given role.

Equally, when removing a role from a subject, DeadboltHandler#getPermissionsForRole can be used to determine the subset of permissions to remove from the subject, e.g. (permissions of removed role) \ (permissions of all other roles held by subject).

Parameter Type Default Notes
value String   The role name.
content String ”” A hint to indicate the content expected in
      the response. This value will be passed
      to DeadboltHandler#onAccessFailure. The value
      of this parameter is completely arbitrary.
handlerKey String “defaultHandler” The name of a handler in the HandlerCache
deferred boolean false If true, the interceptor will not be applied
      until a DeadboltDeferred annotation is
      encountered.
Using a role name to specify permissions
1 @RoleBasedPermissions("foo")
2 public CompletionStage<Result> someMethod() 
3 {
4     // the method will execute if the subject has one or more of the permissions obtained
5     // via DeadboltHandler#getPermissionsForRole("fooa")
6 }

5.5 Dynamic

The most flexible constraint - this is a completely user-defined constraint that uses DynamicResourceHandler#isAllowed to determine access.

@Dynamic can be used at the class or method level.

Parameter Type Default Notes
value String   The name of the constraint.
meta String   Additional information passed into isAllowed.
content String ”” A hint to indicate the content expected in
      the response. This value will be passed
      to DeadboltHandler#onAccessFailure. The
      value of this parameter is completely arbitrary.
handlerKey String “defaultHandler” The name of a handler in the HandlerCache
deferred boolean false If true, the interceptor will not be applied
      until a DeadboltDeferred annotation is
      encountered.
Using a user-defined test to determine access
1 @Dynamic(name = "name of the test")
2 public CompletionStage<Result> someMethod() 
3 {
4     // the method will execute if the user-defined test returns true
5 }

5.6 Pattern

This uses the Subjects Permissions to perform a variety of checks.

@Pattern uses a Subject’s Permissions to perform a variety of checks. The check depends on the pattern type.

  • EQUALITY - the subject must have a permission whose value is exactly the same as the value parameter
  • REGEX - the subject must have a permission which matches the regular expression given in the value parameter
  • CUSTOM - the DynamicResourceHandler#checkPermission function is used to determine access

It’s possible to invert the constraint by setting the invert parameter to true. This changes the meaning of the constraint in the following way.

  • EQUALITY - the subject must NOT have a permission whose value is exactly the same as the value parameter
  • REGEX - the subject must have NO permissions that match the regular expression given in the value parameter
  • CUSTOM - the DynamicResourceHandler#checkPermission function, where the OPPOSITE of the Boolean resolved from the function is used to determine access

@Pattern can be used at the class or method level.

A note on inverted custom constraints

When using invert and CUSTOM, care must be taken to ensure the desired result is achieved. For example, consider an implementation of checkPermission where a subject is required and that subject must have a certain attribute; access is denied if there is no subject present OR that attribute is not present. When inverting the result, access would be allowed if the subject is not present OR that attribute is not present. This is because when invert is true, the boolean resolved from checkPermission is negated.

If you only mean to allow access if a subject is present but does not have the attribute, you will need to engage in some annoying double negation.

Just before checkPermission is invoked, the value of invert is stored in the arguments of the HTTP context using the ConfigKeys.PATTERN_INVERT key; you can use this to determine what to return.

In the following example, one of four things can happen: - A subject is present, and it satisfies the arbitrary test. - A subject is present, and it does not satisfy the arbitrary test - A subject is not present, and invert is true - A subject is not present, and invert is false - There is also a fallback assumption that invert is false if ConfigKeys.PATTERN_INVERT is not in the context; Deadbolt guarantees this will not happen, but it doesn’t hurt to make sure

Inverting the test, not the need to have a subject
 1 @Override
 2 public CompletionStage<Boolean> checkPermission(final String permissionValue,
 3                                                 final DeadboltHandler deadboltHandler,
 4                                                 final Http.Context ctx)
 5 {
 6     // just checking for zombies...just like I do every night before I go to bed
 7     return deadboltHandler.getSubject(ctx)
 8                           .thenApplyAsync(option -> 
 9         option.map(subject -> subject.getPermissions()
10                                      .stream()
11                                      .filter(perm -> perm.getValue().contains("zombie"))
12                                      .count() > 0)
13               .orElseGet(() -> (Boolean) ctx.args.getOrDefault(ConfigKeys.PATTERN_INVERT,
14                                                                false)),
15                                           ec.current());
16 }

So, in cases where we have a subject we just test like usual; the negation of the result, if required by invert, will be handled by Deadbolt. In cases where there is no subject, we return the value of invert itself - if it’s false, no negation will be internally applied, and if it’s true it will be negated to false and access denied. Thus, the test for perm.getValue().contains("zombie") is separated from the requirement to have a subject.

TL;DR Double negation sucks.

Parameter Type Default Notes
value String   The pattern value. Its context depends on the pattern type.
patternType PatternType EQUALITY Additional information passed into isAllowed.
content String ”” A hint to indicate the content expected in the response.
      This value will be passed to DeadboltHandler#onAccessFailure.
      The value of this parameter is completely arbitrary.
handlerKey String “defaultHandler” The name of a handler in the HandlerCache
deferred boolean false If true, the interceptor will not be applied until a
      DeadboltDeferred annotation is encountered.
invert boolean false Invert the result of the test.
Testing for equality of permissions
1 @Pattern("admin.printer")
2 public CompletionStage<Result> someMethodA()
3 {
4     // subject must have a permission with the exact value "admin.printer"
5 }
Testing for regex matching of permissions
1 @Pattern(value = "(.)*\.printer", patternType = PatternType.REGEX)
2 public CompletionStage<Result> someMethodB() 
3 {
4     // subject must have a permission that matches the regular expression (without quotes)\
5  "(.)*\.printer"
6 }
Using a user-defined test to determine access
1 @Pattern(value = "something arbitrary", patternType = PatternType.CUSTOM)
2 public CompletionStage<Result> someMethodC() 
3 {
4     // the checkPermssion method of the current handler's DynamicResourceHandler will be u\
5 sed.  This is a user-defined test
6 }
Inverting the test
1 @Pattern(value = "(.)*\.printer", patternType = PatternType.REGEX, invert = true)
2 public CompletionStage<Result> someMethodB() 
3 {
4     // subject must have no permissions that end in .printer
5 }

5.7 Unrestricted

Unrestricted allows you to over-ride more general constraints, i.e. if a controller is annotated with @SubjectPresent but you want an action in there to be accessible to everyone.

 1 @SubjectPresent
 2 public class MyController extends Controller
 3 {
 4     public CompletionStage<Result> foo()
 5     {
 6         // a subject must be present for this to be accessible
 7     }
 8         
 9     @Unrestricted
10     public CompletionStage<Result> bar()
11     {
12         // anyone can access this action
13     }
14 }

You can also flip this on its head, and use it to show that a controller is explicitly unrestricted - used in this way, it’s a marker of intent rather than something functional. Because method-level constraints are evaluated first, you can have still protected actions within an @Unrestricted controller.

 1 @Unrestricted
 2 public class MyController extends Controller
 3 {
 4     @SubjectPresent
 5     public CompletionStage<Result> foo()
 6     {
 7         // a subject must be present for this to be accessible
 8     }
 9         
10     public CompletionStage<Result> bar()
11     {
12         // anyone can access this action
13     }
14 }

@Unrestricted can be used at the class or method level.

Parameter Type Default Notes
content String ”” A hint to indicate the content expected in
      the response. This value will be passed
      to DeadboltHandler#onAccessFailure. The
      value of this parameter is completely arbitrary.
handlerKey String “defaultHandler” The name of a handler in the HandlerCache
deferred boolean false If true, the interceptor will not be applied
      until a DeadboltDeferred annotation is
      encountered.

5.8 Deferring method-level annotation-driven interceptors

Play executes method-level annotations before controller-level annotations. This can cause issues when, for example, you want a particular action to be applied for method and before the method annotations. A good example is Security.Authenticated(Secured.class), which sets a user’s user name for request().username(). Combining this with method-level annotations that require a user would fail, because the user would not be present at the time the method interceptor is invoked.

One way around this is to apply Security.Authenticated on every method, which violates DRY and causes bloat.

 1 public class DeferredController extends Controller
 2 {
 3     @Security.Authenticated(Secured.class)
 4     @Restrict(value = "admin")
 5     public CompletionStage<Result> someAdminFunction()
 6     {
 7         // logic
 8     }
 9 
10     @Security.Authenticated(Secured.class)
11     @Restrict(value = "editor")
12     public CompletionStage<Result> someEditorFunction()
13     {
14         // logic
15     }
16 }

A better way is to set the deferred parameter of the Deadbolt annotation to true, and then use @DeferredDeadbolt at the controller level to execute the method-level annotations at controller-annotation time. Since annotations are processed in order of declaration, you can specify @DeferredDeadbolt after @Security.Authenticated and so achieve the desired effect.

Deferring authorization checks
 1 @Security.Authenticated(Secured.class)
 2 @DeferredDeadbolt
 3 public class DeferredController extends Controller
 4 {
 5     @Restrict(value = "admin", deferred = true)
 6     public CompletionStage<Result> someAdminFunction()
 7     {
 8         // logic
 9     }
10 
11     @Restrict(value = "editor", deferred = true)
12     public CompletionStage<Result> someEditorFunction()
13     {
14         // logic
15     }
16 }

Specifying a controller-level restriction as deferred will work, if the annotation is higher in the annotation list than @DeferredDeadbolt annotation, but this is essentially pointless. If your constraint is already at the class level, there’s no need to defer it. Just ensure it appears below any other annotations you wish to have processed first.

5.9 Invoking DeadboltHandler#beforeAuthCheck independently

DeadboltHandler#beforeAuthCheck is invoked by each interceptor prior to running the actual constraint tests. If the method call returns an empty Optional, the constraint is applied, otherwise the Result contained in the Optional is returned. The same logic can be invoked independently, using the @BeforeAccess annotation, in which case the call to beforeRoleCheck itself becomes the constraint.

1 @BeforeAccess
2 public CompletionStage<Result> someFunction()
3 {
4     // logic
5 }

@BeforeAccess can be used at the class or method level.

Parameter Type Default Notes
handlerKey String “defaultHandler” The name of a handler in the HandlerCache
alwaysExecute boolean true By default, if another Deadbolt action has
      already been executed in the same request and has
      allowed access, beforeAuthCheck will not be
      executed again. Set this to true if you want it
      to execute unconditionally.
deferred boolean false If true, the interceptor will not be applied
      until a DeadboltDeferred annotation is
      encountered.

5.10 Customising the inputs of annotation-driven actions

One of the problems with Deadbolt’s annotations is they require strings to specify, for example, role names or pattern values. It would be far safer to use enums, but this is not possible for a module - it would completely kill the generic applicability of the annotations. If Deadbolt shipped with an enum containing roles, how would you extend it? You would be stuck with whatever was specified, or forced to fork the codebase and customise it. Similarly, annotations can neither implement interfaces or be extended.

To address this situation, Deadbolt has three constraints whose inputs can be customised to some degree. The trick lies, not with inheritence, but delegation and wrapping. The constraints are

  • Restrict
  • Dynamic
  • Pattern

Here, I’ll explain how to customise the Restrict constraint to use enums as annotation parameters, but the principle is the same for each constraint.

To start, create an enum that represents your roles.

Using an enum to define roles
 1 public enum MyRoles implements Role
 2 {
 3     foo,
 4     bar,
 5     hurdy;
 6 
 7     @Override
 8     public String getRoleName()
 9     {
10         return name();
11     }
12 }

To allow the AND/OR syntax that Restrict uses, another annotation to group them together is required.

1 @Retention(RetentionPolicy.RUNTIME)
2 @Documented
3 public @interface MyRolesGroup
4 {
5     MyRoles[] value();
6 }

Next, create a new annotation to drive your custom version of Restrict. Note that an array of MyRoles values can be placed in the annotation. The standard Restrict annotation is also present to provide further configuration. This means your customisations are minimised.

Defining a custom entry point
 1 @With(CustomRestrictAction.class)
 2 @Retention(RetentionPolicy.RUNTIME)
 3 @Target({ElementType.METHOD, ElementType.TYPE})
 4 @Documented
 5 @Inherited
 6 public @interface CustomRestrict
 7 {
 8     MyRolesGroup[] value();
 9 
10     Restrict config();
11 }

The code above contains @With(CustomRestrictAction.class) in order to specify the action that should be triggered by the annotation. This action can be implemented as follows.

Mapping custom roles to Deadbolt’s requirements
 1 @Override
 2 public CompletionStage<Result> call(Http.Context context) throws Throwable
 3 {
 4     final CustomRestrict outerConfig = configuration;
 5     final RestrictAction restrictAction = new RestrictAction(configuration.config(),
 6                                                              this.delegate)
 7     {
 8         @Override
 9         public List<String[]> getRoleGroups()
10         {
11             final List<String[]> roleGroups = new ArrayList<>();
12             for (MyRolesGroup mrg : outerConfig.value())
13             {
14                 final List<String> group = new ArrayList<>();
15                 for (MyRoles role : mrg.value())
16                 {
17                     group.add(role.getName());
18                 }
19                 roleGroups.add(group.toArray(group.size()));
20             }
21             return roleGroups;
22         }
23     };
24     return restrictAction.call(context);
25 }

To use your custom annotation, you apply it as you would any other Deadbolt annotation.

Using custom roles
1 @CustomRestrict(value = {MyRoles.foo, MyRoles.bar}, config = @Restrict(""))
2 public static CompletionStage<Result> customRestrictOne() 
3 {
4     return CompletableFuture.supplyAsync(accessOk::render,
5                                          ec.current())
6                             .thenApplyAsync(Results::ok,
7                                             ec.current());
8 }

Each customisable action has one or more extension points. These are

Action class Extension points
RestrictAction * List<String[]> getRoleGroups()
DynamicAction * String getValue()
  * String getMeta()
PatternAction * String getValue()

6. Deadbolt Java Templates

Deadbolt’s templates allow you to customise your Twirl templates. Unlike controller-level constraints, where a HTTP request is either allowed or denied, template constraints are applied during the rendering of the template and may occur multiple times within a single template. This also means that any logic inside the constrained content will not execute if authorization fails.

A subject is required for the content to be rendered
1 @subjectPresent {
2     <!-- paragraphs will not be rendered, satellites will not be repositioned -->
3     <!-- and light speed will not be engaged if no subject is present -->
4     <p>Let's see what this thing can do...</p>
5     @repositionSatellite
6     @engageLightSpeed
7 }

This is not a client-side DOM manipulation!

Consider a menu that needs to change based on both the presence and security level of a user.

An unconstrained menu
1 Log in
2 Log out
3 Create account
4 My account
5 News
6 Explore
7 Administer something

Clearly, there are mutually exclusive items in this menu - having both “Log out” and “Log in” is strange, as is “My account” if a subject is not present. “Administer something” should only be visible to administrators. Using Deadbolt’s template constraints, the menu can customised to reflect a more logical arrangement, and is shown here in pseudo-code.

An unconstrained menu
 1 if subject is present
 2   My account
 3   Log out
 4 else
 5   Create account
 6   Log in
 7 
 8 News
 9 Explore
10 if subject is an administrator
11   Administer something
  • The menu for an unknown user will contain “Create account”, “Log in”, “News” and “Explore”.
  • The menu for a regular known user will contain “My account”, “Log out”, “News” and “Explore”
  • An administrator would see “My account”, “Log out”, “News”, “Explore” and “Administer something”

Handlers

Template constraints use the default Deadbolt handler, as obtained via HandlerCache#get() but as with controller constraints you can pass in a specific handler. The cleanest way to do this is to pass the handler into the template and then pass it into the constraints.

Fallback content

Each constraint has an xOr variant, which allows you to render content in place of the unauthorized content. This takes the form <constraint>Or, for example subjectPresentOr

Providing fallback content when a subject is not present
1 @subjectPresentOr {
2     <button>Log out</button>
3 } {
4     <button>Create an accoun</button>
5     <button>Log in</button>
6 }

In each case, the fallback content is defined as a second Content block following the primary body.

Timeouts

Because templates use blocking calls when rendering, the promises returned from the Deadbolt handler, etc, need to be completed during the rendering process. A timeout, with a default value of 1000ms, is used to wait for the completion but you may want to change this. You can do this in two ways.

Set a global timeout

If you want to change the default timeout, define deadbolt.java.view-timeout in your configuration and give it a millisecond value, e.g.

Define timeouts in milliseconds
1 deadbolt {
2   java {
3     view-timeout=1500
4   }
5 }
Use a supplier to provide a timeout

All Deadbolt templates have a timeout parameter which defaults to returning the app-wide value - 1000L if nothing else if defined, otherwise whatever deadbolt.java.view-timeout is set to. But - and here’s the nice part - the timeout parameter is not a Long but rather a Supplier<Long>. This means you can use a timeout that fluctuates based on some metric - say, the number of timeouts that occur during template rendering.

How do I know if timeouts are occurring?

That’s a good question. And the answer is - you need to implement be.objectify.deadbolt.java.TemplateFailureListener. When timeouts occur, the listener is invoked with the timeout and the exception that occurred. The template listener should be exposed via a module - see “Expose your DeadboltHandlers with a HandlerCache” section in chapter 4 for more details on this. If you re-use that chapter 4 module, the binding will look something like this.

Declaring a template failure listener
1 public Seq<Binding<?>> bindings(final Environment environment,
2                                 final Configuration configuration) {
3     return seq(bind(HandlerCache.class).to(MyHandlerCache.class).in(Singleton.class),
4                bind(TemplateFailureListener.class).to(MyTemplateFailureListener.class).in(\
5 Singleton.class));
6 }

Making it a singleton allows you to keep a running count of the failure level; if you’re using it for other purposes, then scope it accordingly.

6.1 subjectPresent

Sometimes, you don’t need fine-grained checked - you just need to see if there is a user present.

  • be.objectify.deadbolt.java.views.html.subjectPresent
  • be.objectify.deadbolt.java.views.html.subjectPresentOr
Parameter Type Default Notes
handler DeadboltHandler handlerCache.get() The handler to use to apply the constraint.
timeout () -> Long A supplier returning The timeout applied to blocking calls.
    deadbolt.java.view-timeout  
    if it’s defined, otherwise  
    1000L  

Examples

Using the default Deadbolt handler
 1 @import be.objectify.deadbolt.java.views.html.{subjectPresent, subjectPresentOr}
 2 
 3 @subjectPresent() {
 4    This content will be present if handler#getSubject results in a non-empty Optional
 5 }
 6 
 7 @subjectPresentOr() {
 8   This content will be present if handler#getSubject results in a non-empty Optional
 9 } {
10   This content will be present if handler#getSubject results in an empty Optional
11 }
Using a specific Deadbolt handler
 1 @(handler: be.objectify.deadbolt.java.DeadboltHandler)
 2 @import be.objectify.deadbolt.java.views.html.{subjectPresent, subjectPresentOr}
 3 
 4 @subjectPresent(handler = handler) {
 5     This content will be present if handler#getSubject results in a non-empty Optional
 6 }
 7 
 8 @subjectPresentOr(handler = handler) {
 9   This content will be present if handler#getSubject results in a non-empty Optional
10 } {
11   This content will be present if handler#getSubject results in an empty Optional
12 }

6.2 subjectNotPresent

Just like subjectPresent requires a subject be present, subjectNotPresent requires no subject to be present.

  • be.objectify.deadbolt.java.views.html.subjectNotPresent
  • be.objectify.deadbolt.java.views.html.subjectNotPresentOr
Parameter Type Default Notes
handler DeadboltHandler handlerCache.get() The handler to use to apply the constraint.
timeout () -> Long A supplier returning The timeout applied to blocking calls.
    deadbolt.java.view-timeout  
    if it’s defined, otherwise  
    1000L  

Examples

Using the default Deadbolt handler
 1 @import be.objectify.deadbolt.java.views.html.{subjectNotPresent, subjectNotPresentOr}
 2 
 3 @subjectNotPresent() {
 4   This content will be present if handler#getSubject results in an empty Optional
 5 }
 6 
 7 @subjectNotPresentOr() {
 8   This content will be present if handler#getSubject results in an empty Optional
 9 } {
10   This content will be present if handler#getSubject results in a non-empty Optional
11 }
Using a specific Deadbolt handler
 1 @(handler: be.objectify.deadbolt.java.DeadboltHandler)
 2 @import be.objectify.deadbolt.java.views.html.{subjectNotPresent, subjectNotPresentOr}
 3 
 4 @subjectNotPresent(handler = handler) {
 5   This content will be present if handler#getSubject results in an empty Optional
 6 }
 7 
 8 @subjectNotPresentOr(handler = handler) {
 9   This content will be present if handler#getSubject results in an empty Optional
10 } {
11   This content will be present if handler#getSubject results in a non-empty Optional
12 }

6.3 Restrict

Use Subjects Roles to perform AND/OR/NOT checks. The values given to the builder must match the Role.name of the subject’s roles.

  • be.objectify.deadbolt.java.views.html.restrict
  • be.objectify.deadbolt.java.views.html.restrictOr

AND is defined as an Array[String], OR is a List[Array[String]], and NOT is a rolename with a ! preceding it.

anyOf and allOf are convenience methods for creating a List[Array[String]] and an Array[String]. There is also allOfGroup, which allows anyOf(allOf("foo")) to be written as allOfGroup("foo"). They can be imported using @import be.objectify.deadbolt.core.utils.TemplateUtils.{anyOf, allOf, allOfGroup}.

Parameter Type Default Notes
handler DeadboltHandler handlerCache.get() The handler to use to apply the constraint.
roles List[Array[String]]   The AND/OR/NOT restrictions. One array defines
      an AND, multiple arrays define OR. See notes
      on anyOf and allOf above.
timeout () -> Long A supplier returning The timeout applied to blocking calls.
    deadbolt.java.view-timeout  
    if it’s defined, otherwise  
    1000L  

Examples

The subject is obtained from the default handler, and must have the foo role
1 @import be.objectify.deadbolt.java.views.html.restrict
2 
3 @restrict(roles = allOfGroup("foo")) {
4   Subject requires the foo role for this to be visible
5 }
The subject is obtained from the default handler, and must have the foo AND bar role
1 @import be.objectify.deadbolt.java.views.html.restrict
2 
3 @restrict(roles = allOfGroup("foo", "bar")) {
4   Subject requires the foo AND bar roles for this to be visible
5 }
The subject is obtained from the default handler, and must have the foo OR bar role
1 @import be.objectify.deadbolt.java.views.html.restrict
2 
3 @restrict(roles = anyOf(allOf("foo"), allOf("bar", "hurdy"))) {
4   Subject requires the foo OR (bar and hurdy) roles for this to be visible
5 }
Providing fallback content
1 @import be.objectify.deadbolt.java.views.html.restrictOr
2 
3 @restrictOr(roles = allOfGroup("foo", "bar")) {
4   Subject requires the foo AND bar roles for this to be visible
5 } {
6   Subject does not have the necessary roles
7 }
Getting the subject via a specific Deadbolt handler
1 @(handler: be.objectify.deadbolt.java.DeadboltHandler)
2 @import be.objectify.deadbolt.java.views.html.restrict
3 
4 @restrict(roles = allOfGroup("foo"), handler = handler) {
5   Subject requires the foo role for this to be visible
6 }

6.4 Pattern

Use the Subjects Permissions to perform a variety of checks. This may be a regular expression match, an exact match or some custom constraint using DynamicResourceHandler#checkPermission.

  • be.objectify.deadbolt.java.views.html.pattern
  • be.objectify.deadbolt.java.views.html.patternOr
Parameter Type Default Notes
handler DeadboltHandler handlerCache.get() The handler to use to apply the constraint.
value String   The value of the pattern, e.g. a regex or a
      precise match.
patternType PatternType PatternType.EQUALITY  
       
invert Boolean false If true, do NOT render content if the subject’s
      permissions satisfy the pattern.
timeout () -> Long A supplier returning The timeout applied to blocking calls.
    deadbolt.java.view-timeout  
    if it’s defined, otherwise  
    1000L  

Examples

The subject must have a permission with the exact value ‘admin.printer’
1 @import be.objectify.deadbolt.java.views.html.pattern
2 
3 @pattern(value = "admin.printer") {
4   Subject must have a permission with the exact value "admin.printer" for this to be visib\
5 le
6 }
The subject must have a permission that matches the specified regular expression
1 @import be.objectify.deadbolt.java.views.html.pattern
2 
3 @pattern(value = "(.)*\.printer", patternType = PatternType.REGEX) {
4   Subject must have a permission that matches the regular expression (.)*\.printer for thi\
5 s to be visible
6 }
A custom test is applied to determine access
1 @import be.objectify.deadbolt.java.views.html.pattern
2 
3 @pattern(value = "something arbitrary", patternType = PatternType.CUSTOM) {
4   DynamicResourceHandler#checkPermission must result in true for this to be visible
5 }
Providing fallback content
1 @import be.objectify.deadbolt.java.views.html.patternOr
2 
3 @patternOr(value = "something arbitrary", patternType = PatternType.CUSTOM) {
4   DynamicResourceHandler#checkPermission must result in true for this to be visible
5 } {
6   Tough luck
7 }
Using a specific Deadbolt handler
1 @(handler: be.objectify.deadbolt.java.DeadboltHandler)
2 @import be.objectify.deadbolt.java.views.html.pattern
3 
4 @pattern(handler = handler, value = "(.)*\.printer", patternType = PatternType.REGEX) {
5   Subject must have a permission that matches the regular expression (.)*\.printer for thi\
6 s to be visible
7 }
Inverting the constraint
1 @import be.objectify.deadbolt.java.views.html.pattern
2 
3 @pattern(value = "(.)*\.printer", patternType = PatternType.REGEX, invert = true) {
4   Subject must have no permissions that end with '.printer'
5 }

6.5 Dynamic

The most flexible constraint - this is a completely user-defined constraint that uses DynamicResourceHandler#isAllowed to determine access.

  • be.objectify.deadbolt.java.views.html.dynamic
  • be.objectify.deadbolt.java.views.html.dynamicOr
Parameter Type Default Notes
handler DeadboltHandler handlerCache.get() The handler to use to apply the constraint.
name String   The name of the constraint, passed into the
      DynamicResourceHandler.
meta String null  
       
timeout () -> Long A supplier returning The timeout applied to blocking calls.
    deadbolt.java.view-timeout  
    if it’s defined, otherwise  
    1000L  

Examples

The DynamicResourceHandler is obtained from the default handler and is used to apply a named constraint to the content
1 @import be.objectify.deadbolt.java.views.html.dynamic
2 
3 @dynamic(name = "someName") {
4   DynamicResourceHandler#isAllowed must result in true for this to be visible
5 }
Providing meta data to the constraint
1 @import be.objectify.deadbolt.java.views.html.dynamic
2 
3 @dynamic(name = "someName", meta = "foo") {
4   DynamicResourceHandler#isAllowed must result in true for this to be visible
5 }
Meta data does not have to be hard-coded
1 @(someMetaValue: String)
2 @import be.objectify.deadbolt.java.views.html.dynamic
3 
4 @dynamic(name = "someName", meta = someMetaValue) {
5   DynamicResourceHandler#isAllowed must result in true for this to be visible
6 }
Providing fallback content
1 @import be.objectify.deadbolt.java.views.html.dynamicOr
2 
3 @dynamicOr(name = "someName") {
4   DynamicResourceHandler#isAllowed must result in true for this to be visible
5 } {
6   Better luck next time
7 }
Using a specific Deadbolt handler
1 @(handler: be.objectify.deadbolt.java.DeadboltHandler)
2 @import be.objectify.deadbolt.java.views.html.dynamic
3 
4 @dynamic(handler = handler, name = "someName") {
5   DynamicResourceHandler#isAllowed must result in true for this to be visible
6 }

7. A deeper look at dynamic rules

The @Dynamic annotation and @dynamic template tag allow you to specify arbitrary rules to control access. For example:

  • Simple (contrived) examples
    • Allow access if today is Tuesday
    • Allow access if a user has a date of birth defined
  • Some more complex examples
    • Allow access if the user is a beta tester AND the controller/action/template area is available for beta testing
    • Allow a maximum of 300 requests per hour per API key
  • Blacklisting
    • Deny access only if user registered within the last 30 days

7.1 Using sessions and requests in your rules

In order to make these decisions, information is helpful. You may need access to the session, or the current request. Both are available from the Http.Context object passed into your DynamicResourceHandler (DRH) implementations.

Sessions

In Play, sessions implement the java.util.Map interface and so data can get accessed in the usual way, i.e. session.get(key).

 1 public class MyDynamicResourceHandler implements DynamicResourceHandler
 2 {
 3     public F.Promise<Boolean> isAllowed(final String name,
 4                                         final String meta,
 5                                         final DeadboltHandler deadboltHandler,
 6                                         final Http.Context ctx)
 7     {
 8 	    return F.Promise.promise(() -> ctx.session())
 9 	                    .map(session -> // and so on
10 	    ...
11     }
12 							 
13 
14     public boolean checkPermission(String permissionValue,
15                                    DeadboltHandler deadboltHandler,
16                                    Http.Context ctx)
17     {
18 	    return F.Promise.promise(() -> ctx.session())
19 	                    .map(session -> // and so on
20 	    ...
21     }
22 }

You can also store write data into the session - just don’t forget that sessions are stored as client-side cookies! Any and all confidential information should be kept out of sessions.

Request query parameters

Using the Http.Request#queryString() method, you can get a map of all query parameters for the current request. For example, for the URL

1 http://localhost:9000/users?userName=foo

a call to queryString() would result in a map containing the key userName mapped to the value foo.

 1 public class MyDynamicResourceHandler implements DynamicResourceHandler
 2 {
 3     public F.Promise<Boolean> isAllowed(final String name,
 4                                         final String meta,
 5                                         final DeadboltHandler deadboltHandler,
 6                                         final Http.Context ctx)
 7     {
 8 	    return F.Promise.promise(() -> ctx.request())
 9 	                    .map(request -> request.queryString())
10 	                    .map((Map<String, String> queryStrings) -> // and so on
11 		...
12     }
13 							 
14 
15     public F.Promise<Boolean> checkPermission(final String permissionValue,
16                                               final DeadboltHandler deadboltHandler,
17                                               final Http.Context ctx)
18     {
19 	    return F.Promise.promise(() -> ctx.request())
20 	                    .map(request -> request.queryString())
21 	                    .map((Map<String, String> queryStrings) -> // and so on
22 		...
23     }
24 }

A major problem here is that your DRH needs knowledge of the methods - or, more precisely, the parameters of the method - to which it is applied. This can be alleviated somewhat through the use of convention, or by passing metadata into the DRH. Both will be covered later in this chapter.

Request bodies

When using a HTTP POST or PUT operation, the request body will typically contain information. Bodies can be accessed in a variety of formats, including custom formats.

 1 public class MyDynamicResourceHandler implements DynamicResourceHandler
 2 {
 3     public F.Promise<Boolean> isAllowed(final String name,
 4                                         final String meta,
 5                                         final DeadboltHandler deadboltHandler,
 6                                         final Http.Context ctx)
 7     {
 8         return F.Promise.promise(() -> ctx.request().body())
 9                         .map(body -> body.asJson())
10                         .map( // and so on
11 		...
12     }
13 							 
14 
15     public F.Promise<Boolean> checkPermission(final String permissionValue,
16                                               final DeadboltHandler deadboltHandler,
17                                               final Http.Context ctx)
18     {
19         return F.Promise.promise(() -> ctx.request().body())
20                         .map(body -> body.as(MyCustomType.class))
21                         .map(myCustomType -> // and so on
22 		...
23     }
24 }

Once you have obtained the information from the body, you’re back in the decision-making process of your rule.

Request path parameters

Path parameters are, unfortunately, the point at which we hit a problem. Play encourages developers to create clean, RESTful URLs, for example

1 http://localhost:9000/users/foo

in place of the query parameter example used earlier,

1 http://localhost:9000/users?userName=foo

The benefits of clean URLs are many - they can be bookmarked, are easily shared and are more easily cached, to name just a few. The one thing that path parameters are not, in Play, is available at runtime. There is no way in Play to get these parameters without deriving them manually by comparing the route definition with the actual URL.

It has been stated on the Play Framework group that path parameters should not be accessible by calling Request#queryStrings(), because path parameters are not query parameters. This is correct, but doesn’t really help in the real world - things will be much easier when path parameters can be accessed as easily as other request information.

7.2 Strategies for using dynamic resource handlers

To the best of my knowledge, there are two ways in which to use dynamic resource handlers in Deadbolt:

  1. Use a single DynamicResourceHandler that deals with all dynamic security
  2. Use a single DynamicResourceHandler as a façade

1. Use a single, potentially huge DRH

A friend of mine, sitting in a second-year university course discussion on object-oriented design, witnessed a student put up his hand and say, “I don’t get it. Why don’t we just put everything in one big class?”. If you would ask a similar question, this is the approach for you. For the rest of us, I think we can all appreciate that any DRH dealing with more than a couple of separate dynamic restrictions would get very large, very quickly.

2. Use a single DRH façade that dispatches to other DRHs

If you have a single DRH acting as a façade, you can aggregate or compose logic into discrete chunks. Furthermore, by extending AbstractDynamicResourceHandler, you can create specific handlers for individual methods in the façade, i.e. isAllowed-specific handlers and checkPermission-specific ones.

 1 public class FacadeDRH implements DynamicResourceHandler
 2 {
 3     private final Map<String, DynamicResourceHandler> allowed = new HashMap<>();
 4     private final Map<String, DynamicResourceHandler> permitted = new HashMap<>();
 5 
 6     public FacadeDRH()
 7     {
 8         // populate the maps, either through new instance creation, constructor parameters\
 9 , etc
10     }
11 
12     public F.Promise<Boolean> isAllowed(final String name,
13                                         final String meta,
14                                         final DeadboltHandler handler,
15                                         final Http.Context ctx)
16     {
17         return allowed.get(name).isAllowed(name,
18                                            meta,
19                                            handler,
20                                            ctx);
21     }
22 
23 
24     public F.Promise<Boolean> checkPermission(final String permissionValue,
25                                               final DeadboltHandler handler,
26                                               final Http.Context ctx)
27     {
28         return permitted.get(permissionValue).checkPermission(permissionValue,
29                                                               handler,
30                                                               ctx);
31     }
32 }

8. Integrating with authentication providers

Authorization is all well and good, but some constraints are pointless if the user is not known to the application. You could write a Dynamic constraint that only allows access on Thursdays - this doesn’t need to know anything about even the concept of a user; a Restrict constraint, on the other hand, uses Roles obtained from a Subject. The question is, what is a subject and how do we know who it is?

Imagine an application where you can post short messages and read the messages of others, something along the lines of Twitter. By default, you can read any message on the system unless the user has marked that message as restricted to only users who are logged into the application.. In order to write a message, you have to have an account and be logged in. We can sketch out a controller for this very simple application thus:

A controller with Deadbolt constraints
 1 package be.objectify.messages;
 2 
 3 import javax.inject.Inject;
 4 import play.libs.F;
 5 import play.libs.Json;
 6 import play.mvc.Controller;
 7 import play.mvc.Result;
 8 import be.objectify.deadbolt.java.actions.SubjectPresent;
 9 
10 public class Messages extends Controller {
11 
12     private final MessageDao messageDao;
13 
14     @Inject
15     public MessagesController(final MessageDao messageDao) {
16         this.messageDao = messageDao;
17     }
18 
19     public F.Promise<Result> getPublicMessages() {
20         return F.Promise.promise(messageDao::getAllPublic)
21                         .map(Json::toJson)
22                         .map(Results::ok);
23     }
24 
25     @SubjectPresent
26     public F.Promise<Result> getAllMessages() {
27         return F.Promise.promise(messageDao::getAll)
28                         .map(Json::toJson)
29                         .map(Results::ok);
30     }
31 
32     @SubjectPresent
33     public F.Promise<Result> createMessage() {
34         return F.Promise.promise(() -> body.asJson())
35                         .map(json -> Json.fromJson(json, Message.class))
36                         .map(messageDao::save)
37                         .map(Results::ok);
38     }
39 }

This very simple app has a very simple routes file to go with it:

The application routes
1 GET    /messages/public   be.objectify.messages.Messages.getPublicMessages()
2 GET    /messages          be.objectify.messages.Messages.getAllMessages()
3 POST   /create            be.objectify.messages.Messages.createMessage()

An authenticated user can access all three of these routes and obtain a successful result. An unauthenticated user would hit a brick wall when accessing /messages or /create - specifically, they would run into whatever behaviour was specified in the onAuthFailure method of the current DeadboltHandler.

This is a good time to review the difference between the HTTP status codes 401 Unauthorized and 403 Forbidden. A 401 means you don’t have access at the moment, but you should try again after authenticating. A 403 means the subject cannot access the resource with their current authorization rights, and re-authenticating will not solve the problem - in fact, the specification explicitly states that you shouldn’t even attempt to re-authenticate. A well-behaved application should respect the difference between the two.

We can consider the onAuthFailure method to be the Deadbolt equivalent of a brick wall. For a DeadboltHandler used by a RESTful controller, the status code should be enough to indicate the problem. If you have an application that uses server-side rendering, you may well want to return content in the body of the response. The end result is the same though - You Can’t Do That. Note the return type, a Promise containing a Result and not an Optional<Result> - access has very definitely failed at this point, and it needs to be dealt with.

Handling access failure
1 public F.Promise<Result> onAuthFailure(final Http.Context context,
2                                        final String content) {
3     return F.Promise.promise(Results::forbidden);
4 }

Unlike onAuthFailure, the beforeAuthCheck method allows for the possibility that everything is fine. It’s perfectly reasonable to return an empty option from this method, indicating that no action should be taken and the request should continue unimpeded.

Doing nothing before a constraint is applied
1 public F.Promise<Optional<Result>> beforeAuthCheck(final Http.Context context) {
2     return F.Promise.promise(Optional::empty);
3 }

This has the net effect of not getting in the way of the constraint; with this implementation, a user accessing /messages in our little application would receive a 403. But, our application aims to be well behaved and return a 401 when it’s needed, so a little more work is required. Because beforeAuthCheck is only called when a constraint has been applied, we can use this to trigger an authenication request if needed. In this application, we’re going to say that every constraint requires an authenticated subject to be present - do not confuse this with @SubjectPresent constraints in the example controller, the same would equally be true if we were using @Restrict or @Pattern. For the more advanced app that uses the subject-less dynamic rule of Thursdays-only, either more logic is required or (preferably) a different DeadboltHandler implementation is used.

The logic here is simple - if a user is present, no action is required otherwise short-cut the request with a 401 response.

Requiring a subject
1 public F.Promise<Optional<Result>> beforeAuthCheck(final Http.Context context) {
2     return getSubject(context).map(maybeSubject -> 
3         maybeSubject.map(subject -> Optional.<Result>empty())
4                     .orElseGet(() -> Optional.of(Results.unauthorized())));
5 }

On the other hand, you may choose to never implement any logic in beforeAuthCheck and instead have the behaviour driven by authorization failure. The choice is entirely in the hands of the implementor; personally, I tend to use onAuthFailure to handle the 401/403 behaviour, because it removes the assumptions required by implementing checks in beforeAuthCheck.

It will become very clear, very quickly, the same approach is used for all authentication systems; this means that swapping out authentication without affecting authorization is both possible and trivial. We’ll start with the built-in authentication mechanism of Play and adapt from there; with surprisingly few changes, you’ll see how we can move from basic authentication through to using anything from Play-specific OAuth libraries such as Play Authenticate and even HTTP calls to dedicated identity platforms such as Auth0.

8.1 Play’s built-in authentication support

Play ships with a very simple interceptor that requires an authenticated user to be present; there’s no concept of authorization. It uses an annotation-driven approach similar to that of Deadbolt, allowing you to annotate either entire controllers at the class level, or individual methods within a controller.

Using Play’s authentication support
1 @Security.Authenticated
2 public F.Promise<Result> getAllMessages() {
3     return F.Promise.promise(messageDao::getAll)
4                     .map(Json::toJson)
5                     .map(Results::ok);
6 }

By default, the Security.Authenticated annotation will trigger an interceptor that uses Security.Authenticator to look in the session for a value mapped to "username" - if the value is non-null, the user is considered to be authenticated. If you want to customize how the user identification string is obtained, you can extend Security.Authenticator implement your own solution.

Customizing Play’s authentication support
 1 package be.objectify.messages.security;
 2 
 3 import java.util.Optional;
 4 import javax.inject.Inject;
 5 import be.objectify.messages.dao.UserDao;
 6 import be.objectify.messages.models.User;
 7 import play.mvc.Http;
 8 import play.mvc.Security;
 9 
10 public class AuthenticationSupport extends Security.Authenticator {
11 
12     private final UserDao userDao;
13 
14     @Inject
15     public AuthenticationSupport(final UserDao userDao) {
16         this.userDao = userDao;
17     }
18     
19     @Override
20     public String getUsername(final Http.Context context) {
21         return getTokenFromHeader(context).flatMap(userDao::findByToken)
22                                           .map(User::getIdentifier)
23                                           .orElse(null);
24     }
25 
26     private Optional<String> getTokenFromHeader(final Http.Context context) {
27         return Optional.ofNullable(context.request().headers().get("X-AUTH-TOKEN"))
28                        .filter(arr -> arr.length == 1)
29                        .filter(arr -> arr[0] != null)
30                        .map(arr -> arr[0]);
31     }
32 }

The class of this customized implementation can then be passed to the annotation with @Security.Authenticated(AuthenticationSupport.class). However, all mention of Deadbolt’s constraints have vanished and so we’ve replaced a fine-grained authorization system with a coarse-grained authentication-only system. To fix this, we need to revert back to using Deadbolt in the controller and move AuthenticationSupport (or even the basic Security.Authenticator) integration into the DeadboltHandler.

Integrating Play’s authentication with Deadbolt
 1 package be.objectify.messages.security;
 2 
 3 import java.util.Optional;
 4 import javax.inject.Inject;
 5 import be.objectify.deadbolt.core.models.Subject;
 6 import be.objectify.deadbolt.java.AbstractDeadboltHandler;
 7 import be.objectify.messages.dao.UserDao;
 8 import play.libs.F;
 9 import play.mvc.Http;
10 import play.mvc.Result;
11 import play.mvc.Results;
12 
13 public class MyDeadboltHandler extends AbstractDeadboltHandler {
14 
15     private final AuthenticationSupport authenticator;
16     private final UserDao userDao;
17 
18     @Inject
19     public MyDeadboltHandler(final AuthenticationSupport authenticator,
20                              final UserDao userDao) {
21         this.authenticator = authenticator;
22         this.userDao = userDao;
23     }
24 
25     @Override
26     public F.Promise<Optional<Result>> beforeAuthCheck(final Http.Context context) {
27         return getSubject(context).map(maybeSubject -> 
28             maybeSubject.map(subject -> Optional.<Result>empty())
29                         .orElseGet(() -> Optional.of(Results.unauthorized())));
30     }
31 
32     @Override
33     public F.Promise<Optional<Subject>> getSubject(final Http.Context context) {
34         return F.Promise.promise(() -> 
35             Optional.ofNullable(authenticator.getUsername(context))
36                     .flatMap(userDao::findByUserName)
37                     .map(user -> user));
38     }
39 
40     @Override
41     public F.Promise<Result> onAuthFailure(final Http.Context context,
42                                            final String content) {
43         // you could also use the behaviour of the authenticator, e.g.
44         // return F.Promise.promise(() -> authenticator.onUnauthorized(context));
45         return F.Promise.promise(() -> forbidden("You can't do that"));
46     }
47 }

There are three things going on here, only one of which is explicitly tied into the authentication system, and that is getSubject. Of the other two methods, onAuthFailure gives an arbitrary (but hopefully meaningful) response and beforeAuthCheck is essentially generic code. There is scope here for performance improvements and will depend on your specific implementations; for example, the user retrieved from the database by the authenticator can be stored in context.args for re-use by the Deadbolt handler.

Per-request caching of the user
 1 public class AuthenticationSupport extends Security.Authenticator {
 2 
 3     // other methods
 4 
 5     @Override
 6     public String getUsername(final Http.Context context) {
 7         final Optional<User> maybeUser = getTokenFromHeader(context).flatMap(userDao::find\
 8 ByToken);
 9         return maybeUser.map(user -> {
10             context.args.put("user", maybeUser);
11             return user.getIdentifier();
12         }).orElse(null);
13     }
14 }
15 
16 public class MyDeadboltHandler extends AbstractDeadboltHandler {
17 
18     // other methods
19 
20     @Override
21     public F.Promise<Optional<Subject>> getSubject(final Http.Context context) {
22         return F.Promise.promise(() -> (Optional<Subject>)context.args.computeIfAbsent(
23                 "user",
24                 key -> {
25                     final String userName = authenticator.getUsername(context);
26                     return userDao.findByUserName(userName);
27                 }));
28     }
29 }

8.2 Third-party user management

I like the concept of third-party user (or identity) management. It minimizes sensitive data held locally, provides features like multifactor authentication and provides a unified API for multiple authentication sources. This last feature makes it very easy to create a simple integration point with Deadbolt, driving interaction with the user management on a per-event basis (with a little caching thrown in). The sequence for this looks a little complicated, but it can be broken down into 4 distinct steps:

  • the initial authentication
  • subsequent use of cached user details
  • re-retrieving user details when the cache doen’t contain them
  • re-authenticating when the user’s authentication has expired on the user management platform

If you’re using Deadbolt, it’s reasonable to assume you have one of two security models - either all actions require authorization or some actions require authorization, and that authorization may simply be “a user must be present”. As a result, the point the initial authentication occurs depends on your application. The good news, as far as this requirement goes, is the implementation is both quite simple and common to all cases. It comes down to the implementation of the onAuthFailure method of your DeadboltHandler, and might look something like this:

Triggering authentication when authorization fails - naive version
1 public F.Promise<Result> onAuthFailure(final Http.Context context,
2                                        final String contentType) {
3     return F.Promise.promise(login::render)
4                     .map(Results::unauthorized);
5 }

But wait, this is wrong! As discussed above, this assumes that all authorization failure occurs because there is no user present, and this ignores the difference between 401 Unauthorized and 403 Forbidden. A better implementation wiil take this into account, by checking if there is a subject present. If there is a subject present, it’s a 403; if there isn’t, it’s a 401 and we can redirect to somewhere the user can log in.

Triggering authentication when authorization fails
1 public F.Promise<Result> onAuthFailure(final Http.Context context,
2                                        final String contentType) {
3     return getSubject(context).map(maybeSubject -> 
4         maybeSubject.map(subject -> 
5             Optional.of((User)subject))
6                     .map(denied::render)
7                     .orElseGet(() -> login.render()))
8                     .map(Results::unauthorized);
9 }

There’s still a problem here - while the rendered output observes the difference between unauthorized and forbidden, but the HTTP status code is hard-wired to a 401. One more tweak should fix this.

Synchronizing the content and HTTP status code
 1 @Override
 2 public F.Promise<Result> onAuthFailure(final Http.Context context,
 3                                        final String s) {
 4     return getSubject(context)
 5             .map(maybeSubject ->
 6                          maybeSubject.map(subject -> 
 7                                               Optional.of((User)subject))
 8                                      .map(user -> 
 9                                               new F.Tuple<>(true,
10                                                             denied.render(user)))
11                                      .orElseGet(() -> 
12                                               new F.Tuple<>(false,
13                                                             login.render(clientId,
14                                                                          domain,
15                                                                          redirectUri))))
16             .map(subjectPresentAndContent -> 
17                      subjectPresentAndContent._1
18                          ? Results.forbidden(subjectPresentAndContent._2)
19                          : Results.unauthorized(subjectPresentAndContent._2));
20 }

Integrating with Auth0

Auth0 is a great identity management platform, and I’m not writing that just because they gave me a t-shirt. One of the nice features they offer is a whole bunch of code you can pretty much drop into your application, including code for a Play 2 Scala controller. Since we’re in the Java portion of the book, and the Java example provided by Auth0 uses the JEE Servlet API, I’ve rewritten the Scala version for Java. This was the only customization required, which I have to say was pretty impressive - the total time to integrate and have a working solution was less than 15 minutes.

There are three core elements to the solution. These are, in no particular order,

  • a log-in page
  • a controller to receive callbacks from Auth0
  • a DeadboltHandler implementation

I’ve also added a small utility class called AuthSupport to help with the cache usage, which also makes testing easier, but this contains code that could happily live in the controller.

A working example for this section can be found at auth0-integration. To run it, you will need to create an application on Auth0 and fill in the client ID, client secret, etc, into conf/application.conf. For the redirect URI, you can use http://localhost:9000/callback - don’t forget to adjust the port if necessary.

AuthSupport

This class has two simple function - it standardises the key used for caching the subject, and it wraps the cache result in an Optional.

Integrating the authentication flow
 1 package be.objectify.examples.auth0.security;
 2 
 3 import java.util.Optional;
 4 import javax.inject.Inject;
 5 import javax.inject.Singleton;
 6 import be.objectify.deadbolt.core.models.Subject;
 7 import be.objectify.examples.auth0.models.User;
 8 import play.cache.CacheApi;
 9 import play.mvc.Http;
10 
11 /**
12  * Utility methods for user caching.
13  *
14  * @author Steve Chaloner (steve@objectify.be)
15  */
16 @Singleton
17 public class AuthSupport {
18 
19     private final CacheApi cache;
20 
21     @Inject
22     public AuthSupport(final CacheApi cache) {
23         this.cache = cache;
24     }
25 
26     public Optional<User> currentUser(final Http.Context context)
27     {
28         return Optional.ofNullable(cache.get(cacheKey(context.session().get("idToken"))));
29     }
30 
31     public String cacheKey(final String key)
32     {
33         return "user.cache." + key;
34     }
35 }
The log-in page

In order to log in, Auth0 provide a JavaScript solution that customises the form based on your configuration options; for example, an app registered in Auth0 for username/password support plus a couple of OAuth providers will receive a form that reflects those choices. The simplest possible implementation of a log-in page, without concern for appearance, is as follows.

An Auth0 log-in form
 1 @(clientId: String, domain: String, redirectUri: String)
 2 
 3 <!DOCTYPE html>
 4 <html lang="en">
 5     <body>
 6         <div id="root">
 7             Log-in area
 8         </div>
 9         <script src="https://cdn.auth0.com/js/lock-7.12.min.js"></script>
10         <script>
11             var lock = new Auth0Lock('@clientId', '@domain');
12             lock.show({
13                 container: 'root',
14                 callbackURL: '@redirectUri',
15                 responseType: 'code',
16                 authParams: { scope: 'openid profile' }
17             });
18         </script>
19     </body>
20 </html>

In the browser, you now have a completely functional log-in form that will trigger the authentication flow. Once the form is submitted, Auth0 takes over until the authentication requirements are satisfied and then we receive a callback.

An Auth0 log-in form
An Auth0 log-in form
The controller

The bulk of the logic is contained here, and this code is reasonably generic - barring the User and UserDao classes, this code can be used in any Play 2 application. In broad terms, three things happen during a successful authentication flow - all of these are rooted in the callback method of the controller.

  1. The controller receives a callback, in the form of a HTTP request from Auth0 containing an authorization code.
  2. The controller makes a HTTP request to Auth0 to get the token.
  3. The controller makes a HTTP request to Auth0 to get the user details.
Processing the callback from Auth0
 1 public F.Promise<Result> callback(final F.Option<String> maybeCode,
 2                                   final F.Option<String> maybeState) {
 3     return maybeCode.map(code -> getToken(code) // get the authentication token
 4              .flatMap(token -> getUser(token))  // get the user details
 5              .map(userAndToken -> {
 6                  // userAndToken._1 is the user
 7                  // userAndToken._2 is the token
 8                  cache.set(authSupport.cacheKey(userAndToken._2._1),
 9                            userAndToken._1,
10                            60 * 15); // cache the subject for 15 minutes
11                  session("idToken",
12                          userAndToken._2._1);
13                  session("accessToken",
14                          userAndToken._2._2);
15                  return redirect(routes.Application.index());
16              }))
17             .getOrElse(F.Promise.pure(badRequest("No parameters supplied")));
18 }

This callback provides the starting point for further interaction with Auth0 by giving us an authorization code. With this code, we can request token information; access_token allows us to work with the subject’s attributes, and ´id_token´ is a signed Json Web Token used to authenticate API calls.

Get the token information
 1 private F.Promise<F.Tuple<String, String>> getToken(final String code) {
 2     final ObjectNode root = Json.newObject();
 3     root.put("client_id",
 4              this.clientId);
 5     root.put("client_secret",
 6              this.clientSecret);
 7     root.put("redirect_uri",
 8              this.redirectUri);
 9     root.put("code",
10              code);
11     root.put("grant_type",
12              "authorization_code");
13     return WS.url(String.format("https://%s/oauth/token",
14                                 this.domain))
15              .setHeader(Http.HeaderNames.ACCEPT,
16                         Http.MimeTypes.JSON)
17              .post(root)
18              .map(WSResponse::asJson)
19              .map(json -> new F.Tuple<>(json.get("id_token").asText(),
20                                         json.get("access_token").asText()));
21 }

With these token data, we can retrieve the subject attributes. At this point, it’s possible to cache the subject to reduce network calls. In this example, we have no concept of a database and so we rely entirely on Auth0 to provide subject information. If you keep some user information local, this might be a good place to either create or retrieve that information.

Get the subject attributes
 1 private F.Promise<F.Tuple<User, F.Tuple<String, String>>>
 2                         getUser(final F.Tuple<String, String> token) {
 3     return WS.url(String.format("https://%s/userinfo",
 4                                 this.domain))
 5              .setQueryParameter("access_token",
 6                                 token._2)
 7              .get()
 8              .map(WSResponse::asJson)
 9              .map(json -> new User(json.get("user_id").asText(),
10                                    json.get("name").asText(),
11                                    json.get("picture").asText()))
12              .map(localUser -> new F.Tuple<>(localUser,
13                                              token));
14 }

Logging out simply requires the token information to be removed from the session, and the removal of the subject from the cache.

Log the subject out
1 public F.Promise<Result> logOut() {
2     return F.Promise.promise(() -> {
3         final Http.Session session = session();
4         final String idToken = session.remove("idToken");
5         session.remove("accessToken");
6         cache.remove(authSupport.cacheKey(idToken));
7         return "ignoreThisValue";
8     }).map(id -> redirect(routes.AuthController.logIn()));
9 }

This is a lot of code, but authentication is now handled. We now have a way to log in, and a way to retrieve the user details from Auth0. This controller needs to be exposed in the routes file, and this also provides a nice overview to see what we’ve achieved.

Authentication controller routes
1 GET  /logIn     be.objectify.whale.controllers.AuthController.logIn()
2 GET  /callback  be.objectify.whale.controllers.AuthController.callback(code: play.libs.F.O\
3 ption[String], state: play.libs.F.Option[String])
4 GET  /logOut    be.objectify.whale.controllers.AuthController.logOut()
5 GET  /denied    be.objectify.whale.controllers.AuthController.denied()

Now we have a /logIn route, that means you can have an explicit link to log in from your application. The one thing remaining to do is to have the log-in view displayed automatically when authorization fails.

The DeadboltHandler

There are only two methods that are required for this example to work. getSubject will retrieve the subject from the cache, and onAuthFailure will handle things as discussed above.

Authentication controller routes
 1 @Override
 2 public F.Promise<Optional<Subject>> getSubject(final Http.Context context) {
 3     return F.Promise.promise(() -> Optional.ofNullable(cache.get(authSupport.cacheKey(cont\
 4 ext.session().get("idToken")))));
 5 }
 6 
 7 @Override
 8 public F.Promise<Result> onAuthFailure(final Http.Context context,
 9                                        final String s) {
10     return getSubject(context)
11             .map(maybeSubject ->
12                          maybeSubject.map(subject -> Optional.of((User)subject))
13                                      .map(user -> 
14                                           new F.Tuple<>(true,
15                                           denied.render(user)))
16                                      .orElseGet(() -> 
17                                           new F.Tuple<>(false,
18                                                         login.render(clientId,
19                                                                      domain,
20                                                                      redirectUri))))
21             .map(subjectPresentAndContent -> 
22                      subjectPresentAndContent._1
23                          ? Results.forbidden(subjectPresentAndContent._2)
24                          : Results.unauthorized(subjectPresentAndContent._2));
25 }
Improvements

This is a very simple example, but it demonstrates how easily it is to use event-driven behaviour and third-party identity management. There is one major problem, however, and you have until the end of this sentence to figure out what it is.

When the subject attributes are retrieved from Auth0, the resulting User object is cached for an arbitrary time - 15 minutes, in this case. With the implementation of DeadboltHandler given above, once that 15 minutes have passed the user will need to re-authenticate. However, it’s possible their authenticate period on Auth0 is still valid and so we’re placing an unnecessary burden on the end user. A simple improvement would be to attempt retrieval of the user attributes from DeadboltHandler#getSubject when the cache doesn’t contain the user.

It’s also possible to store meta data in Auth0, and so you can represent your roles and permissions there and bind them into local models when retrieving the subject’s attributes.

II Deadbolt for Scala

9. Using Deadbolt 2 with Play 2 Scala projects

Deadbolt for Scala provides an idiomatic API for dealing with Scala controllers and templates rendered from Scala controllers in Play applications.

9.1 The Deadbolt Handler

For any module - or framework - to be useable, it must provide a mechanism by which it can be hooked into your application. For Deadbolt, the central hook is the be.objectify.deadbolt.scala.DeadboltHandler trait. The four functions defined by this trait are crucial to Deadbolt - for example, DeadboltHandler#getSubject gets the current user (or subject, to use the correct security terminology), whereas DeadboltHandler#onAccessFailure is used to generate a response when authorization fails.

DeadboltHandler implementations should be stateless.

For each method, a Future is returned. If the future may complete to have an empty value, e.g. calling getSubject when no subject is present, the return type is Future[Option].

Despite the use of the definite article in the section title, you can have as many Deadbolt handlers in your app as you wish.

Performing pre-constraint tests

Before a constraint is applied, the Future[Option[Result]] beforeAuthCheck[A](request: Request[A]) function of the current handler is invoked. If the resulting future completes Some, the target action is not invoked and instead the result of beforeAuthCheck is used for the HTTP response; if the resulting future completes to None the action is invoked with the Deadbolt constraint applied to it.

Obtaining the subject

To get the current subject, the Future[Option[Subject]] getSubject[A](request: Request[A]) function is invoked. Returning a None indicates there is no subject present - this is a valid scenario.

Dealing with authorization failure

When authorization fails, the Future[Result] onAccessFailure[A](request: Request[A]) function is used to obtain a result for the HTTP response. The result required from the Future returned from this method is a regular play.api.mvc.Result, so it can be anything you chose. You might want to return a 403 forbidden, redirect to a location accessible to everyone, etc.

Dealing with dynamic constraints

Dynamic constraints, which are Dynamic and Pattern.CUSTOM constraints, are dealt with by implementations of DynamicResourceHandler; this will be explored in a later chapter. For now, it’s enough to say Future[Optional[DynamicResourceHandler]] getDynamicResourceHandler[A](request: Request[A]) is invoked when a dynamic constraint it used.

9.2 Expose your DeadboltHandlers with a HandlerCache

Deadbolt uses dependency injection to expose handlers in a type-safe manner. Various components of Deadbolt, which will be explored in later chapters, require an instance of be.objectify.deadbolt.scala.cache.HandlerCache - however, no such implementations are provided.

Instead, you need to implement your own version. This trait extends Function[HandlerKey, DeadboltHandler] and Function0[DeadboltHandler] and uses them as follows

  • handlers() will express the default handler
  • handlers(key: HandlerKey) will express a named handler

Here’s one possible implementation, using hard-coded handlers.

An example handler cache
 1 @Singleton
 2 class MyHandlerCache extends HandlerCache {
 3     val defaultHandler: DeadboltHandler = new MyDeadboltHandler
 4 
 5     // HandlerKeys is an user-defined object, containing instances 
 6     // of a case class that extends HandlerKey
 7     val handlers: Map[Any, DeadboltHandler] = 
 8         Map(HandlerKeys.defaultHandler -> defaultHandler,
 9             HandlerKeys.altHandler -> 
10                   new MyDeadboltHandler(Some(MyAlternativeDynamicResourceHandler)),
11             HandlerKeys.userlessHandler -> new MyUserlessDeadboltHandler)
12 
13     // Get the default handler.
14     override def apply(): DeadboltHandler = defaultHandler
15 
16     // Get a named handler
17     override def apply(handlerKey: HandlerKey): DeadboltHandler = handlers(handlerKey)
18 }

Finally, create a small module which binds your implementation.

Binding the handler cache
 1 package com.example.modules
 2 
 3 import be.objectify.deadbolt.scala.cache.HandlerCache
 4 import play.api.inject.{Binding, Module}
 5 import play.api.{Configuration, Environment}
 6 import com.example.security.MyHandlerCache
 7 
 8 class CustomDeadboltHook extends Module {
 9     override def bindings(environment: Environment, 
10                           configuration: Configuration): Seq[Binding[_]] = Seq(
11         bind[HandlerCache].to[MyHandlerCache]
12     )
13 }

9.3 application.conf

Declare the necessary modules

Both be.objectify.deadbolt.scala.DeadboltModule and your custom bindings module must be declared in the configuration.

Enable your module
1 play {
2   modules {
3     enabled += be.objectify.deadbolt.scala.DeadboltModule
4     enabled += com.example.modules.CustomDeadboltHook
5   }
6 }

9.4 Using compile-time dependency injection

If you prefer to wire everything together with compile-time dependency, you don’t need to create a custom module or add DeadboltModule to play.modules.

Instead, dependencies are handled using a custom ApplicationLoader. To make things easier, various Deadbolt components are made available via the be.objectify.deadbolt.scala.DeadboltComponents trait. You will still need to provide a couple of things, such as your HandlerCache implementation, and you’ll then have access to all the usual pieces of Deadbolt.

An example ApplicationLoader for compile-time DI
 1 class CompileTimeDiApplicationLoader extends ApplicationLoader  {
 2   override def load(context: Context): Application 
 3              = new ApplicationComponents(context).application
 4 }
 5 
 6 class ApplicationComponents(context: Context) 
 7                             extends BuiltInComponentsFromContext(context) 
 8                             with DeadboltComponents
 9                             with EhCacheComponents {
10 
11   // Define a pattern cache implementation
12   // defaultCacheApi is a component from EhCacheComponents
13   override lazy val patternCache: PatternCache = new DefaultPatternCache(defaultCacheApi)
14 
15   // Declare something required by MyHandlerCache
16   lazy val subjectDao: SubjectDao = new TestSubjectDao
17 
18   // Specify the DeadboltHandler implementation to use
19   override lazy val handlers: HandlerCache = new MyHandlerCache(subjectDao) 
20 
21   // everything from here down is application-level
22   // configuration, unrelated to Deadbolt, such as controllers, routers, etc
23   // ...
24 }

The components provided by Deadbolt are

  • scalaAnalyzer - constraint logic
  • deadboltActions - for composing actions
  • actionBuilders - for building actions
  • viewSupport - for template constraints
  • patternCache - for caching regular expressions. You need to define this yourself in the application loader, but as in the example above it’s easy to use the default implementation
  • handlers - the implementation of HandlerCache that you provide
  • configuration - the application configuration
  • ecContextProvider - the execution context for concurrent operations. Defaults to scala.concurrent.ExecutionContext.global
  • templateFailureListenerProvider - for listening to Deadbolt-related errors that occur when rendering templates. Defaults to a no-operation implementation

Once you’ve defined your ApplicationLoader, you need to add it to your application.conf.

Specify the application loader to use
1 play {
2   application {
3     loader=com.example.myapp.CompileTimeDiApplicationLoader
4   }
5 }

9.5 Tweaking Deadbolt

Deadbolt Scala-specific configuration lives in the deadbolt.scala namespace.

There is one setting, deadbolt.scala.view-timeout, which is millisecond timeout applied to blocking calls when rendering templates. This defaults to 1000ms.

Personally, I prefer the HOCON (Human-Optimized Config Object Notation) syntax supported by Play, so I would recommend the following:

Example configuration
1 deadbolt {
2   scala {
3     view-timeout=500
4   }
5 }

Execution context

By default, all futures are executed in the scala.concurrent.ExecutionContext.global context. If you want to provide a separate execution context, you can plug it into Deadbolt by implementing the DeadboltExecutionContextProvider trait.

Providing a custom execution context
1 import be.objectify.deadbolt.scala.DeadboltExecutionContextProvider
2 
3 class CustomDeadboltExecutionContextProvider extends DeadboltExecutionContextProvider {
4     override def get(): ExecutionContext = ???
5 }

NB: This provider is invoked twice, once in DeadboltActions and once in ViewSupport. Make sure you take this into account when you implement the get() function.

Once you’ve implemented the provider, you need to declare it in your custom module (see CustomDeadboltHook above for further information).

Binding the custom execution context provider
1 class CustomDeadboltHook extends Module {
2     override def bindings(environment: Environment, 
3                           configuration: Configuration): Seq[Binding[_]] = Seq(
4         bind[HandlerCache].to[MyHandlerCache],
5         bind[DeadboltExecutionContextProvider].to[CustomDeadboltExecutionContextProvider]
6     )
7 }

10. Scala controller constraints

Controller-level constraints can be added in two different ways - through the use of action builders and through action composition. The resulting behaviour is identical, so choose whichever suites your style best.

10.1 Controller constraints with the action builder

To get started, inject ActionBuilders into your controller.

1 class ExampleController @Inject() (actionBuilder: ActionBuilders) extends Controller

You now have builders for all the constraint types, which we’ll take a quick look at in a minute. In the following examples I’m using the default handler, i.e. .defaultHandler() but it’s also possible to use a different handler with .key(HandlerKey) or pass in a handler directly using .withHandler(DeadboltHandler).

10.2 Controller constraints with action composition

Using the DeadboltActions class, you can compose constrained functions. To get started, inject DeadboltActions into your controller.

1 class ExampleController @Inject() (deadbolt: DeadboltActions) extends Controller

You now have functions equivalent to those of the builders mentioned above. In the following examples I’m using the default handler, i.e. no handler is specified, but it’s also possible to use a different handler with handler = <some handler, possibly from the handler cache>.

10.3 SubjectPresent

Sometimes, you don’t need fine-grained checks - you just need to see if there is a user present.

Action builder

DeadboltHandler#getSubject must result in a Some for access to be granted
1 def someFunctionA = actionBuilder.SubjectPresentAction().defaultHandler() { Ok(accessOk())\
2  }

Action composition

Parameter Type Default Notes
handler DeadboltHandler HandlerCache.apply() The DeadboltHandler instance to use.
DeadboltHandler#getSubject must result in a Some for access to be granted
1 def someFunctionA = deadbolt.SubjectPresent() {
2   Action {
3     Ok(accessOk())
4   }
5 }

10.4 SubjectNotPresent

Sometimes, you don’t need fine-grained checks - you just need to see if there is no user present.

Action builder

DeadboltHandler#getSubject must result in a None for access to be granted
1 def someFunctionB = actionBuilder.SubjectNotPresentAction().defaultHandler() { Ok(accessOk\
2 ()) }

Action composition

Parameter Type Default Notes
handler DeadboltHandler HandlerCache.apply() The DeadboltHandler instance to use.
DeadboltHandler#getSubject must result in a None for access to be granted
1 def someFunctionB = deadbolt.SubjectNotPresent() {
2   Action {
3     Ok(accessOk())
4   }
5 }

10.5 Restrict

Restrict uses a Subject’s Roles to perform AND/OR/NOT checks. The values given to the builder must match the Role.name of the subject’s roles.

AND is defined as an Array[String] (or more correctly, String*), OR is a List[Array[String]], and NOT is a rolename with a ! preceding it.

Action builder

Parameter Type Default Notes
roles List[Array[String]]   Allows the definition of OR’d constraints by
      having multiple arrays in the list.
roles String*   A short-hand of defining a single role or AND
      constraint.
The subject must have the foo role
1 def restrictedFunctionA = actionBuilder.RestrictAction("foo")
2                                        .defaultHandler() { Ok(accessOk()) }
The subject must have the foo AND bar roles
1 def restrictedFunctionB = actionBuilder.RestrictAction("foo", "bar")
2                                        .defaultHandler() { Ok(accessOk()) }
The subject must have the foo OR bar roles
1 def restrictedFunctionC = actionBuilder.RestrictAction(List(Array("foo"), Array("bar")))
2                                        .defaultHandler() { Ok(accessOk()) }

Action composition

Parameter Type Default Notes
roleGroups List[Array[String]]   Allows the definition of OR’d constraints by
      having multiple arrays in the list.
handler DeadboltHandler HandlerCache.apply() The DeadboltHandler instance to use.
The subject must have the foo role
1 def restrictedFunctionA = deadbolt.Restrict(List(Array("foo")) {
2   Action {
3     Ok(accessOk())
4   }
5 }
The subject must have the foo AND bar roles
1 def restrictedFunctionB = deadbolt.Restrict(List(Array("foo", "bar")) {
2   Action {
3     Ok(accessOk())
4   }
5 }
The subject must have the foo OR bar roles
1 def restrictedFunctionB = deadbolt.Restrict(List(Array("foo"), Array("bar")) {
2   Action {
3     Ok(accessOk())
4   }
5 }

10.6 Pattern

Pattern uses a Subject’s Permissions to perform a variety of checks. The check depends on the pattern type.

  • EQUALITY - the subject must have a permission whose value is exactly the same as the value parameter
  • REGEX - the subject must have a permission which matches the regular expression given in the value parameter
  • CUSTOM - the DynamicResourceHandler#checkPermission function is used to determine access

It’s possible to invert the constraint by setting the invert parameter to true. This changes the meaning of the constraint in the following way.

  • EQUALITY - the subject must NOT have a permission whose value is exactly the same as the value parameter
  • REGEX - the subject must have NO permissions that match the regular expression given in the value parameter
  • CUSTOM - the DynamicResourceHandler#checkPermission function, where the OPPOSITE of the Boolean resolved from the function is used to determine access

Action builder

Parameter Type Default Notes
value String   The value of the permission.
patternType PatternType PatternType.EQUALITY One of EQUALITY, REGEX or CUSTOM.
invert Boolean false Invert the result of the test
subject must have a permission with the exact value ‘admin.printer’
1 def permittedFunctionA = actionBuilders.PatternAction(value = "admin.printer",
2                                                       patternType = PatternType.EQUALITY)
3                                        .defaultHandler() { Ok(accessOk()) }
subject must have a permission that matches the regular expression (without quotes) ‘(.)*.printer’
1 def permittedFunctionB = actionBuilders.PatternAction(value = "(.)*\.printer", 
2                                                       patternType = PatternType.REGEX)
3                                       .defaultHandler() { Ok(accessOk()) }
checkPermission is used to determine access
1 // the checkPermssion function of the current handler's DynamicResourceHandler
2 // will be used.  This is a user-defined test
3 def permittedFunctionC = actionBuilders.PatternAction(value = "something arbitrary", 
4                                                       patternType = PatternType.CUSTOM)
5                                        .defaultHandler() { Ok(accessOk()) }
subject must have no permissions that end in .printer
1 def permittedFunctionB = actionBuilders.PatternAction(value = "(.)*\.printer", 
2                                                       patternType = PatternType.REGEX,
3                                                       invert = true)
4                                       .defaultHandler() { Ok(accessOk()) }

Action composition

Parameter Type Default Notes
value String   The value of the permission.
patternType PatternType PatternType.EQUALITY One of EQUALITY, REGEX or CUSTOM.
handler DeadboltHandler HandlerCache.apply() The DeadboltHandler instance to use.
invert Boolean false Invert the result of the test
subject must have a permission with the exact value ‘admin.printer’
1 def permittedFunctionA = deadbolt.Pattern(value = "admin.printer", 
2                                           patternType = PatternType.EQUALITY) {
3   Action {
4     Ok(accessOk())
5   }
6 }
subject must have a permission that matches the regular expression (without quotes) ‘(.)*.printer’
1 def permittedFunctionB = deadbolt.Pattern(value = "(.)*\.printer", 
2                                           patternType = PatternType.REGEX) {
3   Action {
4     Ok(accessOk())
5   }
6 }
checkPermission is used to determine access
1 // the checkPermssion function of the current handler's DynamicResourceHandler
2 // will be used.
3 def permittedFunctionC = deadbolt.Pattern(value = "something arbitrary", 
4                                           patternType = PatternType.CUSTOM) {
5   Action {
6     Ok(accessOk())
7   }
8 }
subject must have no permissions that end in .printer
1 def permittedFunctionB = deadbolt.Pattern(value = "(.)*\.printer", 
2                                           patternType = PatternType.REGEX,
3                                           invert = true) {
4   Action {
5     Ok(accessOk())
6   }
7 }

10.7 Dynamic

The most flexible constraint - this is a completely user-defined constraint that uses DynamicResourceHandler#isAllowed to determine access.

Action builder

Parameter Type Default Notes
name String   The name of the constraint.
meta String   Additional information for the constraint
      implementation to use.
use the constraint associated with the name ‘someClassifier’ to control access
1 def foo = actionBuilder.DynamicAction(name = "someClassifier")
2                        .defaultHandler() { Ok(accessOk()) }

Action composition

Parameter Type Default Notes
name String   The name of the constraint.
meta String   Additional information for the constraint
      implementation to use.
handler DeadboltHandler HandlerCache.apply() The DeadboltHandler instance to use.
use the constraint associated with the name ‘someClassifier’ to control access
1 def foo = deadbolt.Dynamic(name = "someClassifier") {
2   Action {
3     Ok(accessOk())
4   }
5 }

11. Deadbolt Scala Templates

This is not a client-side DOM manipulation, but rather the exclusion of content when templates are rendered. This also means that any logic inside the constrained content will not execute if authorization fails.

A subject is required for the content to be rendered
1 @subjectPresent {
2   <!-- paragraphs will not be rendered, satellites will not be repositioned -->
3   <!-- and light speed will not be engaged if no subject is present -->
4   <p>Let's see what this thing can do...</p>
5   @repositionSatellite
6   @engageLightSpeed
7 }

One important thing to note here is that templates are blocking, so any Futures used need to be completed for the resuly to be used in the template constraints. As a result, each constraint can take a function that expresses a Long, which is the millisecond value of the timeout. It defaults to 1000 milliseconds, but you can change this globally by setting the deadbolt.scala.view-timeout value in your application.conf.

11.1 Handlers

By default, template constraints use the default Deadbolt handler but as with controller constraints you can pass in a specific handler. The cleanest way to do this is to pass the handler into the template and then pass it into the constraints. Another advantage of this approach is you can pass in a wrapped version of the handler that will cache the subject; if you have a lot of constraints in a template, this can yield a significant gain.

Fallback content

Each constraint has an xOr variant, which allows you to render content in place of the unauthorized content. This takes the form <constraint>Or, for example subjectPresentOr

Providing fallback content when a subject is not present
 1 @subjectPresentOr {
 2     <!-- paragraphs will not be rendered, satellites will not be repositioned -->
 3     <!-- and light speed will not be engaged if no subject is present -->
 4     <p>Let's see what this thing can do...</p>
 5     @repositionSatellite
 6     @engageLightSpeed
 7 } {
 8     <marquee>Sorry, you are not authorized to perform clichéd actions from B movies.  Suff\
 9 er the marquee!</marquee>
10 }

In each case, the fallback content is defined as a second Content block following the primary body.

Timeouts

Because templates use blocking calls when rendering, the futures returned from the Deadbolt handler, etc, need to be completed during the rendering process. A timeout, with a default value of 1000ms, is used to wait for the completion but you may want to change this. You can do this in two ways.

Set a global timeout

If you want to change the default timeout, define deadbolt.scala.view-timeout in your configuration and give it a millisecond value, e.g.

Define timeouts in milliseconds
1 deadbolt {
2   scala {
3     view-timeout=1500
4   }
5 }
Use a supplier to provide a timeout

All Deadbolt templates have a timeout parameter which defaults to expressing the app-wide value - 1000L if nothing else if defined, otherwise whatever deadbolt.java.view-timeout is set to. But - and here’s the nice part - the timeout parameter is not a Long but rather a Function0<Long>. This means you can use a timeout that fluctuates based on some metric - say, the number of timeouts that occur during template rendering.

How do I know if timeouts are occurring?

That’s a good question. And the answer is - you need to implement be.objectify.deadbolt.scala.TemplateFailureListener and bind it using a module; see “Expose your DeadboltHandlers with a HandlerCache” section in chapter 8 for more details on this. If you re-use that chapter 8 module, the binding will look something like this.

Declaring a template failure listener
1 class CustomDeadboltHook extends Module {
2     override def bindings(environment: Environment, 
3                           configuration: Configuration): Seq[Binding[_]] = Seq(
4         bind[HandlerCache].to[MyHandlerCache],
5         bind[TemplateFailureListener].to[MyTemplateFailureListener]
6     )
7 }

Making it a singleton allows you to keep a running count of the failure level; if you’re using it for other purposes, then scope it accordingly.

11.2 SubjectPresent

Sometimes, you don’t need fine-grained checked - you just need to see if there is a user present.

  • be.objectify.deadbolt.scala.views.html.subjectPresent
  • be.objectify.deadbolt.scala.views.html.subjectPresentOr
Parameter Type Default Notes
handler DeadboltHandler handlerCache.apply() The handler to use to apply the constraint.
timeout () ⇒ Long A function returning The timeout applied to blocking calls.
    deadbolt.scala.view-timeout  
    if it’s defined, otherwise  
    1000L  

Example 1

The default Deadbolt handler is used to obtain the subject.

1 @subjectPresent() {
2     This content will be present if handler#getSubject results in a Some
3 }
4 
5 @subjectPresentOr() {
6     This content will be present if handler#getSubject results in a Some
7 } {
8 	fallback content
9 }

Example 2

A specific Deadbolt handler is used to obtain the subject.

 1 @(handler: DeadboltHandler)
 2 @subjectPresent(handler = handler) {
 3     This content will be present if handler#getSubject results in a Some
 4 }
 5 
 6 @subjectPresentOr(handler = handler) {
 7     This content will be present if handler#getSubject results in a Some
 8 } {
 9 	fallback content
10 }

11.3 SubjectNotPresent

Sometimes, you don’t need fine-grained checked - you just need to see if there is no user present.

  • be.objectify.deadbolt.scala.views.html.subjectNotPresent
  • be.objectify.deadbolt.scala.views.html.subjectNotPresentOr
Parameter Type Default Notes
handler DeadboltHandler handlerCache.apply() The handler to use to apply the constraint.
timeout () ⇒ Long A function returning The timeout applied to blocking calls.
    deadbolt.scala.view-timeout  
    if it’s defined, otherwise  
    1000L  

Example 1

The default Deadbolt handler is used to obtain the subject.

1 @subjectNotPresent() {
2     This content will be present if handler#getSubject results in a None 
3 }
4 
5 @subjectNotPresentOr() {
6     This content will be present if handler#getSubject results in a None 
7 } {
8 	fallback content
9 }

Example 2

A specific Deadbolt handler is used to obtain the subject.

 1 @(handler: DeadboltHandler)
 2 @subjectNotPresent(handler = handler) {
 3     This content will be present if handler#getSubject results in a None 
 4 }
 5 
 6 @subjectNotPresentOr(handler = handler) {
 7     This content will be present if handler#getSubject results in a None 
 8 } {
 9 	fallback content
10 }

11.4 Restrict

Use Subjects Roles to perform AND/OR/NOT checks. The values given to the constraint must match the Role.name of the subject’s roles.

AND is defined as an Array[String], OR is a List[Array[String]], and NOT is a rolename with a ! preceding it.

  • be.objectify.deadbolt.scala.views.html.restrict
  • be.objectify.deadbolt.scala.views.html.restrictOr
Parameter Type Default Notes
handler DeadboltHandler handlerCache.apply() The handler to use to apply the constraint.
roles List[Array[String]]   The AND/OR/NOT restrictions. One array defines
      an AND, multiple arrays define OR.
timeout () ⇒ Long A function returning The timeout applied to blocking calls.
    deadbolt.scala.view-timeout  
    if it’s defined, otherwise  
    1000L  

Example 1

The subject must have the “foo” role.

1 @restrict(roles = List(Array("foo"))) {
2     Subject requires the foo role for this to be visible
3 }

Example 2

The subject must have the “foo” AND “bar” roles.

1 @restrict(List(Array("foo", "bar")) {
2      Subject requires the foo AND bar roles for this to be visible
3 }

Example 3

The subject must have the “foo” OR “bar” roles.

1 @restrict(List(Array("foo"), Array("bar"))) {
2      Subject requires the foo OR bar role for this to be visible
3 }

Example 3

The subject must have the “foo” OR “bar” roles, or fallback content will be displayed.

1 @restrictOr(List(Array("foo", "bar")) {
2      Subject requires the foo AND bar roles for this to be visible
3 } {
4 	Subject does not have the necessary roles
5 }

11.5 Pattern

Use the Subjects Permissions to perform a variety of checks.

  • be.objectify.deadbolt.scala.views.html.pattern
  • be.objectify.deadbolt.scala.views.html.patternOr
Parameter Type Default Notes
handler DeadboltHandler handlerCache.apply() The handler to use to apply the constraint.
value String   The value of the pattern, e.g. a regex or a
      precise match.
patternType PatternType PatternType.EQUALITY  
       
timeout () ⇒ Long A function returning The timeout applied to blocking calls.
    deadbolt.scala.view-timeout  
    if it’s defined, otherwise  
    1000L  

Example 1

The subject and DynamicResourceHandler are obtained from the default handler, and must have a permission with the exact value “admin.printer”.

1 @pattern("admin.printer") {
2     Subject must have a permission with the exact value "admin.printer" for this to be vis\
3 ible
4 }

Example 2

The subject and DynamicResourceHandler are obtained from the default handler, and must have a permission that matches the specified regular expression.

1 @pattern("(.)*\.printer", PatternType.REGEX) {
2 	Subject must have a permission that matches the regular expression (without quotes) "(.)*\
3 \.printer" for this to be visible
4 }

Example 3

The DynamicResourceHandler is obtained from the default handler and used to apply the custom test

1 @pattern("something arbitrary", PatternType.CUSTOM) {
2 	DynamicResourceHandler#checkPermission must result in true for this to be visible
3 }

Example 4

Fallback content is displayed if the user does not have a permission exactly matching “admin.printer”.

1 @patternOr("admin.printer") {
2     Subject must have a permission with the exact value "admin.printer" for this to be vis\
3 ible
4 } {
5 	Subject did not have necessary permissions
6 }

11.6 Dynamic

The most flexible constraint - this is a completely user-defined constraint that uses DynamicResourceHandler#isAllowed to determine access.

  • be.objectify.deadbolt.scala.views.html.dynamic
  • be.objectify.deadbolt.scala.views.html.dynamicOr
Parameter Type Default Notes
handler DeadboltHandler handlerCache.apply() The handler to use to apply the constraint.
name String   The name of the constraint, passed into the
      DynamicResourceHandler.
meta PatternType null  
       
timeout () ⇒ Long A function returning The timeout applied to blocking calls.
    deadbolt.scala.view-timeout  
    if it’s defined, otherwise  
    1000L  

Example 1

The DynamicResourceHandler is obtained from the default handler and is used to apply a named constraint to the content.

1 @dynamic(name = "someName") {
2     DynamicResourceHandler#isAllowed must result in true for this to be visible
3 }

Example 2

The DynamicResourceHandler is obtained from the default handler and is used to apply a named constraint to the content with some hard-coded meta data.

1 @dynamic(name = "someName", meta = "foo") {
2     DynamicResourceHandler#isAllowed must result in true for this to be visible
3 }

Example 3

The DynamicResourceHandler is obtained from the default handler and is used to apply a named constraint to the content with some dynamically-defined meta data.

1 @(someMetaValue: String)
2 @dynamic(name = "someName", meta = someMetaValue) {
3     DynamicResourceHandler#isAllowed must result in true for this to be visible
4 }

Example 4

The DynamicResourceHandler is obtained from a specific handler and is used to apply a named constraint.

1 @(handler: DeadboltHandler)
2 @dynamic(handler = handler, name = "someName") {
3     DynamicResourceHandler#isAllowed must result in true for this to be visible
4 }