5 Replies Latest reply on Mar 20, 2008 11:52 PM by Shane Bryzak

    Identiy.logout behavior across sessions

    Maurice Ling Newbie

      Hi Everyone,


      I'm writing an application where I keep track of all the users that have logged into the system.  This involves putting each user's Identity object in a list that has Application scope. 


      The Administrator then can view all the users logged into the system in a list, and then, optionally, force a particular user to log out.


      The problem is that when I call identity.logout() for a particular user, it logs out both the current user (the administrator), and the selected user.  Plus, the org.jboss.seam.security.loggedOut event gets raised for the current user, but not the selected user.


      The proper behavior should be that the user who's identity got logged out should receive the loggedOut event and the current user's session should continue to be active.


      Comments?  Is there a better way to do this?


      I can post some example code if that will help.


      Thanks and regards,


      -Maurice

        • 2. Re: Identiy.logout behavior across sessions
          Maurice Ling Newbie

          Hi All,


          Here are example code segments.  Please see the embedded comments.  I'm running this using Seam 2.0.1.GA and JBoss 4.2.2.GA.


          -Maurice


          UserLoginMonitor:


          /**
           * Monitor user logins.  The security events require JBoss Seam 2.0.1.GA.
           **/
          @Name("userLoginMonitor")
          @Scope(ScopeType.APPLICATION)
          public class UserLoginMonitor
          {
            @Logger
              private static Log log_;
          
            /**
             * Selected User session
             **/
            @DataModelSelection
              @Out(required=false)
              private UserSessionInfo selectedSession_;
          
            /**
             * Hashmap of user ID to User Sessions
             **/
            private LinkedHashMap<String, UserSessionInfo> userSessions_ = 
              new LinkedHashMap<String, UserSessionInfo>();
          
            /**
             * Default constructor
             **/
            public UserLoginMonitor()
            {
            }
          
            /**
             * Observe loginSuccessful events
             **/
            @Observer("org.jboss.seam.security.loginSuccessful")
              public void loginDetected()
            {
              Identity id = Identity.instance();
              final String userId = id.getUsername();
              
              log_.info("Logged In:  "+userId);
              
              loginUser(id);
          
              printSessions();
            }
          
            /**
             * Observe loggedOut events
             **/
            @Observer("org.jboss.seam.security.loggedOut")
              public void logoutDetected()
            {
              Identity id = Identity.instance();
              final String userId = id.getUsername();
              
              log_.info("Logged Out:  "+userId);
              logoutUser(id);
          
              printSessions();
            }
          
            /**
             * Observer loginFailed events
             **/
            @Observer("org.jboss.seam.security.loginFailed")
              public void loginFailedDetected()
            {
              Identity id = Identity.instance();
              final String userId = id.getUsername();
              
              log_.info("Login Failed:  "+userId);
              failLogin(userId);
            }
          
            /**
             * Track logged in user sessions
             **/
            public void loginUser(Identity id)
            {
              final String userName = id.getUsername();
              userSessions_.put(userName, new UserSessionInfo(id, new Date()));
            }
          
            /**
             * Remove session when user logs out
             **/
            public void logoutUser(Identity id)
            {
              userSessions_.remove(id.getUsername());
            }
          
            /**
             * Get list of user sessions
             **/
            @DataModel("userSessions")
              public List <UserSessionInfo> getUserSessions()
            {
              return new ArrayList<UserSessionInfo>(userSessions_.values());
            } 
          
            /**
             * TODO:  Implement failed login tracking
             **/
            private void failLogin(String user)
            {
              
            }
          
            /**
             * Force the selected user to logout.  In Seam 2.0.1, this causes
             * both the current user and the selected user to be logged out.
             * The org.jboss.seam.security.loggedOut event gets sent for the current
             * user, not the selected user.
             **/
            public void forceLogout()
            {
              Identity id = selectedSession_.getIdentity();
          
              //
              // logs the correct user name
              //
              log_.info("Forcing "+id.getUsername()+" to Logout");
          
              //
              // causes the current and selected user to logout and 
              // raises the loggedOut event for the current user.
              //
              id.logout();
            }
          
            /**
             * For debugging
             **/
            private void printSessions()
            {
              List<UserSessionInfo> sessions = getUserSessions();
          
              Iterator<UserSessionInfo> it = sessions.iterator();
              while (it.hasNext())
              {
                log_.info(it.next());
              }
            }
          }
          


          UserSessionInfo:


          /**
           * Hold User session login information
           **/
          public class UserSessionInfo
          {
            private Identity id_ = null;
            private Date loginDate_ = null;
            
            /**
             * 
             **/
            UserSessionInfo(Identity id, Date loginDate)
            {
              id_ = id;
              loginDate_ = loginDate;
            }
              
            public Identity getIdentity()
            {
              return id_;
            }
          
            public Date getLoginDate()
            {
              return loginDate_;
            }
          
            /**
             * Print out string version of User session information
             **/
            public String toString()
            {
              return "User "+id_.getUsername()+", Logged in since "+loginDate_;
            }
          }
          

          • 3. Re: Identiy.logout behavior across sessions
            Shane Bryzak Master

            This approach isn't going to work, for a number of reasons.  First of all, you should only be interacting with the identity component within the scope of its own session.  Secondly, what happens if the user logs in from two locations simultaneously?  I would be tracking the users by their session instead of their username. 


            Also, rather than pulling the rug out from under their feet so to speak when you want to log them out, the approach I would instead take would be simply setting some kind of flag in application scope which can be read the next time the user makes a request.  Then, if the flag is set force the logout to happen within the user's session itself.

            • 4. Re: Identiy.logout behavior across sessions
              Maurice Ling Newbie

              Hi Shane,


              Thanks for your response. 


              There is an assumption here that the identity component should only be used within the scope of its own session.  Is this a valid assumption?  Based on the API documentation and the way the API is written, it seems like calling identity.logout() should logout that particular identity.  Currently it does that, but has the undesired side effect of logging the current user out too, and consequently seems to violate the encapsulation principle.


              If indeed using the Identity instance should be done within its own session scope, then maybe there should be some safeguards to enforce this constraint.


              As far as your proposed work around is concerned, it assumes that the user would be making a request.  Consider the following scenario:  A user is logged in and is using a significant amount of resources, perhaps locking out other users.  The user then leaves for the weekend without logging out.  The session timeout is set to be a few days, and the administrator would like to free up the resources occupied by the session without waiting for the timeout to happen.


              I agree that tracking by Identity is better than using the user name as key.  However, the simple example is primarily intended to illustrate the effect of calling identity.logout from a different session.


              Regards,


              -Maurice

              • 5. Re: Identiy.logout behavior across sessions
                Shane Bryzak Master

                Your assumption is correct, calling Identity.logout() invalidates the current session so it should only be invoked within the scope of its own session.  In fact any kind of inter-session interaction/manipulation like that is generally regarded as a bad thing.


                I can't say that having a session timeout of a few days is a good idea, or for that matter having a session locking other users out.  However what you probably need to solve your issue is to use a Tomcat API call to kill the user's session.  I haven't tried this myself, but the method that you probably want is StandardManager.expireSession().


                http://tomcat.apache.org/tomcat-4.1-doc/catalina/docs/api/org/apache/catalina/session/StandardManager.html