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 }