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.java.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 }