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.crypto import constant_time_compare, force_bytes
|
||||||
from max.db import get_user_by_email, update_user
|
from max.db import get_user_by_email, update_user
|
||||||
|
from max.sessions import session_storage
|
||||||
from max.translations import t
|
from max.translations import t
|
||||||
|
|
||||||
SSHA512_PREFIX = "{SSHA512}"
|
SSHA512_PREFIX = "{SSHA512}"
|
||||||
@@ -35,7 +36,13 @@ def logout():
|
|||||||
def change_password(user, password):
|
def change_password(user, password):
|
||||||
hash = hash_password(password)
|
hash = hash_password(password)
|
||||||
update_user(user.id, passwordhash=hash)
|
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):
|
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.auth import change_password
|
||||||
from max.permissions import AllowAdminOrSelf
|
from max.permissions import AllowAdminOrSelf
|
||||||
from max.sessions import session_storage
|
|
||||||
from max.translations import t
|
from max.translations import t
|
||||||
|
|
||||||
from ..base_user_views import BackToUsersMixin, FetchUserTemplateGetView
|
from ..base_user_views import BackToUsersMixin, FetchUserTemplateGetView
|
||||||
@@ -28,6 +27,5 @@ class UserChangePassword(AllowAdminOrSelf, BackToUsersMixin, FetchUserTemplateGe
|
|||||||
return
|
return
|
||||||
|
|
||||||
change_password(self.user, pw1)
|
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")
|
flash(t("Password changed successfully!"), category="success")
|
||||||
return self.redirect()
|
return self.redirect()
|
||||||
@@ -91,6 +91,12 @@ class Session(CallbackDict, SessionMixin):
|
|||||||
def set_new_id(self):
|
def set_new_id(self):
|
||||||
self.id = get_random_string(32)
|
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):
|
class SessionInterface(FlaskSessionInterface):
|
||||||
def _get_signer(self, app):
|
def _get_signer(self, app):
|
||||||
|
|||||||
Reference in new issue
Block a user