Skip to content

Commit

Permalink
Merge pull request #5226 from freedomofpress/source-submission-wtform
Browse files Browse the repository at this point in the history
Use WTForm for source interface submission form
  • Loading branch information
zenmonkeykstop authored May 4, 2020
2 parents 3f5dd75 + 412103d commit ec2220c
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 20 deletions.
2 changes: 2 additions & 0 deletions securedrop/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ def to_json(self):


class Submission(db.Model):
MAX_MESSAGE_LEN = 100000

__tablename__ = 'submissions'
id = Column(Integer, primary_key=True)
uuid = Column(String(36), unique=True, nullable=False)
Expand Down
24 changes: 21 additions & 3 deletions securedrop/source_app/forms.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from flask import current_app
from flask_babel import lazy_gettext as gettext
from flask_wtf import FlaskForm
from wtforms import PasswordField
from wtforms.validators import InputRequired, Regexp, Length
from wtforms import FileField, PasswordField, TextAreaField
from wtforms.validators import InputRequired, Regexp, Length, ValidationError

from models import Source
from models import Source, Submission


class LoginForm(FlaskForm):
Expand All @@ -17,3 +18,20 @@ class LoginForm(FlaskForm):
# Make sure to allow dashes since some words in the wordlist have them
Regexp(r'[\sA-Za-z0-9-]+$', message=gettext('Invalid input.'))
])


class SubmissionForm(FlaskForm):
msg = TextAreaField("msg", render_kw={"placeholder": gettext("Write a message.")})
fh = FileField("fh")

def validate_msg(self, field):
if len(field.data) > Submission.MAX_MESSAGE_LEN:
message = gettext("Message text too long.")
if current_app.instance_config.allow_document_uploads:
message = "{} {}".format(
message,
gettext(
"Large blocks of text must be uploaded as a file, not copied and pasted."
)
)
raise ValidationError(message)
28 changes: 19 additions & 9 deletions securedrop/source_app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from source_app.utils import (logged_in, generate_unique_codename,
async_genkey, normalize_timestamps,
valid_codename, get_entropy_estimate)
from source_app.forms import LoginForm
from source_app.forms import LoginForm, SubmissionForm


def make_blueprint(config):
Expand Down Expand Up @@ -145,13 +145,21 @@ def lookup():
replies=replies,
flagged=g.source.flagged,
new_user=session.get('new_user', None),
haskey=current_app.crypto_util.get_fingerprint(
g.filesystem_id))
haskey=current_app.crypto_util.get_fingerprint(g.filesystem_id),
form=SubmissionForm(),
)

@view.route('/submit', methods=('POST',))
@login_required
def submit():
allow_document_uploads = current_app.instance_config.allow_document_uploads
form = SubmissionForm()
if not form.validate():
for field, errors in form.errors.items():
for error in errors:
flash(error, "error")
return redirect(url_for('main.lookup'))

msg = request.form['msg']
fh = None
if allow_document_uploads and 'fh' in request.files:
Expand Down Expand Up @@ -190,21 +198,23 @@ def submit():
fh.stream))

if first_submission:
msg = render_template('first_submission_flashed_message.html')
flash(Markup(msg), "success")
flash_message = render_template('first_submission_flashed_message.html')
flash(Markup(flash_message), "success")

else:
if msg and not fh:
html_contents = gettext('Thanks! We received your message.')
elif not msg and fh:
elif fh and not msg:
html_contents = gettext('Thanks! We received your document.')
else:
html_contents = gettext('Thanks! We received your message and '
'document.')

msg = render_template('next_submission_flashed_message.html',
html_contents=html_contents)
flash(Markup(msg), "success")
flash_message = render_template(
'next_submission_flashed_message.html',
html_contents=html_contents
)
flash(Markup(flash_message), "success")

new_submissions = []
for fname in fnames:
Expand Down
4 changes: 2 additions & 2 deletions securedrop/source_templates/lookup.html
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ <h2 class="headline">{{ gettext('Submit Messages') }}</h2>
{% if allow_document_uploads %}
<div class="attachment grid-item center">
<img class="center" id="upload-icon" src="{{ url_for('static', filename='i/arrow-upload-large.png') }}" width="56" height="56">
<input type="file" name="fh" autocomplete="off">
{{ form.fh() }}
<p class="center" id="max-file-size">{{ gettext('Maximum upload size: 500 MB') }}</p>
</div>
{% endif %}
<div class="message grid-item{% if not allow_document_uploads %} wide{% endif %}">
<textarea name="msg" class="fill-parent" placeholder="{{ gettext('Write a message.') }}"></textarea>
{{ form.msg(class="fill-parent") }}
</div>
</div>

Expand Down
10 changes: 4 additions & 6 deletions securedrop/tests/test_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,11 +352,9 @@ def test_submit_empty_message(source_app):


def test_submit_big_message(source_app):
'''
When the message is larger than 512KB it's written to disk instead of
just residing in memory. Make sure the different return type of
SecureTemporaryFile is handled as well as BytesIO.
'''
"""
Test the message size limit.
"""
with source_app.test_client() as app:
new_codename(app, session)
_dummy_submission(app)
Expand All @@ -366,7 +364,7 @@ def test_submit_big_message(source_app):
follow_redirects=True)
assert resp.status_code == 200
text = resp.data.decode('utf-8')
assert "Thanks! We received your message" in text
assert "Message text too long." in text


def test_submit_file(source_app):
Expand Down

0 comments on commit ec2220c

Please sign in to comment.