Skip to content

Commit

Permalink
first pass through "/lookup"
Browse files Browse the repository at this point in the history
pass 1.5 through /lookup with replies
  • Loading branch information
cfm committed Jul 15, 2021
1 parent 99750b7 commit 1f69640
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 52 deletions.
2 changes: 2 additions & 0 deletions securedrop/sass/source.sass
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,14 @@ p#codename
height: 0
overflow: hidden
text-align: center
visibility: hidden

&:target
margin: auto
padding: auto
height: auto
overflow: visible
visibility: visible

.danger
display: inline-block
Expand Down
35 changes: 23 additions & 12 deletions securedrop/source_app/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,32 @@

class LoginForm(FlaskForm):

codename = PasswordField('codename', validators=[
InputRequired(message=gettext('This field is required.')),
Length(1, PassphraseGenerator.MAX_PASSPHRASE_LENGTH,
message=gettext(
'Field must be between 1 and '
'{max_codename_len} characters long.'.format(
max_codename_len=PassphraseGenerator.MAX_PASSPHRASE_LENGTH))),
# Make sure to allow dashes since some words in the wordlist have them
Regexp(r'[\sA-Za-z0-9-]+$', message=gettext('Invalid input.'))
])
codename = PasswordField(
"codename",
validators=[
InputRequired(message=gettext("This field is required.")),
Length(
1,
PassphraseGenerator.MAX_PASSPHRASE_LENGTH,
message=gettext(
"Field must be between 1 and "
"{max_codename_len} characters long.".format(
max_codename_len=PassphraseGenerator.MAX_PASSPHRASE_LENGTH
)
),
),
# 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")
msg = TextAreaField("msg",
render_kw={"aria-label": gettext("Write a message."),
"placeholder": gettext("Write a message."),
})
fh = FileField("fh", render_kw={"aria-label": gettext("Select a file to upload.")})

def validate_msg(self, field: wtforms.Field) -> None:
if len(field.data) > Submission.MAX_MESSAGE_LEN:
Expand Down
4 changes: 2 additions & 2 deletions securedrop/source_templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@

<div class="content">
<div class="container">
{% block header %}
<header>
{% block header %}
<a href="{% if 'logged_in' in session %}{{ url_for('main.lookup') }}{% else %}{{ url_for('main.index') }}{% endif %}" class="no-bottom-border">
{# FIXME:
<img src="{{ g.logo }}" class="logo small" alt="{{ g.organization_name }} | {{ gettext('SecureDrop Home') }}" width="250">
#}
{{ g.organization_name }} | {{ gettext('SecureDrop Home')}}
</a>
{% include 'locales.html' %}
</header>
{% endblock %}
</header>

<main>
{% if g.show_offline_message %}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{# FIXME:
<img src="{{ url_for('static', filename='i/success_checkmark.png') }}" alt="{{ gettext('Success') }}" height="64" width="74">
<div class="message"><strong>{{ gettext('Success!') }}</strong>
#}
<section class="message">
<h2>{{ gettext('Success!') }}</h2>
<p>{{ gettext('Thank you for sending this information to us. Please check back later for replies.') }}
<a href="#codename-hint-visible">
{{ gettext('Forgot your codename?') }}
Expand Down
10 changes: 8 additions & 2 deletions securedrop/source_templates/flashed.html
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<section id="flashed" role="alert">
<ul>
{% for category, message in messages %}
{% if category != 'banner-warning' %}
<div class="flash {{ category }}">
<li class="flash {{ category }}">
{# FIXME:
{% if category == 'notification' %}
<img src="{{ url_for('static', filename='i/font-awesome/info-circle-black.png') }}" alt="{{ gettext('Notification') }} width="20" height="16">
{% elif category == 'error' %}
<img class="pull-left" src="{{ url_for('static', filename='i/font-awesome/exclamation-triangle-black.png') }}" alt="{{ gettext('Error') }}" width="20" height="17">
{% elif category == 'important' %}
<img src="{{ url_for('static', filename='i/bang-stop.png') }}" alt="{{ gettext('Important') }}" width="22" height="22">
{% endif %}
#}
{{ message }}
</div>
</li>
{% endif %}
{% endfor %}
</ul>
</section>
{% endif %}
{% endwith %}
80 changes: 56 additions & 24 deletions securedrop/source_templates/lookup.html
Original file line number Diff line number Diff line change
@@ -1,31 +1,42 @@
{% extends "base.html" %}
{% block body %}

{% block header %}
{{ super() }}
{% if new_user %}
<div class="code-reminder pull-left" id="codename-hint">
<section class="code-reminder pull-left" id="codename-hint" aria-label="{{ gettext('Codename') }}">
<div id="codename-hint-visible">
<img class="pull-left" src="{{ url_for('static', filename='i/font-awesome/lock-black.png') }}" alt="" width="17" height="20"> {{ gettext('Remember, your codename is:') }}
<a id="codename-hint-show" class="show pull-right visible-codename" href="#codename-hint-visible">{{ gettext('Show') }}</a>
{# FIXME:
<img class="pull-left" src="{{ url_for('static', filename='i/font-awesome/lock-black.png') }}" alt="" width="17" height="20">
#}
<label id="codename-label" for="codename" aria-hidden="true">{{ gettext('Remember, your codename is:') }}</label>
<a id="codename-hint-show" class="show pull-right visible-codename" href="#codename-hint-visible" aria-hidden="true">{{ gettext('Show') }}</a>
<div id="codename-hint-content" class="hidden-codename codename">
<a id="codename-hint-hide" class="pull-right" href="#codename-hint">{{ gettext('Hide') }}</a>
<p>{{ codename }}</p>
<a id="codename-hint-hide" class="pull-right" href="#codename-hint" aria-hidden="true">{{ gettext('Hide') }}</a>
<output id="codename" aria-labelledby="codename-label">{{ codename }}</output>
</div>
</div>
</div>
</section>
{# FIXME:
<hr class="header-separator-high">
#}
{% else %}
<hr class="no-line">
{# FIXME:
<hr class="no-line">
#}
{% endif %}
{% endblock %}

{% block body %}
<div class="center">
{% include 'flashed.html' %}
</div>

<section aria-labelledby="submit-heading">
{% if allow_document_uploads %}
<h2 class="headline">{{ gettext('Submit Files or Messages') }}</h2>
<h2 id="submit-heading" class="headline">{{ gettext('Submit Files or Messages') }}</h2>
<p class="explanation">{{ gettext('You can submit any kind of file, a message, or both.') }}</p>
{% else %}
<h2 class="headline">{{ gettext('Submit Messages') }}</h2>
<h2 id="submit-heading" class="headline">{{ gettext('Submit Messages') }}</h2>
{% endif %}

<p class="explanation extended-explanation">
Expand All @@ -36,15 +47,19 @@ <h2 class="headline">{{ gettext('Submit Messages') }}</h2>
{% endif %}
{{ gettext('<a href="{url}" class="text-link">Learn more</a>.').format(url=url_for('info.why_download_public_key')) }}</p>

{# FIXME:
<hr class="no-line">
#}

<form id="upload" method="post" action="{{ url_for('main.submit') }}" enctype="multipart/form-data" autocomplete="off">
<input name="csrf_token" type="hidden" value="{{ csrf_token() }}">
<div class="snippet">
{% if allow_document_uploads %}
<div class="attachment grid-item center">
{# FIXME:
<img class="center" id="upload-icon" src="{{ url_for('static', filename='i/arrow-upload-large.png') }}" alt="" width="56" height="56">
{{ form.fh() }}
#}
{{ form.fh(**{"aria-describedby": "max-file-size"}) }}
<p class="center" id="max-file-size">{{ gettext('Maximum upload size: 500 MB') }}</p>
</div>
{% endif %}
Expand All @@ -53,7 +68,9 @@ <h2 class="headline">{{ gettext('Submit Messages') }}</h2>
</div>
</div>

{# FIXME:
<hr class="no-line">
#}
<div class="pull-right">
<button type="submit" id="submit-doc-button">
{{ gettext('SUBMIT') }}
Expand All @@ -62,56 +79,71 @@ <h2 class="headline">{{ gettext('Submit Messages') }}</h2>


<a href="{{ url_for('main.lookup') }}" class="btn secondary" id="cancel">
{# FIXME:
<img class="icon off-hover" src="{{ url_for('static', filename='i/x_icon-button_blue.png') }}" alt="" width="9" height="11">
<img class="icon on-hover" src="{{ url_for('static', filename='i/x_icon-grimace_blue.png') }}" alt="" width="9" height="11">
#}
{{ gettext('CANCEL') }}
</a>
</div>
</form>
</section>

{# FIXME:
<hr class="no-line">
#}

<h2 class="headline">{{ gettext('Read Replies') }}</h2>
<section aria-labelledby="replies-heading">
<h2 id="replies-heading" class="headline">{{ gettext('Read Replies') }}</h2>

<div id="replies">
{% if replies %}
<aside>
<output role="status">
<p>{{ gettext("You have received a reply. To protect your identity in the unlikely event someone learns your codename, please delete all replies when you're done with them. This also lets us know that you are aware of our reply. You can respond by submitting new files and messages above.") }}</p>
</aside>
</output>
{# FIXME:
<hr class="no-line">
#}
{% for reply in replies %}
<div class="reply">
<time class="date" title="{{ reply.date|rel_datetime_format }}" datetime="{{ reply.date }}">{{ reply.date|rel_datetime_format(relative=True) }}</time>
<article class="reply" role="article" aria-labelledby="timestamp-{{ reply.filename }}">
<time id="timestamp-{{ reply.filename }}" class="date" title="{{ reply.date|rel_datetime_format }}" datetime="{{ reply.date }}" aria-hidden="true">{{ reply.date|rel_datetime_format(relative=True) }}</time>
<form id="delete" class="message" method="post" action="{{ url_for('main.delete') }}" autocomplete="off">
<input name="csrf_token" type="hidden" value="{{ csrf_token() }}">
<input type="hidden" name="reply_filename" value="{{ reply.filename }}" autocomplete="off">
<a href="#confirm-delete-{{ reply.filename }}" class="delete">
<a href="#confirm-delete-{{ reply.filename }}" id="delete-reply-{{ reply.filename }}" class="delete" aria-label="{{ gettext('Delete this reply') }}">
{{ gettext('Delete') }}
{# FIXME:
<img class="icon off-hover" id="delete-reply-{{ reply.filename }}" src="{{ url_for('static', filename='i/trash-x-out.png') }}" alt="{{ gettext('Delete reply') }}" width="16" height="20">
<img class="icon on-hover" id="delete-reply-{{ reply.filename }}" src="{{ url_for('static', filename='i/trash-x-solid.png') }}" alt="{{ gettext('Delete reply') }}" width="16" height="20">
#}
</a>
<div id="confirm-delete-{{ reply.filename }}" class="confirm-prompt">
<dialog open id="confirm-delete-{{ reply.filename }}" class="confirm-prompt" role="dialog" aria-labelledby="delete-heading-{{ reply.filename }}">
<h3 id="delete-heading-{{ reply.filename }}" hidden>Delete reply from {{ reply.date|rel_datetime_format(relative=True) }}?</h3>
<p>{{ gettext('Delete this reply?') }}
<a href="#delete">{{ gettext('Cancel') }}</a>
<button type="submit" class="danger" id="confirm-delete-reply-button-{{ reply.filename }}">{{ gettext('DELETE') }}</button>
</p>
</div>
</dialog>
</form>
<blockquote>{{ reply.decrypted | nl2br }}</blockquote>
{# FIXME:
<div class="clearfix"></div>
</div>
#}
</article>
{% endfor %}
<form id="delete-all" method="post" action="{{ url_for('main.batch_delete') }}">
<a class="btn danger" href="#delete-all-confirm">{{ gettext('DELETE ALL REPLIES') }}</a>
<input name="csrf_token" type="hidden" value="{{ csrf_token() }}">
<div id="delete-all-confirm" class="hidden-prompt">
<p><strong>{{ gettext('Are you finished with the replies?') }}</strong></p>
<dialog open id="delete-all-confirm" class="hidden-prompt" role="dialog" aria-labelledby="delete-all-heading">
<h3 id="delete-all-heading"><strong>{{ gettext('Are you finished with the replies?') }}</strong></h3>
<button class="danger" type="submit">{{ gettext('YES, DELETE ALL REPLIES') }}</button>
<a class="btn" href="#delete-all">{{ gettext('NO, NOT YET') }}</a>
</div>
</dialog>
</form>
{% else %}
<h4 id="no-replies" class="explanation">{{ gettext('— No Messages —') }}</h4>
<output role="status" id="no-replies" class="explanation">{{ gettext('— No Messages —') }}</output>
{% endif %}
</div>
</section>

{% endblock %}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{# FIXME:
<img src="{{ url_for('static', filename='i/success_checkmark.png') }}" alt="{{ gettext('Success') }}">
#}
<div class="message">
<p>{{ html_contents }}</p>
</div>
2 changes: 1 addition & 1 deletion securedrop/tests/functional/source_navigation_steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def _source_shows_codename(self, verify_source_name=True):

self.wait_for(lambda: content.is_displayed())
assert content.is_displayed()
content_content = self.driver.find_element_by_css_selector("#codename-hint-content p")
content_content = self.driver.find_element_by_css_selector("#codename-hint-content output")
if verify_source_name:
assert content_content.text == self.source_name

Expand Down
20 changes: 10 additions & 10 deletions securedrop/tests/functional/test_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,26 +43,26 @@ def test_lookup_codename_hint(self):


class TestDownloadKey(
functional_test.FunctionalTest,
journalist_navigation_steps.JournalistNavigationStepsMixin):

functional_test.FunctionalTest,
journalist_navigation_steps.JournalistNavigationStepsMixin,
):
def test_journalist_key_from_source_interface(self):
data = self.return_downloaded_content(self.source_location +
"/public-key", None)
data = self.return_downloaded_content(
self.source_location + "/public-key", None
)

data = data.decode('utf-8')
data = data.decode("utf-8")
assert "BEGIN PGP PUBLIC KEY BLOCK" in data


class TestDuplicateSourceInterface(
functional_test.FunctionalTest,
source_navigation_steps.SourceNavigationStepsMixin):

functional_test.FunctionalTest, source_navigation_steps.SourceNavigationStepsMixin
):
def get_codename_generate(self):
return self.driver.find_element_by_css_selector("#codename").text

def get_codename_lookup(self):
return self.driver.find_element_by_css_selector("#codename-hint-content p").text
return self.driver.find_element_by_css_selector("#codename-hint-content output").text

def test_duplicate_generate_pages(self):
# Test generation of multiple codenames in different browser tabs, ref. issue 4458.
Expand Down

0 comments on commit 1f69640

Please sign in to comment.