Extend SecureActionBuilder to Validate Request Before Parsing the Body - scala

I am working on writing a web application that takes multipart files as input and uploads them to an S3 instance. Since some of the files can be very large, I am using a custom Body Parser to send the chunks to S3 as they come in.
I want to do validation on the request prior to uploading the file (to check that the user has permission/enough space, etc). From reading the Play docs, it seems like extending ActionBuilder is the right way to do this. I noticed in SecureSocial, there is a SecureActionBuilder and I believe I should extend this in order to build a secure action (which is what I want).
I tried this simple test to see if I could print out the userId, therefore being able to perform actions based on the user.
object FileValidationAction extends SecuredActionBuilder {
def invokeBlock[A](request: SecuredRequest[A], block: SecuredRequest[A] => Future[SimpleResult]) = {
Logger.info("User id is " + request.user.userProfile.userId)
block(request)
}
}
However, the method never got called.
Next, I tried overriding the method from the SecuredActionBuilder object:
object FileValidationAction extends SecuredActionBuilder {
override def invokeBlock[A](request: Request[A], block: SecuredRequest[A] => Future[SimpleResult]) = {
val securedResult: Option[SecuredRequest[A]] = request match {
case r: SecuredRequest[A] => Option(r)
case _ => None
}
Logger.info("Calling action ------- WOO!")
securedResult match {
case Some(r) =>
block(r)
case _ =>
Future.successful(Forbidden)
}
}
}
That method gets called but the request coming in is not a SecuredRequest as I was hoping.
How do I build a SecuredAction, using a custom body parser, that I can do validation on before it completes (or even starts) the upload?
EDIT:
To clarify, I will be calling the Action with the following method signature:
def upload = FileValidationAction(streamingBodyParser(streamConstructor)) { request =>

The problem is that you are not invoking the original code in SecuredActionBuilder that actually checks if the user is there and constructs the SecuredRequest instance.
Something like this should work:
// A sample usage of the action
def checkFile = FileValidationAction { request =>
Ok("")
}
// The builder implementation
object FileValidationAction extends FileValidationActionBuilder {
def apply[A]() = new FileValidationActionBuilder()
}
class FileValidationActionBuilder(authorize: Option[Authorization[DemoUser]] = None) extends SecuredActionBuilder(authorize) {
def validateFile[A](block: SecuredRequest[A] => Future[SimpleResult]): SecuredRequest[A] => Future[SimpleResult] = { securedRequest =>
Logger.info(s"User id is ${securedRequest.user.main.userId}")
block(securedRequest)
}
override def invokeBlock[A](request: Request[A], block: (SecuredRequest[A]) => Future[SimpleResult]): Future[SimpleResult] = {
invokeSecuredBlock(authorize, request, validateFile(block))
}
}
You would need to add this inside the controller where you plan to use this actions. If you need to use it in multiple controllers then create a trait that you can extend with this.
Also note that in this sample code I am using the DemoUser type I have in the samples. You will need to change that to the type you are using in your app to represent users.

Related

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))
}

How To Use EssentialAction

I have a custom body parser that streams the request body to an Amazon S3 instance and I want to do validation prior to uploading the file.
In the headers of the request I have access to the content size and the user's auth token. With these two things, I can validate that the user has permission to upload the file.
After reading Play!'s documentation, it seems that EssentialAction or Action Composition is the way to go.
Here is my controller method without any decoration:
def upload = Action(streamingBodyParser(streamConstructor)) { request =>
...
}
I've tried creating a custom essential action and using that before my body parsing action but I couldn't quite get the syntax right:
class HasEnoughSpace(action: EssentialAction) extends EssentialAction {
override def apply(request: RequestHeader): EssentialAction = {
val maybeToken = request.headers.get("X-Auth-Token")
maybeToken match {
case Some(t) => {
}
case _ =>
}
}
}
def upload = HasEnoughSpace { Action(streamingBodyParser(streamConstructor)) { request =>
...
}
I also tried using the new ActionBuilder method but that still parsed the body prior to allowing me to do validation.
Is there a way to accomplish this with EssentialAction? Is there a better way to accomplish my goal of validating the headers prior to parsing the body?
Yes, that's exactly what EssentialAction is meant to be used for. It enables you to inspect the request header, but ignoring the body, what should be sufficient for your case.
EssentialActions are bascially just functions taking a RequestHeader and returning an Iteratee[Array[Byte], Result]:
trait EssentialAction extends (RequestHeader => Iteratee[Array[Byte], Result]) with Handler
Therefore it would be easier if you rewrite your HasEnoughSpace action to be a function that creates an EssentialAction instead of trying to extend the EssentialAction trait:
def HasEnoughSpace(action: EssentialAction): EssentialAction = EssentialAction { request =>
val token = request.headers.get("X-Auth-Token")
val contentSize = request.headers.get(play.api.http.HeaderNames.CONTENT_LENGTH)
(token, contentSize) match {
// perform validation if both headers are present
case (Some(_), Some(len)) if validateContentLength(len) => {
action(request)
}
case _ =>
// replace with your client error
Done(Unauthorized("401 No Token\n"))
}
}
In the above code you'd still need to write the validateContentLength function.

Scala/Implicit/Playframework/Action: Wrapping Action Function

It's exactly a Scala question, but it is in the context of the PlayFramework Action:
An Action takes something in the form (play.api.mvc.Request => play.api.mvc.Result). And then you'll pass a block like:
{ request =>
Ok(views.html.index("Your new application is ready."))
}
Now I have defined a function with the same signature. One with and one without implicit for request.
def WithUser2(request: Request[AnyContent])(block: (Request[AnyContent]) => Result):Result = {
block(request)
}
def WithUser3(block: (Request[AnyContent]) => Result)(implicit request: Request[AnyContent]):Result = {
block(request)
}
I'm calling it this way now (2 versions):
def index2 = Action { implicit request => WithUser2(request) { request =>
Ok(views.html.index("Your new application is ready. " ))
}}
def index3 = Action ( implicit request => WithUser3 { request =>
Ok(views.html.index("Your new application is ready. " ))
})
Is there a way to directly attach my function to Action? I mean without placing implicit request => before WithUserX so that my function replaces the complete block after Action directly? Now my function is inside the block and I think this is the problem why I need to pass request forward. So I need a way to get rid of the block around my function.
It's not 100% clear what you're trying to do, because WithUserX essentially does nothing special. It seems like you're trying to make your own Action, and for that we have Action Composition.
If you were to define a new Action like this:
object WithUser extends ActionBuilder[Request] {
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
// Do something special maybe?
block(request)
}
}
You could then use it like this:
def index = WithUser { request =>
...
Ok("Ok!")
}
This would be much better than trying to stuff it within the stock Action, when it doesn't need to be.

How to wrap Actions (in any order) when using Play's ActionBuilder?

I am using Play's ActionBuilder to create various Actions that secure my controllers. For instance, I implemented IsAuthenticated to make sure that certain actions can only be accessed if the user would be logged in:
case class AuthRequest[A](user: String, request: Request[A]) extends WrappedRequest[A](request)
private[controllers] object IsAuthenticated extends ActionBuilder[AuthRequest] {
def invokeBlock[A](req: Request[A], block: (AuthRequest[A]) => Future[SimpleResult]) = {
req.session.get("user").map { user =>
block(new AuthRequest(user, req))
} getOrElse {
Future.successful(Results.Unauthorized("401 No user\n"))
}
}
}
Using IsAuthenticated I can (1) restrict an action to users who are logged in, and (b) access the user being logged in:
def auth = IsAuthenticated { implicit authRequest =>
val user = authRequest.user
Ok(user)
}
Furthermore, I use ActionBuilder HasToken to ensure that an action was invoked with a token being present in the request's header (and, I can access the token value):
case class TokenRequest[A](token: String, request: Request[A]) extends WrappedRequest[A](request)
private[controllers] object HasToken extends ActionBuilder[TokenRequest] {
def invokeBlock[A](request: Request[A], block: (TokenRequest[A]) => Future[SimpleResult]) = {
request.headers.get("X-TOKEN") map { token =>
block(TokenRequest(token, request))
} getOrElse {
Future.successful(Results.Unauthorized("401 No Security Token\n"))
}
}
}
That way, I can make sure that an action was called with that token present:
def token = HasToken { implicit tokeRequest =>
val token = tokeRequest.token
Ok(token)
}
So far, so good...
But, how could I wrap (or, nest / compose) such actions as those defined above? For instance, I would like to ensure (a) that a user would be logged in and (b) that the token would be present:
def tokenAndAuth = HasToken { implicit tokeRequest =>
IsAuthenticated { implicit authRequest =>
val token = tokeRequest.token
val user = authRequest.user
}
}
However, the above action does not compile. I tried many different implementations but always failed to achieve the desired behavior.
In general terms: How could I compose Actions defined using Play's ActionBuilder in arbitrary order? In the above example, it would not matter if I would wrap IsAuthenticated in HasToken or the other way around -- the effect would be the same: the user would have to be logged in and would have to present the token.
Note: I have created a Gist that provides the complete source code.
ActionBuilder
ActionBuilders are not made for ad-hoc composition, but rather to build a hierarchy of actions so you end up using only a couple of actions throughout your controllers.
So in your example you should build IsAuthenticated on top of HasToken as I illustrated here.
This is a viable solution and can actually simplify your code. How often do you really need to compose on the spot?
EssentialAction
Ad-hoc composition could be achieved with EssentialActions (simply because they haven't changed from 2.1), but they have a few downsides, as Johan pointed out. Their API is not really intended for ad-hoc use either, and Iteratees are too low-level and too cumbersome for controller actions.
Actions
So finally your last option would be to write Actions directly. Actions do not support passing a WrappedRequest by default (that's why ActionBuilder exists). However you can still pass a WrappedRequest and have the next Action deal with it.
The following is the best I have come up with so far and is rather fragile I guess.
case class HasToken[A](action: Action[A]) extends Action[A] {
def apply(request: Request[A]): Future[SimpleResult] = {
request.headers.get("X-TOKEN") map { token =>
action(TokenRequest(token, request))
} getOrElse {
Future.successful(Results.Unauthorized("401 No Security Token\n"))
}
}
lazy val parser = action.parser
}
case class IsAuthenticated[A](action: Action[A]) extends Action[A] {
def apply(request: Request[A]): Future[SimpleResult] = {
request.session.get("user").map { user =>
action(new AuthRequest(user, request))
} getOrElse {
Future.successful(Results.Unauthorized("401 No user\n"))
}
}
lazy val parser = action.parser
}
object ActionComposition extends Controller {
def myAction = HasToken {
Action.async(parse.empty) { case TokenRequest(token, request) =>
Future {
Ok(token)
}
}
}
def myOtherAction = IsAuthenticated {
Action(parse.json) { case AuthRequest(user, request) =>
Ok
}
}
def both = HasToken {
IsAuthenticated {
Action(parse.empty) { case AuthRequest(user, request: TokenRequest[_]) =>
Ok(request.token)
}
}
}
}
Results
You can also compose at the Result level and only use the built-in actions. This is especially useful when trying to factor out error handling and other repetitive stuff. I have an example here.
Conclusion
We are still missing the capabilities that Play 2.1's action composition offered. So far to me it seems that ActionBuilder + Result composition is the winner as its successor.
The output from an action builder is an action, an action is essentially a function from request => future result, so you can actually just call it like this:
def tokenAndAuth = HasToken.async { implicit tokenRequest =>
IsAuthenticated { implicit authRequest =>
val token = tokenRequest
val user = authRequest.user
Ok("woho!")
}(tokenRequest) // <-- call the inner action yourself, returns Future[SimpleResult]
}
There may be problems if there is a body that you want to parse though, I think it will be parsed by the body parser of the outer request but I'm not sure what the inner one will do if you would specify a parser.
The reason why you cannot do this in a simpler way is that you do not only want to compose logic but also pass data into each action, if you had actions that would just bail on missing auth or token you could absolutely compose them as the play docs on action composition describes.
Side note: since you are only looking at headers and not accessing the body in either of your custom action decorators it might be a better idea to look into EssentialAction:s as those would let you reject the request without first parsing the body when auth or token is missing.

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