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 }