An simple to use authorization module for Play 2.5.x This project allows you to add very basic authorization logic to your Play application.
Download the sources from this repository and run
sbt publishLocal
After the command completes you can add a dependency to your project using the following lines:
libraryDependencies += "nl.fizzylogic" %% "padlock" % "0.1.0-SNAPSHOT"
To authorize access to an operation you can use one of the following two action builders.
- ActionWithOptionalPrincipal
- AuthenticatedAction
class MyController @Inject() (val authorizationHandler: AuthorizationHandler)
extends Controller with AuthorizationSupport {
def myAction = AuthenticatedAction { implicit request =>
Ok("Hello world")
}
}
The controller needs access to an AuthorizationHandler implementation. This implementation looks like this:
import play.api.mvc.Results._
class MyAuthorizationHandler extends AuthorizationHandler {
override def denied[A](request: RequestWithPrincipal[A]) = {
Forbidden(views.html.accessDenied())
}
override def unauthorized[A](request: RequestWithOptionalPrincipal[A]) = {
Unauthorized(views.html.login())
}
override def principal(request: Request[A]) = {
//TODO: Retrieve a principal from a user store
request.session("userId").map(Some(MyPrincipal(_,seq.empty)))
}
}
Whenever an AuthenticatedAction
determines that there's no current user
it will invoke the unauthorized
method on the AuthorizationHandler
.
The current user is determined using the principal
method on the
AuthorizationHandler
.
The authorization logic relies on a Principal
implementation to
get information about the current user. You can create your own by implementing the Principal
trait.
The basic authorization operations allow you to specify whether an action should have a principal or that it is optional but accessible for an operation.
If you want to create more complex authorization operations you can
implement a custom authorization policy by implementing the AuthorizationPolicy
trait.
import nl.fizzylogic.padlock._
class MyAuthorizationPolicy extends MyAuthorizationPolicy {
override def allowed[A](request: RequestWithPrincipal[A]) = {
request.principal.claims.filter(x => x.claimType.equals("SomeType") && x.value.equals("SomeValue")).length > 0
}
}
You can use the authorization policy using a specific action refiner
implementation in the AuthorizationSupport
trait.
def myAction = (AuthenticatedAction andThen authorizeUsingPolicy(myPolicyInstance)) {
Ok("Hello world")
}
When the user is not authorized to access the action based on the policy,
the user will automatically get the result as produced by the denied
method
in the AuthorizationHandler
.
One of my biggest gripes with existing frameworks is that they don't allow you to authorize access to specific pieces of data. All they do is provide a way to authorize specific HTTP endpoints.
My solution doesn't do any better at the moment and I want to change that. I want you to be able to authorize access to specific blogposts or whatever your app happens to have as resources.
I'm more than happy to accept pull requests for this project. Open one up and show your cool ideas!