def setup_g(): """Store commonly used values in Flask's special g object""" if 'expires' in session and datetime.utcnow() >= session['expires']: session.clear() flash(gettext('You have been logged out due to inactivity'), 'error') session['expires'] = datetime.utcnow() + \ timedelta(minutes=getattr(config, 'SESSION_EXPIRATION_MINUTES', 120)) uid = session.get('uid', None) if uid: g.user = Journalist.query.get(uid) g.locale = i18n.get_locale(config) g.text_direction = i18n.get_text_direction(g.locale) g.html_lang = i18n.locale_to_rfc_5646(g.locale) g.locales = i18n.get_locale2name() if request.endpoint not in _insecure_views and not logged_in(): return redirect(url_for('main.login')) if request.method == 'POST': filesystem_id = request.form.get('filesystem_id') if filesystem_id: g.filesystem_id = filesystem_id g.source = get_source(filesystem_id)
def bulk(): action = request.form['action'] doc_names_selected = request.form.getlist('doc_names_selected') selected_docs = [ doc for doc in g.source.collection if doc.filename in doc_names_selected ] if selected_docs == []: if action == 'download': flash(gettext("No collections selected for download."), "error") elif action in ('delete', 'confirm_delete'): flash(gettext("No collections selected for deletion."), "error") return redirect(url_for('col.col', filesystem_id=g.filesystem_id)) if action == 'download': source = get_source(g.filesystem_id) return download(source.journalist_filename, selected_docs) elif action == 'delete': return bulk_delete(g.filesystem_id, selected_docs) elif action == 'confirm_delete': return confirm_bulk_delete(g.filesystem_id, selected_docs) else: abort(400)
def setup_g(): """Store commonly used values in Flask's special g object""" if 'expires' in session and datetime.utcnow() >= session['expires']: session.clear() flash(gettext('You have been logged out due to inactivity'), 'error') session['expires'] = datetime.utcnow() + \ timedelta(minutes=getattr(config, 'SESSION_EXPIRATION_MINUTES', 120)) uid = session.get('uid', None) if uid: g.user = Journalist.query.get(uid) g.locale = i18n.get_locale() g.text_direction = i18n.get_text_direction(g.locale) g.html_lang = i18n.locale_to_rfc_5646(g.locale) g.locales = i18n.get_locale2name() if request.endpoint not in _insecure_views and not logged_in(): return redirect(url_for('main.login')) if request.method == 'POST': filesystem_id = request.form.get('filesystem_id') if filesystem_id: g.filesystem_id = filesystem_id g.source = get_source(filesystem_id)
def setup_g() -> 'Optional[Response]': """Store commonly used values in Flask's special g object""" if 'expires' in session and datetime.utcnow() >= session['expires']: session.clear() flash(gettext('You have been logged out due to inactivity.'), 'error') uid = session.get('uid', None) if uid: user = Journalist.query.get(uid) if user and 'nonce' in session and \ session['nonce'] != user.session_nonce: session.clear() flash( gettext('You have been logged out due to password change'), 'error') session['expires'] = datetime.utcnow() + \ timedelta(minutes=getattr(config, 'SESSION_EXPIRATION_MINUTES', 120)) # Work around https://github.com/lepture/flask-wtf/issues/275 # -- after upgrading from Python 2 to Python 3, any existing # session's csrf_token value will be retrieved as bytes, # causing a TypeError. This simple fix, deleting the existing # token, was suggested in the issue comments. This code will # be safe to remove after Python 2 reaches EOL in 2020, and no # supported SecureDrop installations can still have this # problem. if sys.version_info.major > 2 and type( session.get('csrf_token')) is bytes: del session['csrf_token'] uid = session.get('uid', None) if uid: g.user = Journalist.query.get(uid) g.locale = i18n.get_locale(config) g.text_direction = i18n.get_text_direction(g.locale) g.html_lang = i18n.locale_to_rfc_5646(g.locale) g.locales = i18n.get_locale2name() if not app.config['V3_ONION_ENABLED'] or app.config['V2_ONION_ENABLED']: g.show_v2_onion_eol_warning = True if request.path.split('/')[1] == 'api': pass # We use the @token_required decorator for the API endpoints else: # We are not using the API if request.endpoint not in _insecure_views and not logged_in(): return redirect(url_for('main.login')) if request.method == 'POST': filesystem_id = request.form.get('filesystem_id') if filesystem_id: g.filesystem_id = filesystem_id g.source = get_source(filesystem_id) return None
def delete_single(filesystem_id): """deleting a single collection from its /col page""" source = get_source(filesystem_id) delete_collection(filesystem_id) flash( gettext("{source_name}'s collection deleted").format( source_name=source.journalist_designation), "notification") return redirect(url_for('main.index'))
def col(filesystem_id): form = ReplyForm() source = get_source(filesystem_id) source.has_key = crypto_util.getkey(filesystem_id) return render_template("col.html", filesystem_id=filesystem_id, source=source, form=form)
def delete_single(filesystem_id): """deleting a single collection from its /col page""" source = get_source(filesystem_id) delete_collection(filesystem_id) flash(gettext("{source_name}'s collection deleted") .format(source_name=source.journalist_designation), "notification") return redirect(url_for('main.index'))
def col(filesystem_id: str) -> str: form = ReplyForm() source = get_source(filesystem_id) source.has_key = current_app.crypto_util.get_fingerprint(filesystem_id) return render_template("col.html", filesystem_id=filesystem_id, source=source, form=form)
def download_unread_filesystem_id(filesystem_id: str) -> werkzeug.Response: id = Source.query.filter(Source.filesystem_id == filesystem_id) \ .filter_by(deleted_at=None).one().id submissions = Submission.query.filter(Submission.source_id == id).all() unseen_submissions = [s for s in submissions if not s.seen] if unseen_submissions == []: flash(gettext("No unread submissions for this source.")) return redirect(url_for('col.col', filesystem_id=filesystem_id)) source = get_source(filesystem_id) return download(source.journalist_filename, unseen_submissions)
def download_unread_filesystem_id(filesystem_id: str) -> werkzeug.Response: unseen_submissions = (Submission.query.join(Source).filter( Source.deleted_at.is_(None), Source.filesystem_id == filesystem_id).filter( ~Submission.seen_files.any(), ~Submission.seen_messages.any()).all()) if len(unseen_submissions) == 0: flash(gettext("No unread submissions for this source."), "error") return redirect(url_for("col.col", filesystem_id=filesystem_id)) source = get_source(filesystem_id) return download(source.journalist_filename, unseen_submissions)
def download_unread_filesystem_id(filesystem_id): id = Source.query.filter(Source.filesystem_id == filesystem_id) \ .one().id submissions = Submission.query.filter( Submission.source_id == id, Submission.downloaded == false()).all() if submissions == []: flash(gettext("No unread submissions for this source.")) return redirect(url_for('col.col', filesystem_id=filesystem_id)) source = get_source(filesystem_id) return download(source.journalist_filename, submissions)
def delete_single(filesystem_id: str) -> werkzeug.Response: """deleting a single collection from its /col page""" source = get_source(filesystem_id) try: delete_collection(filesystem_id) except ValueError as e: current_app.logger.error("error deleting collection: %s", e) abort(500) flash( gettext("{source_name}'s collection deleted.").format( source_name=source.journalist_designation), "notification") return redirect(url_for('main.index'))
def col(filesystem_id: str) -> str: form = ReplyForm() source = get_source(filesystem_id) try: EncryptionManager.get_default().get_source_public_key( filesystem_id) source.has_key = True except GpgKeyNotFoundError: source.has_key = False return render_template("col.html", filesystem_id=filesystem_id, source=source, form=form)
def setup_g() -> "Optional[Response]": """Store commonly used values in Flask's special g object""" if "expires" in session and datetime.now(timezone.utc) >= session["expires"]: session.clear() flash(gettext("You have been logged out due to inactivity."), "error") uid = session.get("uid", None) if uid: user = Journalist.query.get(uid) if user and "nonce" in session and session["nonce"] != user.session_nonce: session.clear() flash(gettext("You have been logged out due to password change"), "error") session["expires"] = datetime.now(timezone.utc) + timedelta( minutes=getattr(config, "SESSION_EXPIRATION_MINUTES", 120) ) uid = session.get("uid", None) if uid: g.user = Journalist.query.get(uid) # pylint: disable=assigning-non-slot i18n.set_locale(config) if InstanceConfig.get_default().organization_name: g.organization_name = ( # pylint: disable=assigning-non-slot InstanceConfig.get_default().organization_name ) else: g.organization_name = gettext("SecureDrop") # pylint: disable=assigning-non-slot try: g.logo = get_logo_url(app) # pylint: disable=assigning-non-slot except FileNotFoundError: app.logger.error("Site logo not found.") if request.path.split("/")[1] == "api": pass # We use the @token_required decorator for the API endpoints else: # We are not using the API if request.endpoint not in _insecure_views and not logged_in(): return redirect(url_for("main.login")) if request.method == "POST": filesystem_id = request.form.get("filesystem_id") if filesystem_id: g.filesystem_id = filesystem_id # pylint: disable=assigning-non-slot g.source = get_source(filesystem_id) # pylint: disable=assigning-non-slot return None
def bulk() -> Union[str, werkzeug.Response]: action = request.form["action"] error_redirect = url_for("col.col", filesystem_id=g.filesystem_id) doc_names_selected = request.form.getlist("doc_names_selected") selected_docs = [ doc for doc in g.source.collection if doc.filename in doc_names_selected ] if selected_docs == []: if action == "download": flash( Markup("<b>{}</b> {}".format( # Translators: Error shown when a user has not selected items to act on. escape(gettext("Nothing Selected")), escape( gettext( "You must select one or more items for download" )), )), "error", ) elif action == "delete": flash( Markup("<b>{}</b> {}".format( # Translators: Error shown when a user has not selected items to act on. escape(gettext("Nothing Selected")), escape( gettext( "You must select one or more items for deletion" )), )), "error", ) else: abort(400) return redirect(error_redirect) if action == "download": source = get_source(g.filesystem_id) return download(source.journalist_filename, selected_docs, on_error_redirect=error_redirect) elif action == "delete": return bulk_delete(g.filesystem_id, selected_docs) else: abort(400)
def delete_single(filesystem_id: str) -> werkzeug.Response: """deleting a single collection from its /col page""" source = get_source(filesystem_id) try: delete_collection(filesystem_id) except ValueError as e: current_app.logger.error("error deleting collection: %s", e) abort(500) flash( Markup("<b>{}</b> {}".format( # Translators: Precedes a message confirming the success of an operation. escape(gettext("Success!")), escape( gettext( "The account and data for the source {} have been deleted." ).format(source.journalist_designation)))), 'success') return redirect(url_for('main.index'))
def bulk(): action = request.form['action'] doc_names_selected = request.form.getlist('doc_names_selected') selected_docs = [doc for doc in g.source.collection if doc.filename in doc_names_selected] if selected_docs == []: if action == 'download': flash(gettext("No collections selected for download."), "error") elif action in ('delete', 'confirm_delete'): flash(gettext("No collections selected for deletion."), "error") return redirect(url_for('col.col', filesystem_id=g.filesystem_id)) if action == 'download': source = get_source(g.filesystem_id) return download(source.journalist_filename, selected_docs) elif action == 'delete': return bulk_delete(g.filesystem_id, selected_docs) elif action == 'confirm_delete': return confirm_bulk_delete(g.filesystem_id, selected_docs) else: abort(400)