Action builders

Using the ActionsBuilders class, you can quickly assemble constraints around your functions. To get started, inject ActionBuilders into your controller.

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

SubjectPresent and SubjectNotPresent

Sometimes, you don't need fine-grained checks - you just need to see if there is a user present (or not present).

// DeadboltHandler#getSubject must result in a Some for access to be granted
def someFunctionA = actionBuilder.SubjectPresentAction().defaultHandler() { authRequest =>
  Future {
    Ok(accessOk()) 
  }
}

// DeadboltHandler#getSubject must result in a None for access to be granted
def someFunctionB = actionBuilder.SubjectNotPresentAction().defaultHandler() { authRequest =>
  Future {
    Ok(accessOk()) 
  }
}

Restrict

This uses the 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.

// subject must have the "foo" role 
def restrictedFunctionA = actionBuilder.RestrictAction("foo").defaultHandler() { authRequest =>
  Future {
    Ok(accessOk()) 
  }
}

// subject must have the "foo" AND "bar" roles 
def restrictedFunctionB = actionBuilder.RestrictAction("foo", "bar").defaultHandler() { authRequest =>
  Future {
    Ok(accessOk()) 
  }
}

// subject must have the "foo" OR "bar" roles 
def restrictedFunctionC = actionBuilder.RestrictAction(List(Array("foo"), Array("bar"))).defaultHandler() { authRequest =>
  Future {
    Ok(accessOk()) 
  }
}

That whole and/or/array/list thing looks pretty ugly, so instead you can use anyOf, allOf and allOfGroup from the be.objectify.deadbolt.scala package object instead.

  • allOf can be used to define an AND relationship, e.g. Array("foo", "bar") can be written as allOf("foo", "bar") - this would then need to be placed in a list for use within a restriction.
  • anyOf can be used to define an OR relationship, e.g. List(Array("foo"), Array("bar")) can be written as anyOf(allOf("foo"), allOf("bar")).
  • allOfGroup is a convenient way of defining a single AND relationship, e.g. List(Array("foo", "bar")) can be written as allOfGroup("foo", "bar").

This means restrictedFunctionC from the previous template can be rewritten as follows.

// subject must have the "foo" OR "bar" roles 
def restrictedFunctionC = actionBuilder.RestrictAction(anyOf(allOf("foo"), allOf("bar"))).defaultHandler() { authRequest =>
  Future {
    Ok(accessOk()) 
  }
}

Pattern

This uses the Subject's Permissions to perform a variety of checks.

// subject must have a permission with the exact value "admin.printer" 
def permittedFunctionA = actionBuilders.PatternAction(value = "admin.printer",
                                                      patternType = PatternType.EQUALITY).defaultHandler() { authRequest =>
  Future {
    Ok(accessOk()) 
  }
}

// subject must have a permission that matches the regular expression (without quotes) "(.)*\.printer" 
def permittedFunctionB = actionBuilders.PatternAction(value = "(.)*\.printer",
                                                      patternType = PatternType.REGEX).defaultHandler() { authRequest =>
  Future {
    Ok(accessOk()) 
  }
}

// the checkPermssion function of the current handler's DynamicResourceHandler will be used.  This is a user-defined test 
def permittedFunctionC = actionBuilders.PatternAction(value = "something arbitrary",
                                                      patternType = PatternType.CUSTOM).defaultHandler() { authRequest =>
  Future {
    Ok(accessOk()) 
  }
}

// the checkPermssion function of the current handler's DynamicResourceHandler will be used.  Additional information is passed to it via the meta parameter.  This is a user-defined test 
def permittedFunctionC = actionBuilders.PatternAction(value = "something arbitrary",
                                                      patternType = PatternType.CUSTOM,
                                                     meta = Some("this can be an Option[Any]")).defaultHandler() { authRequest =>
  Future {
    Ok(accessOk()) 
  }
}

// subject must have no permissions with the exact value "admin.printer" 
def permittedFunctionA = actionBuilders.PatternAction(value = "admin.printer",
                                                      patternType = PatternType.EQUALITY,
                                                      invert = true).defaultHandler() { authRequest =>
  Future {
    Ok(accessOk()) 
  }
}

Dynamic

The most flexible constraint - this is a completely user-defined constraint that uses DynamicResourceHandler#isAllowed to determine access.

def foo = actionBuilder.DynamicAction(name = "someClassifier").defaultHandler() { authRequest =>
  Future {
    Ok(accessOk()) 
  }
}

You can also pass addition information to the constraint using the meta parameter.

def foo = actionBuilder.DynamicAction(name = "someClassifier",
                                     meta = Some("this can be an Option[Any]")).defaultHandler() { authRequest =>
  Future {
    Ok(accessOk()) 
  }
}