Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Playframework Secure module: how do you "log in" to test a secured controller in a FunctionalTest?

EDIT: I'm using Play! version 1.2 (production release)

I want to test controller actions that are secured by Secure module class, so I need to log in prior to testing my controller (otherwise I will be redirected to the login page).

I've tried to log in prior to calling a secured action. Here's what my FunctionalTest looks like:

@Test
public void someTestOfASecuredAction() {
    Map<String, String> loginUserParams = new HashMap<String, String>(); 
    loginUserParams.put("username", "admin"); 
    loginUserParams.put("password", "admin");

    // Login here so the following request will be authenticated:
    Response response = POST("/login", loginUserParams);

    // The following is an action that requires an authenticated user:
    Map<String, String> params;
    params.put("someparam", "somevalue");
    response = POST("/some/secured/action", params);

    assertIsOk(response); // this always fails because it is a 302 redirecting to /login
}

Stepping through the code, I've verified that the login post works - it causes a redirect response with location set to the home page (which indicates a successful login).

But then in the subsequent call to a secured action, I am always redirected to the "/login" page - indicating that my previous login did not stick for the second POST request.

Looking into the source code of FunctionalTest I saw there was an @Before interceptor that clears all cookies. I tried overriding this intercepter in my own intermediary superclass (to preserve the cookies), but that didn't work either.

EDIT: I was confusing the play.mvc.Before interceptor with org.junit.Before - the former for use with Play! controllers, the latter for JUnit tests. The @Before in the FuncitonTest is a JUnit interceptor, so it should have any affect on cookies (since it gets run once prior to the test being run).

I do not want to have to write a Selenium test for every secured action - since almost all will be secured. Is there a way to "fool" the Secure module into believing you're authenticated? Or maybe some other very obvious way for handling this (seemingly common) scenario in a FunctionalTest?

Thanks in advance,

Mark

EDIT: Working code, Codemwnci's answer marked as correct

Codemwnci's answer is correct. Here is my workaround for preserving the cookies from one request to the next:

@Test
public void someTestOfASecuredAction() {
    Map<String, String> loginUserParams = new HashMap<String, String>();
    loginUserParams.put("username", "admin");
    loginUserParams.put("password", "admin");
    Response loginResponse = POST("/login", loginUserParams);

    Request request = newRequest();
    request.cookies = loginResponse.cookies; // this makes the request authenticated
    request.url = "/some/secured/action";
    request.method = "POST";
    request.params.put("someparam", "somevalue");
    Response response = makeRequest(request);
    assertIsOk(response); // Passes!
}
like image 494
Mark S Avatar asked Apr 23 '11 04:04

Mark S


1 Answers

I think there must be a misunderstanding of what the @Before interceptor does. It executes before your test is executed. If your test then logs you in, then this event happens AFTER the @Before code has been executed and the results of the secure module should be saved to the Cookie.

Therefore, I can only assume that the Cookie is not being sent with the following request, therefore, I would suggest trying the following...

get the cookie used by the secure cookie from the Response object immediately following your login. Create a Request object and set the Cookie to the request object, then call your POST method, passing in your request object.

I have not tested this code, so not sure how it is going to react to mixing a pre-built request object, and passing in a URL, but not sure what else to suggest.

like image 126
Codemwnci Avatar answered Dec 11 '22 07:12

Codemwnci