From 422eaf9605505276391db4d8e3da268d7b57aa2b Mon Sep 17 00:00:00 2001 From: "J. King" Date: Thu, 25 Jul 2019 22:34:58 -0400 Subject: [PATCH] Invalidate sessions on password change; closes #170 --- CHANGELOG | 3 ++- lib/Database.php | 14 ++++++++++---- lib/User.php | 4 ++++ tests/cases/Database/SeriesSession.php | 10 ++++++++++ 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 961082f9..caf1e616 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,13 +5,14 @@ New features: - Support for the Fever protocol (see README.md for details) - Command line functionality for clearing a password, disabling the account - Command line options for dealing with Fever passwords -- Command line functionality for exporting subscriptions to OPML +- Command line functionality for importing and exporting OPML - Command line functionality for cron-based feed updating - Command line documentation of all commands and options Bug fixes: - Treat command line option -h the same as --help - Sort Tiny Tiny RSS special feeds according to special ordering +- Invalidate sessions when passwords are changed Version 0.7.1 (2019-03-25) ========================== diff --git a/lib/Database.php b/lib/Database.php index daca2344..10e66eb6 100644 --- a/lib/Database.php +++ b/lib/Database.php @@ -340,15 +340,21 @@ class Database { * This function can be used to explicitly invalidate a session after a user logs out * * @param string $user The user who owns the session to be destroyed - * @param string $id The identifier of the session to destroy + * @param string|null $id The identifier of the session to destroy */ - public function sessionDestroy(string $user, string $id): bool { + public function sessionDestroy(string $user, string $id = null): bool { // If the user isn't authorized to perform this action then throw an exception. if (!Arsse::$user->authorize($user, __FUNCTION__)) { throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); } - // delete the session and report success. - return (bool) $this->db->prepare("DELETE FROM arsse_sessions where id = ? and \"user\" = ?", "str", "str")->run($id, $user)->changes(); + if (is_null($id)) { + // delete all sessions and report success unconditionally if no identifier was specified + $this->db->prepare("DELETE FROM arsse_sessions where \"user\" = ?", "str")->run($user); + return true; + } else { + // otherwise delete only the specified session and report success. + return (bool) $this->db->prepare("DELETE FROM arsse_sessions where id = ? and \"user\" = ?", "str", "str")->run($id, $user)->changes(); + } } /** Resumes a session, returning available session data diff --git a/lib/User.php b/lib/User.php index 4f529803..691d6faf 100644 --- a/lib/User.php +++ b/lib/User.php @@ -110,6 +110,8 @@ class User { if (Arsse::$db->userExists($user)) { // if the password change was successful and the user exists, set the internal password to the same value Arsse::$db->userPasswordSet($user, $out); + // also invalidate any current sessions for the user + Arsse::$db->sessionDestroy($user); } return $out; } @@ -123,6 +125,8 @@ class User { if (Arsse::$db->userExists($user)) { // if the password change was successful and the user exists, set the internal password to the same value Arsse::$db->userPasswordSet($user, null); + // also invalidate any current sessions for the user + Arsse::$db->sessionDestroy($user); } return $out; } diff --git a/tests/cases/Database/SeriesSession.php b/tests/cases/Database/SeriesSession.php index ad9a45b2..9e8a3884 100644 --- a/tests/cases/Database/SeriesSession.php +++ b/tests/cases/Database/SeriesSession.php @@ -116,6 +116,16 @@ trait SeriesSession { $this->assertFalse(Arsse::$db->sessionDestroy($user, $id)); } + public function testDestroyAllSessions() { + $user = "jane.doe@example.com"; + $this->assertTrue(Arsse::$db->sessionDestroy($user)); + $state = $this->primeExpectations($this->data, ['arsse_sessions' => ["id", "created", "expires", "user"]]); + unset($state['arsse_sessions']['rows'][0]); + unset($state['arsse_sessions']['rows'][1]); + unset($state['arsse_sessions']['rows'][2]); + $this->compareExpectations(static::$drv, $state); + } + public function testDestroyASessionForTheWrongUser() { $user = "john.doe@example.com"; $id = "80fa94c1a11f11e78667001e673b2560";