Skip to content

Commit

Permalink
Merge pull request #3057 from aydwi/image-upload-enhancements
Browse files Browse the repository at this point in the history
Improvements in image upload functionality
  • Loading branch information
heartsucker authored Apr 3, 2018
2 parents d272e85 + 8372f43 commit 9880a30
Show file tree
Hide file tree
Showing 16 changed files with 59 additions and 18 deletions.
1 change: 1 addition & 0 deletions install_files/ansible-base/group_vars/all/securedrop
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,6 @@ appserver_dependencies:
- apparmor-utils
- redis-server
- supervisor
- libjpeg-dev

tor_apt_repo_url: https://tor-apt.freedom.press
2 changes: 1 addition & 1 deletion install_files/securedrop-app-code/DEBIAN/control
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ Homepage: https://securedrop.org
Package: securedrop-app-code
Version: 0.7.0~rc1
Architecture: amd64
Depends: python-pip,apparmor-utils,gnupg2,haveged,python,python-pip,secure-delete,sqlite,apache2-mpm-worker,libapache2-mod-wsgi,libapache2-mod-xsendfile,redis-server,supervisor,securedrop-keyring,securedrop-config
Depends: python-pip,apparmor-utils,gnupg2,haveged,python,python-pip,secure-delete,sqlite,apache2-mpm-worker,libapache2-mod-wsgi,libapache2-mod-xsendfile,redis-server,supervisor,securedrop-keyring,securedrop-config,libjpeg-dev
Description: Packages the SecureDrop application code pip dependencies and apparmor profiles. This package will put the apparmor profiles in enforce mode. This package does use pip to install the pip wheelhouse
2 changes: 1 addition & 1 deletion molecule/builder/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ LABEL image_name="$NAME"

RUN apt-get -y update && apt-get install -y python rsync sudo bash \
devscripts git aptitude ipython libssl-dev ntp \
python-dev python-pip python-ipdb ruby tmux vim paxctl && \
python-dev python-pip python-ipdb ruby tmux vim paxctl libjpeg-dev && \
aptitude -y dist-upgrade && \
apt-get clean
2 changes: 1 addition & 1 deletion securedrop/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ RUN apt-get update && \
apt-get install -y devscripts \
python-pip libpython2.7-dev libssl-dev secure-delete \
gnupg2 ruby redis-server firefox git xvfb haveged curl \
gettext paxctl x11vnc enchant
gettext paxctl x11vnc enchant libjpeg-dev

RUN gem install sass -v 3.4.23

Expand Down
2 changes: 1 addition & 1 deletion securedrop/journalist_app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
# http://flake8.pycqa.org/en/latest/user/error-codes.html?highlight=f401
from sdconfig import SDConfig # noqa: F401

_insecure_views = ['main.login', 'static']
_insecure_views = ['main.login', 'main.select_logo', 'static']


def create_app(config):
Expand Down
20 changes: 15 additions & 5 deletions securedrop/journalist_app/admin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-

from PIL import Image

import os

from flask import (Blueprint, render_template, request, url_for, redirect, g,
Expand Down Expand Up @@ -31,11 +33,19 @@ def manage_config():
form = LogoForm()
if form.validate_on_submit():
f = form.logo.data
static_filepath = os.path.join(config.SECUREDROP_ROOT,
"static/i/logo.png")
f.save(static_filepath)
flash(gettext("Image updated."), "logo-success")
return redirect(url_for("admin.manage_config"))
custom_logo_filepath = os.path.join(config.SECUREDROP_ROOT,
"static/i/custom_logo.png")
try:
with Image.open(f) as im:
im.thumbnail((500, 450), resample=3)
im.save(custom_logo_filepath, "PNG")
flash(gettext("Image updated."), "logo-success")
except Exception:
flash("Unable to process the image file."
" Try another one.", "logo-error")
finally:
return redirect(url_for("admin.manage_config"))

else:
for field, errors in form.errors.items():
for error in errors:
Expand Down
3 changes: 2 additions & 1 deletion securedrop/journalist_app/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,6 @@ class LogoForm(FlaskForm):
logo = FileField(validators=[
FileRequired(message=gettext('File required.')),
FileAllowed(['jpg', 'png', 'jpeg'],
message=gettext('Upload images only.'))
message=gettext("You can only upload JPG/JPEG"
" or PNG image files."))
])
10 changes: 10 additions & 0 deletions securedrop/journalist_app/main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-

import os

from datetime import datetime
from flask import (Blueprint, request, current_app, session, url_for, redirect,
render_template, g, flash, abort)
Expand Down Expand Up @@ -43,6 +45,14 @@ def logout():
session.pop('expires', None)
return redirect(url_for('main.index'))

@view.route('/org-logo')
def select_logo():
if os.path.exists(os.path.join(current_app.static_folder, 'i',
'custom_logo.png')):
return redirect(url_for('static', filename='i/custom_logo.png'))
else:
return redirect(url_for('static', filename='i/logo.png'))

@view.route('/')
def index():
unstarred = []
Expand Down
2 changes: 1 addition & 1 deletion securedrop/journalist_templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
<div class="container">
{% block header %}
<div id="header">
<a href="{{ url_for('main.index') }}"><img src="/static/i/{{ header_image }}" class="logo small" alt="SecureDrop" width="250px"></a>
<a href="{{ url_for('main.index') }}"><img src="{{ url_for('main.select_logo') }}" class="logo small" alt="SecureDrop" width="250px"></a>
{% include 'locales.html' %}
{% if use_custom_header_image %}
<div class="powered">
Expand Down
5 changes: 4 additions & 1 deletion securedrop/journalist_templates/config.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ <h2>{{ gettext('Logo Image') }}</h2>
<p>{{ gettext('Here you can update the image displayed on the SecureDrop web interfaces:') }}</p>

<p>
<img src="{{ url_for('static', filename='i/logo.png')|autoversion }}" class="logo small" alt="SecureDrop" width="250px">
<img src="{{ url_for('main.select_logo') }}" class="logo small" alt="SecureDrop" width="250px">
</p>

<form method="post" enctype="multipart/form-data">
Expand All @@ -32,6 +32,9 @@ <h2>{{ gettext('Logo Image') }}</h2>
{{ form.logo(id="logo-upload") }}
<br>
</p>
<h5>
{{ gettext('Recommended size: 500px * 450px') }}
</h5>
<button type="submit" class="sd-button" id="submit-logo-update">
<i class="fas fa-pencil-alt"></i>{{ gettext('UPDATE LOGO') }}
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ scrypt
SQLAlchemy
typing
Werkzeug==0.12.2
Pillow
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ itsdangerous==0.24 # via flask
jinja2==2.10
jsmin==2.2.2
markupsafe==1.0 # via jinja2
pillow==5.0.0
psutil==5.4.3
pycryptodomex==3.4.7
pyotp==2.2.6
Expand Down
8 changes: 8 additions & 0 deletions securedrop/source_app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ def generate():
session['new_user'] = True
return render_template('generate.html', codename=codename)

@view.route('/org-logo')
def select_logo():
if os.path.exists(os.path.join(current_app.static_folder, 'i',
'custom_logo.png')):
return redirect(url_for('static', filename='i/custom_logo.png'))
else:
return redirect(url_for('static', filename='i/logo.png'))

@view.route('/create', methods=['POST'])
def create():
filesystem_id = current_app.crypto_util.hash_codename(
Expand Down
2 changes: 1 addition & 1 deletion securedrop/source_templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
{% block header %}
<div id="header">
<a href="{% if 'logged_in' in session %}{{ url_for('main.lookup') }}{% else %}{{ url_for('main.index') }}{% endif %}">
<img src="/static/i/{{ header_image }}" class="logo small" alt="SecureDrop" width="250px">
<img src="{{ url_for('main.select_logo') }}" class="logo small" alt="SecureDrop" width="250px">
</a>
{% include 'locales.html' %}
{% if use_custom_header_image %}
Expand Down
2 changes: 1 addition & 1 deletion securedrop/source_templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
See _source_index.sass for a more full understanding. #}
<div class="index-wrap">
<div class="header">
<img src="/static/i/{{ header_image }}" alt="SecureDrop">
<img src="{{ url_for('main.select_logo') }}" alt="SecureDrop">
{% if use_custom_header_image %}
<div class="powered">
{{ gettext('Powered by') }}<br>
Expand Down
14 changes: 10 additions & 4 deletions securedrop/tests/test_journalist.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
import random
import unittest
import zipfile
import base64

from cStringIO import StringIO
from io import BytesIO
from flask import url_for, escape, session, current_app, g
from flask_testing import TestCase
from mock import patch
Expand Down Expand Up @@ -970,9 +972,11 @@ def test_logo_upload_with_valid_image_succeeds(self):

try:
self._login_admin()

# Create 1px * 1px 'white' PNG file from its base64 string
form = journalist_app_module.forms.LogoForm(
logo=(StringIO('imagedata'), 'test.png')
logo=(BytesIO(base64.decodestring
("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQ"
"VR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=")), 'test.png')
)
self.client.post(url_for('admin.manage_config'),
data=form.data,
Expand All @@ -993,8 +997,10 @@ def test_logo_upload_with_invalid_filetype_fails(self):
resp = self.client.post(url_for('admin.manage_config'),
data=form.data,
follow_redirects=True)
self.assertMessageFlashed("Upload images only.", "logo-error")
self.assertIn('Upload images only.', resp.data)
self.assertMessageFlashed("You can only upload JPG/JPEG"
" or PNG image files.", "logo-error")
self.assertIn("You can only upload JPG/JPEG"
" or PNG image files.", resp.data)

def test_logo_upload_with_empty_input_field_fails(self):
self._login_admin()
Expand Down

0 comments on commit 9880a30

Please sign in to comment.