Skip to content

Instantly share code, notes, and snippets.

@bmjsmith
Created July 22, 2012 20:07
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bmjsmith/3160915 to your computer and use it in GitHub Desktop.
Save bmjsmith/3160915 to your computer and use it in GitHub Desktop.
Securing play 2.0 controllers sans boilerplate.
object Admin extends SecureableController {
implicit val requiredRight: Option[Right] = Some(Right("admin"))
def categories = authorized[AnyContent] { implicit request =>
Ok(html.adminCategories(Messages("admin.categories.title")))
}
}
package controllers
import models.User
import models.Right
import play.api.mvc._
import play.api.mvc.Results.Unauthorized
import play.api.mvc.BodyParsers._
import play.mvc.Http
trait SecureableController extends Controller {
case class AuthenticatedRequest[A](val user: User, val request: Request[A]) extends WrappedRequest(request)
implicit def user(implicit request : RequestHeader) : Option[User] = {
// substitute in your own user lookup mechanism here
session.get("email").flatMap(User.findOne(_))
}
/**
* A utility function that produces different actions by applying either of two request handling
* functions depending on whether or not a user is authenticated
*
* @param allowed the function used where an authenticated user is found
* @param notAllowed the function used where no user is found
* @param parser a BodyParser
*
*/
def authenticated[A](allowed: AuthenticatedRequest[A] => Result,
notAllowed: Request[A] => Result = ((r : Request[A]) => Unauthorized))
(implicit parser: BodyParser[A] = parse.anyContent) = {
Action(parser) { implicit request =>
user.map { user =>
allowed(AuthenticatedRequest(user, request))
}.getOrElse(notAllowed(request))
}
}
/**
* A utility function that produces different actions by applying either of two request handling
* functions depending on whether or not a user is authenticated and has the required rights
*
* @param allowed the function used where an authenticated user is found
* @param requiredRight the right required for this to be allowed
* @param notAllowed the function used where no user is found
* @param parser a BodyParser
*
*/
def authorized[A](allowed: AuthenticatedRequest[A] => Result,
notAllowed: Request[A] => Result = ((r : Request[A]) => Unauthorized))
(implicit parser: BodyParser[A] = parse.anyContent, requiredRight: Option[Right] = None) = {
authenticated ({ implicit request: AuthenticatedRequest[A] =>
requiredRight match {
case None => allowed(request)
case Some(right) => if (request.user.permitted(right)) allowed(request) else notAllowed(request)
}
},notAllowed)(parser)
}
}
@bmjsmith
Copy link
Author

The idea here was to create a low boilerplate way of securing Play 2.0 controllers depending on the user's rights.

The user model is simple. User is a case class which has a companion which extends salat ModelCompanion (since I'm using Mongo). User has Roles, Role has Rights, simple case classes again. User.permitted checks that User.roles contains a Right which permits the right passed in (at the moment just simple string ==). All this pipework is easily switchable.

The SecureableController trait can be extended by any Controller and used as in the Admin example. It will default to a simple 401 empty response if the user isn't present, or is present but isn't permitted, otherwise it'll evaluate the "allowed" function. Also, your User will be in scope for any implicits in resulting views.

I've posted it as an example you're welcome to use, I'd love to hear of any ways to make this simpler, cleaner or more useful. I used info from Julien Richard-Foy's excellent answer to this question on Stack Overflow and the Play 2.0 documentation on Action composition to put this together.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment