首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用deadbolt2 DeadboltActions测试控制器,或者是否有另一个框架可以轻松地实现这一点?

如何使用deadbolt2 DeadboltActions测试控制器,或者是否有另一个框架可以轻松地实现这一点?
EN

Stack Overflow用户
提问于 2016-04-13 15:46:07
回答 2查看 448关注 0票数 1

我使用Play!2.4和Deadbolt2进行授权。但是,由于我引入了授权规则,所以无法为我的控制器编写成功的测试。例如:

代码语言:javascript
复制
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))
      )
    }
  }
}

我在测试中使用specs2。我的测试看起来像这个自动取款机:

代码语言:javascript
复制
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)
    }
  }
}

如何以指定用户将被允许访问的方式来模拟deadboltActions?

还有别的办法吗?也许通过提供一个不同的DeadboltHandler?这似乎是一条很明显的路,我只是看不出来,而且也没有太多的Deadbolt2例子(至少对于scala来说是这样)。

或者,更极端的是,任何其他的授权框架都可以很好地处理scala play,并且允许将安全性作为一个交叉的关注点来处理,而不需要对控制器进行模糊处理?由于这个原因,Deadbolt2太有限了,但老实说,我找不到一个更好的授权框架(除非我自己编写)。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-04-20 13:48:50

它没有完全回答我最初的问题,这个问题主要与Deadbolt2有关,但我一直对必须在控制器中指定授权规则感到沮丧,这并不是真正的横切。

史蒂夫·查洛纳提供的答案是有帮助的,但仍然迫使我经历了一些困难。

输入潘诺普特。该授权框架基于过滤器而不是操作链,因此它允许在中央位置和控制器外部轻松地指定授权规则。

Panoptes中设置您的安全规则有点类似于security,如下所示:

代码语言:javascript
复制
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")
    )
  }
}

除此之外,您还需要几行代码来声明过滤器并插入AuthorizationHandler。

代码语言:javascript
复制
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])
  }
}

git存储库中的自述文件包含更多细节和代码示例。

它还可以定制到允许创建您自己的AuthorizationRules的程度。在我的项目中,我需要检查发出呼叫的移动设备是否已在系统中注册。我可以为每个路径与我的模式匹配的请求编写一个AuthorizationRule来处理这个问题。

单元测试控制器非常简单,因为任何对安全层的模拟都可以避免。它们可以像其他任何类一样被测试。

如果您有类似的问题,或者也认为授权规则不属于控制器,那么可以使用潘诺普特,它可能适合您的需要。希望这能帮到别人。

票数 0
EN

Stack Overflow用户

发布于 2016-04-14 09:09:41

有许多不同的方法可以做到这一点。

如果您的DeadboltHandler中注入了用于访问主题的DAO,则可以重写DAO的绑定,以提供包含测试主题的DAO。

代码语言:javascript
复制
abstract class AbstractControllerSpec extends PlaySpecification {
  sequential
  isolated

  def testApp: Application =  new  GuiceApplicationBuilder().in(Mode.Test).bindings(bind[SubjectDao].to[TestSubjectDao]).build()
}

有关使用此方法的示例,请参阅测试应用程序

或者,您可以扩展您的DeadboltHandler实现以覆盖getSubject并从这里提供一个测试主题。绑定的处理方式与上面相同。

最后,您可以保持所有代码的原样,并使用主题填充测试数据库;您发送的请求将由您的身份验证需求(标头、cookie中的内容等)形成。

对于单元测试,类似的情况也适用。如果有一些用于测试的硬编码主题的SubjectDao,则可以使用WithApplication和注射器查找来获取所需的内容。

代码语言:javascript
复制
class TestSubjectDao extends SubjectDao {

  val subjects: Map[String, Subject] = Map("greet" -> new SecuritySubject("greet",
                                                                           List(SecurityRole("foo"),
                                                                                 SecurityRole("bar")),
                                                                           List(SecurityPermission("killer.undead.zombie"))),
                                            "lotte" -> new SecuritySubject("lotte",
                                                                            List(SecurityRole("hurdy")),
                                                                            List(SecurityPermission("killer.undead.vampire"))),
                                            "steve" -> new SecuritySubject("steve",
                                                                            List(SecurityRole("bar")),
                                                                            List(SecurityPermission("curator.museum.insects"))),
                                            "mani" -> new SecuritySubject("mani",
                                                                           List(SecurityRole("bar"),
                                                                                 SecurityRole("hurdy")),
                                                                           List(SecurityPermission("zombie.movie.enthusiast"))),
                                            "trippel" -> new SecuritySubject("trippel",
                                                                           List(SecurityRole("foo"),
                                                                                 SecurityRole("hurdy")),
                                                                           List[SecurityPermission]()))

  override def user(userName: String): Option[Subject] = subjects.get(userName)
}

使用如下所示的控制器:

代码语言:javascript
复制
class Subject @Inject()(deadbolt: DeadboltActions) extends Controller {

  def subjectMustBePresent = deadbolt.SubjectPresent()() { authRequest =>
    Future {
      Ok("Content accessible")
    }
  }
}

然后我们可以像这样对它进行单元测试:

代码语言:javascript
复制
import be.objectify.deadbolt.scala.DeadboltActions
import be.objectify.deadbolt.scala.test.controllers.composed.Subject
import be.objectify.deadbolt.scala.test.dao.{SubjectDao, TestSubjectDao}
import play.api.Mode
import play.api.inject._
import play.api.inject.guice.GuiceApplicationBuilder
import play.api.mvc.{Result, Results}
import play.api.test.{FakeRequest, PlaySpecification, WithApplication}

import scala.concurrent.Future

object SubjectPresentUnitSpec extends PlaySpecification with Results {
  "Subject present " should {
    "should result in a 401 when no subject is present" in new WithApplication(new GuiceApplicationBuilder().in(Mode.Test).bindings(bind[SubjectDao].to[TestSubjectDao]).build()) {
      val deadbolt: DeadboltActions = implicitApp.injector.instanceOf[DeadboltActions]
      val controller = new Subject(deadbolt)
      val result: Future[Result] = call(controller.subjectMustBePresent(), FakeRequest())
      val statusCode: Int = status(result)
      statusCode must be equalTo 401
    }

    "should result in a 200 when a subject is present" in new WithApplication(new GuiceApplicationBuilder().in(Mode.Test).bindings(bind[SubjectDao].to[TestSubjectDao]).build()) {
      val deadbolt: DeadboltActions = implicitApp.injector.instanceOf[DeadboltActions]
      val controller = new Subject(deadbolt)
      val result: Future[Result] = call(controller.subjectMustBePresent(), FakeRequest().withHeaders(("x-deadbolt-test-user", "greet")))
      val statusCode: Int = status(result)
      statusCode must be equalTo 200
    }
  }
}  
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36603480

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档