changeset 1339:8109d2d763d1

Fix for #94 password change should invalidate sessions
author "Miks Kalnins <MiksKalnins@MaikuMori.com>"
date Tue, 24 Apr 2012 20:07:06 +0300
parents 79ec6223a86c
children 860ef379fc9e
files MoinMoin/_tests/test_user.py MoinMoin/app.py MoinMoin/auth/__init__.py MoinMoin/constants/keys.py MoinMoin/user.py
diffstat 5 files changed, 54 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/_tests/test_user.py	Mon Apr 09 01:31:36 2012 +0200
+++ b/MoinMoin/_tests/test_user.py	Tue Apr 24 20:07:06 2012 +0300
@@ -341,6 +341,28 @@
         expected = [u'MoinTest:item_added']
         assert result == expected
 
+    # Sessions -------------------------------------------------------
+
+    def test_sessions(self):
+        name = u'Test_User_sessions'
+        password = name
+        self.createUser(name, password)
+        theUser = user.User(name=name, password=password)
+
+        # generate test token and validate it
+        test_token = theUser.generate_session_token()
+        result_success = theUser.validate_session(test_token)
+        assert result_success
+
+        # check if the token is saved
+        test_new_token = theUser.get_session_token()
+        assert test_token == test_new_token
+
+        # check if password change invalidates the token
+        theUser.set_password(password, False)
+        result_failure = theUser.validate_session(test_token)
+        assert not result_failure
+
     # Other ----------------------------------------------------------
 
     def test_recovery_token(self):
--- a/MoinMoin/app.py	Mon Apr 09 01:31:36 2012 +0200
+++ b/MoinMoin/app.py	Tue Apr 24 20:07:06 2012 +0300
@@ -218,6 +218,7 @@
         session['user.trusted'] = userobj.trusted
         session['user.auth_method'] = userobj.auth_method
         session['user.auth_attribs'] = userobj.auth_attribs
+        session['user.session_token'] = userobj.get_session_token()
     return userobj
 
 
--- a/MoinMoin/auth/__init__.py	Mon Apr 09 01:31:36 2012 +0200
+++ b/MoinMoin/auth/__init__.py	Tue Apr 24 20:07:06 2012 +0300
@@ -434,6 +434,7 @@
         trusted = session['user.trusted']
         auth_method = session['user.auth_method']
         auth_attribs = session['user.auth_attribs']
+        session_token = session['user.session_token']
         logging.debug("got from session: {0!r} {1!r} {2!r} {3!r}".format(itemid, trusted, auth_method, auth_attribs))
         logging.debug("current auth methods: {0!r}".format(app.cfg.auth_methods))
         if auth_method and auth_method in app.cfg.auth_methods:
@@ -441,6 +442,9 @@
                                 auth_method=auth_method,
                                 auth_attribs=auth_attribs,
                                 trusted=trusted)
+            if userobj.valid and not userobj.validate_session(session_token):
+                logging.debug("session token doesn't validate")
+                userobj = None
     logging.debug("session started for user {0!r}".format(userobj))
     return userobj
 
--- a/MoinMoin/constants/keys.py	Mon Apr 09 01:31:36 2012 +0200
+++ b/MoinMoin/constants/keys.py	Tue Apr 24 20:07:06 2012 +0300
@@ -70,6 +70,8 @@
 SUBSCRIBED_ITEMS = "subscribed_items"
 BOOKMARKS = "bookmarks"
 QUICKLINKS = "quicklinks"
+SESSION_KEY = "session_key"
+SESSION_TOKEN = "session_token"
 RECOVERPASS_KEY = "recoverpass_key"
 EDIT_ON_DOUBLECLICK = "edit_on_doubleclick"
 SHOW_COMMENTS = "show_comments"
--- a/MoinMoin/user.py	Mon Apr 09 01:31:36 2012 +0200
+++ b/MoinMoin/user.py	Tue Apr 24 20:07:06 2012 +0300
@@ -430,6 +430,8 @@
         if not is_encrypted:
             password = crypt_password(password)
         self.profile[ENC_PASSWORD] = password
+        # Invalidate all other browser sessions except this one.
+        session['user.session_token'] = self.generate_session_token(False)
 
     def save(self, force=False):
         """
@@ -648,6 +650,29 @@
         """ Check if this user object is the user doing the current request """
         return flaskg.user.name == self.name
 
+    # Sessions ---------------------------------------------------
+
+    def generate_session_token(self, save=True):
+        """ Generate new session token and key pair. Used to validate sessions. """
+        key, token = generate_token()
+        self.profile[SESSION_TOKEN] = token
+        self.profile[SESSION_KEY] = key
+        if save:
+            self.save()
+
+        return token
+
+    def get_session_token(self):
+        """ Get current session token. If there is no token, generate a new one. """
+        try:
+            return self.profile[SESSION_TOKEN]
+        except KeyError:
+            return self.generate_session_token()
+
+    def validate_session(self, token):
+        """ Check if the session token is valid. """
+        return valid_token(self.profile[SESSION_KEY], token)
+
     # Account verification / Password recovery -------------------------------
 
     def generate_recovery_token(self):