Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix session expiry message #5582

Merged
merged 8 commits into from
Oct 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions securedrop/source_app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -109,7 +109,7 @@ def check_tor2web() -> None:
'provide anonymity. '
'<a href="{url}">Why is this dangerous?</a>')
.format(url=url_for('info.tor2web_warning'))),
"banner-warning")
"banner-warning")

@app.before_request
@ignore_static
Expand All @@ -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() + \
Expand Down
2 changes: 1 addition & 1 deletion securedrop/source_app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
rmol marked this conversation as resolved.
Show resolved Hide resolved
"skipping key generation. entropy: {}".format(
entropy_avail))

Expand Down
4 changes: 4 additions & 0 deletions securedrop/source_app/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
20 changes: 20 additions & 0 deletions securedrop/tests/test_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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:
Expand Down