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.
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.
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.
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
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.
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.
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
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 }
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
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 }
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
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 }
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 }
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 }
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 }
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
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 }
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 }
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 }
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 }
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 }
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
DynamicResourceHandler is obtained from the default handler and is used to apply a named constraint to the content1 @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 }
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 }
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 }
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 }
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 }