Esempio n. 1
0
    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)
Esempio n. 2
0
    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)
Esempio n. 3
0
    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)
Esempio n. 4
0
    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
Esempio n. 5
0
 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'))
Esempio n. 6
0
 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)
Esempio n. 7
0
 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'))
Esempio n. 8
0
 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)
Esempio n. 9
0
 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)
Esempio n. 10
0
 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)
Esempio n. 11
0
 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)
Esempio n. 12
0
 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)
Esempio n. 13
0
    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'))
Esempio n. 14
0
    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)
Esempio n. 15
0
    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
Esempio n. 16
0
    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)
Esempio n. 17
0
    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'))
Esempio n. 18
0
    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)
Esempio n. 19
0
 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)