Added option to disconnect existing sessions of same user on login
authorVictor Kirhenshtein <victor@netxms.org>
Mon, 30 May 2016 16:28:24 +0000 (19:28 +0300)
committerVictor Kirhenshtein <victor@netxms.org>
Mon, 30 May 2016 16:28:24 +0000 (19:28 +0300)
12 files changed:
ChangeLog
include/nxcldefs.h
src/java/client/netxms-client/src/main/java/org/netxms/client/users/AbstractUserObject.java
src/java/netxms-eclipse/UserManager/src/org/netxms/ui/eclipse/usermanager/propertypages/Authentication.java
src/server/core/client.cpp
src/server/core/mdsession.cpp
src/server/core/session.cpp
src/server/core/userdb.cpp
src/server/core/userdb_objects.cpp
src/server/include/nms_core.h
src/server/include/nms_users.h
webui/webapp/UserManager/src/org/netxms/ui/eclipse/usermanager/propertypages/Authentication.java

index b0d9445..e1a6b65 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -18,6 +18,7 @@
 - Correct notifications on threshold script errors
 - Option to use last known value for cluster data aggregation in case of data collecion failure
 - Added server configuration parameter to ignore syslog message timestamps and always use server time
+- Added option to disconnect existing sessions of same user on login
 - NXSL: implemented compound assignment operators and prefix increment/decrement for array elements
 - NXSL: can access event parameters as event object attributes (like $event->$1 or $event->instance)
 - Management console
index 2eeb32a..65584ba 100644 (file)
@@ -660,6 +660,7 @@ enum SessionState
 #define UF_PASSWORD_NEVER_EXPIRES   0x0040
 #define UF_LDAP_USER                0x0080
 #define UF_SYNC_EXCEPTION           0x0100
+#define UF_CLOSE_OTHER_SESSIONS     0x0200
 
 /**
  * Fields for NXCModifyUserEx
index 4d5e296..77693fd 100644 (file)
@@ -43,6 +43,7 @@ public abstract class AbstractUserObject
        public static final int PASSWORD_NEVER_EXPIRES = 0x0040;
    public static final int LDAP_USER = 0x0080;
    public static final int SYNC_EXCEPTION = 0x0100;
+   public static final int CLOSE_OTHER_SESSIONS = 0x0200;
        
    // User object fields
    public static final int MODIFY_LOGIN_NAME        = 0x00000001;
index b396905..bf59b7e 100644 (file)
@@ -50,6 +50,7 @@ public class Authentication extends PropertyPage
        private Button checkDisabled;
        private Button checkChangePassword;
        private Button checkFixedPassword;
+   private Button checkCloseSessions;
        private Combo comboAuthMethod;
        private Combo comboMappingMethod;
        private Text textMappingData;
@@ -96,7 +97,11 @@ public class Authentication extends PropertyPage
       checkFixedPassword = new Button(groupFlags, SWT.CHECK);
       checkFixedPassword.setText(Messages.get().Authentication_CannotChangePassword);
       checkFixedPassword.setSelection(object.isPasswordChangeForbidden());
-               
+
+      checkCloseSessions = new Button(groupFlags, SWT.CHECK);
+      checkCloseSessions.setText("&Close other sessions after login");
+      checkCloseSessions.setSelection((object.getFlags() & User.CLOSE_OTHER_SESSIONS) != 0);
+      
       Group groupMethod = new Group(dialogArea, SWT.NONE);
       groupMethod.setText(Messages.get().Authentication_AuthMethod_Group);
       GridLayout groupMethodLayout = new GridLayout();
@@ -170,6 +175,8 @@ public class Authentication extends PropertyPage
                        flags |= AbstractUserObject.CHANGE_PASSWORD;
                if (checkFixedPassword.getSelection())
                        flags |= AbstractUserObject.CANNOT_CHANGE_PASSWORD;
+               if (checkCloseSessions.getSelection())
+         flags |= AbstractUserObject.CLOSE_OTHER_SESSIONS;
                flags |= object.getFlags() & AbstractUserObject.LDAP_USER;
                object.setFlags(flags);
                
index 8b43b7c..9fd75a1 100644 (file)
@@ -457,3 +457,22 @@ bool IsLoggedIn(UINT32 dwUserId)
    RWLockUnlock(m_rwlockSessionListAccess);
    return result;
 }
+
+/**
+ * Close all user's sessions except given one
+ */
+void CloseOtherSessions(UINT32 userId, UINT32 thisSession)
+{
+   RWLockReadLock(m_rwlockSessionListAccess, INFINITE);
+   for(int i = 0; i < MAX_CLIENT_SESSIONS; i++)
+   {
+      if ((m_pSessionList[i] != NULL) &&
+          (m_pSessionList[i]->getUserId() == userId) &&
+          (m_pSessionList[i]->getId() != thisSession))
+      {
+         nxlog_debug(4, _T("CloseOtherSessions(%d,%d): disconnecting session %d"), userId, thisSession, m_pSessionList[i]->getId());
+         m_pSessionList[i]->kill();
+      }
+   }
+   RWLockUnlock(m_rwlockSessionListAccess);
+}
index 1d52c18..c8abda1 100644 (file)
@@ -462,7 +462,7 @@ void MobileDeviceSession::login(NXCPMessage *pRequest)
    NXCPMessage msg;
    TCHAR szLogin[MAX_USER_NAME], szPassword[1024];
        int nAuthType;
-   bool changePasswd = false, intruderLockout = false;
+   bool changePasswd = false, intruderLockout = false, closeOtherSessions = false;
    UINT32 dwResult;
 #ifdef _WITH_ENCRYPTION
        X509 *pCert;
@@ -498,7 +498,8 @@ void MobileDeviceSession::login(NXCPMessage *pRequest)
                                pRequest->getFieldAsUtf8String(VID_PASSWORD, szPassword, 1024);
 #endif
                                dwResult = AuthenticateUser(szLogin, szPassword, 0, NULL, NULL, &m_dwUserId,
-                                                                                                        &userRights, &changePasswd, &intruderLockout, false);
+                                                                                                        &userRights, &changePasswd, &intruderLockout,
+                                                                                                        &closeOtherSessions, false);
                                break;
                        case NETXMS_AUTH_TYPE_CERTIFICATE:
 #ifdef _WITH_ENCRYPTION
@@ -511,7 +512,8 @@ void MobileDeviceSession::login(NXCPMessage *pRequest)
                                        dwSigLen = pRequest->getFieldAsBinary(VID_SIGNATURE, signature, 256);
                                        dwResult = AuthenticateUser(szLogin, (TCHAR *)signature, dwSigLen, pCert,
                                                                                                                 m_challenge, &m_dwUserId, &userRights,
-                                                                                                                &changePasswd, &intruderLockout, false);
+                                                                                                                &changePasswd, &intruderLockout,
+                                                                                                                &closeOtherSessions, false);
                                        X509_free(pCert);
                                }
                                else
index 077a96e..34af390 100644 (file)
@@ -1874,6 +1874,7 @@ void ClientSession::login(NXCPMessage *pRequest)
 
    if (!(m_dwFlags & CSF_AUTHENTICATED))
    {
+      bool closeOtherSessions = false;
       pRequest->getFieldAsString(VID_LOGIN_NAME, szLogin, MAX_USER_NAME);
                nAuthType = (int)pRequest->getFieldAsUInt16(VID_AUTH_TYPE);
       debugPrintf(6, _T("authentication type %d"), nAuthType);
@@ -1886,7 +1887,8 @@ void ClientSession::login(NXCPMessage *pRequest)
                                pRequest->getFieldAsUtf8String(VID_PASSWORD, szPassword, 1024);
 #endif
                                dwResult = AuthenticateUser(szLogin, szPassword, 0, NULL, NULL, &m_dwUserId,
-                                                                                                        &m_dwSystemAccess, &changePasswd, &intruderLockout, false);
+                                                                                                        &m_dwSystemAccess, &changePasswd, &intruderLockout,
+                                                                                                        &closeOtherSessions, false);
                                break;
                        case NETXMS_AUTH_TYPE_CERTIFICATE:
 #ifdef _WITH_ENCRYPTION
@@ -1899,7 +1901,8 @@ void ClientSession::login(NXCPMessage *pRequest)
                                        dwSigLen = pRequest->getFieldAsBinary(VID_SIGNATURE, signature, 256);
                                        dwResult = AuthenticateUser(szLogin, (TCHAR *)signature, dwSigLen, pCert,
                                                                                                                 m_challenge, &m_dwUserId, &m_dwSystemAccess,
-                                                                                                                &changePasswd, &intruderLockout, false);
+                                                                                                                &changePasswd, &intruderLockout,
+                                                                                                                &closeOtherSessions, false);
                                        X509_free(pCert);
                                }
                                else
@@ -1917,7 +1920,8 @@ void ClientSession::login(NXCPMessage *pRequest)
             {
                debugPrintf(5, _T("SSO ticket %hs is valid, login name %s"), ticket, szLogin);
                                   dwResult = AuthenticateUser(szLogin, NULL, 0, NULL, NULL, &m_dwUserId,
-                                                                                                           &m_dwSystemAccess, &changePasswd, &intruderLockout, true);
+                                                                                                           &m_dwSystemAccess, &changePasswd, &intruderLockout,
+                                                                                                           &closeOtherSessions, true);
             }
             else
             {
@@ -1971,6 +1975,12 @@ void ClientSession::login(NXCPMessage *pRequest)
          debugPrintf(3, _T("User %s authenticated (language=%s clientInfo=\"%s\")"), m_sessionName, m_language, m_clientInfo);
                        WriteAuditLog(AUDIT_SECURITY, TRUE, m_dwUserId, m_workstation, m_id, 0,
             _T("User \"%s\" logged in (language: %s; client info: %s)"), szLogin, m_language, m_clientInfo);
+
+                       if (closeOtherSessions)
+                       {
+                          debugPrintf(5, _T("Closing other sessions for user %s"), m_loginName);
+                          CloseOtherSessions(m_dwUserId, m_id);
+                       }
       }
       else
       {
index 15414de..8777f7e 100644 (file)
@@ -297,12 +297,13 @@ void SaveUsers(DB_HANDLE hdb)
  */
 UINT32 AuthenticateUser(const TCHAR *login, const TCHAR *password, UINT32 dwSigLen, void *pCert,
                         BYTE *pChallenge, UINT32 *pdwId, UINT64 *pdwSystemRights,
-                                                          bool *pbChangePasswd, bool *pbIntruderLockout, bool ssoAuth)
+                                                          bool *pbChangePasswd, bool *pbIntruderLockout, bool *closeOtherSessions, bool ssoAuth)
 {
    int i, j;
    UINT32 dwResult = RCC_ACCESS_DENIED;
    BOOL bPasswordValid = FALSE;
 
+   *closeOtherSessions = false;
    RWLockWriteLock(s_userDatabaseLock, INFINITE);
    User *user = s_users.get(login);
    if ((user != NULL) && !user->isDeleted())
@@ -457,6 +458,7 @@ result:
             if (dwResult != RCC_NO_GRACE_LOGINS)
             {
                *pdwSystemRights = GetEffectiveSystemRights(user);
+               *closeOtherSessions = (user->getFlags() & UF_CLOSE_OTHER_SESSIONS) != 0;
                user->updateLastLogin();
                dwResult = RCC_SUCCESS;
             }
index 2e60f8f..940e4fe 100644 (file)
@@ -195,13 +195,13 @@ void UserDatabaseObject::modifyFromMessage(NXCPMessage *msg)
        if (fields & USER_MODIFY_FLAGS)
        {
           flags = msg->getFieldAsUInt16(VID_USER_FLAGS);
-               // Modify only UF_DISABLED, UF_CHANGE_PASSWORD, UF_CANNOT_CHANGE_PASSWORD and UF_LDAP_USER flags from message
+               // Modify only UF_DISABLED, UF_CHANGE_PASSWORD, UF_CANNOT_CHANGE_PASSWORD and UF_CLOSE_OTHER_SESSIONS flags from message
                // Ignore all but CHANGE_PASSWORD flag for superuser and "everyone" group
-               m_flags &= ~(UF_DISABLED | UF_CHANGE_PASSWORD | UF_CANNOT_CHANGE_PASSWORD);
+               m_flags &= ~(UF_DISABLED | UF_CHANGE_PASSWORD | UF_CANNOT_CHANGE_PASSWORD | UF_CLOSE_OTHER_SESSIONS);
                if ((m_id == 0) || (m_id == GROUP_EVERYONE))
                        m_flags |= flags & UF_CHANGE_PASSWORD;
                else
-                       m_flags |= flags & (UF_DISABLED | UF_CHANGE_PASSWORD | UF_CANNOT_CHANGE_PASSWORD);
+                       m_flags |= flags & (UF_DISABLED | UF_CHANGE_PASSWORD | UF_CANNOT_CHANGE_PASSWORD | UF_CLOSE_OTHER_SESSIONS);
 
        }
 
index 15611b4..ede20c6 100644 (file)
@@ -981,6 +981,7 @@ void NXCORE_EXPORTABLE NotifyClientGraphUpdate(NXCPMessage *update, UINT32 graph
 int GetSessionCount(bool includeSystemAccount);
 bool IsLoggedIn(UINT32 dwUserId);
 bool NXCORE_EXPORTABLE KillClientSession(int id);
+void CloseOtherSessions(UINT32 userId, UINT32 thisSession);
 
 void GetSysInfoStr(TCHAR *pszBuffer, int nMaxSize);
 InetAddress GetLocalIpAddr();
index bf801f4..ae7340e 100644 (file)
@@ -391,7 +391,7 @@ void SendUserDBUpdate(int code, UINT32 id, UserDatabaseObject *object);
 void SendUserDBUpdate(int code, UINT32 id);
 UINT32 AuthenticateUser(const TCHAR *login, const TCHAR *password, UINT32 dwSigLen, void *pCert,
                         BYTE *pChallenge, UINT32 *pdwId, UINT64 *pdwSystemRights,
-                                                          bool *pbChangePasswd, bool *pbIntruderLockout, bool ssoAuth);
+                                                          bool *pbChangePasswd, bool *pbIntruderLockout, bool *closeOtherSessions, bool ssoAuth);
 bool AuthenticateUserForXMPPCommands(const char *xmppId);
 bool AuthenticateUserForXMPPSubscription(const char *xmppId);
 
index b396905..bf59b7e 100644 (file)
@@ -50,6 +50,7 @@ public class Authentication extends PropertyPage
        private Button checkDisabled;
        private Button checkChangePassword;
        private Button checkFixedPassword;
+   private Button checkCloseSessions;
        private Combo comboAuthMethod;
        private Combo comboMappingMethod;
        private Text textMappingData;
@@ -96,7 +97,11 @@ public class Authentication extends PropertyPage
       checkFixedPassword = new Button(groupFlags, SWT.CHECK);
       checkFixedPassword.setText(Messages.get().Authentication_CannotChangePassword);
       checkFixedPassword.setSelection(object.isPasswordChangeForbidden());
-               
+
+      checkCloseSessions = new Button(groupFlags, SWT.CHECK);
+      checkCloseSessions.setText("&Close other sessions after login");
+      checkCloseSessions.setSelection((object.getFlags() & User.CLOSE_OTHER_SESSIONS) != 0);
+      
       Group groupMethod = new Group(dialogArea, SWT.NONE);
       groupMethod.setText(Messages.get().Authentication_AuthMethod_Group);
       GridLayout groupMethodLayout = new GridLayout();
@@ -170,6 +175,8 @@ public class Authentication extends PropertyPage
                        flags |= AbstractUserObject.CHANGE_PASSWORD;
                if (checkFixedPassword.getSelection())
                        flags |= AbstractUserObject.CANNOT_CHANGE_PASSWORD;
+               if (checkCloseSessions.getSelection())
+         flags |= AbstractUserObject.CLOSE_OTHER_SESSIONS;
                flags |= object.getFlags() & AbstractUserObject.LDAP_USER;
                object.setFlags(flags);