I'm trying to implement authentication in my Play 2.2.1 app, and I can't quite figure out how to make it work with an action that returns a Future[Result].
This post describes pretty close to what I'm trying to do, except without returning Future[Result]:
Play 2.0 Framework, using a BodyParser with an authenticated request
How can I get it to work with Futures? I.e. how would I implement this function:
def IsAuthenticated(f: => String => Request[Any] => Future[Result])
or, better yet, this function:
def IsAuthenticated[A}(b:BodyParser[A])(f: => String => Request[Any] => Future[Result])
which would feed into this function:
def AuthenticatedUser(g: Account => Request[AnyContent] => SimpleResult) = IsAuthenticated {...}
to wrap asynchronous actions in my controllers?
This part I can do:
def IsAuthenticated(f: => String => Request[AnyContent] => Future[SimpleResult]) = {
Security.Authenticated(email, onUnauthorized) {
user => Action.async(request => f(user)(request))
}
}
But if I try to use IsAuthenticated in my wrapper function:
def AuthenticatedUser(g: Account => Request[AnyContent] => Future[SimpleResult]) = IsAuthenticated {
email => implicit request => Account.find(email).map {
opt => opt match {
case Some(account) => g(account)(request)
case None => Future(onUnauthorized(request))
}
}
}
(Account.find returns a Future[Option[Account]] 'cause it's a mongodb call that may take some time. The desire to do the future thing right is what's causing me so much grief now)
I can't get AuthenticatedUser to satisfy the compiler. It says it's getting a Future[Future[SimpleResult]] instead of a Future[SimpleResult].
So, how best to build this whole thing? I need to be able to make authentication wrappers that rely on db calls that are asynchronous.
I'm sure I'm just dense and missing something obvious...
EDIT: Here's what I ended up with. Thank you Jean for pointing me in the right direction.
I found AuthenticatedController while rooting around and it's pretty close to what I'm looking for. I wanted two types of authentication: User (authenticated user) and Administrator (to wrap code for admin tasks).
package controllers
import models.Account
import play.api.mvc._
import scala.concurrent.Future
trait Secured {
class AuthenticatedRequest[A](val account: Account, request: Request[A]) extends WrappedRequest[A](request)
object User extends ActionBuilder[AuthenticatedRequest] {
def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[SimpleResult]) = {
request.session.get("email") match {
case Some(email) => {
Account.find(email).flatMap {
case Some(account) => {
block(new AuthenticatedRequest(account, request))
}
case _ => Future(Results.Redirect(routes.Index.index()))
}
}
case _ => Future(Results.Redirect(routes.Index.index()))
}
}
}
object Administrator extends ActionBuilder[AuthenticatedRequest] {
def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[SimpleResult]) = {
request.session.get("email") match {
case Some(email) => {
Account.find(email).flatMap {
case Some(account) => if (account.admin) {
block(new AuthenticatedRequest(account, request))
} else {
Future(Results.Redirect(routes.Index.index()))
}
case _ => Future(Results.Redirect(routes.Index.index()))
}
}
case _ => Future(Results.Redirect(routes.Index.index()))
}
}
}
}
There have been changes in play 2.2 to make it easier to compose actions. The resource you are referring to is outdated.
Instead you should create a custom action builder by extending ActionBuilder to create your action, this will get you all the fancy apply methods you may need (including async support and all)
For example you may do :
trait MyAction extends Results{
class MyActionBuilder[A] extends ActionBuilder[({ type R[A] = Request[A] })#R] {
def invokeBlock[A](request: Request[A],
block: Request[A] => Future[SimpleResult]) ={
// your authentication code goes here :
request.cookies.get("loggedIn").map { _=>
block(request)
} getOrElse Future.successful(Unauthorized)
}
}
object MyAction extends MyActionBuilder
}
which you can then use as such :
object MyController extends Controller with MyAction{
def authenticatedAction=MyAction {
Ok
}
def asyncAuthenticatedAction=MyAction.async {
Future.successful(Ok)
}
def authenticatedActionWithBodyParser = MyAction(parse.json){ request =>
Ok(request.body)
}
}
For brevity's sake I used a very trivial authentication mechanism you will want to change that :)
Additionally, you can create a custom "request" type to provide additional information. For instance you could define a AuthenticatedRequest as such :
case class AuthenticatedRequest[A](user: User, request: Request[A]) extends WrappedRequest(request)
Provided you have a way to get your user such as
object User{
def find(s:String): Option[User] = ???
}
Then change your builder definition a bit as such
class MyActionBuilder[A] extends
ActionBuilder[({ type R[A] = AuthenticatedRequest[A] })#R] {
def invokeBlock[A](request: Request[A],
block: AuthenticatedRequest[A] => Future[SimpleResult]) ={
// your authentication code goes here :
(for{
userId <- request.cookies.get("userId")
user <- User.find(userId.value)
}yield {
block(AuthenticatedRequest(user,request))
}) getOrElse Future.successful(Unauthorized)
}
}
Your controller now has access to your user in authenticatedActions:
object MyController extends Controller with MyAction{
val logger = Logger("application.controllers.MyController")
def authenticatedAction=MyAction { authenticatedRequest =>
val user = authenticatedRequest.user
logger.info(s"User(${user.id} is accessing the authenticatedAction")
Ok(user.id)
}
def asyncAuthenticatedAction = MyAction.async { authenticatedRequest=>
Future.successful(Ok(authenticatedRequest.user.id))
}
def authenticatedActionWithBodyParser = MyAction(parse.json){ authenticatedRequest =>
Ok(authenticatedRequest.body)
}
}
Related
In my Play application, I service my requests usings cats-effect's IO, instead of Future in the controller, like this (super-simplified):
def handleServiceResult(serviceResult: ServiceResult): Result = ...
def serviceMyRequest(request: Request): IO[ServiceResult] = ...
def myAction = Action { request =>
handleServiceResult(
serviceMyRequest(request).unsafeRunSync()
)
}
Requests are then processed (asynchronously) on Play's default thread pool. Now, I want to implement multiple thread pools to handle different sorts of requests. Were I using Futures, I could do this:
val myCustomExecutionContext: ExecutionContext = ...
def serviceMyRequest(request: Request): Future[ServiceResult] = ...
def myAction = Action.async { request =>
Future(serviceMyRequest(request))(myCustomExecutionContext)
.map(handleServiceResult)(defaultExecutionContext)
}
But I'm not using Futures, I'm using IO, and I'm not sure about the right way to go about implementing it. This looks promising, but seems a bit clunky:
def serviceMyRequest(request: Request): IO[ServiceResult] = ...
def myAction = Action { request =>
val ioServiceResult = for {
_ <- IO.shift(myCustomExecutionContext)
serviceResult <- serviceMyRequest(request)
_ <- IO.shift(defaultExecutionContext)
} yield {
serviceResult
}
handleServiceResult(ioServiceResult.unsafeRunSync())
}
Is this the right way to implement it? Is there a best practice here? Am I screwing up badly? Thanks.
Ok, so since this doesn't seem to be well-trodden ground, this is what I ended up implementing:
trait PlayIO { self: BaseControllerHelpers =>
implicit class IOActionBuilder[A](actionBuilder: ActionBuilder[Request, A]) {
def io(block: Request[A] => IO[Result]): Action[A] = {
actionBuilder.apply(block.andThen(_.unsafeRunSync()))
}
def io(executionContext: ExecutionContext)(block: Request[A] => IO[Result]): Action[A] = {
val shiftedBlock = block.andThen(IO.shift(executionContext) *> _ <* IO.shift(defaultExecutionContext))
actionBuilder.apply(shiftedBlock.andThen(_.unsafeRunSync()))
}
}
}
Then (using the framework from the question) if I mix PlayIO into the controller, I can do this,
val myCustomExecutionContext: ExecutionContext = ...
def handleServiceResult(serviceResult: ServiceResult): Result = ...
def serviceMyRequest(request: Request): IO[ServiceResult] = ...
def myAction = Action.io(myCustomExecutionContext) { request =>
serviceMyRequest(request).map(handleServiceResult)
}
such that I execute the action's code block on myCustomExecutionContext and then, once complete, thread-shift back to Play's default execution context.
Update:
This is a bit more flexible:
trait PlayIO { self: BaseControllerHelpers =>
implicit class IOActionBuilder[R[_], A](actionBuilder: ActionBuilder[R, A]) {
def io(block: R[A] => IO[Result]): Action[A] = {
actionBuilder.apply(block.andThen(_.unsafeRunSync()))
}
def io(executionContext: ExecutionContext)(block: R[A] => IO[Result]): Action[A] = {
if (executionContext == defaultExecutionContext) io(block) else {
val shiftedBlock = block.andThen(IO.shift(executionContext) *> _ <* IO.shift(defaultExecutionContext))
io(shiftedBlock)
}
}
}
}
Update2:
Per the comment above, this will ensure we always shift back to the default thread pool:
trait PlayIO { self: BaseControllerHelpers =>
implicit class IOActionBuilder[R[_], A](actionBuilder: ActionBuilder[R, A]) {
def io(block: R[A] => IO[Result]): Action[A] = {
actionBuilder.apply(block.andThen(_.unsafeRunSync()))
}
def io(executionContext: ExecutionContext)(block: R[A] => IO[Result]): Action[A] = {
if (executionContext == defaultExecutionContext) io(block) else {
val shiftedBlock = block.andThen { ioResult =>
IO.shift(executionContext).bracket(_ => ioResult)(_ => IO.shift(defaultExecutionContext))
}
io(shiftedBlock)
}
}
}
}
I really like play framework 2.3's ActionBuilder and the andThen method that allows you to dynamically compose actions.
Here's a snippet of how I want to use action composition:
def showHomepage = RedirectingAction andThen
AuthenticatedAction andThen NotificationAction async { request =>
Future {
Ok(views.html.homepage.render(request.user, request.notifications ))
}
}
As you can guess, NotificationAction depends on AuthenticatedAction, and hence needs AuthenticatedRequest which contains the User object.
The code complains on:
object NotificationAction extends ActionBuilder[NotificationAuthRequest] {
def invokeBlock[A](request: AuthenticatedRequest[A], block: (NotificationAuthRequest[A]) => Future[Result]) = { ...
The error is:
object creation impossible, since method invokeBlock in trait ActionFunction of type [A](request: play.api.mvc.Request[A], block: controllers.v3.ScalaHomepageController.NotificationAuthRequest[A] => scala.concurrent.Future[play.api.mvc.Result])scala.concurrent.Future[play.api.mvc.Result] is not defined
Apparently it allows only:
def invokeBlock[A](request: Request[A], block: ...
but not:
def invokeBlock[A](request: AuthenticatedRequest[A], block: ...
I'd really appreciate if someone could throw light on this. Maybe my approach is wrong, but I don't like the idea of precomposed actions (like using ActionFunction) because I could have more actions that I could be mixing in at a later point.
Here's the code:
case class AuthenticatedRequest[A](val user: Option[User], request: Request[A]) extends WrappedRequest(request)
case class NotificationAuthRequest[A](val user: Option[User], val notifications: Option[List[UserNotificationData]], request: Request[A]) extends WrappedRequest(request)
object RedirectingAction extends ActionBuilder[Request] {
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
Future {
Redirect(REDIRECT_URL + request.uri + paramString)
}
}
}
object AuthenticatedAction extends ActionBuilder[AuthenticatedRequest] {
def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[Result]) = {
request.cookies.get("uid") map {
cookie =>
val user = userClient.getUserById(userId(cookie)).get
block(AuthenticatedRequest[A](user, request))
} getOrElse {
block(AuthenticatedRequest[A](userClient.getUserById(uid).get, request))
}
}
def userId(cookie: Cookie) = {
if(AppUtil.isProd) cookie.value else IMPERSONATE_ID.getOrElse(cookie.value)
}
}
object NotificationAction extends ActionBuilder[NotificationAuthRequest] {
def invokeBlock[A](request: AuthenticatedRequest[A], block: (NotificationAuthRequest[A]) => Future[Result]) = {
request.user.map {
user => block(NotificationAuthRequest[A](Some(user), userClient.getNotifications(user.getId).get.map(_.toList), request))
}.getOrElse {
block(NotificationAuthRequest[A](None, None, request))
}
}
}
Reading the docs I think you need to have ActionRefiners and ActionTransformers.
This is what I came up with:
package controllers
import play.api.mvc._
import scala.concurrent.Future
case class User(id: Long)
case class UserNotificationData(text: String)
case class AuthRequest[A](user: Option[User], request: Request[A]) extends WrappedRequest(request)
case class AuthNotificationRequest[A](user: Option[User], notifications: Option[List[UserNotificationData]], request: Request[A]) extends WrappedRequest(request)
object RedirectingAction extends ActionBuilder[Request] {
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
block(request)
}
}
object AuthenticatedAction extends ActionBuilder[AuthRequest] with ActionTransformer[Request, AuthRequest] {
def transform[A](request: Request[A]) = Future.successful {
request.cookies.get("uid") map {
cookie =>
val user = Some(User(1))
AuthRequest[A](user, request)
} getOrElse {
AuthRequest[A](Some(User(1)), request)
}
}
}
object WithNotifications extends ActionTransformer[AuthRequest, AuthNotificationRequest] {
def transform[A](request: AuthRequest[A]) = Future.successful {
request.user.map { user => AuthNotificationRequest[A](Some(user), Some(List(UserNotificationData("Notification"))), request)} getOrElse {
AuthNotificationRequest[A](None, None, request)
}
}
}
object Application extends Controller {
def index = (RedirectingAction andThen AuthenticatedAction andThen WithNotifications) { request: AuthNotificationRequest[AnyContent] =>
Ok(views.html.index("Your new application is ready."))
}
}
I am trying to create an ActionBuilder which checks if the user is loggedin and if so, add the user object to the request(AuthenticatedRequest). With MySQL this would be easy because resolving the user would not get a Future object. But in this particular case, we use MongoDB with ReactiveMongo for Play, which does return a future value.
I have made this little snippet here so far. But it gets me a type mismatch:
type mismatch; found : scala.concurrent.Future[Option[models.User]] => scala.concurrent.Future[Object] required: Object => ?
object Authenticated extends ActionBuilder[AuthenticatedRequest] {
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[SimpleResult]) = {
import models.User
(for{
sID <- request.session.get("sessionID")
code <- request.session.get("otherCode")
user: Future[Option[User]] <- models.Session.getUserBySessionAndCode(sID, code)
} yield {
(for{
uAbs <- user
} yield {
if(uAbs.isDefined) {
block(AuthenticatedRequest(uAbs.get, request))
}else{
BadRequest
}
})
}).getOrElse(Future.successful(BadRequest))
}
}
Do you have any idea how to move on from here? Maybe this is even the wrong approach.
Thanks!
How about separating the steps into smaller chunks and explicitly typing them the way you expect the types should be, this way it will be clearer and you will find out where your idea and what you have written goes in different directions, for example:
def userFromRequest(request: Request): Future[Option[User]] =
for{
sID <- request.session.get("sessionID")
code <- request.session.get("otherCode")
maybeUser <- models.Session.getUserBySessionAndCode(sID, code)
} yield maybeUser
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[SimpleResult]) = {
userFromRequest(request).flatMap {
case None => Future.successful(BadRequest)
case Some(user) => block(AuthenticatedRequest(user, request))
}
}
I'm trying to learn to use Action composition in the Play framework in Scala.
I understand the ability to do basic action composition and WrappedRequests, my question however is : Is it possible to access the contents of multiple WrappedRequests in a composed Action?
Let me explain, I have the following code:
class RequestWithUser[A](val user: models.User, request: Request[A]) extends WrappedRequest[A](request)
def UserAction(userId: Long) = new ActionBuilder[RequestWithUser] {
def invokeBlock[A](request: Request[A], block: (RequestWithUser[A]) => Future[SimpleResult]) = {
models.UsersDAO.findById(userId).map { user =>
block(new RequestWithUser(user, request))
} getOrElse {
Future.successful(NotFound)
}
}
}
case class AuthenticatedRequest[A](user: models.User, request: Request[A]) extends WrappedRequest[A](request)
object Authorized extends ActionBuilder[AuthenticatedRequest] {
def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[SimpleResult]) = {
userTokenForm.bindFromRequest()(request).fold(
formWithErrors => {
resolve(Results.Unauthorized(formWithErrors.errorsAsJson))
},
userData => {
models.UsersDAO.findByToken(userData.token) map { user=>
block(AuthenticatedRequest(user, request))
} getOrElse {
resolve(Results.Unauthorized("Token matched no one."))
}
}
)
}
}
I'd like to be able to compose them into a third action potentially called "UserPermissionAction" which composes Authorized and UserAction.
It should check if the Authorized user is the same as user in the RequestWithUser (i.e. only allow users to edit themselves).
Is this possible?
Here is what I ended up doing.
it works but I'd like to see feedback if there is more idiomatic Scala way:
/** Contains the security token, extracted from the RequestHeader */
case class AuthenticatedRequest[A](user: models.User, request: Request[A]) extends WrappedRequest[A](request)
case class AuthorizedAction[A](action: Action[A]) extends Action[A] {
lazy val parser = action.parser
def apply(request: Request[A]): Future[SimpleResult] = {
userTokenForm.bindFromRequest()(request).fold(
formWithErrors => {
resolve(Results.Unauthorized(formWithErrors.errorsAsJson))
},
userData => {
models.UsersDAO.findByToken(userData.token) map { user=>
action(AuthenticatedRequest(user, request))
} getOrElse {
resolve(Results.Unauthorized("Token matched no one."))
}
}
)
}
}
class RequestWithUser[A](val user: models.User, request: Request[A]) extends WrappedRequest[A](request)
case class UserAction[A](userId: Long, action: Action[A]) extends Action[A] {
lazy val parser = action.parser
def apply(request: Request[A]): Future[SimpleResult] = {
models.UsersDAO.findById(userId).map { user =>
action(new RequestWithUser(user, request))
} getOrElse {
Future.successful(NotFound)
}
}
}
def UserHasPermission[A](userId: Long) = new ActionBuilder[AuthenticatedRequest] {
def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[SimpleResult]) = {
request match {
case req: AuthenticatedRequest[A] => {
{
for (
authUserId <- req.user.id
if authUserId == userId
) yield block(req)
} getOrElse Future.successful(Unauthorized)
}
case _ => Future.successful(BadRequest)
}
}
override def composeAction[A](action: Action[A]) = UserAction(userId, AuthorizedAction(action) )
}
I'm trying to implement an authentication mechanism similar to this example:
def HasToken(action: String => EssentialAction): EssentialAction = EssentialAction { requestHeader =>
val maybeToken = requestHeader.headers.get("X-SECRET-TOKEN")
maybeToken map { token =>
action(token)(requestHeader) // apply requestHeader to EssentialAction produces the Iteratee[Array[Byte], SimpleResult]
} getOrElse {
Done(Unauthorized("401 No Security Token\n")) // 'Done' means the Iteratee has completed its computations
}
}
However, in my case I'm mapping a random token value to a session on the server side stored in Mongodb. The goal was to be able to let a user terminate all his other sessions at will.
However, the data I get from ReactiveMongo is going to be wrapped in a Future.
I would like something like this:
def HasToken(action: String => EssentialAction): EssentialAction = EssentialAction { requestHeader =>
val maybeToken = requestHeader.headers.get("session")
maybeToken map { token =>
//This returns a future..
Session.find(session).map { result =>
result match
case Some(session) => action(session)(requestHeader)
case None => Done(Unauthorized())
}
} getOrElse {
Done(Unauthorized("401 No Security Token\n")) // 'Done' means the Iteratee has completed its computations
}
}
Is this possible with EssentialAction?
Iteratee.flatten goes from Future[Iteratee[A, E]] => Iteratee[A, E] so you could do it like this:
def HasToken(action: String => EssentialAction): EssentialAction = EssentialAction { requestHeader =>
val maybeToken = requestHeader.headers.get("session")
val futureIteratee: Future[Iteratee[Array[Byte], SimpleResult]] = maybeToken map { token =>
//This returns a future..
Session.find(token).map {
case Some(session) => action(session)(requestHeader)
case None => Done[Array[Byte], SimpleResult](Unauthorized("Invalid token"))
}
} getOrElse {
Future.successful(Done[Array[Byte], SimpleResult](Unauthorized("401 No Security Token\n")))
}
Iteratee.flatten(futureIteratee)
}
You can use an ActionBuilder as the invokeBlock method return a Future[SimpleResult] so you can flatMap your future into a call to the underlying block
Something like
object Authenticated extends ActionBuilder[AuthenticatedRequest] {
def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[SimpleResult]) = {
Session.find(session).map { result =>
result match
case Some(session) => block(new AuthenticatedRequest(session))
case None => Unauthorized()
}
}
}
where AuthenticatedRequest is your request type that wraps your session object