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