ICEfaces
  1. ICEfaces
  2. ICE-4904

First request after a secure session expires is incorrect

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 1.8.1
    • Fix Version/s: 1.8.2
    • Component/s: Framework
    • Labels:
      None
    • Environment:
      secure security

      Description

      Normally, with form-based authentication, the credentials are stored in the session and subsequent requests can access secured resources. However, when such a session expires, the next resource request is "remembered" by the container while the login form is presented. If proper credentials are provided, the container makes an attempt to redirect to the originally requested resource. However, with ICEfaces, the cached request appears to be dispose-views:

      http://[host]:[port]/[context]/block/dispose-views

      Of course, this doesn't return anything useful.

        Activity

        Ken Fyten made changes -
        Status Resolved [ 5 ] Closed [ 6 ]
        Assignee Deryk Sinotte [ deryk.sinotte ]
        Ken Fyten made changes -
        Status Open [ 1 ] Resolved [ 5 ]
        Fix Version/s 1.8.2 [ 10190 ]
        Assignee Priority P1
        Resolution Fixed [ 1 ]
        Hide
        Stefano Bortoli added a comment -

        Ok, I have the feeling that my situation is a bit more complicated... although it might be related somehow with this problem.

        SSL connection works fine using 'direct internet' connection, but it keeps on expiring when I access the application through an http proxy. The public part works also behind the proxy, but not the secured. After some refresh I can do something, but the session expires very fast. Are you aware of any SSL + Proxy issue?

        Show
        Stefano Bortoli added a comment - Ok, I have the feeling that my situation is a bit more complicated... although it might be related somehow with this problem. SSL connection works fine using 'direct internet' connection, but it keeps on expiring when I access the application through an http proxy. The public part works also behind the proxy, but not the secured. After some refresh I can do something, but the session expires very fast. Are you aware of any SSL + Proxy issue?
        Hide
        Ted Goddard added a comment -

        The above suggestion worked well in the test environment. Does your environment have different configuration?

        Show
        Ted Goddard added a comment - The above suggestion worked well in the test environment. Does your environment have different configuration?
        Hide
        Stefano Bortoli added a comment -

        Hello,

        I see that this issue is still open and no progress was done since September 2009. Does this mean that the problem with Secured connection still exists?

        Does the patch published above work? Please let me know. We just deployed on production with certificate based authentication and session keeps on expiring.

        Show
        Stefano Bortoli added a comment - Hello, I see that this issue is still open and no progress was done since September 2009. Does this mean that the problem with Secured connection still exists? Does the patch published above work? Please let me know. We just deployed on production with certificate based authentication and session keeps on expiring.
        Repository Revision Date User Message
        ICEsoft Public SVN Repository #19264 Wed Sep 16 09:21:16 MDT 2009 ted.goddard added sessionExpiredServerRedirect (ICE-4904)
        Files Changed
        Commit graph MODIFY /icefaces/trunk/icefaces/core/src/com/icesoft/faces/webapp/http/servlet/MainSessionBoundServlet.java
        Commit graph MODIFY /icefaces/trunk/icefaces/core/src/com/icesoft/faces/webapp/http/core/RequestVerifier.java
        Commit graph MODIFY /icefaces/trunk/icefaces/core/src/com/icesoft/faces/webapp/http/core/SessionExpiredResponse.java
        Ted Goddard made changes -
        Attachment core-changes.zip [ 11992 ]
        Hide
        Ted Goddard added a comment -

        Changed files supporting

        <context-param>
        <param-name>
        com.icesoft.faces.sessionExpiredServerRedirect
        </param-name>
        <param-value>true</param-value>
        </context-param>

        Show
        Ted Goddard added a comment - Changed files supporting <context-param> <param-name> com.icesoft.faces.sessionExpiredServerRedirect </param-name> <param-value>true</param-value> </context-param>
        Hide
        Ted Goddard added a comment -

        The server (at least Tomcat does) hides the difference between requests made directly by the browser and requests delayed and replayed through the authentication process. The difficulty with this is that in one case, the response is being delivered to a POST made by XMLHttpRequest, and in the other case the response is being delivered by a browser redirecting with a full page GET.

        What appears to be workable is to add a special case for session expired being "browser readable" and taking the form of a redirect request to the expiry page (as sent from the server, not just as a redirect performed by the JavaScript bridge). For instance (diff has been edited so line numbers are not valid) the following seems to work as desired on tomcat:

        ===================================================================
        — src/com/icesoft/faces/webapp/http/core/RequestVerifier.java (revision 19235)
        +++ src/com/icesoft/faces/webapp/http/core/RequestVerifier.java (working copy)
        @@ -3,6 +3,7 @@
        import com.icesoft.faces.webapp.http.common.Request;
        import com.icesoft.faces.webapp.http.common.Server;
        import com.icesoft.faces.webapp.http.common.standard.EmptyResponse;
        +import com.icesoft.faces.webapp.http.common.Configuration;
        import org.apache.commons.logging.Log;
        import org.apache.commons.logging.LogFactory;

        @@ -10,10 +11,19 @@

        public class RequestVerifier implements Server {
        private final static Log log = LogFactory.getLog(RequestVerifier.class);
        + private Configuration configuration;
        private String sessionID;
        private Server server;

        • public RequestVerifier(String sessionID, Server server)
          Unknown macro: {+ public RequestVerifier(Configuration configuration, String sessionID, Server server) { + this.configuration = configuration; this.sessionID = sessionID; this.server = server; }@@ -28,7 +38,8 @@ server.service(request); }

          else

          { log.debug("Missmatched 'ice.session' value. Session has expired."); - request.respondWith(SessionExpiredResponse.Handler); + request.respondWith(SessionExpiredResponse.getRedirectingHandler(configuration.getAttribute("sessionExpiredRedirectURI"))); +// request.respondWith(SessionExpiredResponse.Handler); }

          } else

          Unknown macro: { if( log.isDebugEnabled() ){ Index: src/com/icesoft/faces/webapp/http/core/SessionExpiredResponse.java =================================================================== --- src/com/icesoft/faces/webapp/http/core/SessionExpiredResponse.java (revision 19235) +++ src/com/icesoft/faces/webapp/http/core/SessionExpiredResponse.java (working copy) @@ -1,6 +1,8 @@ package com.icesoft.faces.webapp.http.core; import com.icesoft.faces.webapp.command.Command; +import com.icesoft.faces.webapp.http.common.Response; +import com.icesoft.faces.webapp.http.common.ResponseHandler; import com.icesoft.faces.webapp.http.common.standard.FixedXMLContentHandler; import java.io.IOException; @@ -14,4 +16,14 @@ SessionExpiredCommand.serializeTo(writer); } }

          ;
          +
          + public static ResponseHandler getRedirectingHandler(final String redirectURI) {
          + ResponseHandler handler = new ResponseHandler()

          Unknown macro: {+ public void respond(Response response) throws Exception { + response.setStatus(302); + response.setHeader("Location", redirectURI); + }+ }

          ;
          + return handler;
          + }
          }
          Index: src/com/icesoft/faces/webapp/http/servlet/MainSessionBoundServlet.java
          ===================================================================

            • src/com/icesoft/faces/webapp/http/servlet/MainSessionBoundServlet.java (revision 19235)
              +++ src/com/icesoft/faces/webapp/http/servlet/MainSessionBoundServlet.java (working copy)
              @@ -124,13 +124,13 @@
              receivePing = OKServer;
              } else { //setup blocking connection server - sendUpdatedViews = new RequestVerifier(sessionID, new PushServerDetector(session, sessionID, synchronouslyUpdatedViews, allUpdatedViews, monitorRunner, configuration, messageService, this)); - sendUpdates = new RequestVerifier(sessionID, new SendUpdates(configuration, views, this)); - receivePing = new RequestVerifier(sessionID, new ReceivePing(views, this)); + sendUpdatedViews = new RequestVerifier(configuration, sessionID, new PushServerDetector(session, sessionID, synchronouslyUpdatedViews, allUpdatedViews, monitorRunner, configuration, messageService, this)); + sendUpdates = new RequestVerifier(configuration, sessionID, new SendUpdates(configuration, views, this)); + receivePing = new RequestVerifier(configuration, sessionID, new ReceivePing(views, this)); }

        Server upload = new UploadServer(views, configuration);

        • Server receiveSendUpdates = new RequestVerifier(sessionID, new ReceiveSendUpdates(views, synchronouslyUpdatedViews, sessionMonitor, this));
          + Server receiveSendUpdates = new RequestVerifier(configuration, sessionID, new ReceiveSendUpdates(views, synchronouslyUpdatedViews, sessionMonitor, this));

        dispatchOn(".*block\\/receive\\-updated
        -views$", new EnvironmentAdaptingServlet(sendUpdatedViews, configuration, session.getServletContext()));
        PathDispatcherServer dispatcherServer = new PathDispatcherServer();

        Show
        Ted Goddard added a comment - The server (at least Tomcat does) hides the difference between requests made directly by the browser and requests delayed and replayed through the authentication process. The difficulty with this is that in one case, the response is being delivered to a POST made by XMLHttpRequest, and in the other case the response is being delivered by a browser redirecting with a full page GET. What appears to be workable is to add a special case for session expired being "browser readable" and taking the form of a redirect request to the expiry page (as sent from the server, not just as a redirect performed by the JavaScript bridge). For instance (diff has been edited so line numbers are not valid) the following seems to work as desired on tomcat: =================================================================== — src/com/icesoft/faces/webapp/http/core/RequestVerifier.java (revision 19235) +++ src/com/icesoft/faces/webapp/http/core/RequestVerifier.java (working copy) @@ -3,6 +3,7 @@ import com.icesoft.faces.webapp.http.common.Request; import com.icesoft.faces.webapp.http.common.Server; import com.icesoft.faces.webapp.http.common.standard.EmptyResponse; +import com.icesoft.faces.webapp.http.common.Configuration; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -10,10 +11,19 @@ public class RequestVerifier implements Server { private final static Log log = LogFactory.getLog(RequestVerifier.class); + private Configuration configuration; private String sessionID; private Server server; public RequestVerifier(String sessionID, Server server) Unknown macro: {+ public RequestVerifier(Configuration configuration, String sessionID, Server server) { + this.configuration = configuration; this.sessionID = sessionID; this.server = server; }@@ -28,7 +38,8 @@ server.service(request); } else { log.debug("Missmatched 'ice.session' value. Session has expired."); - request.respondWith(SessionExpiredResponse.Handler); + request.respondWith(SessionExpiredResponse.getRedirectingHandler(configuration.getAttribute("sessionExpiredRedirectURI"))); +// request.respondWith(SessionExpiredResponse.Handler); } } else Unknown macro: { if( log.isDebugEnabled() ){ Index: src/com/icesoft/faces/webapp/http/core/SessionExpiredResponse.java =================================================================== --- src/com/icesoft/faces/webapp/http/core/SessionExpiredResponse.java (revision 19235) +++ src/com/icesoft/faces/webapp/http/core/SessionExpiredResponse.java (working copy) @@ -1,6 +1,8 @@ package com.icesoft.faces.webapp.http.core; import com.icesoft.faces.webapp.command.Command; +import com.icesoft.faces.webapp.http.common.Response; +import com.icesoft.faces.webapp.http.common.ResponseHandler; import com.icesoft.faces.webapp.http.common.standard.FixedXMLContentHandler; import java.io.IOException; @@ -14,4 +16,14 @@ SessionExpiredCommand.serializeTo(writer); } } ; + + public static ResponseHandler getRedirectingHandler(final String redirectURI) { + ResponseHandler handler = new ResponseHandler() Unknown macro: {+ public void respond(Response response) throws Exception { + response.setStatus(302); + response.setHeader("Location", redirectURI); + }+ } ; + return handler; + } } Index: src/com/icesoft/faces/webapp/http/servlet/MainSessionBoundServlet.java =================================================================== src/com/icesoft/faces/webapp/http/servlet/MainSessionBoundServlet.java (revision 19235) +++ src/com/icesoft/faces/webapp/http/servlet/MainSessionBoundServlet.java (working copy) @@ -124,13 +124,13 @@ receivePing = OKServer; } else { //setup blocking connection server - sendUpdatedViews = new RequestVerifier(sessionID, new PushServerDetector(session, sessionID, synchronouslyUpdatedViews, allUpdatedViews, monitorRunner, configuration, messageService, this)); - sendUpdates = new RequestVerifier(sessionID, new SendUpdates(configuration, views, this)); - receivePing = new RequestVerifier(sessionID, new ReceivePing(views, this)); + sendUpdatedViews = new RequestVerifier(configuration, sessionID, new PushServerDetector(session, sessionID, synchronouslyUpdatedViews, allUpdatedViews, monitorRunner, configuration, messageService, this)); + sendUpdates = new RequestVerifier(configuration, sessionID, new SendUpdates(configuration, views, this)); + receivePing = new RequestVerifier(configuration, sessionID, new ReceivePing(views, this)); } Server upload = new UploadServer(views, configuration); Server receiveSendUpdates = new RequestVerifier(sessionID, new ReceiveSendUpdates(views, synchronouslyUpdatedViews, sessionMonitor, this)); + Server receiveSendUpdates = new RequestVerifier(configuration, sessionID, new ReceiveSendUpdates(views, synchronouslyUpdatedViews, sessionMonitor, this)); dispatchOn(".*block\\/receive\\-updated -views$", new EnvironmentAdaptingServlet(sendUpdatedViews, configuration, session.getServletContext())); PathDispatcherServer dispatcherServer = new PathDispatcherServer();
        Hide
        Ted Goddard added a comment -

        A successful interaction with the j_security_check form after session expiry results in:

        HTTP/1.1 302 Moved Temporarily
        Server: Apache-Coyote/1.1
        Location: http://localhost:8080/sf-8548/block/send-receive-updates
        Content-Length: 0
        Date: Wed, 09 Sep 2009 23:35:31 GMT

        Causing the browser to issue:

        GET /sf-8548/block/send-receive-updates HTTP/1.1
        Host: localhost:8080
        User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2
        Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
        Accept-Language: en-us,en;q=0.5
        Accept-Encoding: gzip,deflate
        Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
        Keep-Alive: 300
        Connection: keep-alive
        Referer: http://localhost:8080/sf-8548/jsf/main.iface
        Cookie: JSESSIONID=06C3CDCAD50680859CD3E53AB8A5632A; ice.sessions=RUIqnGNXOPnGBIZxA_C_JQ#1

        A key difference here is that the browser is making a GET request (not a POST) to send-receive-updates. In this case, it is clear that the bridge is not making the request, and session expiry should redirect to a "session expired" page.

        Show
        Ted Goddard added a comment - A successful interaction with the j_security_check form after session expiry results in: HTTP/1.1 302 Moved Temporarily Server: Apache-Coyote/1.1 Location: http://localhost:8080/sf-8548/block/send-receive-updates Content-Length: 0 Date: Wed, 09 Sep 2009 23:35:31 GMT Causing the browser to issue: GET /sf-8548/block/send-receive-updates HTTP/1.1 Host: localhost:8080 User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 Accept: text/html,application/xhtml+xml,application/xml;q=0.9, / ;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive Referer: http://localhost:8080/sf-8548/jsf/main.iface Cookie: JSESSIONID=06C3CDCAD50680859CD3E53AB8A5632A; ice.sessions=RUIqnGNXOPnGBIZxA_C_JQ#1 A key difference here is that the browser is making a GET request (not a POST) to send-receive-updates. In this case, it is clear that the bridge is not making the request, and session expiry should redirect to a "session expired" page.
        Hide
        Ted Goddard added a comment -

        Adding this to the login page allows the ajax interaction to proceed to "session-expired":

        <script>
        window.onbeforeunload = null;
        </script>

        The problem is that the JavaScript bridge is still active on the page containing the plain HTML login form.

        I will next investigate how the session-expired can be better handled on the server under these circumstances.

        Show
        Ted Goddard added a comment - Adding this to the login page allows the ajax interaction to proceed to "session-expired": <script> window.onbeforeunload = null; </script> The problem is that the JavaScript bridge is still active on the page containing the plain HTML login form. I will next investigate how the session-expired can be better handled on the server under these circumstances.
        Hide
        Deryk Sinotte added a comment -

        To fully describe what is going on, assume something like the following security constraint declaration taken from the client's test case:

        <login-config>
        <auth-method>FORM</auth-method>
        <form-login-config>
        <form-login-page>/jsp/login.jsp</form-login-page>
        <form-error-page>/jsp/login.jsp</form-error-page>
        </form-login-config>
        </login-config>
        <security-constraint>
        <display-name>UsersConstraint</display-name>
        <web-resource-collection>
        <web-resource-name>Resources</web-resource-name>
        <url-pattern>*.html</url-pattern>
        <url-pattern>*.jsp</url-pattern>
        <url-pattern>*.jspx</url-pattern>
        <url-pattern>*.iface</url-pattern>
        <url-pattern>/block/*</url-pattern>
        <url-pattern>/xmlhttp/*</url-pattern>
        <http-method>GET</http-method>
        <http-method>POST</http-method>
        </web-resource-collection>
        <auth-constraint>
        <description>
        Authorisation constraint for User roles
        </description>
        <role-name>Users</role-name>
        </auth-constraint>
        </security-constraint>
        <security-role>
        <description>
        General role for the application users
        </description>
        <role-name>Users</role-name>
        </security-role>

        1) If you try to access a secured resource in the application using form-based security, you are first routed to the specified login page to enter your security credentials. .

        2) Assuming valid credentials are supplied, the container redirects you to the originally specified resource (the URL of the secured resource that was originally requested is "remembered" by the container).

        3) The credentials are stored in the session so as long as the session is active, you can access the resources that you have access to.

        4) When the session expires, so do the security credentials stored in the session so the container intercepts the next request to the secured resource (just like in step 1) and remembers it, but redirects you to the login page to request credentials again.

        5) Assuming valid credentials are supplied, the container redirects you to the secured resource that was initially requested (and remembered by the container).

        Between 4 and 5 is where the problem occurs because, with ICEfaces, the request that is remembered by the container is an Ajax request - in this case, dispose-views as the page unloads. It's further complicated by the fact that, with security enabled, the container doesn't pass on the requests when it intercepts them (Tomcat, WebSphere, whatever).

        If you adjust the security constraints so that /block requests are not secured, then dispose-views will not be intercepted and remembered (which could be a security issue I suppose). However, the next response that comes back is the <session-expired/> message which kills the bridge. If you have set the context parameter for that result, it will redirect to that page but that may not be what you want.

        Show
        Deryk Sinotte added a comment - To fully describe what is going on, assume something like the following security constraint declaration taken from the client's test case: <login-config> <auth-method>FORM</auth-method> <form-login-config> <form-login-page>/jsp/login.jsp</form-login-page> <form-error-page>/jsp/login.jsp</form-error-page> </form-login-config> </login-config> <security-constraint> <display-name>UsersConstraint</display-name> <web-resource-collection> <web-resource-name>Resources</web-resource-name> <url-pattern>*.html</url-pattern> <url-pattern>*.jsp</url-pattern> <url-pattern>*.jspx</url-pattern> <url-pattern>*.iface</url-pattern> <url-pattern>/block/*</url-pattern> <url-pattern>/xmlhttp/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <description> Authorisation constraint for User roles </description> <role-name>Users</role-name> </auth-constraint> </security-constraint> <security-role> <description> General role for the application users </description> <role-name>Users</role-name> </security-role> 1) If you try to access a secured resource in the application using form-based security, you are first routed to the specified login page to enter your security credentials. . 2) Assuming valid credentials are supplied, the container redirects you to the originally specified resource (the URL of the secured resource that was originally requested is "remembered" by the container). 3) The credentials are stored in the session so as long as the session is active, you can access the resources that you have access to. 4) When the session expires, so do the security credentials stored in the session so the container intercepts the next request to the secured resource (just like in step 1) and remembers it, but redirects you to the login page to request credentials again. 5) Assuming valid credentials are supplied, the container redirects you to the secured resource that was initially requested (and remembered by the container). Between 4 and 5 is where the problem occurs because, with ICEfaces, the request that is remembered by the container is an Ajax request - in this case, dispose-views as the page unloads. It's further complicated by the fact that, with security enabled, the container doesn't pass on the requests when it intercepts them (Tomcat, WebSphere, whatever). If you adjust the security constraints so that /block requests are not secured, then dispose-views will not be intercepted and remembered (which could be a security issue I suppose). However, the next response that comes back is the <session-expired/> message which kills the bridge. If you have set the context parameter for that result, it will redirect to that page but that may not be what you want.
        Hide
        Deryk Sinotte added a comment -

        I tried altering the <security-constraint> to NOT include the <url-pattern>/block/*</url-pattern>. Running the same test changes the behaviour in that, after supplying the security credentials again, it now redirects to the custom session expired page specified in the test case.

        <context-param>
        <param-name>
        com.icesoft.faces.sessionExpiredRedirectURI
        </param-name>
        <param-value>../jsp/sessionExpired.jsp</param-value>
        </context-param>

        Commenting out the session expired redirection parameter results in nothing happening when the session expires. Our bridge receives the session-expired message from the core but nothing is done with it to redirect to another resource.

        Show
        Deryk Sinotte added a comment - I tried altering the <security-constraint> to NOT include the <url-pattern>/block/*</url-pattern>. Running the same test changes the behaviour in that, after supplying the security credentials again, it now redirects to the custom session expired page specified in the test case. <context-param> <param-name> com.icesoft.faces.sessionExpiredRedirectURI </param-name> <param-value>../jsp/sessionExpired.jsp</param-value> </context-param> Commenting out the session expired redirection parameter results in nothing happening when the session expires. Our bridge receives the session-expired message from the core but nothing is done with it to redirect to another resource.
        Deryk Sinotte made changes -
        Field Original Value New Value
        Environment secure secure security
        Salesforce Case [50070000009f5FA]
        Assignee Priority P1
        Assignee Deryk Sinotte [ deryk.sinotte ]
        Hide
        Deryk Sinotte added a comment -

        Adding reference to SalesForce case.

        I've modified the test case so that it runs on Tomcat and I can replicate the behaviour there to help make it a bit easier to debug.

        Show
        Deryk Sinotte added a comment - Adding reference to SalesForce case. I've modified the test case so that it runs on Tomcat and I can replicate the behaviour there to help make it a bit easier to debug.
        Deryk Sinotte created issue -

          People

          • Assignee:
            Unassigned
            Reporter:
            Deryk Sinotte
          • Votes:
            4 Vote for this issue
            Watchers:
            5 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: