How to implement Secure.Authenticator in scala Controller? - scala

I am following this guide -
https://www.playframework.com/documentation/2.1.1/JavaGuide4#Implementing-authenticators
I have implemented the authenticator as below in Scala:
package controllers
import play.mvc.Security
import play.mvc.Http.Context
import play.mvc.Result
import play.mvc.Results
class Secured extends Security.Authenticator {
override def getUsername(ctx: Context) : String = {
return ctx.session().get("username");
}
override def onUnauthorized(ctx: Context) : Result = {
Results.redirect(routes.Application.login())
}
}
I applied it as an annotation to the hello action in the below controller.
package controllers
import services.UserServiceImpl
import models.User._
import play.api.mvc._
import play.mvc.Security.Authenticated
object Application extends Controller {
def index = Action {
Ok(views.html.index("Your new application is ready."))
}
#Authenticated(classOf[Secured])
def hello = Action {
Ok("Hello")
}
def login = Action {
Ok("Login")
}
}
I am using Play 2.4 and play.api.mvc.Controller instead of play.mvc.Controller as in the guide. Is this the reason this is not working? How do I get it to work with play.api.mvc.Controller?

I figured an example would probably help explain things. If you want similar functionality in Play Scala you would do something like the following:
object AuthenticatedAction extends ActionBuilder[Request] {
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
request.session.get("username") match {
case Some(user) => block(request)
case None => Redirect(routes.Application.login())
}
}
You would then use this in your controller:
def hello = AuthenticatedAction { implicit request =>
Ok("Hello")
}

Related

How to test a custom directive / extract value from akka.http.scaladsl.server.Directive?

I have a custom directive with a function like the following that returns a Directive1[ValidatedParameters], where ValidatedParameters is just a simple case class:
class MyCustomDirective {
def validateParameters(...): Directive1[ValidatedParameters] = {
...
provide(ValidatedParameters(...))
}
}
I'm using it like this in my route:
myCustomDirective.validateParameters(top, skip, modifiedDate) {
(validatedParameters: ValidatedParameters) => {
However, I have a unit test where I'd basically like to call the above function and verify that ValidatedParameters is what I expect:
val actualResult: Directive1[ValidatedParameters] = new MyCustomDirective().validateParameters(...)
So actualResult is a Directive1[ValidatedParameters], is there a way I can get access to the ValidatedParameters case class within this directive from a unit test?
Here is a solution for writing a test case for the custom directive as mentioned here:
REQUEST ~> ROUTE ~> check {
ASSERTIONS
}
For a custom directive to get page and per_page query paramters:
case class Paginate(page: Option[String], perPage: Option[String])
trait PaginationDirective extends ParameterDirectives with RespondWithDirectives {
def withPagination: Directive1[Paginate] = {
parameters(
(
"page".as[String].?,
"per_page".as[String].?
)
).tmap { case (pageOpt, perPageOpt) => Paginate(pageOpt, perPageOpt) }
}
def logCurrentPage(currentPage: Int): Directive0 = {
mapResponse(response => {
logger.info(s"currentPage is $currentPage")
response
})
}
}
Test case for the above custom directive:
import akka.http.scaladsl.server.Directives.complete
import akka.http.scaladsl.testkit.ScalatestRouteTest
import org.scalatest.{Matchers, WordSpec}
class PaginationDirectiveTest extends WordSpec with Matchers with PaginationDirective with ScalatestRouteTest {
"paginate directive" should {
"return page and per page parameters" in {
Get("/?page=1&per_page=25") ~> withPagination(i => complete(i.toString)) ~> check {
responseAs[String] shouldEqual "[Paginate(Some(1), Some(25))]"
}
}
"logCurrentPage" should {
Get("/hello") ~> logCurrentPage(5)(complete("Dummy msg")) ~> check {
//validate test here.
}
}
}
I ended up with the following:
import akka.http.scaladsl.server.Directives.complete
import akka.http.scaladsl.testkit.ScalatestRouteTest
import de.heikoseeberger.akkahttpcirce.ErrorAccumulatingCirceSupport._
import org.scalamock.scalatest.MockFactory
import org.scalatest.{BeforeAndAfter, Matchers}
class MyTest extends ScalatestRouteTest {
...
"validate(top=None, skip=None, modifiedDate=None)" should "pass validation" in {
myCustomDirective.validate(top, skip, modifiedDate) {
(validatedParameters: ValidatedParameters) => {
validatedParameters shouldEqual expectedResult
complete("") // Needed to pass compilation
}
}
}
}

Scala Play 2.4 how to test action composition (using action builder and action filter)

I have written an action builder and apply this new action to my controller with Injection.
Below is my code:
class AuthAction #Inject()(authService: AuthService) {
def AuthAction(userId: Int) = new ActionBuilder[Request] with ActionFilter[Request] {
def filter[A](request: Request[A]): Future[Option[Result]] = {
request.headers.get("Authorization").map(header => {
authService.isAuth(header).flatMap {
case true => Future.successfuly(None)
case false => Future.successfuly(Some(Forbidden))
}
})
}
}
}
class UserController #Inject()(auth: AuthAction, xxxService: XxxService) extends Controller {
def xxx(userId: Int) = auth.AuthAction(userId).async { implicit request =>
xxxService.xxx().map.......
}
}
Without using customised action, I can easily test UserController with ScalaTest and Mockito (Fake request and mock xxxService)
My question is how can I mock AuthAction and test UserController using ScalaTest and Mockito?
Thanks

Play 2.3 override global SecurityHeadersFilter X-FRAME-OPTIONS for a specific action

I have enabled SecurityHeadersFilter globally in my Play 2.3.
This is my setting:
play.filters.headers.frameOptions="SAMEORIGIN"
But how can I override an action with X-Frame-Options=GOFORIT instead of SAMEORIGIN?
Use action composition.
First, create the Action :
import play.libs.F.Promise;
import play.mvc.Action.Simple;
import play.mvc.Http.Context;
import play.mvc.Result;
public class XFrameOptionAction extends Simple {
#Override
public Promise<Result> call(Context ctx) throws Throwable {
ctx.response().setHeader("X-Frame-Options", "GOFORIT");
return delegate.call(ctx);
}
}
Then, use it in your controller
import play.mvc.Controller;
import play.mvc.Result;
import play.mvc.With;
public class MyController extends Controller {
#With(XFrameOptionAction.class)
public static Result index() {
return ok();
}
}
This will add the header for the index action of MyController.
You can also place the annotation at controller level to make all the actions of your controller behave like this.
If you want to make it general to all your controllers, you need to create a controller that all your controllers will extend.
This is what I am using to avoid the header not getting overridden by SecurityHeadersFilter.
import play.api.libs.iteratee._
object FilterChainByRequestHeader {
def apply[A](action: EssentialAction, filtersFun: (RequestHeader) => List[EssentialFilter]): EssentialAction = new EssentialAction {
def apply(rh: RequestHeader): Iteratee[Array[Byte], Result] = {
val chain = filtersFun(rh).reverse.foldLeft(action) { (a, i) => i(a) }
chain(rh)
}
}
}
val securityFilter = SecurityHeadersFilter()
val defaultFilters = List(securityFilter)
def filters(rh: RequestHeader) = {
if (rh.path.startsWith("/cors_path"))
defaultFilters.filterNot(_.eq(securityFilter))
else defaultFilters
}
override def doFilter(a: EssentialAction): EssentialAction = {
FilterChainByRequestHeader(super.doFilter(a), filters)
}

How can I test this custom Action that extends ActionBuilder?

I want to write a unit test to test my custom action builder.
Once I set the ActionWithSession as in instance in my test, how do I instantiate it like it would be in a controller?
How can I test for the edge cases if it redirects or returns with the Session?
class SessionAwareRequest[A](val context: MyContext, request: Request[A]) extends WrappedRequest[A](request)
class ActionWithSession #Inject()(siteConfig: SiteConfig, userService: UserService)
extends ActionBuilder[SessionAwareRequest] {
def invokeBlock[A](request: Request[A], block: (SessionAwareRequest[A]) => Future[Result]) = {
val session = loadSession(request)
if(session.isDefined) {
val result: Future[Result] = block(new SessionAwareRequest(session.get, request))
result.map(_.withCookies(CookieHelper.newSession(session.get)(request)))
}
else {
Future.successful(Results.Redirect(routes.MessagesController.show("error!")))
}
def loadSession(requestHeader: RequestHeader): Option[MySession] = {
...
Some(MySession(...))
else
None
}
}
If you're using Play 2.3, you should be able to utilize the PlaySpecification trait, which provides a call method to test Actions. Have a look at the Documentation, especially the section about "Unit Testing EssentialAction
".
Here's an artificial, minimal example based on the code you posted. It basically just checks whether the request headers contains a "foo" field and if so, returns OK together with an additional cookie "baz" that is added via the custom action builder. If the "foo" header is missing it returns BadRequest. Hope it helps.
import play.api.mvc._
import play.api.test.{FakeRequest, PlaySpecification}
import scala.concurrent.Future
class ActionSpec extends PlaySpecification {
case class MySession()
class SessionAwareRequest[A](val context: MySession, request: Request[A]) extends WrappedRequest[A](request)
object ActionWithSession extends ActionBuilder[SessionAwareRequest] {
def loadSession(requestHeader: RequestHeader): Option[MySession] = {
if (requestHeader.headers.get("foo").isDefined)
Some(MySession())
else
None
}
def invokeBlock[A](request: Request[A], block: (SessionAwareRequest[A]) => Future[Result]): Future[Result] = {
val session = loadSession(request)
if (session.isDefined) {
val result: Future[Result] = block(new SessionAwareRequest(session.get, request))
result.map(_.withCookies(Cookie("baz", "quux")))
} else {
Future.successful(Results.BadRequest)
}
}
}
"My custom action" should {
"return HTTP code 400 in case the request header does not contain a 'foo' field" in {
val foo = call(ActionWithSession { request =>
Results.Ok("Yikes")
}, FakeRequest())
status(of = foo) must beEqualTo(BAD_REQUEST)
cookies(of = foo).get("baz") must beNone
}
"return HTTP code 200 in case the request header does contain a 'foo' field" in {
val foo = call(ActionWithSession { request =>
Results.Ok("Yikes")
}, FakeRequest().withHeaders("foo" -> "bar"))
status(of = foo) must beEqualTo(OK)
cookies(of = foo).get("baz") must beSome
}
}
}

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.
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
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."))
}
}
Take a look at Deadbolt: https://github.com/schaloner/deadbolt-2 . There are exhaustive examples and guides.
Works perfectly in my Play 2 project.
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.