Skip to content

Commit

Permalink
add server os eol warning
Browse files Browse the repository at this point in the history
Signed-off-by: Allie Crevier <[email protected]>
  • Loading branch information
Allie Crevier committed Feb 13, 2021
1 parent 2137669 commit 2f99b13
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@
/var/www/securedrop/journalist_app/decorators.py r,
/var/www/securedrop/journalist_app/forms.py r,
/var/www/securedrop/journalist_app/main.py r,
/var/www/securedrop/journalist_app/os_eol.py r,
/var/www/securedrop/journalist_app/utils.py r,
/var/www/securedrop/journalist_templates/_confirmation_modal.html r,
/var/www/securedrop/journalist_templates/_source_row.html r,
Expand Down
12 changes: 11 additions & 1 deletion securedrop/journalist_app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

from crypto_util import CryptoUtil
from db import db
from journalist_app import account, admin, api, main, col
from journalist_app import account, admin, api, main, col, os_eol
from journalist_app.utils import (get_source, logged_in,
JournalistInterfaceSessionInterface,
cleanup_expired_revoked_tokens)
Expand Down Expand Up @@ -56,6 +56,11 @@ def create_app(config: 'SDConfig') -> Flask:
def _url_exists(u: str) -> bool:
return path.exists(path.join(config.SECUREDROP_DATA_ROOT, u))

app.config.update(
OS_PAST_EOL=os_eol.is_server_os_past_eol(),
OS_UPCOMING_EOL=os_eol.is_server_os_close_to_eol()
)

# TODO: Attaching a Storage dynamically like this disables all type checking (and
# breaks code analysis tools) for code that uses current_app.storage; it should be refactored
app.storage = Storage(config.STORE_DIR,
Expand Down Expand Up @@ -157,6 +162,11 @@ def setup_g() -> 'Optional[Response]':
else:
g.organization_name = gettext('SecureDrop')

if app.config['OS_PAST_EOL']:
g.show_os_past_eol_warning = True
elif app.config['OS_UPCOMING_EOL']:
g.show_os_upcoming_eol_warning = True

if request.path.split('/')[1] == 'api':
pass # We use the @token_required decorator for the API endpoints
else: # We are not using the API
Expand Down
25 changes: 25 additions & 0 deletions securedrop/journalist_app/os_eol.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from datetime import date

FOCAL_VER = "20.04"
XENIAL_EOL_DATE = date(2021, 4, 30)

with open("/etc/lsb-release", "r") as f:
server_os = f.readlines()[1].split("=")[1].strip("\n")


def is_server_os_past_eol() -> bool:
"""
Assumption: Any OS that is not Focal is an earlier version of the OS.
"""
if (server_os != FOCAL_VER and date.today() > XENIAL_EOL_DATE):
return True
return False


def is_server_os_close_to_eol() -> bool:
"""
Assumption: Any OS that is not Focal is an earlier version of the OS.
"""
if (server_os != FOCAL_VER and date.today() <= XENIAL_EOL_DATE):
return True
return False
14 changes: 6 additions & 8 deletions securedrop/journalist_templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,13 @@
<body>

{% if g.user %}
{% if g.show_v2_onion_eol_warning %}
<div id="v2-onion-eol" class="alert-banner">
<img src="{{ url_for('static', filename='i/bang-circle.png') }}" width="20" height="20"> {{ gettext('<strong>Update Required</strong>&nbsp;&nbsp;Set up v3 Onion Services before April 30 to keep your SecureDrop servers online. Please contact your administrator. <a href="//securedrop.org/v2-onion-eol" rel="noreferrer">Learn More</a>') }}
{% if g.show_os_past_eol_warning %}
<div id="os-eol-reached" class="alert-banner">
<img src="{{ url_for('static', filename='i/bang-circle.png') }}" width="20" height="20"> {{ gettext ('<strong>Critical Security:</strong>&nbsp;&nbsp;The operating system used by your SecureDrop servers has reached its end-of-life. A manual update is required to re-enable the Source Interface and remain safe. <a href="//securedrop.org/xenial-eol" rel="noreferrer">Learn More</a>') }}
</div>
{% endif %}

{% if g.show_v2_onion_migration_warning %}
<div id="v2-complete-migration" class="alert-banner">
<img src="{{ url_for('static', filename='i/bang-circle.png') }}" width="20" height="20"> {{ gettext('<strong>Update Required</strong>&nbsp;&nbsp;Complete the v3 Onion Services setup before April 30. Please contact your administrator. <a href="//securedrop.org/v2-onion-eol" rel="noreferrer">Learn More</a>') }}
{% elif g.show_os_upcoming_eol_warning %}
<div id="os-eol-upcoming" class="alert-banner">
<img src="{{ url_for('static', filename='i/bang-circle.png') }}" width="20" height="20"> {{ gettext ('<strong>Critical Security:</strong>&nbsp;&nbsp;The operating system used by your SecureDrop servers is approaching its end-of-life on April 30, 2021. A manual update is urgently required to remain safe. <a href="//securedrop.org/xenial-eol" rel="noreferrer">Learn More</a>') }}
</div>
{% endif %}

Expand Down
67 changes: 67 additions & 0 deletions securedrop/tests/test_journalist.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,73 @@ def _login_user(app, username, password, otp_secret):
assert hasattr(g, 'user') # ensure logged in


def test_user_sees_os_warning_if_server_past_eol(config, journalist_app, test_journo):
journalist_app.config.update(OS_PAST_EOL=True, OS_UPCOMING_EOL=False)
with journalist_app.test_client() as app:
_login_user(
app,
test_journo['username'],
test_journo['password'],
test_journo['otp_secret'])

resp = app.get(url_for('main.index'))

text = resp.data.decode('utf-8')
assert 'id="os-eol-reached"' in text, text
assert 'id="os-eol-upcoming"' not in text, text


def test_user_sees_os_warning_if_server_past_eol_sanity_check(config, journalist_app, test_journo):
"""
Sanity check (both conditions cannot be True but test guard against developer error)
"""
journalist_app.config.update(OS_PAST_EOL=True, OS_UPCOMING_EOL=True)
with journalist_app.test_client() as app:
_login_user(
app,
test_journo['username'],
test_journo['password'],
test_journo['otp_secret'])

resp = app.get(url_for('main.index'))

text = resp.data.decode('utf-8')
assert 'id="os-eol-reached"' in text, text
assert 'id="os-eol-upcoming"' not in text, text


def test_user_sees_os_warning_if_server_close_to_eol(config, journalist_app, test_journo):
journalist_app.config.update(OS_PAST_EOL=False, OS_UPCOMING_EOL=True)
with journalist_app.test_client() as app:
_login_user(
app,
test_journo['username'],
test_journo['password'],
test_journo['otp_secret'])

resp = app.get(url_for('main.index'))

text = resp.data.decode('utf-8')
assert 'id="os-eol-reached"' not in text, text
assert 'id="os-eol-upcoming"' in text, text


def test_user_does_not_see_os_warning_if_server_is_current(config, journalist_app, test_journo):
journalist_app.config.update(OS_PAST_EOL=False, OS_UPCOMING_EOL=False)
with journalist_app.test_client() as app:
_login_user(
app,
test_journo['username'],
test_journo['password'],
test_journo['otp_secret'])

resp = app.get(url_for('main.index'))

text = resp.data.decode('utf-8')
assert 'id="os-eol-reached"' not in text, text
assert 'id="os-eol-upcoming"' not in text, text


def test_user_with_whitespace_in_username_can_login(journalist_app):
# Create a user with whitespace at the end of the username
with journalist_app.app_context():
Expand Down

0 comments on commit 2f99b13

Please sign in to comment.