diff --git a/swh/web/ui/main.py b/swh/web/ui/main.py index 90f9864a..46a385a6 100644 --- a/swh/web/ui/main.py +++ b/swh/web/ui/main.py @@ -1,114 +1,116 @@ # Copyright (C) 2015 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU Affero General Public License version 3, or any later version # See top-level LICENSE file for more information import logging import os from flask import Flask from swh.core import config DEFAULT_CONFIG = { 'storage_args': ('list[str]', ['http://localhost:5000/']), 'storage_class': ('str', 'remote_storage'), 'log_dir': ('string', '/tmp/swh/log'), 'debug': ('bool', None), 'host': ('string', '127.0.0.1'), 'port': ('int', 6543), 'secret_key': ('string', 'development key'), - 'max_upload_size': ('int', 16 * 1024 * 1024) + 'max_upload_size': ('int', 16 * 1024 * 1024), + 'upload_folder': ('string', '/tmp/swh-web-ui/uploads'), + 'upload_allowed_extensions': ('string', '*') } # api's definition app = Flask(__name__) def read_config(config_file): """Read the configuration file `config_file`, update the app with parameters (secret_key, conf) and return the parsed configuration as a dict""" conf = config.read(config_file, DEFAULT_CONFIG) config.prepare_folders(conf, 'log_dir') if conf['storage_class'] == 'remote_storage': from swh.storage.api.client import RemoteStorage as Storage else: from swh.storage import Storage conf['storage'] = Storage(*conf['storage_args']) return conf def load_controllers(): """Load the controllers for the application""" from swh.web.ui import controller # flake8: noqa def storage(): """Return the current application's storage. """ return app.config['conf']['storage'] def run_from_webserver(environ, start_response): """Run the WSGI app from the webserver, loading the configuration.""" load_controllers() config_path = '/etc/softwareheritage/webapp/webapp.ini' conf = read_config(config_path) app.secret_key = conf['secret_key'] app.config['conf'] = conf app.config['MAX_CONTENT_LENGTH'] = conf['max_upload_size'] logging.basicConfig(filename=os.path.join(conf['log_dir'], 'web-ui.log'), level=logging.INFO) return app(environ, start_response) def run_debug_from(config_path, verbose=False): """Run the api's server in dev mode. Args: conf is a dictionary of keywords: - 'db_url' the db url's access (through psycopg2 format) - 'content_storage_dir' revisions/directories/contents storage on disk - 'host' to override the default 127.0.0.1 to open or not the server to the world - 'port' to override the default of 5000 (from the underlying layer: flask) - 'debug' activate the verbose logs - 'secret_key' the flask secret key Returns: Never """ load_controllers() conf = read_config(config_path) app.secret_key = conf['secret_key'] app.config['conf'] = conf app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 host = conf.get('host', '127.0.0.1') port = conf.get('port') debug = conf.get('debug') log_file = os.path.join(conf['log_dir'], 'web-ui.log') logging.basicConfig(level=logging.DEBUG if verbose else logging.INFO, handlers=[logging.FileHandler(log_file), logging.StreamHandler()]) app.run(host=host, port=port, debug=debug) diff --git a/swh/web/ui/upload.py b/swh/web/ui/upload.py index 74188596..e30082ef 100644 --- a/swh/web/ui/upload.py +++ b/swh/web/ui/upload.py @@ -1,65 +1,68 @@ # Copyright (C) 2015 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU Affero General Public License version 3, or any later version # See top-level LICENSE file for more information import os import tempfile import shutil from werkzeug import secure_filename +from swh.web.ui import main -UPLOAD_FOLDER = '/tmp/swh-web-ui/uploads' -# ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif']) - -def allowed_file(filename): +def allowed_file(filename, allowed_extensions='*'): """Filter on filename extensions. The filename to check for permission. """ - # return '.' in filename and \ - # filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS - return True + if allowed_extensions == '*': + return True + return '.' in filename and filename.rsplit('.', 1)[1] in allowed_extensions def save_in_upload_folder(file): """Persist uploaded file on server. Args: File object (as per Flask's submission form) Returns: a triplet: - the temporary directory holding the persisted file - the filename without any path from the file entry - the complete path filepath """ + main_conf = main.app.config['conf'] + upload_folder = main_conf['upload_folder'] + allowed_extensions = main_conf['upload_allowed_extensions'] + if not file: return None, None, None filename = file.filename - if allowed_file(filename): + if allowed_file(filename, allowed_extensions): filename = secure_filename(filename) tmpdir = tempfile.mkdtemp(suffix='tmp', prefix='swh.web.ui-', - dir=UPLOAD_FOLDER) + dir=upload_folder) filepath = os.path.join(tmpdir, filename) file.save(filepath) # persist on disk (not found how to avoid this) return tmpdir, filename, filepath def cleanup(tmpdir): """Clean up after oneself. Args: The directory dir to destroy. """ - assert (os.path.commonprefix([UPLOAD_FOLDER, tmpdir]) == UPLOAD_FOLDER) + upload_folder = main.app.config['conf']['upload_folder'] + assert (os.path.commonprefix([upload_folder, tmpdir]) == upload_folder) shutil.rmtree(tmpdir)