scalaplayframeworkauthorizationspecs2deadbolt-2

How to test controllers using deadbolt2 DeadboltActions or is there another framework which allows this easily?


I'm using Play! 2.4 with Deadbolt2 for authorization. However, since I introduced the authorization rules, I'm unable to write successful tests for my controllers. As an example:

class VisitController @Inject() (authorization: DeadboltActions) extends Controller {
  def fetchDailyVisits(date: Date) = authorization.Restrict(List(Array(ADMIN_ROLE), Array(MANAGER_ROLE))) {
    Action.async {
      visitService.findDailyVisits(date).map(result =>
        Ok(Json.toJson(result))
      )
    }
  }
}

I'm using specs2 in the tests. My test looks like this atm:

class VisitControllerSpec extends PlaySpecification with Mockito with ScalaFutures {
  val deadboltActions = mock[DeadboltActions]
"VisitControllerSpec#fetchDailyVisits" should {

    val testDate = Date.from(LocalDate.of(2016, 2, 25)
      .atStartOfDay(ZoneId.systemDefault()).toInstant)

    "Return Status Ok with returned list" in {

      val expected = List(completeVisitWithId, anotherCompleteVisitWithId)
      visitService.findDailyVisits(testDate) returns Future { expected }

      val request = FakeRequest(GET, "/visits?date=2016-02-25")

      val result = new VisitController(deadboltActions)
        .fetchDailyVisits(testDate)(request)

      result.futureValue.header.status must beEqualTo(OK)
      contentAsJson(result) must_== Json.toJson(expected)
    }
  }
}

How do I mock deadboltActions in a way I can specify the user will be allowed access?

Is there another way? Maybe by providing a different DeadboltHandler? It seems kind of obvious this would be the way to go, I just don't seem to be able to figure it out and there aren't a lot of Deadbolt2 examples out there (at least for scala).

Or, being more extreme, any other authorization framework out there that works well with scala play and allows to handle security as a cross-cutting concern without poluting the controllers? Deadbolt2 is too limited for this reason, but I honestly can't find a better authorization framework (unless I write my own).


Solution

  • It doesn't answer exactly to my original question, which was mostly related with Deadbolt2, but I kept getting frustrated with the fact I had to specify my authorization rules in my controllers, which is not truly cross cutting.

    The answer provided by Steve Chaloner helps, but still forced me to go through a few hoops.

    Enter Panoptes. This authorization framework is based on Filters instead of Action chaining, so it allows to easily specify authorization rules in a central location and outside of the controllers.

    Setting your security rules in Panoptes is somewhat similar to Spring Security and it looks like this:

    class BasicAuthHandler extends AuthorizationHandler {
    
      override def config: Set[(Pattern, _ <: AuthorizationRule)] = {
        Set(
          Pattern(Some(POST), "/products") -> atLeastOne(withRole("Admin"), withRole("Manager"))
          Pattern(Some(GET), "/cart[/A-Za-z0-9]*") -> withRole("Admin"),
          Pattern(None, "/orders[/A-Za-z0-9]*") -> withRole("Admin")
        )
      }
    }
    

    Other than that, you need a couple of lines to declare the filter and plug in your AuthorizationHandler.

    class Filters @Inject()(securityFilter: SecurityFilter) extends HttpFilters {
      override def filters = Seq(securityFilter)
    }
    
    class ControllerProviderModule extends AbstractModule {
      override def configure(): Unit = {   bind(classOf[AuthorizationHandler]).to(classOf[MyAuthorizationHandler])
      }
    }
    

    The README file in the git repository has more details and code samples.

    It's also customizable to the point it allows to create your own AuthorizationRules. In my project I have a requirement where I need to check the mobile device that makes the call is registered in the system. I can write an AuthorizationRule to handle this for me for every request whose path matches my pattern.

    Unit testing controllers is extra simple, because any mocking of the security layer can be ommited. They can be tested like any other class.

    If you're having similar issues or also believe authorization rules don't belong in the controllers, have a go at Panoptes, it might suit your needs. Hope this helps someone else.