<commit_before># -*- coding: utf-8 -*-

from datetime import datetime
import functools

from flask import (Flask, request, render_template, send_file, redirect, flash,
                   url_for, g, abort, session)
from flask_wtf.csrf import CSRFProtect
from flask_assets import Environment
from jinja2 import Markup
from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy.exc import IntegrityError
from sqlalchemy.sql.expression import false

import config
import version
import crypto_util
from rm import srm
import i18n
from flask_babel import gettext, ngettext
import store
import template_filters
from db import (db_session, Source, Journalist, Submission, Reply,
                SourceStar, get_one_or_else, LoginThrottledException,
                PasswordError, InvalidUsernameException)
import worker

app = Flask(__name__, template_folder=config.JOURNALIST_TEMPLATES_DIR)
app.config.from_object(config.JournalistInterfaceFlaskConfig)
CSRFProtect(app)

i18n.setup_app(app)

assets = Environment(app)

app.jinja_env.globals['version'] = version.__version__
if getattr(config, 'CUSTOM_HEADER_IMAGE', None):
    app.jinja_env.globals['header_image'] = config.CUSTOM_HEADER_IMAGE
    app.jinja_env.globals['use_custom_header_image'] = True
else:
    app.jinja_env.globals['header_image'] = 'logo.png'
    app.jinja_env.globals['use_custom_header_image'] = False

app.jinja_env.filters['datetimeformat'] = template_filters.datetimeformat
app.jinja_env.filters['filesizeformat'] = template_filters.filesizeformat


@app.teardown_appcontext
def shutdown_session(exception=None):
    """Automatically remove database sessions at the end of the request, or
    when the application shuts down"""
    db_session.remove()


def get_source(filesystem_id):
    """Return a Source object, representing the database row, for the source
    with the `filesystem_id`"""
    source = None
    query = Source.query.filter(Source.filesystem_id == filesystem_id)
    source = get_one_or_else(query, app.logger, abort)

    return source


@app.before_request
def setup_g():
    """Store commonly used values in Flask's special g object"""
    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.method == 'POST':
        filesystem_id = request.form.get('filesystem_id')
        if filesystem_id:
            g.filesystem_id = filesystem_id
            g.source = get_source(filesystem_id)


def logged_in():
    # When a user is logged in, we push their user ID (database primary key)
    # into the session. setup_g checks for this value, and if it finds it,
    # stores a reference to the user's Journalist object in g.
    #
    # This check is good for the edge case where a user is deleted but still
    # has an active session - we will not authenticate a user if they are not
    # in the database.
    return bool(g.get('user', None))


def login_required(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        if not logged_in():
            return redirect(url_for('login'))
        return func(*args, **kwargs)
    return wrapper


def admin_required(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        if logged_in() and g.user.is_admin:
            return func(*args, **kwargs)
        # TODO: sometimes this gets flashed 2x (Chrome only?)
        flash(gettext("Only administrators can access this page."),
              "notification")
        return redirect(url_for('index'))
    return wrapper


@app.route('/login', methods=('GET', 'POST'))
def login():
    if request.method == 'POST':
        try:
            user = Journalist.login(request.form['username'],
                                    request.form['password'],
                                    request.form['token'])
        except Exception as e:
            app.logger.error("Login for '{}' failed: {}".format(
                request.form['username'], e))
            login_flashed_msg = gettext('Login failed.')

            if isinstance(e, LoginThrottledException):
                login_flashed_msg += " "
                login_flashed_msg += gettext(
                    "Please wait at least {seconds} seconds "
                    "before logging in again.").format(
                        seconds=Journalist._LOGIN_ATTEMPT_PERIOD)
            else:
                try:
                    user = Journalist.query.filter_by(
                        username=request.form['username']).one()
                    if user.is_totp:
                        login_flashed_msg += " "
                        login_flashed_msg += gettext(
                            "Please wait for a new two-factor token"
                            " before logging in again.")
                except:
                    pass

            flash(login_flashed_msg, "error")
        else:
            app.logger.info("'{}' logged in with the token {}".format(
                request.form['username'], request.form['token']))

            # Update access metadata
            user.last_access = datetime.utcnow()
            db_session.add(user)
            db_session.commit()

            session['uid'] = user.id
            return redirect(url_for('index'))

    return render_template("login.html")


@app.route('/logout')
def logout():
    session.pop('uid', None)
    return redirect(url_for('index'))


@app.route('/admin', methods=('GET', 'POST'))
@admin_required
def admin_index():
    users = Journalist.query.all()
    return render_template("admin.html", users=users)


@app.route('/admin/add', methods=('GET', 'POST'))
@admin_required
def admin_add_user():
    if request.method == 'POST':
        form_valid = True
        username = request.form['username']

        password = request.form['password']
        is_admin = bool(request.form.get('is_admin'))

        if form_valid:
            try:
                otp_secret = None<commit_msg>added session expiration to journalist interface<commit_after># -*- coding: utf-8 -*-

from datetime import datetime, timedelta
import functools

from flask import (Flask, request, render_template, send_file, redirect, flash,
                   url_for, g, abort, session)
from flask_wtf.csrf import CSRFProtect
from flask_assets import Environment
from jinja2 import Markup
from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy.exc import IntegrityError
from sqlalchemy.sql.expression import false

import config
import version
import crypto_util
from rm import srm
import i18n
from flask_babel import gettext, ngettext
import store
import template_filters
from db import (db_session, Source, Journalist, Submission, Reply,
                SourceStar, get_one_or_else, LoginThrottledException,
                PasswordError, InvalidUsernameException)
import worker

app = Flask(__name__, template_folder=config.JOURNALIST_TEMPLATES_DIR)
app.config.from_object(config.JournalistInterfaceFlaskConfig)
CSRFProtect(app)

i18n.setup_app(app)

assets = Environment(app)

app.jinja_env.globals['version'] = version.__version__
if getattr(config, 'CUSTOM_HEADER_IMAGE', None):
    app.jinja_env.globals['header_image'] = config.CUSTOM_HEADER_IMAGE
    app.jinja_env.globals['use_custom_header_image'] = True
else:
    app.jinja_env.globals['header_image'] = 'logo.png'
    app.jinja_env.globals['use_custom_header_image'] = False

app.jinja_env.filters['datetimeformat'] = template_filters.datetimeformat
app.jinja_env.filters['filesizeformat'] = template_filters.filesizeformat


@app.teardown_appcontext
def shutdown_session(exception=None):
    """Automatically remove database sessions at the end of the request, or
    when the application shuts down"""
    db_session.remove()


def get_source(filesystem_id):
    """Return a Source object, representing the database row, for the source
    with the `filesystem_id`"""
    source = None
    query = Source.query.filter(Source.filesystem_id == filesystem_id)
    source = get_one_or_else(query, app.logger, abort)

    return source


@app.before_request
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', 30))

    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.method == 'POST':
        filesystem_id = request.form.get('filesystem_id')
        if filesystem_id:
            g.filesystem_id = filesystem_id
            g.source = get_source(filesystem_id)


def logged_in():
    # When a user is logged in, we push their user ID (database primary key)
    # into the session. setup_g checks for this value, and if it finds it,
    # stores a reference to the user's Journalist object in g.
    #
    # This check is good for the edge case where a user is deleted but still
    # has an active session - we will not authenticate a user if they are not
    # in the database.
    return bool(g.get('user', None))


def login_required(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        if not logged_in():
            return redirect(url_for('login'))
        return func(*args, **kwargs)
    return wrapper


def admin_required(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        if logged_in() and g.user.is_admin:
            return func(*args, **kwargs)
        # TODO: sometimes this gets flashed 2x (Chrome only?)
        flash(gettext("Only administrators can access this page."),
              "notification")
        return redirect(url_for('index'))
    return wrapper


@app.route('/login', methods=('GET', 'POST'))
def login():
    if request.method == 'POST':
        try:
            user = Journalist.login(request.form['username'],
                                    request.form['password'],
                                    request.form['token'])
        except Exception as e:
            app.logger.error("Login for '{}' failed: {}".format(
                request.form['username'], e))
            login_flashed_msg = gettext('Login failed.')

            if isinstance(e, LoginThrottledException):
                login_flashed_msg += " "
                login_flashed_msg += gettext(
                    "Please wait at least {seconds} seconds "
                    "before logging in again.").format(
                        seconds=Journalist._LOGIN_ATTEMPT_PERIOD)
            else:
                try:
                    user = Journalist.query.filter_by(
                        username=request.form['username']).one()
                    if user.is_totp:
                        login_flashed_msg += " "
                        login_flashed_msg += gettext(
                            "Please wait for a new two-factor token"
                            " before logging in again.")
                except:
                    pass

            flash(login_flashed_msg, "error")
        else:
            app.logger.info("'{}' logged in with the token {}".format(
                request.form['username'], request.form['token']))

            # Update access metadata
            user.last_access = datetime.utcnow()
            db_session.add(user)
            db_session.commit()

            session['uid'] = user.id
            return redirect(url_for('index'))

    return render_template("login.html")


@app.route('/logout')
def logout():
    session.pop('uid', None)
    session.pop('expires', None)
    return redirect(url_for('index'))


@app.route('/admin', methods=('GET', 'POST'))
@admin_required
def admin_index():
    users = Journalist.query.all()
    return render_template("admin.html", users=users)


@app.route('/admin/add', methods=('GET', 'POST'))
@admin_required
def admin_add_user():
    if request.method == 'POST':
        form_valid = True
        username = request.form['username']

        password = request.form['password']
        is_admin = bool(request.form.get('is_admin'))

        if form_valid:
            try:
                otp_secret = None
{'fraction_non_alphanumeric': 0.10548054011119937, 'fraction_numerical': 0.003335980937251787, 'mean_word_length': 2.6538015089959375, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 4, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6404260', 'n_tokens_mistral': 3715, 'n_tokens_neox': 3554, 'n_words': 923}