2016-04-13 7 views
1

私はPlayを使用しています! 2.4認可のためにDeadbolt2で。しかし、私は認可ルールを導入して以来、私は自分のコントローラーに成功したテストを書くことができません。例として:deadbolt2 DeadboltActionsを使ってコントローラをテストするにはどうしたらいいですか?

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を使用しています。私のテストでは、この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) 
    } 
    } 
} 

は、どのように私は、私がアクセスを許可するユーザーを指定することができるように、deadboltActionsを欺くのですか?

別の方法がありますか?多分、別のDeadboltHandlerを提供することによって?これは道のりがはっきりしているようですが、私はそれを理解することができないようであり、少なくともデカルト2の例はありません(少なくともスカラーの場合)。

さらに、極端な場合、スケーラビリでうまく機能し、コントローラーを汚染せずにセキュリティをクロスカッティングの懸念事項として扱うことができる他の承認フレームワークですか?この理由からDeadbolt2はあまりにも限られていますが、正直なところ、(自分で書かない限り)より良い認証フレームワークを見つけることはできません。

答えて

0

それは私の元の質問に正確に答えていないが、これは主にDeadbolt2に関連していましたが、コントローラーで許可規則を指定しなければならないという事実に悩まされていました。これは本当にクロスカットではありません。

答えはSteve Chalonerで提供されていますが、まだいくつかのフープを実行する必要があります。

Panoptesと入力します。この認可フレームワークは、アクション連鎖の代わりにフィルタに基づいているため、中央の場所とコントローラの外に認可ルールを簡単に指定することができます。 Panoptesにセキュリティルールを設定

は春のセキュリティと少し似ていると、それは次のようになります。それ以外は

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でフィルタやプラグインを宣言するために行のカップルを必要とします。

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リポジトリのREADMEファイルには、詳細とコードサンプルがあります。

独自のAuthorizationRulesを作成できるようにカスタマイズすることもできます。私のプロジェクトでは、コールをシステムに登録するモバイルデバイスを確認する必要があるという要件があります。パスを自分のパターンに一致させるリクエストごとに、私のためにこれを処理するAuthorizationRuleを書くことができます。

ユニットテストコントローラーは、セキュリティレイヤーのどんなモックも省略することができるので、非常に簡単です。他のクラスと同様にテストできます。

同様の問題が発生している場合や、承認ルールがコントローラに属していないと思われる場合は、Panoptesにアクセスしてください。これが他の人に役立つことを願っています

+1

このアプローチは、あなたの答えに触発され、Panoptesを見て、2.5.1-SNAPSHOTとしてDeadboltで利用できるようになりました。 https://deadbolt-scala.readme.io/v2.5.1/docs/authorized-routes –

+0

すごくいいですね。私のクライアントの締め切りは厳しいので、すでにPanoptesの作成作業を行っていましたが、今のところそれに固執しますが、Deadboltにはこれらの機能もあります。ちなみに、ドキュメントでは言及していませんが、特質を実装してカスタムの認可ルールを書くこともできます。 Cheers – redwulf

+0

絶対に - あなたは単に 'FilterFunction'を実装することができます。私はあなたがパノプテスを書いたことを気づかなかった - 素敵な仕事! –

0

これにはさまざまな方法があります。

DeadboltHandlerにサブジェクトにアクセスするためのDAOが注入されている場合は、DAOのバインディングをオーバーライドして、テスト対象を含むDAOを提供できます。

abstract class AbstractControllerSpec extends PlaySpecification { 
    sequential 
    isolated 

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

このアプローチを使用する例については、the test appを参照してください。

また、DeadboltHandler実装を拡張してgetSubjectを上書きし、ここからテスト対象を提供することもできます。バインディングは上記と同じ方法で処理されます。

最後に、コードをそのまま使用してテストデータベースにサブジェクトを移入することができます。送信するリクエストは、認証要件(ヘッダー、クッキー内の何かなど)によって形成されます。

ユニットテストでは、同様のことが適用されます。テストのためにハードコードされた件名を持っているSubjectDaoがあれば、WithApplicationとインジェクタルックアップを使って必要なものを得ることができます。私たちは、その後、ユニットテストが、このようにすることができます

class Subject @Inject()(deadbolt: DeadboltActions) extends Controller { 

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

:このような何かに見えるコントローラで

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) 
} 

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

ちょうど例を見ましたが、スタック全体、コントローラ、入力、期待される出力をテストしようとしていません。そのために、私のコントローラが使用するサービスを模擬する必要があります。例。つまり、DeadboltActionsもまた嘲笑される必要があります。あるいは、何らかの形で全体のコンテキストを作成し、DeadboltActionsの有効な完全インスタンスを挿入する必要があります。私の問題は、それを行う方法を考え出しているので、助けがあればいいでしょう。または私は何かを逃していますか? – redwulf

+0

@redwulf私は単体テストの完全な例を追加するように編集しました。 –

+0

WithApplicationコンストラクタは、Play 2.5.11以降は引数をとりません。 –

関連する問題