Testing scala Play controllers (2.2.1) with CSRF protection

I'm having problems with test controllers that use Play CSRF protection. To demonstrate this, I created a very simple Play application that minimally demonstrates the problem.

https://github.com/adamnfish/csrftest

Full details are contained in the README of this repository, but in brief:

Consider a controller designed to handle form submissions. It has a GET method that uses CSRFAddToken and a POST method that uses CSRFCheck. The first adds a CSRF token to the request, so that the form field can be placed in a visualized view containing the actual token. When this form is submitted, if the CSRF check passes and the submission is valid, something else will happen (usually a redirect). If the submission of the form is not valid, the submission of the form is re-displayed along with any errors, so the user can correct the form and submit it again.

It works great!

However, in the tests we have some problems. To test the controller, you can pass it a fake request in the test. The CSRF check itself can be skipped by adding the nocheck header to the fake request, but the view cannot be displayed because there is no token to create the form field. The test fails with RuntimeException, "The CSRF token (csrf.scala: 51) is missing."

Given that it works when it actually runs, but not in tests, it seems like this should be a problem with the way FakeRequests run in Play tests, but I may be doing something wrong. I implemented CSRF protection as described at http://www.playframework.com/documentation/2.2.1/ScalaCsrf and the testing described at http://www.playframework.com/documentation/2.2.1/ScalaFunctionalTest . I would appreciate any pointers if anyone could check out the protected forms of CSRF.

+4
source share
6 answers

One solution is to test using a browser, such as Fluentlenium, as this will manage cookies, etc., so CSRF protection should only work.

Another solution is to add the session to FakeRequest so that it contains the token, for example:

FakeRequest().withSession("csrfToken" -> CSRF.SignedTokenProvider.generateToken) 

Obviously, if you do this a lot, you can create a help method for this.

+4
source

Bonus answer for those interested in Java: I got this to work in the Java version of Play Framework 2.2 by adding

 .withSession(CSRF.TokenName(), CSRFFilter.apply$default$5().generateToken()) 

to fakeRequest()

+4
source

Following @plade, I added a helper method to the base test class:

 protected static FakeRequest csrfRequest(String method, String url) { String token = CSRFFilter.apply$default$5().generateToken(); return fakeRequest(method, url + "?csrfToken=" + token) .withSession(CSRF.TokenName(), token); } 
+1
source

For those who are still interested: I managed to solve this problem globally by enabling CSRF protection in tests. Then the application will create a token for each request that does not contain it. See my answer to this question.

+1
source

For those who may be interested, I created a trait for the game 2.5.x: fooobar.com/questions/1258781 / ...

Then you can use it in your test requests, such as the controller addToken {}:

 val fakeRequest = addToken(FakeRequest(/* params */)) 
+1
source

I use the following method in my base integration integration class:

 def csrfRequest(method: String, uri: String)(implicit app: Application): FakeRequest[AnyContentAsEmpty.type] = { val tokenProvider: TokenProvider = app.injector.instanceOf[TokenProvider] val csrfTags = Map(Token.NameRequestTag -> "csrfToken", Token.RequestTag -> tokenProvider.generateToken) FakeRequest(method, uri, FakeHeaders(), AnyContentAsEmpty, tags = csrfTags) } 

Then you can use it in your tests where you would use FakeRequest .

+1
source

Source: https://habr.com/ru/post/1258783/


All Articles