1

Here my problem: I need to check in a controller the user permission using deadbolt and then add something to the request (using an ActionBuilder). Normally using Play Action Builders would be (action1 andThen action2) but this doesn't work with DeadboltActions.

Here some code:

ActionBuilder

import javax.inject.Inject
import models.Item
import modules.item.services.ItemServiceClient
import play.api.mvc._

import scala.concurrent.{ExecutionContext, Future}

class ItemRequest[A](val items: Seq[Item], request: Request[A]) extends WrappedRequest[A](request)

class ItemAction @Inject()(val parser: BodyParsers.Default)(implicit val executionContext: ExecutionContext)
  extends ActionBuilder[ItemRequest, AnyContent] with ActionTransformer[Request, ItemRequest] {
  def transform[A](request: Request[A]): Future[ItemRequest[A]] = {
    ItemServiceClient.getItems.map{
      new ItemRequest(_, request)
    }recover{
      case _ => new ItemRequest(Seq(), request)
    }

  }
}

Controller:

@Singleton
class ItemController @Inject()(cc: ControllerComponents, deadbolt: DeadboltActions, itemAction: ItemAction) extends AbstractController(cc) with I18nSupport {

def createSomething: Action[AnyContent] = (deadbolt.Pattern("Create_Something", PatternType.EQUALITY) andThen itemAction) { implicit request: ItemRequest[AnyContent] =>
            Ok(modules.item.views.html.createSomething(Something.form, request.items))
    }
}

[error] Unapplied methods are only converted to functions when a function type is expected. You can make this conversion explicit by writing Pattern _ or Pattern(_,_,_,_,_)(_)(_) instead of Pattern.

[error] def createSomething: Action[AnyContent] = (deadbolt.Pattern("Create_Deck", PatternType.EQUALITY)() andThen itemAction).synchronized() { implicit request: ItemRequest[AnyContent] =>

Anyone who already dealt with this?

epinal
  • 1,415
  • 1
  • 13
  • 27

2 Answers2

1

Since DeadboltActions#Pattern returns an Action, I don't think you can use it for Action composition. Instead, at least with Deadbolt 2.5.1, you may be looking for be.objectify.deadbolt.scala.SubjectActionBuilder, which is an ActionBuilder[AuthenticatedRequest], then you can compose that with your ItemAction.

Here's a simple example:

class MyRequest[A](request: Request[A]) extends WrappedRequest(request)
val handler: DeadboltHandler = ???
val action1: ActionFunction[Request, AuthenticatedRequest] = SubjectActionBuilder(None)
val action2: ActionTransformer[Request, MyRequest] = new ActionTransformer[Request, MyRequest] {
  override protected def transform[A](request: Request[A]): Future[MyRequest[A]] = Future.successful(new MyRequest(request))
}
val action3: ActionFunction[Request, MyRequest] = action1 andThen action2

Here's an example that accomplishes what I think you want to do, but does NOT use action composition (at least in the sense I think you mean):

class MyRequest[A](request: Request[A]) extends WrappedRequest[A](request)
class MyAction extends ActionTransformer[Request, MyRequest] {
  override protected def transform[A](request: Request[A]): Future[MyRequest[A]] =
    Future.successful(new MyRequest(request))
}

val deadboltActions: DeadboltActions = ???

def createSomething: Action[AnyContent] = deadboltActions.Pattern("Create_Something")() { authRequest =>
  ((new MyAction) compose Action).async { request: MyRequest[AnyContent] =>
    Future.successful(Ok(""))
  }(authRequest)
}
Jamie
  • 5,994
  • 1
  • 18
  • 15
  • Thanks for the answer. I want to be able to call deadbolt actions (Pattern, SubjectPresent...) and then apply my custom action. You are not using any deadbolt action in the composition. Can you please post an example using deadbolt actions? – epinal Jun 14 '18 at 20:03
  • 2
    To my knowledge (and I definitely could be wrong) you won't be able to use those methods because they return `Action`, which cannot be used for action composition. Assuming this, you would just re-implement those actions as `ActionBuilder`s. If you _do_ figure out how to use them directly, please post an answer to the question, as I'd be very interested in the answer myself! – Jamie Jun 15 '18 at 00:29
  • That's what I thought. I got playframework guys working on it. I'll post it as soon as they give me something. Thanks – epinal Jun 15 '18 at 12:41
1

As Jamie mentioned, when out-of-the-box composition utility methods are not directly applicable we can fall back to composing Actions using the following pattern:

OuterAction { outerRequest =>
  InnerAction { request =>
    // ... some Result
  } (outerRequest)
}

For example, in your case the following might work:

val deadboltAction =
  deadbolt.Pattern[AnyContent](
    value = "admin.printer",
    patternType = PatternType.EQUALITY
  )() _

val itemAction = ...

deadboltAction { implicit authRequest: AuthenticatedRequest[AnyContent] =>
  itemAction { implicit request: ItemRequest[AnyContent] => 
    Ok(modules.item.views.html.createSomething(Something.form, request.items))
  } (authRequest)
}

To tidy it up a bit, we could create the following utility method

def deadboltActionWithItemAction(block: ItemRequest[AnyContent] => Result): Action[AnyContent] =
  deadboltAction { implicit authRequest =>
    itemAction { 
      block 
    }(authRequest)
  }

and then the call site looks something like so

deadboltActionWithItemAction { implicit request: ItemRequest[AnyContent] =>
  Ok(modules.item.views.html.createSomething(Something.form, request.items))
}
Mario Galic
  • 47,285
  • 6
  • 56
  • 98