8 Replies Latest reply on Sep 10, 2010 5:03 AM by blabno

    JSFUnit and Spring-Security problem

    wumbrath

      Hello,

      we have a JSF web app using spring security for authentication. We wrote a JSFUnit test for testing the authentication. Following the (pretty good) documentation on the website the test looks like:

       WebClientSpec wcSpec = new WebClientSpec("/index.jsf");
       FormAuthenticationStrategy formAuth = new FormAuthenticationStrategy("username", "password");
       formAuth.setSubmitComponent("login");
       wcSpec.setInitialRequestStrategy(formAuth);
       JSFSession jsfSession = new JSFSession(wcSpec);
       JSFClientSession client = jsfSession.getJSFClientSession();
       JSFServerSession server = jsfSession.getJSFServerSession();
      


      The submitted login form calls the following piece of code

      
      ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
       RequestDispatcher dispatcher = ((ServletRequest) context.getRequest())
       .getRequestDispatcher("/j_spring_security_check");
      
       dispatcher.forward((ServletRequest) context.getRequest(), (ServletResponse) context.getResponse());
       FacesContext.getCurrentInstance().responseComplete();
      
      


      This works fine when using the web app through the browser.

      The problem is that the test case throws a NullPointerException when trying to call FacesContext.getCurrentInstance() in the last line of the code posting. The user is authenticated successfully but it seems to me that either the forwarding or the session invalidation performed by spring during the authentication process causes JSFUnit to throw the Exception.

      The test output is
      testLogin(my.tests.jsfunit.JSFUnitTest) Time elapsed: 0.859 sec <<< ERROR!
      java.lang.IllegalStateException
       at org.mortbay.jetty.servlet.AbstractSessionManager$Session.getAttribute(AbstractSessionManager.java:784)
       at org.jboss.jsfunit.framework.FacesContextBridge.getCurrentInstance(FacesContextBridge.java:55)
       at org.jboss.jsfunit.jsfsession.JSFServerSession.pageCreated(JSFServerSession.java:172)
       at org.jboss.jsfunit.jsfsession.JSFServerSession.<init>(JSFServerSession.java:54)
       at org.jboss.jsfunit.jsfsession.JSFSession.<init>(JSFSession.java:84)
       at de.dailab.spree.prototypes.ph.jsfunit.JSFUnitTest.testLogin(JSFUnitTest.java:69)
       at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
       at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
       at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
       at java.lang.reflect.Method.invoke(Method.java:597)
       at junit.framework.TestCase.runTest(TestCase.java:168)
       at junit.framework.TestCase.runBare(TestCase.java:134)
       at org.apache.cactus.internal.AbstractCactusTestCase.runBareServer(AbstractCactusTestCase.java:153)
       at org.apache.cactus.internal.server.AbstractWebTestCaller.doTest(AbstractWebTestCaller.java:119)
       at org.apache.cactus.internal.server.AbstractWebTestController.handleRequest_aroundBody0(AbstractWebTestController.java:93)
       at org.apache.cactus.internal.server.AbstractWebTestController.handleRequest_aroundBody1$advice(AbstractWebTestController.java:224)
       at org.apache.cactus.internal.server.AbstractWebTestController.handleRequest(AbstractWebTestController.java)
       at org.apache.cactus.server.ServletTestRedirector.doPost_aroundBody2(ServletTestRedirector.java:101)
      


      Is there a possibility to test the authentication? I could not find a solution in the docs/faq/forum so far.
      If you need any more information please tell me.

      Thank you in advance

        • 1. Re: JSFUnit and Spring-Security problem
          ssilvert

          The problem is with the invalidated session. JSFUnit is trying to set an attribute on an HttpSession that is no longer valid.

          I don't see why the session needs to be invalidated on login, so if there is any way to keep Spring from invalidating then that would be a solution.

          If you must use the new session created by Spring then you might be able to fix this by creating a filter that maps to /j_spring_security_check. I don't remember, but it was either Servlet 2.4 or Servlet 2.5 that allows filters to run on a Forward. You'll need to check into that if you are using an old version of the servlet spec.

          This filter would do some of the same work that the JSFUnitFilter does. The main thing is to call WebConversationFactory.setThreadLocals(request). That makes sure that the proper instance HttpSession is available to JSFUnit.

          Please report back and let me know how it goes.

          Regards,

          Stan

          • 2. Re: JSFUnit and Spring-Security problem
            jerarckill

            Hello,

            I am trying to integrate JSFUnit with an appfuse generated project using Jetty as the webserver, Spring and MyFaces + richFaces.

            I am encountering the exact same problem mentionned above and have absolutely no idea about how to solve this ^^

            All help appreciated!

            • 3. Re: JSFUnit and Spring-Security problem
              jerarckill

              Stan,

              I also must admit reading your reply that I don't feel capable (at least in the finite amount of time I have to work on this) of implementing the kind of solution you are explaining.

              Is there another way to solve this one?

              • 4. Re: JSFUnit and Spring-Security problem

                I am also having the same problem.

                I tried adding a filter to /j_spring_security_check as suggested but still getting the same exception.

                here is what i did.
                in my web.xml i added the following

                <filter>
                 <filter-name>JSFUnitFixFilter</filter-name>
                 <filter-class>com.bp.il3.opsportal.util.JSFUnitFixFilter</filter-class>
                </filter>
                <filter-mapping>
                 <filter-name>JSFUnitFixFilter</filter-name>
                 <url-pattern>/j_spring_security_check</url-pattern>
                 <dispatcher>FORWARD</dispatcher>
                 <dispatcher>REQUEST</dispatcher>
                 <dispatcher>INCLUDE</dispatcher>
                </filter-mapping>


                and in the filter i did the following:-

                public void doFilter(ServletRequest req,
                 ServletResponse res,
                 FilterChain filterChain) throws IOException, ServletException
                 {
                 HttpServletRequest request = (HttpServletRequest)req;
                 HttpServletResponse response = (HttpServletResponse)res;
                 WebConversationFactory.setThreadLocals(request);
                 filterChain.doFilter(req, res);
                }


                Running the test still gives me:

                java.lang.IllegalStateException: getAttribute: Session already invalidated
                 at org.apache.catalina.session.StandardSession.getAttribute(StandardSession.java:1032)
                 at org.apache.catalina.session.StandardSessionFacade.getAttribute(StandardSessionFacade.java:110)
                 at org.jboss.jsfunit.framework.FacesContextBridge.getCurrentInstance(FacesContextBridge.java:56)
                 at org.jboss.jsfunit.jsfsession.JSFServerSession.pageCreated(JSFServerSession.java:170)
                 at org.jboss.jsfunit.jsfsession.JSFServerSession.<init>(JSFServerSession.java:54)
                 at org.jboss.jsfunit.jsfsession.JSFSession.<init>(JSFSession.java:82)
                 at com.bp.il3.opsportal.jsfunit.TestSecurityAuthorization.testLogin(TestSecurityAuthorization.java:94)
                 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
                 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
                 at java.lang.reflect.Method.invoke(Method.java:597)
                 at junit.framework.TestCase.runTest(TestCase.java:154)
                 at junit.framework.TestCase.runBare(TestCase.java:127)
                 at org.apache.cactus.internal.AbstractCactusTestCase.runBareServer(AbstractCactusTestCase.java:153)
                 at org.apache.cactus.internal.server.AbstractWebTestCaller.doTest(AbstractWebTestCaller.java:119)
                 at org.apache.cactus.internal.server.AbstractWebTestController.handleRequest_aroundBody0(AbstractWebTestController.java:93)
                 at org.apache.cactus.internal.server.AbstractWebTestController.handleRequest_aroundBody1$advice(AbstractWebTestController.java:224)
                 at org.apache.cactus.internal.server.AbstractWebTestController.handleRequest(AbstractWebTestController.java)
                 at org.apache.cactus.server.ServletTestRedirector.doPost_aroundBody2(ServletTestRedirector.java:101)
                 at org.apache.cactus.server.ServletTestRedirector.doPost_aroundBody3$advice(ServletTestRedirector.java:224)
                 at org.apache.cactus.server.ServletTestRedirector.doPost(ServletTestRedirector.java)
                 at org.jboss.jsfunit.framework.JSFUnitServletRedirector.doPost(JSFUnitServletRedirector.java:46)
                 at org.apache.cactus.server.ServletTestRedirector.doGet_aroundBody0(ServletTestRedirector.java:72)
                 at org.apache.cactus.server.ServletTestRedirector.doGet_aroundBody1$advice(ServletTestRedirector.java:224)
                 at org.apache.cactus.server.ServletTestRedirector.doGet(ServletTestRedirector.java)
                 at org.jboss.jsfunit.framework.JSFUnitServletRedirector.doGet(JSFUnitServletRedirector.java:52)
                 at javax.servlet.http.HttpServlet.service(HttpServlet.java:690)
                 at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
                 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
                 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                 at org.springframework.security.util.FilterChainProxy.doFilter(FilterChainProxy.java:169)
                 at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:183)
                 at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:138)
                 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
                 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                 at org.jboss.jsfunit.framework.JSFUnitFilter.doFilter(JSFUnitFilter.java:116)
                 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
                 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                 at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230)
                 at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
                 at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
                 at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:104)
                 at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:563)
                 at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
                 at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:261)
                 at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)
                 at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:581)
                 at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
                 at java.lang.Thread.run(Thread.java:619)



                Any help on this will be greatfully appreciated.
                This has become a major show stopper for us using JSFUnit on our project.


                Thanks

                Yinka.


                • 5. Re: JSFUnit and Spring-Security problem
                  ssilvert

                  It looks like j_spring_security_check is invalidating the session. I don't know exactly what it is doing, but I assume that it is giving you a new session after login. So they key is to call WebConversationFactory.setThreadLocals(HttpServletRequest req) after you get that new session. As I'm unfamiliar with j_spring_security_check, I'm unsure how to accomplish this.

                  Another solution I can think of would be to create a filter that wraps the HttpServletrequest with one that returns the JSFUnitHttpSession. The JSFUnitHttpSession does not allow a session to be invalidated. The code would look something like this:

                  import javax.servlet.http.HttpServletRequestWrapper;
                  import org.jboss.jsfunit.context.JSFUnitHttpSession;
                  
                  public class MySessionHttpRequest extends HttpServletRequestWrapper {
                  
                   private HttpSession session;
                  
                   public MySessionHttpRequest(HttpServletRequest req) {
                   super(req);
                   this.session = new JSFUnitHttpSession(req.getSession());
                   }
                  
                   public HttpSession getSession() {
                   return this.session;
                   }
                  
                   public HttpSession getSession(boolean create) {
                   return this.session;
                   }
                  
                  }


                  import javax.servlet.Filter;
                  import javax.servlet.FilterChain;
                  
                  public class OverrideSessionFilter implements Filter {
                  
                  public void doFilter(ServletRequest req,
                   ServletResponse res,
                   FilterChain filterChain) throws IOException, ServletException {
                  
                   HttpServletRequest myReq = new MySessionHttpRequest((HttpServletRequest)req);
                   filterChain.doFilter(myReq, res);
                  }


                  Then map OverrideSessionFilter the same way you map JSFUnitFilter. Maybe that will work?

                  Stan

                  • 6. Re: JSFUnit and Spring-Security problem
                    blabno

                    JSFUnitHttpSession constructor is not public!

                     

                     

                    <security:http>

                         <security:session-management session-fixation-protection="none"/>   

                    </security:http>

                     

                    All we need is turning session fixation protection to off.

                    • 7. Re: JSFUnit and Spring-Security problem
                      ssilvert

                      Bernard Labno wrote:

                       

                      JSFUnitHttpSession constructor is not public!

                       

                       

                      So you want me to change the constructor to public?  Have you tried making the change and rebuilding JSFUnit  to see if it solves your problem?

                       

                      Stan

                      • 8. Re: JSFUnit and Spring-Security problem
                        blabno

                        Stan, my friend!

                        In post #5 you wrote code using JSFUnitHttpSession constructor from external package (so i assume), which would require the constructor to be public. That's why I have made, in post #6, the remark that it is not public.