0
votes

I am using Scala & Play 2.0

I have a URL as such:

/v1/people/search/MTIyOjg3YjYzNmU1YTk5MDdkYjY2MzAyODZiM2RkNTMzYzYw

In my routs file:

POST /v1/people/search/:authToken controllers.v1.Application.searchPeople(authToken: String)

What I am trying to do is create a secure trait and take the :aythToken and validate via a custom Action that replaces it in my function/method call:

Make Action a CustomAuthAction

def searchPeople(authToken: String) = Action { implicit request => }

So that I can reject a request based on the already parsed :authToken without setting this value as a cookie, I already know how to that.

So how can I take the already passed by play :authToken and pass it to a custom action?

Thanks


Here is my solution:

case class CheckSessionRequest(val user: User, request: Request[AnyContent]) extends   WrappedRequest(request)

def RequireAuthToken(f: CheckSessionRequest => Result): Action[AnyContent] = {
Action { request =>
UserValidation.findUserByAuthToken(StringUtil.getAuthTokenFromURL(request.toString)).map     { user =>
f(CheckSessionRequest(user, request))
}.getOrElse( // STRANGE ERROR
Ok(Json.generate(StatusHandler("failed","Unknown Login Error","{}"))).withHeaders(
CONTENT_TYPE -> "application/json"
) 
)
}
}
2
Sorry the question is not very clear to me, can't you make your custom action a separate method and call it from searchPeople?FUD
I'm already doing it that way. Problem is I'm repeating the same logic in 15 other functions/methods and id like to clean the code up.MichaelM

2 Answers

2
votes

Let me quote the Play documentation:

Most of the requests received by a Play application are handled by an Action.

A play.api.mvc.Action is basically a (play.api.mvc.Request => play.api.mvc.Result) function that handles a request and generates a result to be sent to the client.

If you look to the Action Composition, it clearly explains you how to basically create your CustomAction:

def LoggingAction(f: Request[AnyContent] => Result): Action[AnyContent] = {
  Action { request =>
    Logger.info("Calling action")
    f(request)
  }
}

I have done a similar work, and that is the way it looks, it's a little bit more complicated but interesting

trait SecuredController[UserModel<:ValidableEmail] {

  type LoggedRequestHandler  =  UserModel =>Request[AnyContent] => Result

  def loggedUser(implicit request:Request[_]):Option[UserModel]

  def WithAuthenticatedUser( onUserAuthenticated: => LoggedRequestHandler,
                             onUserAuthenticatedButEmailNotValidated: LoggedRequestHandler,
                             onUnauthenticated: Request[AnyContent]=>Result) = {
    Action{
     implicit request =>
        loggedUser match{
          case Some(user) if (user.emailValidated) => onUserAuthenticated(user)(request)
          case Some(user) if(!user.emailValidated) => onUserAuthenticatedButEmailNotValidated(user)(request)
          case None => onUserNotLogged(request)
        }
    }
  }

This trait basically define two methods:

  • One which is able to determine if there is a current logged user from the request (typically using the session)
  • One which basically create an Action with three possible outcomes:
    • onUserAuthenticated in case there is a logged user with email validated
    • onUserAuthenticatedButEmailNotValidated if there is a logged but the email is not validated
    • onUserNotLoggedif there is a logged but the email is not validated

LoggedRequestHandler is just a type alias for a function which take my user UserModel and creates a function Request => Result

Once you have this, you can basically write the following

object EmployeeController extends SecuredController[Employee]{

    def loggedUser(implicit request:Request[_]):Option[Employee]

    def ViewPay = WithAuthenticatedUser{
       ShowPay, 
       ShowEmailNotValidated,
       ShowUserNotLogged        
    }

    def ShowPay(employee:Employee): Request[AnyContent] => Result = {

    }
    def ShowEmailNotValidated(employee:Employee): Request[AnyContent] => Result = {

    }

    def ShowUserNotLogged: Request[AnyContent] => Result = {

    }



}
1
votes

Here is the solution I came up with, works perfectly.

case class CheckSessionRequest(val user: User, request: Request[AnyContent]) extends   WrappedRequest(request)

def RequireAuthToken(f: CheckSessionRequest => Result): Action[AnyContent] = {
    Action { request =>
     UserValidation.findUserByAuthToken(StringUtil.getAuthTokenFromURL(request.toString)).map     { user =>
      f(CheckSessionRequest(user, request))
     }.getOrElse( // STRANGE ERROR
      Ok(Json.generate(StatusHandler("failed","Unknown Login Error","{}"))).withHeaders(
       CONTENT_TYPE -> "application/json"
      ) 
    )
  }
}

Then replace your "Action" call with RequireAuthToken