Integrating Deadbolt
Implementations of the be.objectify.deadbolt.scala.DeadboltHandler
trait are used to provide Deadbolt with what it needs to satisfy your project's authorization constraints. This trait has 4 functions.
getSubject
gets the current user from, for example, the cache or databasebeforeAuthCheck
run a pre-authorization task that can block further executiononAuthFailure
defines behaviour for when authorization requirements are not metgetDynamicResourceHandler
provides a hook into the dynamic constraint types
import be.objectify.deadbolt.scala.models.Subject
import be.objectify.deadbolt.scala.{AuthenticatedRequest, DynamicResourceHandler, DeadboltHandler}
import views.html.security.{login, denied}
import play.api.mvc.{Results, Result, Request}
import play.twirl.api.HtmlFormat
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
class MyDeadboltHandler extends DeadboltHandler {
override def beforeAuthCheck[A](request: Request[A]): Future[Option[Result]] = Future {None}
override def getDynamicResourceHandler[A](request: Request[A]): Future[Option[DynamicResourceHandler]] = Future {None}
override def getSubject[A](request: AuthenticatedRequest[A]): Future[Option[Subject]] =
Future {
request.subject.orElse {
// replace request.session.get("userId") with how you identify the user
request.session.get("userId") match {
case Some(userId) =>
// get from database, identity platform, cache, etc, if some
// identifier is present in the request
case _ => None
}
}}
override def onAuthFailure[A](request: AuthenticatedRequest[A]): Future[Result] = {
def toContent(maybeSubject: Option[Subject]): (Boolean, HtmlFormat.Appendable) =
maybeSubject.map(subject => (true, denied(Some(subject))))
.getOrElse {(false, login(clientId, domain, redirectUri))}
getSubject(request).map(maybeSubject => toContent(maybeSubject))
.map(subjectPresentAndContent =>
if (subjectPresentAndContent._1) Results.Forbidden(subjectPresentAndContent._2)
else Results.Unauthorized(subjectPresentAndContent._2))
}
}
You only need to implement be.objectify.deadbolt.scala.DynamicResourceHandler
if you're planning to use Dynamic
or Pattern.CUSTOM
constraints. Dynamic constraints are tests implemented entirely by your code. This trait has two functions:
isAllowed
is used by the Dynamic constraintcheckPermission
is used by the Pattern constraint when the pattern type isCUSTOM
In order to expose your handler (or handlers - you can have more than one) to Deadbolt, you will need to implement the be.objectify.deadbolt.scala.cache.HandlerCache
trait. This implementation needs to
- Provide a default handler; you can always use a specific handler in a template or controller, but if nothing is specified a well-known instance will be used.
- Provide a named instance
Here's an example of a HandlerCache
implementation.
@Singleton
class MyHandlerCache extends HandlerCache {
val defaultHandler: DeadboltHandler = new MyDeadboltHandler
// HandlerKeys is an user-defined object, containing instances
// of a case class that extends HandlerKey
val handlers: Map[Any, DeadboltHandler] = Map(HandlerKeys.defaultHandler -> defaultHandler,
HandlerKeys.altHandler -> new MyDeadboltHandler(Some(MyAlternativeDynamicResourceHandler)),
HandlerKeys.userlessHandler -> new MyUserlessDeadboltHandler)
// Get the default handler.
override def apply(): DeadboltHandler = defaultHandler
// Get a named handler
override def apply(handlerKey: HandlerKey): DeadboltHandler = handlers(handlerKey)
}
Finally, expose your handlers to Deadbolt. To do this, you will need to create a small module that binds your handler cache by type...
package com.example.modules
import be.objectify.deadbolt.scala.cache.HandlerCache
import play.api.inject.{Binding, Module}
import play.api.{Configuration, Environment}
import com.example.security.MyHandlerCache
class CustomDeadboltHook extends Module {
override def bindings(environment: Environment, configuration: Configuration): Seq[Binding[_]] = Seq(
bind[HandlerCache].to[MyHandlerCache]
)
}
...and add it to your application.conf
.
play {
modules {
enabled += be.objectify.deadbolt.scala.DeadboltModule
enabled += com.example.modules.CustomDeadboltHook
}
}
Updated less than a minute ago