scalaplayframeworkdeadbolt-2

Play 2.5 Deadbolt-2's @subjectPresentOr migrating from Java -> Scala


I'm migrating an existing Java Play 2.5 application to Scala and found in the view main.scala.html the use of the following deadbolt-2 class subjectPresentOr:

@subjectPresentOr() {
  <ul class="nav navbar-nav navbar-right">
    @defining(userProvider.getUser(session())) { user =>
    ... user is present html
    }
} {
  ... user is NOT present html    
}

After migrating all controllers and actions to Scala (and changing the deadbolt-2 dependency from Java -> Scala) I get the following compiler error due to the fact that subjectPresentOr requires an implicit request of type AuthenticatedRequest[Any]:

main.scala.html:49: could not find implicit value for parameter request: 
    be.objectify.deadbolt.scala.AuthenticatedRequest[Any]

and I have some intuition why ... some times e.g. when the user is not yet logged in there is no request of type deadbolt-2's AuthenticatedRequest[_] but the superclass type play.api.mvc.RequestHeader and it can't be rightfully so implicitly matched with its subclass AuthenticatedRequest[_].

The question is why this works in the Java version in the first place? The Java version of @subjectPresentOr doesn't require any implicit request :)

To fix it in the Scala version I would wrap the @subjectPresentOr block with pattern matching to discover the dynamic type of the implicit request and only if its dynamic type is AuthenticatedRequest[_] I will then show the block and pass to @subjectPresentOr explicitly the narrowed request of that type. This is not super elegant though but I can't figure any other way ...


Solution

  • The Java version of Play has a Http.Context available to it via ThreadLocals, whereas the Scala version uses requests. These may be implicit or explicit.

    Looking at your controllers, when you add Deadbolt to an action you will receive an AuthenticatedRequest. So, a non-Deadbolt action of

    def foo = Action { request =>
      // request is of type Request
    }
    

    would receive a request of type Request, whereas the same action protected by Deadbolt

    def foo = actionBuilder.SubjectPresentAction().defaultHandler() { request =>
      // request is of type AuthenticatedRequest
    }
    

    receives a request of type AuthenticatedRequest.

    If you want to convert a Request to an AuthenticatedRequest because you're calling a template that contains Deadbolt constraints yet your controller action is unconstrained, you can use WithAuthRequestAction:

    def foo = actionBuilder.WithAuthRequestAction().defaultHandler() { request =>
      // request is of type AuthenticatedRequest
    }
    

    If you're injecting DeadboltActions in place of ActionBuilders, the same holds true:

    def foo= deadboltActions.SubjectPresent()() { request =>
      // request is of type AuthenticatedRequest
    }
    

    You can also just create an instance of AuthenticatedRequest from the existing Request and with no Subject by using new AuthenticatedRequest(request, None).