diff --git a/securedrop/source_app/__init__.py b/securedrop/source_app/__init__.py index 94bed90ef4..d5ea2333f1 100644 --- a/securedrop/source_app/__init__.py +++ b/securedrop/source_app/__init__.py @@ -23,7 +23,7 @@ from sdconfig import SDConfig from source_app import main, info, api from source_app.decorators import ignore_static -from source_app.utils import logged_in +from source_app.utils import logged_in, was_in_generate_flow from store import Storage @@ -109,7 +109,7 @@ def check_tor2web() -> None: 'provide anonymity. ' 'Why is this dangerous?') .format(url=url_for('info.tor2web_warning'))), - "banner-warning") + "banner-warning") @app.before_request @ignore_static @@ -124,11 +124,24 @@ def setup_g() -> Optional[werkzeug.Response]: if 'expires' in session and datetime.utcnow() >= session['expires']: msg = render_template('session_timeout.html') + # Show expiration message only if the user was + # either in the codename generation flow or logged in + show_expiration_message = any([ + session.get('show_expiration_message'), + logged_in(), + was_in_generate_flow(), + ]) + # clear the session after we render the message so it's localized session.clear() + # Persist this properety across sessions to distinguish users whose sessions expired + # from users who never logged in or generated a codename + session['show_expiration_message'] = show_expiration_message + # Redirect to index with flashed message - flash(Markup(msg), "important") + if session['show_expiration_message']: + flash(Markup(msg), "important") return redirect(url_for('main.index')) session['expires'] = datetime.utcnow() + \ diff --git a/securedrop/source_app/main.py b/securedrop/source_app/main.py index b7092e99ed..d86d4940e8 100644 --- a/securedrop/source_app/main.py +++ b/securedrop/source_app/main.py @@ -257,7 +257,7 @@ def submit() -> werkzeug.Response: current_app.logger.info("generating key, entropy: {}".format( entropy_avail)) else: - current_app.logger.warn( + current_app.logger.warning( "skipping key generation. entropy: {}".format( entropy_avail)) diff --git a/securedrop/source_app/utils.py b/securedrop/source_app/utils.py index 6c72a6de9a..b2fa4c1405 100644 --- a/securedrop/source_app/utils.py +++ b/securedrop/source_app/utils.py @@ -21,6 +21,10 @@ from typing import Optional # noqa: F401 +def was_in_generate_flow() -> bool: + return 'codenames' in session + + def logged_in() -> bool: return 'logged_in' in session diff --git a/securedrop/tests/test_source.py b/securedrop/tests/test_source.py index 090924775d..a434ecc7a0 100644 --- a/securedrop/tests/test_source.py +++ b/securedrop/tests/test_source.py @@ -727,6 +727,7 @@ def test_source_session_expiration(config, source_app): # which is always present and 'csrf_token' which leaks no info) session.pop('expires', None) session.pop('csrf_token', None) + session.pop('show_expiration_message', None) assert not session text = resp.data.decode('utf-8') @@ -752,12 +753,31 @@ def test_source_session_expiration_create(config, source_app): # which is always present and 'csrf_token' which leaks no info) session.pop('expires', None) session.pop('csrf_token', None) + session.pop('show_expiration_message', None) assert not session text = resp.data.decode('utf-8') assert 'You were logged out due to inactivity' in text +def test_source_no_session_expiration_message_when_not_logged_in(config, source_app): + """If sources never logged in, no message should be displayed + after SESSION_EXPIRATION_MINUTES.""" + + with source_app.test_client() as app: + seconds_session_expire = 1 + config.SESSION_EXPIRATION_MINUTES = seconds_session_expire / 60. + + resp = app.get(url_for('main.index')) + assert resp.status_code == 200 + + time.sleep(seconds_session_expire + 1) + + refreshed_resp = app.get(url_for('main.index'), follow_redirects=True) + text = refreshed_resp.data.decode('utf-8') + assert 'You were logged out due to inactivity' not in text + + def test_csrf_error_page(config, source_app): source_app.config['WTF_CSRF_ENABLED'] = True with source_app.test_client() as app: