Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Authorisation check in controller - Scala/Play

This is a simple example of a controller in Play Framework where every action checks the session - if the user is logged in.

object Application extends Controller {

    def index = Action { implicit request =>
        if (request.session.isEmpty) {
            Redirect("/login")
        } else {
            Ok(views.html.index("index"))
        }
    }

    def about = Action { implicit request =>
        if (request.session.isEmpty) {
            Redirect("/login")
        } else {
            Ok(views.html.index("about"))
        }
    }

}

I'd like to handle the session checking in the constructor instead of every action method, but I just don't know how? It should look something like this:

object Application extends Controller {

    //This is where the constructor would check if session exists
    //and if not - redirect to login screen

    def index = Action {
        Ok(views.html.index("index"))
    }

    def about = Action {
        Ok(views.html.index("about"))
    }

}

Is this possible and if so then how?

My stack is Play Framework 2.2.1, Scala 2.10.3, Java 1.8.0-ea 64bit

UPDATE - SOLVED Thanks for all your ideas, solution is now found, see my answer.

like image 499
Caballero Avatar asked Nov 08 '13 20:11

Caballero


4 Answers

You could take advantage of Action Composition to achieve this. From the documentation:

import play.api.mvc._

class AuthenticatedRequest[A](val username: String, request: Request[A]) extend WrappedRequest[A](request)

object Authenticated extends ActionBuilder[AuthenticatedRequest] {
  def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) =>Future[SimpleResult]) = {
    request.session.get("username").map { username =>
      block(new AuthenticatedRequest(username, request))
    } getOrElse {
      Future.successful(Forbidden)
    }
  }
}

And then you could simply do:

def index = Authenticated {
    Ok(views.html.index("index"))
}

Alternatively you could set up a filter instead (as @Robin Green suggested) like so:

object AuthFilter extends Filter {

  override def apply(next: RequestHeader => Result)(rh: RequestHeader): Result = {
    rh.session.get("username").map { user =>
      next(rh)
  }.getOrElse {
    Redirect("/login")
  }
}

In Global.scala scala, add

override def doFilter(action: EssentialAction) = AuthFilter(action)

For more on Filters, see the official docs

like image 71
mantithetical Avatar answered Nov 17 '22 10:11

mantithetical


Solution is to use Action Composition and create a custom action.

Auth.scala:

package core

import play.api.mvc._
import scala.concurrent._
import play.api.mvc.Results._

object AuthAction extends ActionBuilder[Request] {

    def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[SimpleResult]) = {
        if (request.session.isEmpty) {
            //authentication condition not met - redirect to login page
            Future.successful(Redirect("/login"))
        } else {
            //proceed with action as normal
            block(request)
        }
    }

}

Application.scala:

package controllers

import play.api._
import play.api.mvc._
import core._

object Application extends Controller {

    def index = AuthAction {
        Ok(views.html.index("You are logged in."))
    }

}
like image 44
Caballero Avatar answered Nov 17 '22 08:11

Caballero


Take a look at Deadbolt: https://github.com/schaloner/deadbolt-2 . There are exhaustive examples and guides.

Works perfectly in my Play 2 project.

like image 32
Leo Avatar answered Nov 17 '22 10:11

Leo


You could use a Filter, which applies to every request in the application. However, then you would need to have some code in that Filter to allow certain URLs to be accessed without a valid session, otherwise then the user would not be able to login in the first place.

like image 1
Robin Green Avatar answered Nov 17 '22 10:11

Robin Green