You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by je...@apache.org on 2015/02/27 13:50:36 UTC

[03/26] allura git commit: [#7786] Invalidate pwd reset tokens after email/password change

[#7786] Invalidate pwd reset tokens after email/password change


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/b9225b8e
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/b9225b8e
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/b9225b8e

Branch: refs/heads/ib/7830
Commit: b9225b8e03c621ba07b63355d07f4b2c9cc71c14
Parents: 2d9bcf0
Author: Heith Seewald <hs...@slashdotmedia.com>
Authored: Wed Feb 4 08:16:17 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Feb 19 16:21:54 2015 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/auth.py           |  10 +-
 Allura/allura/tests/functional/test_auth.py | 117 +++++++++++++++++++++++
 2 files changed, 126 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/b9225b8e/Allura/allura/controllers/auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/auth.py b/Allura/allura/controllers/auth.py
index 2183032..0bfe966 100644
--- a/Allura/allura/controllers/auth.py
+++ b/Allura/allura/controllers/auth.py
@@ -165,7 +165,7 @@ class AuthController(BaseController):
             raise wexc.HTTPNotFound()
         user = self._validate_hash(hash)
         user.set_password(pw)
-        user.set_tool_data('AuthPasswordReset', hash='', hash_expiry='')
+        user.set_tool_data('AuthPasswordReset', hash='', hash_expiry='')  # Clear password reset token
         h.auditlog_user('Password changed (through recovery process)', user=user)
         flash('Password changed')
         redirect('/auth/?return_to=/')  # otherwise the default return_to would be the forgotten_password referrer page
@@ -409,6 +409,8 @@ class AuthController(BaseController):
             expired_user = M.User.query.get(username=expired_username) if expired_username else None
             ap.set_password(expired_user or c.user, kw['oldpw'], kw['pw'])
             expired_user.set_tool_data('allura', pwd_reset_preserve_session=session.id)
+            expired_user.set_tool_data('AuthPasswordReset', hash='', hash_expiry='')  # Clear password reset token
+
         except wexc.HTTPUnauthorized:
             flash('Incorrect password', 'error')
             redirect(tg.url('/auth/pwd_expired', dict(return_to=return_to)))
@@ -471,11 +473,13 @@ class PreferencesController(BaseController):
                         # clear it now, a new one will get set below
                         user.set_pref('email_address', None)
                         primary_addr = None
+                        user.set_tool_data('AuthPasswordReset', hash='', hash_expiry='')
                 h.auditlog_user('Email address deleted: %s', user.email_addresses[i], user=user)
                 del user.email_addresses[i]
                 if obj:
                     obj.delete()
         if new_addr.get('claim') or new_addr.get('addr'):
+            user.set_tool_data('AuthPasswordReset', hash='', hash_expiry='')  # Clear password reset token
             claimed_emails_limit = config.get('user_prefs.maximum_claimed_emails', None)
             if claimed_emails_limit and len(user.email_addresses) >= int(claimed_emails_limit):
                 flash('You cannot claim more than %s email addresses.' % claimed_emails_limit, 'error')
@@ -505,6 +509,7 @@ class PreferencesController(BaseController):
                         em.send_claim_attempt()
 
                     if not admin:
+                        user.set_tool_data('AuthPasswordReset', hash='', hash_expiry='')
                         flash('A verification email has been sent.  Please check your email and click to confirm.')
 
                     h.auditlog_user('New email address: %s', new_addr['addr'], user=user)
@@ -525,6 +530,7 @@ class PreferencesController(BaseController):
                     primary_addr,
                     user=user)
             user.set_pref('email_address', primary_addr)
+            user.set_tool_data('AuthPasswordReset', hash='', hash_expiry='')
 
     @h.vardec
     @expose()
@@ -561,6 +567,8 @@ class PreferencesController(BaseController):
         try:
             ap.set_password(c.user, kw['oldpw'], kw['pw'])
             c.user.set_tool_data('allura', pwd_reset_preserve_session=session.id)
+            c.user.set_tool_data('AuthPasswordReset', hash='', hash_expiry='')
+
         except wexc.HTTPUnauthorized:
             flash('Incorrect password', 'error')
             redirect('.')

http://git-wip-us.apache.org/repos/asf/allura/blob/b9225b8e/Allura/allura/tests/functional/test_auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_auth.py b/Allura/allura/tests/functional/test_auth.py
index eacabd0..f288b0b 100644
--- a/Allura/allura/tests/functional/test_auth.py
+++ b/Allura/allura/tests/functional/test_auth.py
@@ -324,6 +324,90 @@ class TestAuth(TestController):
         email = M.EmailAddress.find(dict(email=email_address, claimed_by_user_id=user._id)).first()
         assert not email.confirmed
 
+    @staticmethod
+    def _create_password_reset_hash():
+        """ Generates a password reset token for a given user.
+
+        :return: User object
+        :rtype: User
+        """
+        # test-user claimed email address
+        user = M.User.by_username('test-admin')
+        user.set_tool_data('AuthPasswordReset',
+                           hash="generated_hash_value",
+                           hash_expiry="04-08-2020")
+        hash = user.get_tool_data('AuthPasswordReset', 'hash')
+        session(user).flush(user)
+
+        hash_expiry = user.get_tool_data('AuthPasswordReset', 'hash_expiry')
+        assert_equal(hash, 'generated_hash_value')
+        assert_equal(hash_expiry, '04-08-2020')
+        return user
+
+    def test_token_generator(self):
+        """ Generates new token invalidation tests.
+
+        The tests cover: changing, claiming, updating, removing email addresses.
+        :returns: email_change_invalidates_token
+        """
+        _params = [{'new_addr.addr': 'test_abcd@domain.net',  # Change primary address
+                    'primary_addr': 'test@example.com', },
+                   {'new_addr.addr': 'test@example.com',  # Claim new address
+                    'new_addr.claim': 'Claim Address',
+                    'primary_addr': 'test-admin@users.localhost',
+                    'password': 'foo',
+                    'preferences.email_format': 'plain'},
+                   {'addr-1.ord': '1',  # remove test-admin@users.localhost
+                    'addr-1.delete': 'on',
+                    'addr-2.ord': '2',
+                    'new_addr.addr': '',
+                    'primary_addr': 'test-admin@users.localhost',
+                    'password': 'foo',
+                    'preferences.email_format': 'plain'},
+                   {'addr-1.ord': '1',  # Remove email
+                    'addr-2.ord': '2',
+                    'addr-2.delete': 'on',
+                    'new_addr.addr': '',
+                    'primary_addr': 'test-admin@users.localhost'}]
+
+        for param in _params:
+            yield self.email_change_invalidates_token, param
+
+    def email_change_invalidates_token(self, change_params):
+        user = self._create_password_reset_hash()
+        session(user).flush(user)
+
+        self.app.post('/auth/preferences/update_emails',
+                      extra_environ=dict(username='test-admin'),
+                      params=change_params)
+
+        u = M.User.by_username('test-admin')
+        print(u.get_tool_data('AuthPasswordReset', 'hash'))
+        assert_equal(u.get_tool_data('AuthPasswordReset', 'hash'), '')
+        assert_equal(u.get_tool_data('AuthPasswordReset', 'hash_expiry'), '')
+
+    @td.with_user_project('test-admin')
+    def test_change_password(self):
+        # Get and assert user with password reset token.
+        user = self._create_password_reset_hash()
+        old_pass = user.get_pref('password')
+
+        # Change password
+        self.app.post('/auth/preferences/change_password',
+                      extra_environ=dict(username='test-admin'),
+                      params={
+                          'oldpw': 'foo',
+                          'pw': 'asdfasdf',
+                          'pw2': 'asdfasdf',
+                      })
+
+        # Confirm password was changed.
+        assert_not_equal(old_pass, user.get_pref('password'))
+
+        # Confirm any existing tokens were reset.
+        assert_equal(user.get_tool_data('AuthPasswordReset', 'hash'), '')
+        assert_equal(user.get_tool_data('AuthPasswordReset', 'hash_expiry'), '')
+
     @td.with_user_project('test-admin')
     def test_prefs(self):
         r = self.app.get('/auth/preferences/',
@@ -1619,6 +1703,39 @@ class TestPasswordExpire(TestController):
             r = self.login(pwd='foo')
             assert_in('Invalid login', r)
 
+    def test_expired_pwd_change_invalidates_token(self):
+        self.set_expire_for_user()
+        with h.push_config(config, **{'auth.pwdexpire.days': 90}):
+            r = self.login()
+            assert_true(self.expired(r))
+            self.assert_redirects()
+            user = M.User.by_username('test-user')
+            user.set_tool_data('AuthPasswordReset',
+                          hash="generated_hash_value",
+                          hash_expiry="04-08-2020")
+            hash = user.get_tool_data('AuthPasswordReset', 'hash')
+            hash_expiry = user.get_tool_data('AuthPasswordReset', 'hash_expiry')
+            assert_equal(hash, 'generated_hash_value')
+            assert_equal(hash_expiry, '04-08-2020')
+            session(user).flush(user)
+
+            # Change expired password
+            r = self.app.get('/auth/pwd_expired', extra_environ={'username': 'test-user'})
+            f = r.forms[0]
+            f['oldpw'] = 'foo'
+            f['pw'] = 'qwerty'
+            f['pw2'] = 'qwerty'
+            r = f.submit(extra_environ={'username': 'test-user'}, status=302)
+            assert_equal(r.location, 'http://localhost/')
+
+            user = M.User.by_username('test-user')
+            hash = user.get_tool_data('AuthPasswordReset', 'hash')
+            hash_expiry = user.get_tool_data('AuthPasswordReset', 'hash_expiry')
+
+            assert_equal(hash, '')
+            assert_equal(hash_expiry, '')
+
+
     def check_validation(self, oldpw, pw, pw2):
         user = M.User.by_username('test-user')
         old_update_time = user.last_password_updated