From 3d2cb5ab2de498b5fa128faf0d52917401021077 Mon Sep 17 00:00:00 2001 From: Patrick Uiterwijk Date: Apr 30 2018 13:57:42 +0000 Subject: [PATCH 1/2] Mock get_user out Signed-off-by: Patrick Uiterwijk --- diff --git a/tests/__init__.py b/tests/__init__.py index 4eec5b0..f881738 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -158,7 +158,7 @@ def create_maybe_waiter(method, getter): @contextmanager -def user_set(APP, user): +def user_set(APP, user, keep_get_user=False): """ Set the provided user as fas_user in the provided application.""" # Hack used to remove the before_request function set by @@ -176,10 +176,16 @@ def user_set(APP, user): g.fas_user = user g.fas_session_id = b'123' g.authenticated = True + old_get_user = pagure.flask_app._get_user + if not keep_get_user: + pagure.flask_app._get_user = mock.MagicMock( + return_value=pagure.lib.model.User()) with appcontext_pushed.connected_to(handler, APP): yield + pagure.flask_app._get_user = old_get_user + tests_state = { "path": tempfile.mkdtemp(prefix='pagure-tests-'), From ce6d6d48a01236fdb44be1526a6f37744a2806a2 Mon Sep 17 00:00:00 2001 From: Patrick Uiterwijk Date: Apr 30 2018 14:01:57 +0000 Subject: [PATCH 2/2] Allow forcing logout of all sessions before now Signed-off-by: Patrick Uiterwijk --- diff --git a/pagure/flask_app.py b/pagure/flask_app.py index 75d558a..ddb1e98 100644 --- a/pagure/flask_app.py +++ b/pagure/flask_app.py @@ -235,6 +235,18 @@ def set_request(): if pagure.utils.authenticated(): flask.g.forkbuttonform = pagure.forms.ConfirmationForm() + # Force logout if current session started before users' + # refuse_sessions_before + login_time = flask.g.fas_user.login_time + # This is because flask_fas_openid will store this as a posix timestamp + if not isinstance(login_time, datetime.datetime): + login_time = datetime.datetime.utcfromtimestamp(login_time) + user = _get_user(username=flask.g.fas_user.username) + if (user.refuse_sessions_before + and login_time < user.refuse_sessions_before): + logout() + return flask.redirect(flask.url_for('ui_ns.index')) + flask.g.justlogedout = flask.session.get('_justloggedout', False) if flask.g.justlogedout: flask.session['_justloggedout'] = None diff --git a/pagure/lib/model.py b/pagure/lib/model.py index 8da2fc8..f7a8ac4 100644 --- a/pagure/lib/model.py +++ b/pagure/lib/model.py @@ -211,6 +211,11 @@ class User(BASE): default=sa.func.now(), onupdate=sa.func.now()) + refuse_sessions_before = sa.Column( + sa.DateTime, + nullable=True, + default=None) + # Relations group_objs = relation( "PagureGroup", diff --git a/pagure/templates/user_settings.html b/pagure/templates/user_settings.html index bde2cf9..b1e4640 100644 --- a/pagure/templates/user_settings.html +++ b/pagure/templates/user_settings.html @@ -251,6 +251,29 @@ + +
+
+
+ Force logout +
+
+

+ Click the "Force logout everywhere" to forcefully log out from every + current open session. +

+
+ +
+
+ {% endblock %} diff --git a/pagure/ui/app.py b/pagure/ui/app.py index 786b6e7..8cfc548 100644 --- a/pagure/ui/app.py +++ b/pagure/ui/app.py @@ -960,3 +960,24 @@ def revoke_api_user_token(token_id): 'error') return flask.redirect(flask.url_for('ui_ns.user_settings')) + + +@UI_NS.route('/settings/forcelogout/', methods=('POST', )) +@UI_NS.route('/settings/forcelogout', methods=('POST', )) +@login_required +def force_logout(): + """ Set refuse_sessions_before, logging the user out everywhere + """ + if admin_session_timedout(): + if flask.request.method == 'POST': + flask.flash('Action canceled, try it again', 'error') + return flask.redirect( + flask.url_for('auth_login', next=flask.request.url)) + + # Ensure the user is in the DB at least + user = _get_user(username=flask.g.fas_user.username) + + user.refuse_sessions_before = datetime.datetime.utcnow() + flask.g.session.commit() + flask.flash('All active sessions logged out') + return flask.redirect(flask.url_for('ui_ns.user_settings')) diff --git a/tests/test_pagure_flask_ui_login.py b/tests/test_pagure_flask_ui_login.py index 50bfe55..577e7e6 100644 --- a/tests/test_pagure_flask_ui_login.py +++ b/tests/test_pagure_flask_ui_login.py @@ -785,5 +785,34 @@ class PagureFlaskLogintests(tests.SimplePagureTest): self.assertFalse(pagure.flask_app.admin_session_timedout()) + @patch.dict('pagure.config.config', {'PAGURE_AUTH': 'local'}) + def test_force_logout(self): + """ Test forcing logout. """ + user = tests.FakeUser(username='foo') + with tests.user_set(self.app.application, user, keep_get_user=True): + # Test that accessing settings works + output = self.app.get('/settings') + self.assertEqual(output.status_code, 200) + + # Now logout everywhere + output = self.app.post('/settings/forcelogout/') + self.assertEqual(output.status_code, 302) + self.assertEqual(output.headers['Location'], + 'http://localhost/settings') + + # We should now get redirected to index, because our session became + # invalid + output = self.app.get('/settings') + self.assertEqual(output.status_code, 302) + self.assertEqual(output.headers['Location'], + 'http://localhost/') + + # After changing the login_time to now, the session should again be + # valid + user.login_time = datetime.datetime.utcnow() + output = self.app.get('/') + self.assertEqual(output.status_code, 200) + + if __name__ == '__main__': unittest.main(verbosity=2)