Given the followoing ActionBuilder implementations:
class SignedRequest[A](request: Request[A]) extends WrappedRequest[A](request) {}
object SignedAction extends ActionBuilder[SignedRequest] {
def invokeBlock[A](request: Request[A], block: SignedRequest[A] => Future[SimpleResult]) = {
block(new SignedRequest(request))
}
}
class SecuredRequest[A](request: Request[A]) extends WrappedRequest[A](request) {}
object SecuredRequest extends ActionBuilder[SecuredRequest] {
def invokeBlock[A](request: Request[A], block: SecuredRequest[A] => Future[SimpleResult]) = {
block(new SecuredRequest(request))
}
}
How do I combine them? I've tried the following...
object MyController extends Controller {
def doSomething = SignedAction.async(parse.json) {
SecuredAction.async(parse.json) { implicit request =>
Future.successful(Ok)
}}
}
... but I always get the following error message:
/home/j3d/test/controllers/MyController.scala:37: type mismatch;
[error] found : play.api.mvc.Action[play.api.libs.json.JsValue]
[error] required: scala.concurrent.Future[play.api.mvc.SimpleResult]
[error] SecuredAction.async(parse.json) {
^
Am I missing something? Tx.
Function async is expecting a Future[SimpleResult], but the nested SecuredAction.async is returning an Action to the top SignedAction.async (notes that in your sample code you omit to declare requests as class and SignedAction is declared twice).
You can compose result of nested SecuredAction within SignedAction by applying it to the signed request.
package controllers
import scala.concurrent.Future
import play.api._
import play.api.mvc._
case class SignedRequest[A](request: Request[A])
extends WrappedRequest[A](request) {}
object SignedAction extends ActionBuilder[SignedRequest] {
def invokeBlock[A](request: Request[A],
block: SignedRequest[A] => Future[Result]) =
block(new SignedRequest(request))
}
case class SecuredRequest[A](request: Request[A])
extends WrappedRequest[A](request) {}
object SecuredAction extends ActionBuilder[SecuredRequest] {
def invokeBlock[A](request: Request[A],
block: SecuredRequest[A] => Future[Result]) =
block(new SecuredRequest(request))
}
object MyController extends Controller {
def doSomething = SignedAction.async(parse.json) { signedReq =>
SecuredAction.async(parse.json) { implicit securedReq =>
Future.successful(Ok)
} apply signedReq
}
}
Such action composition can also be done without ActionBuilder (which can lead to some extra complexity).
package controllers
import scala.concurrent.Future
import play.api._
import play.api.mvc._
case class SignedRequest[A](request: Request[A])
case class SecuredRequest[A](request: Request[A])
object MyController extends Controller {
def Signed[A](bodyParser: BodyParser[A])(signedBlock: SignedRequest[A] => Future[Result]): Action[A] = Action.async(bodyParser) { req =>
signedBlock(SignedRequest(req))
}
def Secured[A](bodyParser: BodyParser[A])(securedBlock: SecuredRequest[A] => Future[Result]): Action[A] = Action.async(bodyParser) { req =>
securedBlock(SecuredRequest(req))
}
def doSomething = Signed(parse.json) { signedReq =>
Secured(parse.json) { implicit securedReq =>
Future.successful(Ok)
} apply signedReq.request
}
}
Composition can be also done around Future[Result]:
package controllers
import scala.concurrent.Future
import play.api._
import play.api.mvc._
import play.api.libs.json.JsValue
case class SignedRequest[A](request: Request[A])
case class SecuredRequest[A](request: Request[A])
object MyController extends Controller {
def Signed[A](signedBlock: SignedRequest[A] => Future[Result])(implicit req: Request[A]): Future[Result] = signedBlock(SignedRequest(req))
def Secured[A](signedBlock: SecuredRequest[A] => Future[Result])(implicit req: Request[A]): Future[Result] = signedBlock(SecuredRequest(req))
def doSomething = Action.async(parse.json) { implicit req =>
Signed[JsValue] { signedReq =>
Secured[JsValue] { securedReq => Future.successful(Ok) }
}
}
}
Using action-zipper you can compose ActionBuilders
import jp.t2v.lab.play2.actzip._
object MyController extends Controller {
val MyAction = SignedAction zip SecuredAction
def doSomething = MyAction.async(parse.json) { case (signedReq, secureReqeq) =>
Future.successful(Ok)
}
}
Json parsing will be executed only once :)
to simplify #applicius answer
I think it can be done without the Future, I think async/Future is a separate concern.
Very simply removing Futures and async, we get this:
def signed[A](signedBlock: SignedRequest[A] => Result)(implicit req: Request[A]) = signedBlock(SignedRequest(req))
def secured[A](securedBlock: SecuredRequest[A] => Result)(implicit req: Request[A]) = securedBlock(SecuredRequest(req))
//the use is the same as with Futures except for no async
def doSomething = Action(parse.json) { implicit req =>
signed[JsValue] { signedReq => secured[JsValue] { securedReq =>
Ok
} } }
Related
The documentation on ActionBuilder contains a pipeline of three nodes: the authentication, adding informations, validating step.
I would like to set session values at the authentication step. I mean the .withSession which comes here Ok(_).withSession(_)
import play.api.mvc._
class UserRequest[A](val username: Option[String], request: Request[A]) extends WrappedRequest[A](request)
class UserAction #Inject()(val parser: BodyParsers.Default)(implicit val executionContext: ExecutionContext)
extends ActionBuilder[UserRequest, AnyContent] with ActionTransformer[Request, UserRequest] {
def transform[A](request: Request[A]) = Future.successful {
new UserRequest(request.session.get("username"), request)
}
}
You'll need to do action composition to add values to the request session like so:
object WithSession extends ActionBuilder[Request] {
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
block(request).map(_.withSession("key" -> "value"))
}
}
in your controller:
def index = WithSession {
Ok("result")
}
I upgraded my Play application from 2.5 to 2.6 today and i got a problem with ActionBuilder. The docs state:
The Scala ActionBuilder trait has been modified to specify the type of the body as a type parameter, and add an abstract parser member as the default body parsers. You will need to modify your ActionBuilders and pass the body parser directly.
documentation
Sadly I haven't found any example and i don't know how to fix that:
class AuthenticatedRequest[A](val token: ProfileTokenData, request: Request[A]) extends WrappedRequest[A](request)
trait Secured {
object SetExtractor {
def unapplySeq[T](s: Set[T]): Option[Seq[T]] = Some(s.toSeq)
}
def Authenticated = new ActionBuilder[AuthenticatedRequest] with JWTTokenProcess {
override def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[Result]): Future[Result] = {
request.jwtSession.claimData.asOpt[JWTToken] match {
case Some(token) => block(new AuthenticatedRequest(ProfileTokenData(null, token.sub, AuthRole.None), request)).map(_.refreshJwtSession(request))
case _ => Future.successful(Unauthorized)
}
}
}
def Registered = new ActionBuilder[AuthenticatedRequest] with JWTTokenProcess {
override def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[Result]): Future[Result] =
this.processJWTToken(request, block, Seq(AuthRole.Admin, AuthRole.Customer, AuthRole.Registered))
}
def Customer = new ActionBuilder[AuthenticatedRequest] with JWTTokenProcess {
override def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[Result]): Future[Result] =
this.processJWTToken(request, block, Seq(AuthRole.Admin, AuthRole.Customer))
}
def Admin = new ActionBuilder[AuthenticatedRequest] with JWTTokenProcess {
override def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[Result]): Future[Result] =
this.processJWTToken(request, block, Seq(AuthRole.Admin))
}
}
Does anyone know whic BodyParser i have to pass as second argument?
Had a similar problem. Play 2.6 injects a ControllerComponents, which has a default body parser. Maybe this helps:
class CheckApiKey(apiKeyToCheck: String, cc: ControllerComponents)
extends ActionBuilder[Request, AnyContent] with ActionFilter[Request] {
...
override protected def executionContext: ExecutionContext = cc.executionContext
override def parser: BodyParser[AnyContent] = cc.parsers.defaultBodyParser
}
I got the same problem. In Play 2.6.x you need to inject BodyParser and extend ActionBuilder. You can also try it this way
class SecuredActionBuilder(val parser: BodyParser[AnyContent])(implicit val executionContext: ExecutionContext) extends ActionBuilder[SecuredRequest, AnyContent] {
def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[Result]): Future[Result] = {
request.jwtSession.claimData.asOpt[JWTToken] match {
...
}
}
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 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)
}
since Play Framework 2.4 there is the possibility to use dependency injection (with Guice).
Before I used objects (for example AuthenticationService) in my ActionBuilders:
object AuthenticatedAction extends ActionBuilder[AuthenticatedRequest] {
override def invokeBlock[A](request: Request[A], block: (AuthenticatedRequest[A]) => Future[Result]): Future[Result] = {
...
AuthenticationService.authenticate (...)
...
}
}
Now AuthenticationService is not an object anymore, but a class. How can I still use the AuthenticationService in my ActionBuilder?
Define your action builders inside a trait with the authentication service as an abstract field. Then mix them into your controllers, into which you inject the service. For example:
trait MyActionBuilders {
// the abstract dependency
def authService: AuthenticationService
def AuthenticatedAction = new ActionBuilder[AuthenticatedRequest] {
override def invokeBlock[A](request: Request[A], block(AuthenticatedRequest[A]) => Future[Result]): Future[Result] = {
authService.authenticate(...)
...
}
}
}
and the controller:
#Singleton
class MyController #Inject()(authService: AuthenticationService) extends Controller with MyActionBuilders {
def myAction(...) = AuthenticatedAction { implicit request =>
Ok("authenticated!")
}
}
I didn't like the way one was required to inherit in the above example. But apparently it's possible to simply wrap a object inside class:
class Authentication #Inject()(authService: AuthenticationService) {
object AuthenticatedAction extends ActionBuilder[Request] {
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
// Do your thing wit the authService...
block(request)
}
}
}
class YourController #Inject() (val auth: Authentication) extends Controller (
def loggedInUser = auth.AuthenticatedAction(parse.json) { implicit request =>
// ...
}
}
I like accepted answer but for some reason the compiler would not recognize the authService reference. I got around this pretty easily by just sending the service in the method signature, a la...
class Authentication #Inject()(authenticationService: AuthenticationService) extends Controller with ActionBuilders {
def testAuth = AuthenticatedAction(authenticationService).async { implicit request =>
Future.successful(Ok("Authenticated!"))
}
}