Handle sessions on password change correctly
remove_user_sessions() got the user id as except_id, so the exception never matched. On an own password change the result was correct only by accident. When an admin changed another user's password, the admin session got a new id, but the old entry stayed valid in the store. - New Session.regenerate(): keeps the data under a new id and removes the old store entry. - change_password() handles the sessions: on an own password change it regenerates the current session and ends all other sessions of the user (except_id is now the session id). When an admin changes another user's password, the admin session stays untouched and all sessions of that user end. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9aed6b5227
commit
7ac4e6c3a7
3 files changed
+15
-4
No files matched your search
+8
-1
@@ -6,6 +6,7 @@ from flask import session
|
||||
|
||||
from max.crypto import constant_time_compare, force_bytes
|
||||
from max.db import get_user_by_email, update_user
|
||||
from max.sessions import session_storage
|
||||
from max.translations import t
|
||||
|
||||
SSHA512_PREFIX = "{SSHA512}"
|
||||
@@ -35,7 +36,13 @@ def logout():
|
||||
def change_password(user, password):
|
||||
hash = hash_password(password)
|
||||
update_user(user.id, passwordhash=hash)
|
||||
session.set_new_id()
|
||||
if session.get("user_id") == user.id:
|
||||
# Own password: keep this session (with a new id), end all others
|
||||
session.regenerate()
|
||||
session_storage.remove_user_sessions(user.id, except_id=session.id)
|
||||
else:
|
||||
# An admin changes the password of another user: end all of their sessions
|
||||
session_storage.remove_user_sessions(user.id)
|
||||
|
||||
|
||||
def hash_password(password: str, salt: bytes = None):
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
from flask import flash, session
|
||||
from flask import flash
|
||||
|
||||
from max.auth import change_password
|
||||
from max.permissions import AllowAdminOrSelf
|
||||
from max.sessions import session_storage
|
||||
from max.translations import t
|
||||
|
||||
from ..base_user_views import BackToUsersMixin, FetchUserTemplateGetView
|
||||
@@ -28,6 +27,5 @@ class UserChangePassword(AllowAdminOrSelf, BackToUsersMixin, FetchUserTemplateGe
|
||||
return
|
||||
|
||||
change_password(self.user, pw1)
|
||||
session_storage.remove_user_sessions(self.user.id, except_id=session["user_id"])
|
||||
flash(t("Password changed successfully!"), category="success")
|
||||
return self.redirect()
|
||||
@@ -91,6 +91,12 @@ class Session(CallbackDict, SessionMixin):
|
||||
def set_new_id(self):
|
||||
self.id = get_random_string(32)
|
||||
|
||||
def regenerate(self):
|
||||
"""Keeps the data under a new id. The old id becomes invalid."""
|
||||
del session_storage[self.id]
|
||||
self.set_new_id()
|
||||
self.modified = True
|
||||
|
||||
|
||||
class SessionInterface(FlaskSessionInterface):
|
||||
def _get_signer(self, app):
|
||||
|
||||
Reference in new issue
Block a user