Skip to content

Commit

Permalink
Merge pull request #1558 from heartsucker/flash-fixes
Browse files Browse the repository at this point in the history
Flash message fixes
  • Loading branch information
redshiftzero authored Feb 6, 2017
2 parents 09ca44c + 8e0a888 commit b83f219
Show file tree
Hide file tree
Showing 14 changed files with 142 additions and 64 deletions.
4 changes: 2 additions & 2 deletions securedrop/journalist_templates/flashed.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
{% if messages %}
{% for category, message in messages %}
{% if category != 'banner-warning' %}
<p class="flash {{ category }}">
<div class="flash {{ category }}">
{% if category == 'notification' %}
<i class="fa fa-info-circle pull-left"></i>
{% elif category == 'error' %}
<i class="fa fa-exclamation-triangle pull-left"></i>
{% endif %}
{{ message }}
</p>
</div>
{% endif %}
{% endfor %}
{% endif %}
Expand Down
21 changes: 21 additions & 0 deletions securedrop/sass/_base.sass
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,18 @@
text-align: left
font-size: medium

.flash
display: flex
flex-flow: row nowrap
align-items: flex-start

&.success
border: 1px solid #E1F1E5
background-color: #E6FFEB

strong
color: #299549

&.notification
border: 1px solid #8ed9f6
background-color: #e3f7fc
Expand All @@ -322,6 +334,15 @@
i
color: #D62727

&.important
border: 1px solid #EBDCEB
background: #FDFAFD

strong
color: #673466
p
color: #555555

.banner-warning
border: 1px solid $color_error_border
background-color: $color_error_background
Expand Down
48 changes: 26 additions & 22 deletions securedrop/source.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import crypto_util
import store
import template_filters
import util
from db import db_session, Source, Submission, Reply, get_one_or_else
from request_that_secures_file_uploads import RequestThatSecuresFileUploads
from jinja2 import evalcontextfilter
Expand Down Expand Up @@ -292,18 +293,24 @@ def submit():
fh.stream))

if first_submission:
flash(
"Thanks for submitting something to SecureDrop! Please check back later for replies.",
"notification")
flash(Markup("""{svg}<div class="message"><strong>Success!</strong>
<p>Thank you for sending this information to us.
Please check back later for replies. <a href="#codename-hint">
Forgot your codename?</a></p></div>
""".format(svg=util.svg('success_checkmark.svg'))),
"success")
else:
if msg:
flash("Thanks! We received your message.", "notification")
if fh:
flash(
'{} "{}".'.format(
"Thanks! We received your document",
fh.filename or '[unnamed]'),
"notification")
if msg and not fh:
things = 'message'
elif not msg and fh:
things = 'document'
else:
things = 'message and document'

flash(Markup("""{svg}<div class="message"><p>Thanks! We received your
{things}.</p></div>
""".format(svg=util.svg('success_checkmark.svg'), things=things)),
"success")

for fname in fnames:
submission = Submission(g.source, fname)
Expand Down Expand Up @@ -392,17 +399,14 @@ def login():
def logout():
if logged_in():
session.clear()
tor_msg = Markup("""<strong>Important:</strong><br>
Thank you for logging out!<br>
Please fully end your session by restarting
Tor Browser:<br>
1. Click the
<img src='static/i/toronion.png' alt='Tor icon' />
Tor onion icon in the toolbar above.<br>
2. Click <strong> New Identity</strong>.<br>
3. Click <strong>Yes</strong> in the dialog box
that appears.""")
flash(tor_msg, "error")
msg = Markup("""<div class="icon">{svg}</div>
<div class="message"><strong>Important!</strong><br>
<p>Thank you for exiting your session! Please select "New
Identity" from the green Onion button in the Tor browser
to clear all history of your SecureDrop usage from this
device.</p></div>
""".format(svg=util.svg('hand_with_fingerprint.svg')))
flash(msg, "important")

return redirect(url_for('index'))

Expand Down
4 changes: 2 additions & 2 deletions securedrop/source_templates/flashed.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
{% if messages %}
{% for category, message in messages %}
{% if category != 'banner-warning' %}
<p class="flash {{ category }}">
<div class="flash {{ category }}">
{% if category == 'notification' %}
<img src="{{ url_for('static', filename='i/font-awesome/black/info.svg') }}" width="17px" height="17px">
{% elif category == 'error' %}
<img class="pull-left" src="{{ url_for('static', filename='i/font-awesome/black/exclamation-triangle.svg') }}" width="17px" height="17px">
{% endif %}
{{ message }}
</p>
</div>
{% endif %}
{% endfor %}
{% endif %}
Expand Down
16 changes: 14 additions & 2 deletions securedrop/source_templates/lookup.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,19 @@
{% include 'flashed.html' %}

{% if flagged and not haskey %}
<p class="notification"> <img class="pull-left" src="{{ url_for('static', filename='i/font-awesome/black/info-circle.svg') }}" width="20px" height="20px">A journalist has been waiting for you to log in again so SecureDrop can generate a crypto key for you. Now that you have logged in, they are able to write you a reply. Check back later for replies.</p>
<div class="flash notification">
<img src="{{ url_for('static', filename='i/relieved_face.svg') }}" alt="relieved-face" class="icon">
<div class="message">
<strong>Whew, it’s you! Now, the embarrassing part...</strong></br>
<p>Our servers experienced an unusual surge of new activity, when you last visited. This could
have been human activity, an automated attack, or just some random blip. To err on the side
of caution, we put a hold on sending all documents from that day through to our journalists.</p>

<p>Now that we know you’re really a human, though, we’ll get your previous submission into the
hands of a journalist straight away. We’re sorry for the delay. Please do check back again
in a week or so.</p>
</div>
</div>
{% endif %}
</div>

Expand Down Expand Up @@ -82,7 +94,7 @@ <h2 class="headline">Get Replies</h2>

<hr class="no-line">

<div class="code-reminder">
<div class="code-reminder" id="codename-hint">
<img class="pull-left" src="{{ url_for('static', filename='i/font-awesome/black/lock.svg') }}" width="20px" height="20px"> Remember your codename is:
<div id="show" class="show pull-right"></div>
<span id="content" class="codename"><p class="alert">{{ codename }}</p></span>
Expand Down
4 changes: 4 additions & 0 deletions securedrop/static/i/relieved_face.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions securedrop/static/i/svg/hand_with_fingerprint.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions securedrop/static/i/svg/success_checkmark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 1 addition & 9 deletions securedrop/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from werkzeug import secure_filename

from secure_tempfile import SecureTemporaryFile
from util import PathException

import logging
log = logging.getLogger(__name__)
Expand All @@ -20,15 +21,6 @@
"^(?P<index>\d+)\-[a-z0-9-_]*(?P<file_type>msg|doc\.(gz|zip)|reply)\.gpg$").match


class PathException(Exception):

"""An exception raised by `store.verify` when it encounters a bad path. A path
can be bad when it is not absolute, not normalized, not within
`config.STORE_DIR`, or doesn't match the filename format.
"""
pass


def verify(p):
"""Assert that the path is absolute, normalized, inside `config.STORE_DIR`, and
matches the filename format.
Expand Down
2 changes: 1 addition & 1 deletion securedrop/tests/functional/journalist_navigation_steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def _admin_adds_a_user(self):

# Successfully verifying the code should redirect to the admin
# interface, and flash a message indicating success
flashed_msgs = self.driver.find_elements_by_css_selector('p.flash')
flashed_msgs = self.driver.find_elements_by_css_selector('.flash')
self.assertIn(("Two factor token successfully verified for user"
" {}!").format(self.new_user['username']),
[el.text for el in flashed_msgs])
Expand Down
15 changes: 6 additions & 9 deletions securedrop/tests/functional/source_navigation_steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,8 @@ def _source_submits_a_file(self):
submit_button.click()

notification = self.driver.find_element_by_css_selector(
'p.notification')
expected_notification = ('Thanks for submitting something '
'to SecureDrop! Please check back '
'later for replies.')
'.success')
expected_notification = 'Thank you for sending this information to us'
self.assertIn(expected_notification, notification.text)

def _source_submits_a_message(self):
Expand All @@ -76,12 +74,11 @@ def _source_submits_a_message(self):
submit_button.click()

notification = self.driver.find_element_by_css_selector(
'p.notification')
self.assertIn('Thanks for submitting something to SecureDrop!'
' Please check back later for replies.',
'.success')
self.assertIn('Thank you for sending this information to us',
notification.text)

def _source_logs_out(self):
logout_button = self.driver.find_element_by_id('logout').click()
notification = self.driver.find_element_by_css_selector('p.error')
self.assertIn('Thank you for logging out!', notification.text)
notification = self.driver.find_element_by_css_selector('.important')
self.assertIn('Thank you for exiting your session!', notification.text)
23 changes: 6 additions & 17 deletions securedrop/tests/test_unit_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def test_login_and_logout(self):
self.assertTrue(session['logged_in'])
resp = c.get('/logout', follow_redirects=True)
self.assertTrue(not session)
self.assertIn('Thank you for logging out!', resp.data)
self.assertIn('Thank you for exiting your session!', resp.data)

def test_login_with_whitespace(self):
"""Test that codenames with leading or trailing whitespace still work"""
Expand Down Expand Up @@ -172,7 +172,7 @@ def test_initial_submission_notification(self):
resp = self._dummy_submission()
self.assertEqual(resp.status_code, 200)
self.assertIn(
"Thanks for submitting something to SecureDrop! Please check back later for replies.",
"Thank you for sending this information to us",
resp.data)

def test_submit_message(self):
Expand All @@ -183,7 +183,7 @@ def test_submit_message(self):
fh=(StringIO(''), ''),
), follow_redirects=True)
self.assertEqual(resp.status_code, 200)
self.assertIn("Thanks! We received your message.", resp.data)
self.assertIn("Thanks! We received your", resp.data)

def test_submit_empty_message(self):
self._new_codename()
Expand All @@ -206,7 +206,7 @@ def test_submit_big_message(self):
fh=(StringIO(''), ''),
), follow_redirects=True)
self.assertEqual(resp.status_code, 200)
self.assertIn("Thanks! We received your message.", resp.data)
self.assertIn("Thanks! We received your", resp.data)

def test_submit_file(self):
self._new_codename()
Expand All @@ -216,12 +216,7 @@ def test_submit_file(self):
fh=(StringIO('This is a test'), 'test.txt'),
), follow_redirects=True)
self.assertEqual(resp.status_code, 200)
self.assertIn(
escape(
'{} "{}"'.format(
"Thanks! We received your document",
"test.txt")),
resp.data)
self.assertIn('Thanks! We received you', resp.data)

def test_submit_both(self):
self._new_codename()
Expand All @@ -231,13 +226,7 @@ def test_submit_both(self):
fh=(StringIO('This is a test'), 'test.txt'),
), follow_redirects=True)
self.assertEqual(resp.status_code, 200)
self.assertIn("Thanks! We received your message.", resp.data)
self.assertIn(
escape(
'{} "{}"'.format(
"Thanks! We received your document",
'test.txt')),
resp.data)
self.assertIn("Thanks! We received your", resp.data)

@patch('gzip.GzipFile')
def test_submit_sanitizes_filename(self, gzipfile):
Expand Down
23 changes: 23 additions & 0 deletions securedrop/tests/test_unit_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
import source
import unittest
import util
from util import PathException


class TestUtil(unittest.TestCase):

def test_svg_valid(self):
with source.app.app_context():
res = util.svg('success_checkmark.svg')
self.assertIn('<svg', res)

def test_svg_bad_extension(self):
with source.app.app_context():
with self.assertRaises(PathException):
util.svg('config.py')

def test_svg_bad_path(self):
with source.app.app_context():
with self.assertRaises(PathException):
util.svg('../../../../etc/hosts')
32 changes: 32 additions & 0 deletions securedrop/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-

from os import path

from flask import current_app


class PathException(Exception):

"""An exception raised by `util.verify` when it encounters a bad path. A path
can be bad when it is not absolute or not normalized.
"""
pass


def svg(filename):
"""Safely takes a filename and returns the contents of the file in the
static directory as a string.
"""

if not filename.endswith('.svg'):
raise PathException('File must have .svg extension, but got {}'
.format(filename))

target_path = path.join(path.abspath(current_app.static_folder), 'i', 'svg',
filename)
if not path.isabs(target_path):
raise PathException('Expected path to SVG to the absolute and '
'normalized, but found {}'.format(target_path))

with open(target_path) as f:
return f.read()

0 comments on commit b83f219

Please sign in to comment.