mkurz / deadbolt-2-scala

Idiomatic Scala API for Deadbolt 2

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Difficulty while using play 2.3.10 for Scala with deadbolt 2.3.2

shuroo opened this issue · comments

Hi,

I am using deadbolt-scala , version 2.3.2, with play-framework 2.3.10 (scala version: 2.11.7).

I would like to consult, in order to improve the following code. This is a repeatable issue I have in my code.

And the matter is:

When I use Deadbolt in my play framework controllers, sometimes the authorization check is nested inside the inner request. meaning, that in this part I would like to return Future[Result] and not Action[AnyContent].

Since using deadbolt "dynamic" method to add authorization check returns action[A], I need to use apply[request] to force returning Future[Result].

Also, when the original request asynchronous and has body, the returned type is of type Action[JsValue] , so I need to convert it to Action[AnyContent] using asInstanceOf (in order to use the right apply method that will return the expected Future[Result] type ).

This is not such a nice code, and using asInstanceOf[Request[AnyContent]] is a Scala anti-pattern, please advice me if there is a better way of achieving my goal.

Thanks.

Code Example:

/code

def doRequest() = {
  Action.async(parse.json) {
    request => {
      /** fetch auth_param here **/.flatMap(_ match {
        case Some(fetched_param) =>
          performOperationWhenValid(auth_param) 
        case None =>
          ..... 
      }.recover {
      case e => doRecover(e)
    }
  }
}

def performOperationWhenValid(auth_param:String) = {
  // Authorization check 
  val allow_or_forbid_action = Dynamic(auth_param:String, "",
  new FL_DeadboltHandler(Option(new AuthorizedDynamicResourceHandler))) {
    Action.async {
      request.body.validate[PerformOperationRequest].fold(
      errors =>
      Future.successful(BadRequest(formatErrorMessage(errors)))
      ,
      patch_request =>
      /*** performOperation here after authorization check is passed successfully... ***/
    }
  }
  allow_or_forbid_action.apply(request.asInstanceOf[AnyContent])
}    

I think the easiest way around this issue is to write your own Dynamic and customize it for your needs. In Deadbolt, the standard implementation of Dynamic is

def Dynamic[A](name: String,
               meta: String = "",
               deadboltHandler: DeadboltHandler)
              (action: Action[A]): Action[A] = {
  Action.async(action.parser) { implicit request =>
    deadboltHandler.beforeAuthCheck(request) match {
      case Some(result) => result
      case _ =>
        deadboltHandler.getDynamicResourceHandler(request) match {
          case Some(dynamicHandler) =>
            if (dynamicHandler.isAllowed(name, meta, deadboltHandler, request)) action(request)
            else deadboltHandler.onAuthFailure(request)
          case None =>
            throw new RuntimeException("A dynamic resource is specified but no dynamic resource handler is provided")
        }
    }
  }
}

In your own implementation, instead of passing in Action[A] you can either make it generic and pass a function into it, or hard-code it for your Future[Result]. You then need to change these two lines to handle that type:

if (dynamicHandler.isAllowed(name, meta, deadboltHandler, request)) action(request) /*replace this with your Future[Result] */
else deadboltHandler.onAuthFailure(request)

Thanks. I've added a method similar to Dynamic(...) to handle this case as you suggested.