Invalidate all session tokens when a user signs out
Fixes https://jira.coreos.com/browse/QS-85
This commit is contained in:
parent
d405f6f158
commit
1d1c6f0606
4 changed files with 16 additions and 4 deletions
|
@ -104,8 +104,7 @@ def change_password(user, new_password):
|
||||||
pw_hash = hash_password(new_password)
|
pw_hash = hash_password(new_password)
|
||||||
user.invalid_login_attempts = 0
|
user.invalid_login_attempts = 0
|
||||||
user.password_hash = pw_hash
|
user.password_hash = pw_hash
|
||||||
user.uuid = str(uuid4())
|
invalidate_all_sessions(user)
|
||||||
user.save()
|
|
||||||
|
|
||||||
# Remove any password required notifications for the user.
|
# Remove any password required notifications for the user.
|
||||||
notification.delete_notifications_by_kind(user, 'password_required')
|
notification.delete_notifications_by_kind(user, 'password_required')
|
||||||
|
@ -593,6 +592,13 @@ def get_user_or_org_by_customer_id(customer_id):
|
||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def invalidate_all_sessions(user):
|
||||||
|
""" Invalidates all existing user sessions by rotating the user's UUID. """
|
||||||
|
if not user:
|
||||||
|
return
|
||||||
|
|
||||||
|
user.uuid = str(uuid4())
|
||||||
|
user.save()
|
||||||
|
|
||||||
def get_matching_user_namespaces(namespace_prefix, username, limit=10):
|
def get_matching_user_namespaces(namespace_prefix, username, limit=10):
|
||||||
namespace_search = prefix_search(Namespace.username, namespace_prefix)
|
namespace_search = prefix_search(Namespace.username, namespace_prefix)
|
||||||
|
|
|
@ -725,6 +725,7 @@ class Signout(ApiResource):
|
||||||
@nickname('logout')
|
@nickname('logout')
|
||||||
def post(self):
|
def post(self):
|
||||||
""" Request that the current user be signed out. """
|
""" Request that the current user be signed out. """
|
||||||
|
model.user.invalidate_all_sessions(get_authenticated_user())
|
||||||
logout_user()
|
logout_user()
|
||||||
identity_changed.send(app, identity=AnonymousIdentity())
|
identity_changed.send(app, identity=AnonymousIdentity())
|
||||||
return {'success': True}
|
return {'success': True}
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
<span class="avatar" size="32" data="user.avatar"></span>
|
<span class="avatar" size="32" data="user.avatar"></span>
|
||||||
{{ user.username }}
|
{{ user.username }}
|
||||||
</a>
|
</a>
|
||||||
<a ng-click="signout()">Sign out</a>
|
<a ng-click="signout()">Sign out all sessions</a>
|
||||||
</li>
|
</li>
|
||||||
<li ng-switch-default>
|
<li ng-switch-default>
|
||||||
<a class="user-view" href="/signin/" ng-if="!externalSigninUrl">Sign in</a>
|
<a class="user-view" href="/signin/" ng-if="!externalSigninUrl">Sign in</a>
|
||||||
|
@ -129,7 +129,7 @@
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li ng-if="user.super_user"><a href="/superuser/"><strong>Super User Admin Panel</strong></a></li>
|
<li ng-if="user.super_user"><a href="/superuser/"><strong>Super User Admin Panel</strong></a></li>
|
||||||
<li><a ng-click="signout()">Sign out</a></li>
|
<li><a ng-click="signout()">Sign out all sessions</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li ng-switch-default>
|
<li ng-switch-default>
|
||||||
|
|
|
@ -895,6 +895,7 @@ class TestSignout(ApiTestCase):
|
||||||
def test_signout(self):
|
def test_signout(self):
|
||||||
self.login(READ_ACCESS_USER)
|
self.login(READ_ACCESS_USER)
|
||||||
|
|
||||||
|
read_user = model.user.get_user(READ_ACCESS_USER)
|
||||||
json = self.getJsonResponse(User)
|
json = self.getJsonResponse(User)
|
||||||
assert json['username'] == READ_ACCESS_USER
|
assert json['username'] == READ_ACCESS_USER
|
||||||
|
|
||||||
|
@ -903,6 +904,10 @@ class TestSignout(ApiTestCase):
|
||||||
# Make sure we're now signed out.
|
# Make sure we're now signed out.
|
||||||
self.getJsonResponse(User, expected_code=401)
|
self.getJsonResponse(User, expected_code=401)
|
||||||
|
|
||||||
|
# Make sure the user's UUID has rotated, to ensure sessions are no longer valid.
|
||||||
|
read_user_again = model.user.get_user(READ_ACCESS_USER)
|
||||||
|
self.assertNotEquals(read_user.uuid, read_user_again.uuid)
|
||||||
|
|
||||||
|
|
||||||
class TestConductSearch(ApiTestCase):
|
class TestConductSearch(ApiTestCase):
|
||||||
def test_noaccess(self):
|
def test_noaccess(self):
|
||||||
|
|
Reference in a new issue