I've made ActionRefiner to read language of current request from parameter in url:
class LangRequest[A](val lang: Lang, request: Request[A]) extends WrappedRequest[A](request)
def LangAction(lang: String) = new ActionRefiner[Request, LangRequest] {
def refine[A](input: Request[A]) = Future.successful {
val availLangs: List[String] = Play.current.configuration.getStringList("play.i18n.langs").get.toList
if (!availLangs.contains(lang))
Left {
input.acceptLanguages.head match {
case Lang(value, _) if availLangs.contains(value) => Redirect(controllers.routes.Application.index(value))
case _ => Redirect(controllers.routes.Application.index(availLangs.head))
}
}
else Right {
new LangRequest(Lang(lang), input)
}
}
}
and try to use it in action like this:
def login(lng: String) = LangAction(lng) { implicit request: Request[AnyContent] =>
Ok("Ok")
}
And I've got
"play.api.mvc.ActionRefiner[play.api.mvc.Request,controllers.actionbuilders.LangRequest]
does not take parameters"
error at compilation time. How can I use this ActionRefiner? In PlayFramework documentation https://www.playframework.com/documentation/2.4.x/ScalaActionsComposition they show ActionRefiner usages with Filter, Transformer like this:
(UserAction andThen ItemAction(itemId) andThen PermissionCheckAction)
and this does work. But how to use one ActionRefiner?
I'm sorry; this is pretty old, but I am facing a similar problem and wanted to post the solution so others could find it. I suspect it is because the first item in the chain of actions should be an ActionBuilder, in addition to whatever else. ActionTransformer is not a subtype of ActionBuilder, though it can be an argument to ActionBuilder#andThen.
It is correct, the ActionBuilder has to be the first element in the chain based on https://www.playframework.com/documentation/2.4.x/ScalaActionsComposition#Putting-it-all-together
On the other hand, there are scenarios where we need to pass arguments to the ActionRefiner due to interactions with the database (e.g. retrieve products using id). At this point, we need to #Inject dependencies like services to get access to DB and the function is a partial solution. So, we can wrap the function in class to inject the dependencies and also inherent from ActionBuilder due to we can have only one element in the chain and we need to start with an ActionBuilder.
class UserActionBuilder #Inject()(
val defaultBodyParsers: BodyParsers.Default,
userService: UserService
)(implicit val ec: ExecutionContext) {
def refine(userEmail: String): ActionRefiner[Request, UserRequest] with ActionBuilder[UserRequest, AnyContent] =
new ActionRefiner[Request, UserRequest] with ActionBuilder[UserRequest, AnyContent] {
def executionContext: ExecutionContext = ec
override def refine[A](request: Request[A]): Future[Either[Result, UserRequest[A]]] = {
userService.retrieveUserByEmail(userEmail)
.map {
case Some(user) if user.isActive => Right(UserRequest(user, request))
case _ => Left(Results.Forbidden("User is inactive"))
}
.recover { case ex: Throwable =>
Left(Results.InternalServerError(ex.getMessage))
}
}
override def parser: BodyParser[AnyContent] = defaultBodyParsers
}
}
case class UserRequest[A](user: User, request: Request[A])
extends WrappedRequest[A](request)
And then
class UserController #Inject()(
arguments...,
userAction: UserActionBuilder
)(implicit executionContext: ExecutionContext) {
def updateUserPurchase(emailUser: String): Action[UserPurchaseRequest] = {
userAction.refine(emailUser)).async(apiParser.parserOf[UserPurchaseRequest]) { implicit request =>
...
doUpdate(request.body, request.user)
...
}
}
}
Related
I am implementing authentication based on this Scala Play Authentication example.
Therefore I use the following ActionBuilder to build an UserAction.
UserAction.scala
class UserRequest[A](val user: Option[Admin], request: Request[A]) extends WrappedRequest[A](request)
class UserAction #Inject()(adminService: AdminService, parser: BodyParsers.Default)(implicit val executionContext: ExecutionContext)
extends ActionBuilder[UserRequest, AnyContent]
with ActionTransformer[Request, UserRequest] {
def transform[A](request: Request[A]) = Future.successful {
val sessionTokenOpt = request.session.get("sessionToken")
val user = sessionTokenOpt
.flatMap(token => Sessions.getSession(token))
.filter(_.expiration.isAfter(LocalDateTime.now(ZoneOffset.UTC)))
.map(_.email)
// Signature: getAdminByEmail(email: String): Future[Option[Admin]]
.flatMap(adminService.getAdminByEmail) // <- Extract Future here
// The user has to be Option[Admin] but is Future[Option[Admin]]
// because of adminService.getAdminByEmail
new UserRequest(user, request)
}
}
Since the example implementation in line 25 does not return a Future because there is no database setup included, I get the following error:
type mismatch;
found : scala.concurrent.Future[Option[models.Tables.Admin]]
required: Option[?]
Because I am new to scala I have no clue how to extract the Future at this point. How to handle the Future result to make sure we return a Option[Admin]?
Fixed it on my own. I just divided the email and fetching into two different parts and moved the Future inside the email match.
class UserAction #Inject()(adminService: AdminService, val parser: BodyParsers.Default)(implicit val executionContext: ExecutionContext)
extends ActionBuilder[UserRequest, AnyContent]
with ActionTransformer[Request, UserRequest] {
def transform[A](request: Request[A]) : Future[UserRequest[A]] = {
val sessionTokenOpt = request.session.get("sessionToken")
val email = sessionTokenOpt
.flatMap(token => Sessions.getSession(token))
.filter(_.expiration.isAfter(LocalDateTime.now(ZoneOffset.UTC)))
.map(_.email)
email match {
case Some(value) =>
adminService.getAdminByEmail(value).map(a => new UserRequest(a, request))
case None =>
Future { new UserRequest(None, request) }
}
}
}
I am trying to create my own ActionRefiner to accommodate authentication, but for some reason the compiler won't let me use implicit variables in the refine[A] function... I have the following code:
trait Auth {
object AuthenticatedAction extends ActionBuilder[AuthRequest] with ActionRefiner[Request, AuthRequest] {
def refine[A](request: Request[A])(implicit userCollection: UserCollection, ex: ExecutionContext): Future[Either[Result, AuthRequest[A]]] = {
request.session.get("username") match {
case Some(username) => userCollection.findByEmail(username).map { userOpt =>
userOpt.map(new AuthRequest(_, request)).toRight(Results.Redirect(routes.LoginController.login()))
}
case None => Future.successful(Left(Results.Redirect(routes.LoginController.login())))
}
}
}
}
class AuthRequest[A](val user: User, request: Request[A]) extends WrappedRequest[A](request)
The Scala compiler tells me that the method refine[A](request: R[A]): Future[Either[Result, P[A]]] is not defined. When I remove the implicit variables it registers, but that leaves me with no UserCollection...
So, How do I correctly use the ActionRefiner?
Thanks to this topic I found a way to make it work. Instead of using an implicit, I can define the UserCollection in the trait and pass it from my controller, like so:
trait Auth {
def userCollection: UserCollection
object AuthenticatedAction extends ActionBuilder[AuthRequest] with ActionRefiner[Request, AuthRequest] {
def refine[A](request: Request[A]): Future[Either[Result, AuthRequest[A]]] = {
...
}
}
}
class HomeController #Inject()(val userCollection: UserCollection)(implicit executionContext: ExecutionContext) extends Controller with Auth {
def index = AuthenticatedAction { implicit request =>
Ok(s"Hello ${request.user.name}")
}
}
I just had gotten so used to using implicits that I completely forgot about this.
I'm using Play Scala 2.5, I would like to know how to override the invokeBlock method so that I could get the body of request as json.
case class AuthenticatedRequest[A](val username: Option[String], val param: Option[String], request: Request[A]) extends WrappedRequest[A](request)
object AuthenticatedAction extends ActionBuilder[AuthenticatedRequest] {
/**
* logger used to log actions.
*/
val logger: Logger = Logger("mylogger")
def invokeBlock[A](request: Request[A], block: AuthenticatedRequest[A] => Future[Result]): Future[Result] = {
request.session.get("username") match {
case Some(username) => {
val param = (request.body.asJson.get \ "param").as[String]
block(new AuthenticatedRequest(Some(username), Some(param), request))
}
case None => Future.successful(Results.Forbidden)
}
}
}
I have to following exception after compiling:
value asJson is not a member of type parameter A
[error] val param = (request.body.asJson.get \ "param").as[String]
The error is linked to the fact that your bodyis returning something of generic type A, for which the asJson method is not defined.
Unfortunately, you cannot use pattern matching directly to get the type of A, because of type-erasure of Scala (at run time, only the class exists, not its type parameters). One work-around is to use the reflection API, as follow :
import scala.reflect.runtime.universe._
def invokeBlock[A](request: Request[A]) = {
request match {
case specificRequest : Request[SpecificRequest] if (typeOf[A] =:= typeOf[SpecificRequest]) => // Do the request processing
}
}
That should allow you to retrieve the type, and be able to call specific methods on it.
I am trying to Create customize action for security. I am using Scala Oauth
for handling security in my application and trying to create custom action and wrap the Scala Oauth security in my custom action. According to Play Framework Documentation, i am using two ways for wrapped request object, but unfortunately, i am not getting my custom Request object in custom Action handler. Following are the Ways:
case class AuthRequest[A](user: User, request: Request[A]) extends WrappedRequest[A](request)
First Way
case class CustomSecurityAction[A](action: Action[A]) extends Action[A] with OAuth2Provider{
def apply(request: Request[A]): Future[Result] = {
implicit val executionContext: ExecutionContext = play.api.libs.concurrent.Execution.defaultContext
request.headers.get("Host").map { host =>
authorize(new SecurityDataHandler(host)) { authInfo =>
action(AuthRequest(authInfo.user, request))
}(request, executionContext)
} getOrElse {
Future.successful(Unauthorized("401 No user\n"))
}}
lazy val parser = action.parser
}
object SecurityAction extends ActionBuilder[Request] with OAuth2Provider {
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
block(request)
}
override def composeAction[A](action: Action[A]) = new CustomSecurityAction(action)
}
Second Way
object SecurityAction extends ActionBuilder[Request] with OAuth2Provider {
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
implicit val executionContext: ExecutionContext = play.api.libs.concurrent.Execution.defaultContext
request.headers.get("Host").map { host =>
authorize(new SecurityDataHandler(host)) { authInfo =>
block(AuthRequest(authInfo.user, request))
}(request, executionContext)
} getOrElse {
Future.successful(Unauthorized("401 No user\n"))
}
}
According to both ways, when i am trying to use user object in my custom handler i am getting following compile time error:
value user is not a member of play.api.mvc.Request[play.api.mvc.AnyContent]
val user = request.user
Following is my handler code:
def testCustomAction = SecurityAction { request =>
val user = request.user
Future.successful(Ok("Apna To Chal Gya"))
}
There is simple problem, in above code. I am using play.api.mvc.Request in invokeBlock method instead of AuthRequest. Please find below code for correction.
object SecurityAction extends ActionBuilder[AuthRequest] {
override def invokeBlock[A](request: Request[A], block: (AuthRequest[A]) => Future[Result]) = {
request match {
case re: AuthRequest[A] => block(re)
case _ => Future.successful(Results.Unauthorized("401 No user\n"))
}
}
override def composeAction[A](action: Action[A]) = CustomSecurityAction(action)
}
I'm trying to wrap my head around Play action composition. My primary reference is: https://www.playframework.com/documentation/2.3.6/ScalaActionsComposition
Here's what I want to achieve (to be able to declare a function in my Controller this way):
def sensitiveOperation() = Protected {request => ...}
So, my action-plan is:
/**
* Now I need to make an Action builder that wraps two things:
* UserAction (to make sure that request is transformed to UserRequest),
* and then... an Action that checks if userId has a value (it proceeds by invoking the actual action)
* , or otherwise return Unauthorized
*/
So... I have these three things:
class UserRequest[A](val userId: Option[String], request: Request[A]) extends WrappedRequest[A](request)
object UserAction extends ActionBuilder[UserRequest] with ActionTransformer[Request, UserRequest] {
def transform[A](request: Request[A]) = Future.successful {
new UserRequest(request.session.get("userId"), request)
}
}
object CheckUserId extends ActionFilter[UserRequest] {
def filter[A](userRequest: UserRequest[A]) = Future.successful {
userRequest.userId match {
case Some(userIdVal) => None
case None => Some(Results.Unauthorized("User is not logged in. Log in first."))
}
}
}
All those three things compile.
My question is: how to declare an Action that makes use of UserAction and CheckUserId ?
object Protected extends ActionBuilder[Request] {
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
block(request)
}
override def composeAction[A](action: Action[A]) = ... ?? how ?? ....
}
I've tried this one below, but it gives compile error:
override def composeAction[A](action: Action[A]) = UserAction andThen CheckUserId
ADDITIONAL ADDITIONAL
For now I can work around it using this:
def sensitiveOperation() = (UserAction andThen CheckUserId) {request => ...}
But, still, I think it would have been prettier if I can define a companion object named "Protected" that practically does the same (and-ing UserAction and CheckUserId)..., so I can go back with the original idea:
def sensitiveOperation() = Protected {request => ...}
How to achieve it?
Thanks in advance,
Raka
You can't do it with an object because an object can not be the result of an expression. But you can make it a def or val, and you can put that in a package object if you want, eg:
package object security {
val Protected = UserAction andThen CheckUserId
}
I think I've found how....
object Protected extends ActionBuilder[UserRequest] {
def invokeBlock[A](request: Request[A], block: (UserRequest[A]) => Future[Result]) = {
(UserAction andThen CheckUserId).invokeBlock(request, block)
}
}