From 1aaab76f8c37b4e996d843c17005d34348bbea18 Mon Sep 17 00:00:00 2001
From: JediKev <kevin@enhancesoft.com>
Date: Tue, 14 Aug 2018 14:34:16 -0500
Subject: [PATCH] sessions: Clear On Password Set/Reset
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This adds a Signal to clean Agent and User sessions upon setting/resetting
their password. If an Agent/User resets their own password and has multiple
sessions open it will log them out of every session except the one they’re
on.
---
 include/class.auth.php   | 28 ++++++++++++++++++++++++++++
 include/class.client.php |  4 +++-
 include/class.staff.php  |  5 +++++
 include/class.user.php   |  2 ++
 4 files changed, 38 insertions(+), 1 deletion(-)

diff --git a/include/class.auth.php b/include/class.auth.php
index 1dc5a9ccf..cf1866a4c 100644
--- a/include/class.auth.php
+++ b/include/class.auth.php
@@ -1313,7 +1313,35 @@ abstract class PasswordPolicy {
     static function register($policy) {
         static::$registry[] = $policy;
     }
+
+    static function cleanSessions($model, $user=null) {
+        $criteria = array();
+
+        switch (true) {
+            case ($model instanceof Staff):
+                $criteria['user_id'] = $model->getId();
+
+                if ($user && ($model->getId() == $user->getId()))
+                    array_push($criteria,
+                        Q::not(array('session_id' => $user->session->session_id)));
+                break;
+            case ($model instanceof User):
+                $regexp = '_auth\|.*"user";[a-z]+:[0-9]+:{[a-z]+:[0-9]+:"id";[a-z]+:'.$model->getId();
+                $criteria['user_id'] = 0;
+                $criteria['session_data__regex'] = $regexp;
+
+                if ($user)
+                    array_push($criteria,
+                        Q::not(array('session_id' => $user->session->session_id)));
+                break;
+            default:
+                return false;
+        }
+
+        return SessionData::objects()->filter($criteria)->delete();
+    }
 }
+Signal::connect('auth.clean', array('PasswordPolicy', 'cleanSessions'));
 
 class osTicketPasswordPolicy
 extends PasswordPolicy {
diff --git a/include/class.client.php b/include/class.client.php
index 53b6376c9..bcf3c4257 100644
--- a/include/class.client.php
+++ b/include/class.client.php
@@ -396,7 +396,7 @@ class ClientAccount extends UserAccount {
         global $cfg;
 
         // FIXME: Updates by agents should go through UserAccount::update()
-        global $thisstaff;
+        global $thisstaff, $thisclient;
         if ($thisstaff)
             return parent::update($vars, $errors);
 
@@ -454,6 +454,8 @@ class ClientAccount extends UserAccount {
             Signal::send('auth.pwchange', $this->getUser(), $info);
             $this->cancelResetTokens();
             $this->clearStatus(UserAccountStatus::REQUIRE_PASSWD_RESET);
+            // Clean sessions
+            Signal::send('auth.clean', $this->getUser(), $thisclient);
         }
 
         return $this->save();
diff --git a/include/class.staff.php b/include/class.staff.php
index e5ed7e775..941f8baba 100644
--- a/include/class.staff.php
+++ b/include/class.staff.php
@@ -217,6 +217,8 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
     }
 
     function setPassword($new, $current=false) {
+        global $thisstaff;
+
         // Allow the backend to update the password. This is the preferred
         // method as it allows for integration with password policies and
         // also allows for remotely updating the password where possible and
@@ -241,6 +243,9 @@ implements AuthenticatedUser, EmailContact, TemplateVariable {
         $this->cancelResetTokens();
         $this->passwdreset = SqlFunction::NOW();
 
+        // Clean sessions
+        Signal::send('auth.clean', $this, $thisstaff);
+
         return $rv;
     }
 
diff --git a/include/class.user.php b/include/class.user.php
index f0d5f235d..e16259ce7 100644
--- a/include/class.user.php
+++ b/include/class.user.php
@@ -1033,6 +1033,8 @@ class UserAccount extends VerySimpleModel {
 
     function setPassword($new) {
         $this->set('passwd', Passwd::hash($new));
+        // Clean sessions
+        Signal::send('auth.clean', $this->getUser());
     }
 
     protected function sendUnlockEmail($template) {
-- 
GitLab