How to set a cookie in ActionBuilder in Play Framework 2.2 / Scala? - scala

To set a cookie you usually manipulate the result in an action like Ok().withCookies(…).
I created an AuthenticatedAction extends ActionBuilder[AuthenticatedRequest] and need to update the expiry date of a user's cookie by setting a new cookie with a new maxAge sometimes. I cannot figure out how to do this, because I can't find a way to manipulate the result.
Within the invokeBlock function I call block(new AuthenticatedRequest(identity, request)), which returns a Future[SimpleResult] and I cannot use withCookies() on a Future[SimpleResult].
Here's my custom AuthenticatedAction:
class AuthenticatedRequest[A](val identity: Identity, request: Request[A]) extends WrappedRequest[A](request)
object AuthenticatedAction extends ActionBuilder[AuthenticatedRequest] {
def redirectToLogin = {
Redirect("/login")
}
def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[SimpleResult]) = {
request.cookies.get("mycookie").map { cookie =>
val maybeIdentity = Auth.validateAndTouchTokenAndGetUser(cookie.value)
maybeIdentity.map { identity =>
// If it's a persistent session, update timestamp by sending a new cookie sometimes!
// To simplify this example, assume we always want to set a new cookie.
val futureResult = block(new MaybeAuthenticatedRequest(maybeIdentity, request))
// What next?
val newMaxAge = 1234
// ???result???.withCookies(Cookie("mycookie", cookie.value, newMaxAge))
} getOrElse {
// Respond with redirect to login and delete cookie and a warning message
Future.successful(
redirectToLogin
.discardingCookies(DiscardingCookie("mycookie"))
.flashing("warning" -> "Your session has expired. Please sign in again.")
)
}
} getOrElse {
// Respond with redirect to login
Future.successful(redirectToLogin)
}
}
}

Ok is a SimpleResult as well. You cannot set cookies for a Future[SimpleResult] but you can do like this:
val futureResult: Future[SimpleResult] = ...
futureResult.map(_.withCookies(Cookie("mycookie", cookie.value, newMaxAge))
Update for Blankman:
The simpliest case for responding with cookies is like this:
def myAction = Action {
.....
Ok(response).withCookies(Cookie("cookie", cookieValue, maxAge))
}
This way you can compose your more complex Action like this (example from my project):
def safe(doSafe: Request[AnyContent] => Future[SimpleResult]): Action[AnyContent] = Action.async { implicit request =>
try {
doSafe(request).map(_.withCookies(Cookie("mycookie", cookie.value, newMaxAge))
} catch {
case e: Exception =>
e.printStackTrace()
//custom failure response here
}
}
Usage:
def someAction = safe { implicit request =>
.... //something that returns a Future[SimpleResult]
}

Related

play framework 2.5 move Action.async logic from controller to service

I have a controller that has many business logic, I would like to move the code inside Action.async block of code. This code works, how can I move to another class(service) the code that is inside of the Action.async?:
def tweetsnew(query: String) = Action.async {
// Move From Here...
credentials.map {
case (consumerKey, requestToken) =>
ws.url("https://api.twitter.com/1.1/search/tweets.json")
.sign(OAuthCalculator(consumerKey, requestToken))
.withQueryString("q" -> query)
.withQueryString("max_id" -> "833342796736167936")
.get().map { twitterResponse =>
if (twitterResponse.status == 200) {
// Here There Are More Complex Logic
Ok("That is fine: "+twitterResponse.body)
} else {
throw new Exception(s"Could not retrieve tweets for $query query term")
}
}
}.getOrElse {
Future.failed(new Exception("You did not correctly configure the Twitter credentials"))
}
//....To Here. To Another Class
}
I have been checked the docummentation, something related to create a Future[Result] but I am not be able to make that the function returns the same type that Action.async expect.
Action.async expects a return type of Future[Result] so you need to create a function that returns a Future[Result]
First step, extract your code to a function:
object TwitterService {
def search(query: String, consumerKey: ConsumerKey, requestToken: RequestToken)(implicit ws: WSClient, ec: ExecutionContext): Future[Result] = {
// your code that make the ws call that returns Ok("...")
}
}
Then in the controller call your function:
def tweetsnew(query: String) = Action.async {
credentials.map {
case (consumerKey, requestToken) => TwitterService.search(query, consumerKey, requestToken)
}.getOrElse {
// Better to send a bad request or a redirect instead of an Exception
Future.successful(BadRequest("Credentials not set"))
}
}

First render in Play 2.3.x does not use correct language

I'm using simple middleware to update the Result
object WithLanguage extends ActionFunction[User, User] {
def invokeBlock[A](request: User[A], block: (User[A]) => Future[Result]): Future[Result] = {
if(request.cookies.get(Play.langCookieName).isEmpty){
val lang: Lang = Lang.get(request.user.language).getOrElse(play.api.i18n.Lang.preferred(request.acceptLanguages))
block(request).withLang(lang))
} else {
block(request)
}
}
}
But the first response is not rendered with the appropriate language - on refresh yes.
I can get it to work by performing a circular redirect in the case of setting a new cookie Future(Redirect(request.uri).withLang(...) but I wonder if there is a cleaner way.
This works ... It's possible to modify the request headers imperatively, but writing this way feels wrong.
object WithLanguage extends ActionFunction[User, User] {
def invokeBlock[A](request: User[A], block: (User[A]) => Future[Result]): Future[Result] = {
if(request.cookies.get(Play.langCookieName).isEmpty){
val cookies = Cookies(request.headers.get(COOKIE)).cookies + (Play.langCookieName -> Cookie(Play.langCookieName, request.user.language.code))
val updatedHeaders = new Headers {
val data: Seq[(String, Seq[String])] = (request.headers.toMap + (COOKIE -> Seq(Cookies.encode(cookies.values.toSeq)))).toSeq
}
val modifiedRequest = User(
user = request.user,
request = Request(request.copy(headers = updatedHeaders), request.body)
)
block(modifiedRequest).map(_.withLang(request.user.language)) // redundant cookie set on result
} else {
block(request)
}
}
}

Play Framework Authentication in a single page app

I am trying to add authentication to my Play Framework single page app.
What I would like to have is something like:
def unsecured = Action {
Ok("This action is not secured")
}
def secured = AuthorizedAction {
// get the authenticated user's ID somehow
Ok("This action is secured")
}
For a traditional web app, I had previously done this, following Play Framework docs:
def authenticate = Action { implicit request =>
loginForm.bindFromRequest.fold(
formWithErrors => BadRequest(views.html.login(formWithErrors)),
user => {
Redirect(routes.Application.home).withSession(Security.username -> user._1)
}
)
}
def logout = Action {
Redirect(routes.Auth.login).withNewSession.flashing(
"success" -> "You are now logged out."
)
}
and the Authorized Action is extending ActionBuilder as follows:
object AuthorizedAction extends ActionBuilder[Request] with Results {
/**
* on auth success: proceed with the request
* on auth failure: redirect to login page with flash
*/
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
// TODO: is "isDefined" enough to determine that user is logged in?
if(request.session.get("username").isDefined) {
block(request)
}
else {
Future.successful(Redirect(routes.Auth.login).flashing(
"failure" -> "You must be logged in to access this page."
))
}
}
}
For single page applications however, this approach doesn't exactly work anymore.
This article by James Ward explains how the new approach is to be designed, and includes a Java implementation:
Securing SPA and rest services
The implementation was redone in Scala by Marius Soutier: Securing SPA in Scala
In his example, he implements a Security trait:
trait Security { self: Controller =>
val cache: CacheApi
val AuthTokenHeader = "X-XSRF-TOKEN"
val AuthTokenCookieKey = "XSRF-TOKEN"
val AuthTokenUrlKey = "auth"
/** Checks that a token is either in the header or in the query string */
def HasToken[A](p: BodyParser[A] = parse.anyContent)(f: String => Long => Request[A] => Result): Action[A] =
Action(p) { implicit request =>
val maybeToken = request.headers.get(AuthTokenHeader).orElse(request.getQueryString(AuthTokenUrlKey))
maybeToken flatMap { token =>
cache.get[Long](token) map { userid =>
f(token)(userid)(request)
}
} getOrElse Unauthorized(Json.obj("err" -> "No Token"))
}
}
Functions are now secured like this instead of a plain Action:
def ping() = HasToken() { token => userId => implicit request =>
user.findByID (userId) map { user =>
Ok(Json.obj("userId" -> userId)).withToken(token -> userId)
} getOrElse NotFound (Json.obj("err" -> "User Not Found"))
}
where .withToken is defined as:
implicit class ResultWithToken(result: Result) {
def withToken(token: (String, Long)): Result = {
cache.set(token._1, token._2, CacheExpiration)
result.withCookies(Cookie(AuthTokenCookieKey, token._1, None, httpOnly = false))
}
def discardingToken(token: String): Result = {
cache.remove(token)
result.discardingCookies(DiscardingCookie(name = AuthTokenCookieKey))
}
}
I am not liking how complex the "ping" function above has become, and would have preferred to use an Action Builder (like the first example), where auth failure is caught and dealt with at a single point. (as of now, if I want to secure functions ping2 and ping3, each one has to check whether the user is found and deal with the "not found" case)
I have tried to put together an action builder, inspired by Marius' implementation, most particularly his use of the cacheApi which is necessary.
However the AuthorizedAction is an object, and cacheApi needs to be injected (so need to change the object to singleton class), or cannot be declared in an object without being defined.
I also feel like the AuthorizedAction needs to remain an object, in order to be used as:
def secured = AuthorizedAction {
Would anyone please clear up the confusion, and possibly help with some implementation details?
Thanks a lot
The simplest way in my opinion is to go with ActionBuilder. You can define an action builder as a class (and pass it some dependencies) or as an object.
First you'll need to define a type a request that will contain the information about the user:
// You can add other useful information here
case class AuthorizedRequest[A](request: Request[A], user: User) extends WrappedRequest(request)
Now define your ActionBuilder
class AuthorizedAction(userService: UserService) extends ActionBuilder[AuthorizedRequest] {
override def invokeBlock[A](request: Request[A], block: (AuthorizedRequest[A]) ⇒ Future[Result]): Future[Result] = {
request.headers.get(AuthTokenHeader).orElse(request.getQueryString(AuthTokenUrlKey)) match {
case Some(token) => userService.findByToken(token).map {
case Some(user) =>
val req = AuthorizedRequest(request, user)
block(req)
case None => Future.successful(Results.Unauthorized)
}
case None => Future.successful(Results.Unauthorized)
}
}
}
Now you can use it in your controller:
val authorizedAction = new AuthorizedAction(userService)
def ping = authorizedAction { request =>
Ok(Json.obj("userId" -> request.user.id))
}

EssentialAction: How to Get the Body of a Request without Parsing It

Given the following EssentialAction...
object MyController extends Controller {
...
def HasToken(action: Token => EssentialAction) = EssentialAction { request =>
...
// this doesn't compile
val body = request.body match {
case json: JsValue => json.toString
case _ => ""
}
// calculate hash with body content here
...
}
// here is an authenticated action
def getUser(userId: Strign) = HasToken { token =>
Action(parse.json) { request =>
request.body.validate[User] match {
...
}
}
}
}
... how do I get the body of the request without parsing it?
I don't want and I don't need to parse the request body in HasToken since the body is going to be parsed in action getUser. I just need the raw content of the body to calculate a hash.
The code in HasToken doesn't compile because request is of type RequestHeader whereas I need a Request, which defines body.
Will this work for you ?
object MyController extends Controller {
// Your HasToken Action
def Authenticate(action: Token => EssentialAction) = EssentialAction { requestHeader =>
// ... execute logic to verify authenticity using requestHeader
}
// Your action to validate tampering of request body and validity of JSON
def Validate[A](action: Token => Request[A]) = Action(parse.json) { request =>
val body = request.body
body match {
case json: JsValue => json.toString
case _ => ""
}
// calculate hash with body content here
body.validate[User] match {
// ...
}
}
def getUser(userId: Strign) = Authenticate { token =>
Validate { user =>
//.... Continue
}
}
}
Authentication only uses RequestHeader
Validation uses Request body. (Bonus: Body is only parsed once)
EDIT:
Question #1: I don't want to validate the body in Validate... since I need a generic validation mechanism that could be used everywhere regardless of the content type (e.g. user, message, etc.).
How about adding another type param (so that it is made generic):
def Validate[A, B](action: Token => Request[A])(implicit reads: Reads[B]) = Action(parse.json) { request =>
// ...
}
Question #2: Furthermore, if the token validation fails, the body don't have to be processed (that's important in case of file upload, which has to be performed if and only if the validation succeeded). That's way, in my opinion, the best option would be to read the raw content of the body in Validate.
This can be easily achieved:
def Validate[A, B](action: Token => Request[A])(implicit reads: Reads[B]) = Action(parse.json) { request =>
val body = request.body
body match {
case json: JsValue => json.toString
case _ => ""
}
// calculate hash with body content here and figure out if the body is tampered
if (bodyIsNotTampered) {
body.validate[B] match {
// ...
}
} else {
// log and return Future.successful(BadRequest)
}
}
EDIT 3: Full solution:
import play.api.libs.json.{Json, JsValue, Format}
object CompilationUtils {
class Token
case class User(name: String)
implicit val UserFormat = Json.format[User]
def authenticate = new Token // authentication logic
def isTampered(body: JsValue) = {
val bodyAsStr: String = Json.stringify(body)
// calculate hash with body content here
false
}
}
object MyController extends Controller {
import CompilationUtils._
// Your HasToken Action
def Authenticate(action: Token => EssentialAction) = EssentialAction { requestHeader =>
action(authenticate)(requestHeader) // your execute logic to verify authenticity using requestHeader
}
// Your action to validate tampering of request body and validity of JSON
def Validate[A, B](request: Request[A])(implicit formatA: Format[A], formatB: Format[B]): Either[Result, B] = {
val body = request.body
val bodyAsJsValue = Json.toJson(body)
if (!isTampered(bodyAsJsValue)) {
bodyAsJsValue.validate[B].fold(
valid = res => Right(res),
invalid = err => Left(BadRequest(err.toString))
)
} else {
Left(BadRequest) // Request Tampered
}
}
def getUser(userId: String) = Authenticate { token =>
Action(parse.json) { request =>
Validate(request).fold(
badReq => badReq,
user =>
// continue...
Ok("")
)
}
}
}

Play Framework 2.2 action composition returning a custom object

I am trying to create a custom play.api.mvc.Action which can be used to populate a CustomerAccount based on the request and pass the CustomerAccount into the controller.
Following the documentation for Play 2.2.x I've created an Action and ActionBuilder but I cannot seem to return the CustomerAccount from within the action.
My current code is:
case class AccountWrappedRequest[A](account: CustomerAccount, request: Request[A]) extends WrappedRequest[A](request)
case class Account[A](action: Action[A]) extends Action[A] {
lazy val parser = action.parser
def apply(request: Request[A]): Future[SimpleResult] = {
AccountService.getBySubdomain(request.host).map { account =>
// Do something to return the account like return a new AccountWrappedRequest?
action(AccountWrappedRequest(account, request))
} getOrElse {
Future.successful(NotFound(views.html.account_not_found()))
}
}
}
object AccountAction extends ActionBuilder[AccountWrappedRequest] {
def invokeBlock[A](request: Request[A], block: (AccountWrappedRequest[A]) => Future[SimpleResult]) = {
// Or here to pass it to the next request?
block(request) // block(AccountWrappedRequest(account??, request))
}
override def composeAction[A](action: Action[A]) = Account(action)
}
Note: This will not compile because the block(request) function is expecting a type of AccountWrappedRequest which I cannot populate. It will compile when using a straight Request
Additionally...
Ultimately I want to be able to combine this Account action with an Authentication action so that the CustomerAccount can be passed into the Authentication action and user authentication can be provided based on that customer's account. I would then want to pass the customer account and user into the controller.
For example:
Account(Authenticated(Action))) { request => request.account; request.user ... } or better yet as individual objects not requiring a custom request object.
I'm not sure if this is the best way to do it but I have managed to come up with a solution that seems to work pretty well.
The key was to match on the request converting it into an AccountWrappedRequest inside invokeBlock before passing it on to the next request. If another Action in the chain is expecting a value from an earlier action in the chain you can then similarly match the request converting it into the type you need to access the request parameters.
Updating the example from the original question:
case class AccountWrappedRequest[A](account: CustomerAccount, request: Request[A]) extends WrappedRequest[A](request)
case class Account[A](action: Action[A]) extends Action[A] {
lazy val parser = action.parser
def apply(request: Request[A]): Future[SimpleResult] = {
AccountService.getBySubdomain(request.host).map { account =>
action(AccountWrappedRequest(account, request))
} getOrElse {
Future.successful(NotFound(views.html.account_not_found()))
}
}
}
object AccountAction extends ActionBuilder[AccountWrappedRequest] {
def invokeBlock[A](request: Request[A], block: (AccountWrappedRequest[A]) => Future[SimpleResult]) = {
request match {
case req: AccountRequest[A] => block(req)
case _ => Future.successful(BadRequest("400 Invalid Request"))
}
}
override def composeAction[A](action: Action[A]) = Account(action)
}
Then inside the apply() method of another Action (the Authenticated action in my case) you can similarly do:
def apply(request: Request[A]): Future[SimpleResult] = {
request match {
case req: AccountRequest[A] => {
// Do something that requires req.account
val user = User(1, "New User")
action(AuthenticatedWrappedRequest(req.account, user, request))
}
case _ => Future.successful(BadRequest("400 Invalid Request"))
}
}
And you can chain the actions together in the ActionBuilder
override def composeAction[A](action: Action[A]) = Account(Authenticated(action))
If AuthenticatedWrappedRequest is then passed into the controller you would have access to request.account, request.user and all the usual request parameters.
As you can see there are a couple of cases where the response is unknown which would generate a BadRequest. In reality these should never get called as far as I can tell but they are in there just incase.
I would love to have some feedback on this solution as I'm still fairly new to Scala and I'm not sure if there might be a better way to do it with the same result but I hope this is of use to someone too.
I wrote a standalone small (ish) example that does what you're looking for:
https://github.com/aellerton/play-login-example
I gave up trying to use the Security classes that exist in the play framework proper. I'm sure they're good, but I just couldn't understand them.
Brief guide...
In the example code, a controller is declared as using the AuthenticatedRequests trait:
object UserSpecificController extends Controller with AuthenticatedRequests {
...
}
Forcing any page to require authentication (or redirect to get it) is done with the RequireAuthentication action:
def authenticatedIndex = RequireAuthentication { implicit request: AuthenticatedRequest[AnyContent] =>
Ok("This content will be accessible only after logging in)
}
Signing out is done by using the AbandonAuthentication action:
def signOut = AbandonAuthentication { implicit request =>
Ok("You're logged out.").withNewSession
}
Note that for this to work, you must override methods from the AuthenticatedRequests trait, e.g.:
override def authenticationRequired[A](request: Request[A]): Future[SimpleResult] = {
Future.successful(
Redirect(routes.LoginController.showLoginForm).withSession("goto" -> request.path)
)
}
There's more to it; best to see the code.
HTH
Andrew