Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    Relogin when you can't use a loginRequiredMarker

    We have a corporate siteminder based login that I can't touch. Our applications are fronted by apache with a siteminder plugin and our apps behind that on tomcat.

    So our rpc's are being redirected to the standard login page when the session has timed out as discussed in http://www.smartclient.com/smartgwt/javadoc/com/smartgwt/client/docs/Relogin.html

    Also we can't "configure your authentication system to return a special response specifically for background requests for data"

    So how do we reliably detect the timeout?

    I can see (via firebug) that 302's are being sent back to the client, but I don't see those in the transformResponse. It seems to have already followed the redirection by then.

    I was also hoping to check the status of the siteminder cookie but I noticed it never gets reset in the response to rpc's, only if the main page is reloaded.
    But I don't get this cause that should mean that our session would timeout even if the user is triggering ajax calls. This doesn't seem to happen so clearly I'm missing something.

    Any help greatly appreciated.

    #2
    302s are followed transparently by xmlHttpRequest (this cannot be detected).

    There's no better approach than to insist upon being able to add the loginRequiredMarker to the login page. It's has no impact on appearance or functionality.

    Comment


      #3
      Unfortunately I work for a very large organisation (>200k employees) and this is the central company wide sso solution. i.e. the login page is company wide so my chances of getting them to add something to this page is likely zero. I will see if I can get them to host another login page for us, but this is unlikely to be easy.

      Assuming this fails, what other options do I have? (You say "no better option" but at this point I'm looking for "any option".)

      The only hooks I can see is where I can insert any processing is in DataSource.transformResponse but I don't have access to the http response here. Also some cases are causing json parse errors resulting in this method not being called.

      Is there any way I can get the cookies to refresh on a response to an rpc? The siteminder cookie is sent in responses. However it doesn't seem to update the cookies if I do Cookies.getCookie(). This only seems to update on page reload.

      Comment


        #4
        OK, jumping down to "any option", what you need is a low-level hook where you can implement some kind of hack to detect the existing SiteMinder response as-is, and you just have to hope that future configuration changes in SiteMinder and/or upgrades of the SiteMinder software do not change the names of Http headers, cookies or whatever you end up relying on.

        We left in an undocumented hook for this in SmartClient - classMethod RPCManager.responseRequiresLogin(xmlHttpRequest, transactionNum). You could override this via JSNI from SmartGWT, but, we would highly recommend instead doing a Feature Sponsorship to create a cleaner API instead.

        Comment


          #5
          Thanks for the pointer. I have something that looks like it's working. It consists of the following

          Code:
          public interface ResponseRequiresLoginCallback
          {
              public boolean responseRequiresLogin(XMLHttpRequest request, int transactionNum);
          }
          Code:
          public class RPCManagerExtensions
          {
              public static native void setResponseRequiresLoginCallback(ResponseRequiresLoginCallback callback) /*-{
                  $wnd.isc.RPCManager.addClassProperties({
                      responseRequiresLogin : $entry(function (xmlHttpRequest, transactionNum) {
                         return callback.@com.jpmorgan.wss.secprice.priceui.client.smartgwtext.ResponseRequiresLoginCallback::responseRequiresLogin(Lcom/google/gwt/xhr/client/XMLHttpRequest;I)(xmlHttpRequest, transactionNum);
                      })
                  });
              }-*/;
          }
          I then register a handler that checks the response for various conditions. So far I have two separate cases

          Code:
          class EmptyResponseChecker implements InvalidSSOConditionChecker
          {
          
              @Override
              public boolean isInvalid(XMLHttpRequest request)
              {
                  return (request.getStatus() == 0
                          && StringUtils.isBlank(request.getStatusText())
                          && StringUtils.isBlank(request.getAllResponseHeaders())
                          && StringUtils.isBlank(request.getResponseText())
                          );
              }
              
          }
          
          class SiteMinderLoginPageChecker implements InvalidSSOConditionChecker
          {
          
              @Override
              public boolean isInvalid(XMLHttpRequest request)
              {
                  return (request.getStatus() == 200
                          && StringUtils.isBlank(request.getStatusText())
                          && StringUtils.contains(request.getResponseText(), "<!DOCTYPE html>")
                          && StringUtils.contains(request.getResponseText(), "SiteMinder")
                          );
              }
              
          }
          I've been deliberately specific as it's largely guess work so runs the risk of flagging a valid response as invalid.

          In my main module I then register the handler and add the following

          Code:
                  RPCManager.setLoginRequiredCallback(new LoginRequiredCallback()
                  {
                      
                      @Override
                      public void loginRequired(int transactionNum, RPCRequest request, RPCResponse response)
                      {
                         Window.Location.reload();
                      }
                  });
          If I had more time I might consider the other approaches identified in the Relogin article, like "Open a new browser window that goes to your plain HTML login form".

          Comment


            #6
            Thanks for posting the working code.

            Opening a new SmartGWT Window containing an iframe is what we did to implement Relogin for SiteMinder in the past because it's quite picky about getting exactly the parameters it writes into the login form.

            Just bear in mind for the future, this is an undocumented API and it may disappear without notice.

            Comment


              #7
              Hi andyholm and Isomorphic,

              I am facing exactly the same problem as you, that the organizational Siteminder page is not able to change.

              I am using SmartGWT PowerEdition 2.5 and I am also new to JSNI.
              One point that I am not very clear that, sample code "RPCManagerExtensions" does not extend RPCManager and how would it be used by the SmartGWT framework?

              I would like to test the same implementation and would you please give me more explaination on how the "RPCManagerExtensionis" will be intercept and run into the custom coding? Thanks!

              Best regards,
              John

              Comment


                #8
                Finally, I made something worked.

                1. Download the SmartGWT 2.5 opensource source code
                2. Add this class: ResponseRequiresLoginCallBack (note that, it must be inside com.smartgwt.client package)
                Code:
                package com.smartgwt.client.rpc.abcde;
                
                import com.google.gwt.xhr.client.XMLHttpRequest;
                import com.smartgwt.client.rpc.LoginRequiredCallback;
                
                public interface ResponseRequiresLoginCallback extends LoginRequiredCallback
                {
                    public boolean responseRequiresLogin(XMLHttpRequest request, int transactionNum);
                }
                3. In opensource com.smartgwt.client.rpc.RPCManager.java, add these code:

                Code:
                import com.smartgwt.client.rpc.abcde.ResponseRequiresLoginCallback; 
                ...........
                ...........
                    public static native void setResponseRequiresLoginCallback(ResponseRequiresLoginCallback callback) /*-{
                        $wnd.isc.RPCManager.addClassProperties({
                            responseRequiresLogin : $entry(function (xmlHttpRequest, transactionNum) {
                               return callback.@com.smartgwt.client.rpc.abcde.ResponseRequiresLoginCallback::responseRequiresLogin(Lcom/google/gwt/xhr/client/XMLHttpRequest;I)(xmlHttpRequest, transactionNum);
                            })
                        });
                    }-*/;
                4. In your SmartGWT entry point classes (XXX implements EntryPoint) , add these code in the mehtod: public void onModuleLoad()
                Code:
                RPCManager.setRequestRequiresLoginCallBack(new RequestRequiresLoginCallBack(){
                	public boolean responseRequiresLogin(XMLHttpRequest request, int transactionNum){
                		if (request.getStatus() == 200
                			&& request.getStatusText().equals("OK")
                            && request.getResponseText().contains("document.AUTOSUBMIT.submit()")
                            && request.getResponseText().contains("siteminderagent")){
                				//Do your own logging
                               	GWT.log("SSO siteminder time out received!");				
                               	Window.Location.reload();
                        }
                	}
                });
                Cheers!
                John

                Comment


                  #9
                  Hello John,
                  I tried your code, but is not working, give me out immediately after login. First is called RPCManager.setResponseRequiresLoginCallback and after that RPCManager.setLoginRequiredCallback that received an RPCResponse.STATUS_LOGIN_REQUIRED.

                  Can you get me some hints why this happen?

                  Best regards,
                  Marius

                  Comment

                  Working...
                  X