Blame pagure/flask_app.py

Pierre-Yves Chibon b130e5
# -*- coding: utf-8 -*-
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
"""
Pierre-Yves Chibon a04944
 (c) 2014-2018 - Copyright Red Hat Inc
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
 Authors:
Pierre-Yves Chibon b130e5
   Pierre-Yves Chibon <pingou@pingoured.fr></pingou@pingoured.fr>
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
"""
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon 67d1cc
from __future__ import unicode_literals, absolute_import
Aurélien Bompard dcf6f6
Pierre-Yves Chibon b130e5
import datetime
Pierre-Yves Chibon b130e5
import gc
Pierre-Yves Chibon b130e5
import logging
Pierre-Yves Chibon 7694ed
import string
Pierre-Yves Chibon b130e5
import time
Pierre-Yves Chibon b130e5
import os
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
import flask
Pierre-Yves Chibon b130e5
import pygit2
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
import pagure.doc_utils
Pierre-Yves Chibon b130e5
import pagure.exceptions
Pierre-Yves Chibon b130e5
import pagure.forms
Pierre-Yves Chibon b130e5
import pagure.lib.git
Pierre-Yves Chibon 930073
import pagure.lib.query
Pierre-Yves Chibon b130e5
import pagure.login_forms
Pierre-Yves Chibon b130e5
import pagure.mail_logging
Pierre-Yves Chibon b130e5
import pagure.proxy
Pierre-Yves Chibon b130e5
import pagure.utils
Pierre-Yves Chibon b130e5
from pagure.config import config as pagure_config
Pierre-Yves Chibon b130e5
from pagure.utils import get_repo_path
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon 9c2953
if os.environ.get("PAGURE_PERFREPO"):
Pierre-Yves Chibon b130e5
    import pagure.perfrepo as perfrepo
Pierre-Yves Chibon b130e5
else:
Pierre-Yves Chibon b130e5
    perfrepo = None
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
logger = logging.getLogger(__name__)
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
REDIS = None
Pierre-Yves Chibon 9c2953
if (
Pierre-Yves Chibon 9c2953
    pagure_config["EVENTSOURCE_SOURCE"]
Pierre-Yves Chibon 9c2953
    or pagure_config["WEBHOOK"]
Pierre-Yves Chibon 9c2953
    or pagure_config.get("PAGURE_CI_SERVICES")
Pierre-Yves Chibon 9c2953
):
Pierre-Yves Chibon 930073
    pagure.lib.query.set_redis(
Pierre-Yves Chibon 9c2953
        host=pagure_config["REDIS_HOST"],
Pierre-Yves Chibon 9c2953
        port=pagure_config["REDIS_PORT"],
Pierre-Yves Chibon 9c2953
        dbname=pagure_config["REDIS_DB"],
Pierre-Yves Chibon b130e5
    )
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon 9c2953
if pagure_config.get("PAGURE_CI_SERVICES"):
Pierre-Yves Chibon 930073
    pagure.lib.query.set_pagure_ci(pagure_config["PAGURE_CI_SERVICES"])
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
def create_app(config=None):
Pierre-Yves Chibon b130e5
    """ Create the flask application. """
Pierre-Yves Chibon e9046e
    app = flask.Flask(__name__)
Pierre-Yves Chibon b130e5
    app.config = pagure_config
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
    if config:
Pierre-Yves Chibon b130e5
        app.config.update(config)
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon 9c2953
    if app.config.get("SESSION_TYPE", None) is not None:
Slavek Kabrda 9c176a
        import flask_session
Pierre-Yves Chibon 9c2953
Slavek Kabrda 9c176a
        flask_session.Session(app)
Slavek Kabrda 9c176a
Pierre-Yves Chibon 24747e
    pagure.utils.set_up_logging(app=app)
Pierre-Yves Chibon b130e5
Patrick Uiterwijk 8ac24e
    @app.errorhandler(500)
Patrick Uiterwijk 8ac24e
    def fatal_error(error):  # pragma: no cover
Patrick Uiterwijk 8ac24e
        """500 Fatal Error page"""
Patrick Uiterwijk 95307d
        logger.exception("Error while processing request")
Patrick Uiterwijk 8ac24e
        return flask.render_template("fatal_error.html", error=error), 500
Patrick Uiterwijk 8ac24e
Pierre-Yves Chibon b130e5
    app.jinja_env.trim_blocks = True
Pierre-Yves Chibon b130e5
    app.jinja_env.lstrip_blocks = True
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
    if perfrepo:
Pierre-Yves Chibon b130e5
        # Do this as early as possible.
Pierre-Yves Chibon b130e5
        # We want the perfrepo before_request to be the very first thing
Pierre-Yves Chibon b130e5
        # to be run, so that we can properly setup the stats before the
Pierre-Yves Chibon b130e5
        # request.
Pierre-Yves Chibon b130e5
        app.before_request(perfrepo.reset_stats)
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon 9c2953
    auth = pagure_config.get("PAGURE_AUTH", None)
Pierre-Yves Chibon 9c2953
    if auth in ["fas", "openid"]:
Pierre-Yves Chibon b130e5
        # Only import and set flask_fas_openid if it is needed
Pierre-Yves Chibon b130e5
        from pagure.ui.fas_login import FAS
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon b130e5
        FAS.init_app(app)
Pierre-Yves Chibon 9c2953
    elif auth == "oidc":
Pierre-Yves Chibon b130e5
        # Only import and set flask_fas_openid if it is needed
Pierre-Yves Chibon b130e5
        from pagure.ui.oidc_login import oidc, fas_user_from_oidc
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon b130e5
        oidc.init_app(app)
Pierre-Yves Chibon b130e5
        app.before_request(fas_user_from_oidc)
Pierre-Yves Chibon 9c2953
    if auth == "local":
Pierre-Yves Chibon 44158d
        # Only import the login controller if the app is set up for local login
Pierre-Yves Chibon 44158d
        import pagure.ui.login as login
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon 44158d
        app.before_request(login._check_session_cookie)
Pierre-Yves Chibon 44158d
        app.after_request(login._send_session_cookie)
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
    # Support proxy
Pierre-Yves Chibon b130e5
    app.wsgi_app = pagure.proxy.ReverseProxied(app.wsgi_app)
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
    # Back port 'equalto' to older version of jinja2
Pierre-Yves Chibon b130e5
    app.jinja_env.tests.setdefault(
Pierre-Yves Chibon 9c2953
        "equalto", lambda value, other: value == other
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
    # Import the application
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
    from pagure.api import API  # noqa: E402
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon b130e5
    app.register_blueprint(API)
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
    from pagure.ui import UI_NS  # noqa: E402
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon b130e5
    app.register_blueprint(UI_NS)
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
    from pagure.internal import PV  # noqa: E402
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon b130e5
    app.register_blueprint(PV)
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b3b86d
    # Import 3rd party blueprints
Pierre-Yves Chibon 97dcb0
    plugin_config = flask.config.Config("")
Pierre-Yves Chibon b3b86d
    if "PAGURE_PLUGIN" in os.environ:
Pierre-Yves Chibon b3b86d
        plugin_config.from_envvar("PAGURE_PLUGIN")
Pierre-Yves Chibon 97dcb0
    for blueprint in plugin_config.get("PLUGINS") or []:
Pierre-Yves Chibon 97dcb0
        logger.info("Loading blueprint: %s", blueprint.name)
Pierre-Yves Chibon b3b86d
        app.register_blueprint(blueprint)
Pierre-Yves Chibon b3b86d
Pierre-Yves Chibon 9c2953
    themename = pagure_config.get("THEME", "default")
Pierre-Yves Chibon 2ebb02
    here = os.path.abspath(
Patrick Uiterwijk 3f97f6
        os.path.join(os.path.dirname(os.path.abspath(__file__)))
Patrick Uiterwijk 3f97f6
    )
Ryan Lerch a20e96
    themeblueprint = flask.Blueprint(
Pierre-Yves Chibon 9c2953
        "theme",
Pierre-Yves Chibon 9c2953
        __name__,
Pierre-Yves Chibon 9c2953
        static_url_path="/theme/static",
Pierre-Yves Chibon 2ebb02
        static_folder=os.path.join(here, "themes", themename, "static"),
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 2ebb02
    # Jinja can be told to look for templates in different folders
Pierre-Yves Chibon 2ebb02
    # That's what we do here
Pierre-Yves Chibon 2ebb02
    template_folders = os.path.join(
Pierre-Yves Chibon 2ebb02
        app.root_path,
Pierre-Yves Chibon 2ebb02
        app.template_folder,
Patrick Uiterwijk 3f97f6
        os.path.join(here, "themes", themename, "templates"),
Patrick Uiterwijk 3f97f6
    )
Pierre-Yves Chibon 2ebb02
    import jinja2
Patrick Uiterwijk 3f97f6
Pierre-Yves Chibon 2ebb02
    # Jinja looks for the template in the order of the folders specified
Pierre-Yves Chibon 2ebb02
    templ_loaders = [
Pierre-Yves Chibon 2ebb02
        jinja2.FileSystemLoader(template_folders),
Pierre-Yves Chibon 2ebb02
        app.jinja_loader,
Pierre-Yves Chibon 2ebb02
    ]
Pierre-Yves Chibon 2ebb02
    app.jinja_loader = jinja2.ChoiceLoader(templ_loaders)
Ryan Lerch a20e96
    app.register_blueprint(themeblueprint)
Ryan Lerch a20e96
Pierre-Yves Chibon b130e5
    app.before_request(set_request)
Pierre-Yves Chibon d5a31d
    app.after_request(after_request)
Patrick Uiterwijk 5ff6c0
    app.teardown_request(end_request)
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
    if perfrepo:
Pierre-Yves Chibon b130e5
        # Do this at the very end, so that this after_request comes last.
Pierre-Yves Chibon b130e5
        app.after_request(perfrepo.print_stats)
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon 9c2953
    app.add_url_rule("/login/", view_func=auth_login, methods=["GET", "POST"])
Pierre-Yves Chibon 9c2953
    app.add_url_rule("/logout/", view_func=auth_logout)
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
    return app
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
def generate_user_key_files():
Pierre-Yves Chibon b130e5
    """ Regenerate the key files used by gitolite.
Pierre-Yves Chibon b130e5
    """
Pierre-Yves Chibon 9c2953
    gitolite_home = pagure_config.get("GITOLITE_HOME", None)
Pierre-Yves Chibon b130e5
    if gitolite_home:
Pierre-Yves Chibon 930073
        users = pagure.lib.query.search_user(flask.g.session)
Pierre-Yves Chibon b130e5
        for user in users:
Pierre-Yves Chibon 930073
            pagure.lib.query.update_user_ssh(
Pierre-Yves Chibon 9c2953
                flask.g.session,
Pierre-Yves Chibon 9c2953
                user,
Patrick Uiterwijk 9b237b
                None,
Pierre-Yves Chibon 9c2953
                pagure_config.get("GITOLITE_KEYDIR", None),
Slavek Kabrda 5ab53d
                update_only=True,
Slavek Kabrda 5ab53d
            )
Pierre-Yves Chibon b130e5
    pagure.lib.git.generate_gitolite_acls(project=None)
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
def admin_session_timedout():
Pierre-Yves Chibon b130e5
    """ Check if the current user has been authenticated for more than what
Pierre-Yves Chibon b130e5
    is allowed (defaults to 15 minutes).
Pierre-Yves Chibon b130e5
    If it is the case, the user is logged out and the method returns True,
Pierre-Yves Chibon b130e5
    otherwise it returns False.
Pierre-Yves Chibon b130e5
    """
Pierre-Yves Chibon b130e5
    timedout = False
Pierre-Yves Chibon b130e5
    if not pagure.utils.authenticated():
Pierre-Yves Chibon b130e5
        return True
Pierre-Yves Chibon b130e5
    login_time = flask.g.fas_user.login_time
Pierre-Yves Chibon b130e5
    # This is because flask_fas_openid will store this as a posix timestamp
Pierre-Yves Chibon b130e5
    if not isinstance(login_time, datetime.datetime):
Pierre-Yves Chibon b130e5
        login_time = datetime.datetime.utcfromtimestamp(login_time)
Pierre-Yves Chibon 9c2953
    if (datetime.datetime.utcnow() - login_time) > pagure_config.get(
Pierre-Yves Chibon 9c2953
        "ADMIN_SESSION_LIFETIME", datetime.timedelta(minutes=15)
Pierre-Yves Chibon 9c2953
    ):
Pierre-Yves Chibon b130e5
        timedout = True
Pierre-Yves Chibon b130e5
        logout()
Pierre-Yves Chibon b130e5
    return timedout
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
def logout():
Pierre-Yves Chibon b130e5
    """ Log out the user currently logged in in the application
Pierre-Yves Chibon b130e5
    """
Pierre-Yves Chibon 9c2953
    auth = pagure_config.get("PAGURE_AUTH", None)
Pierre-Yves Chibon 9c2953
    if auth in ["fas", "openid"]:
Pierre-Yves Chibon 9c2953
        if hasattr(flask.g, "fas_user") and flask.g.fas_user is not None:
Pierre-Yves Chibon b130e5
            from pagure.ui.fas_login import FAS
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon b130e5
            FAS.logout()
Pierre-Yves Chibon 9c2953
    elif auth == "oidc":
Slavek Kabrda a17304
        from pagure.ui.oidc_login import oidc_logout
Pierre-Yves Chibon 9c2953
Slavek Kabrda a17304
        oidc_logout()
Pierre-Yves Chibon 9c2953
    elif auth == "local":
Pierre-Yves Chibon b130e5
        import pagure.ui.login as login
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon b130e5
        login.logout()
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
def set_request():
Pierre-Yves Chibon b130e5
    """ Prepare every request. """
Pierre-Yves Chibon b130e5
    flask.session.permanent = True
Pierre-Yves Chibon 9c2953
    if not hasattr(flask.g, "session") or not flask.g.session:
Pierre-Yves Chibon cf98be
        flask.g.session = pagure.lib.model_base.create_session(
Pierre-Yves Chibon 9c2953
            flask.current_app.config["DB_URL"]
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon 012be9
    flask.g.main_app = flask.current_app
Pierre-Yves Chibon b130e5
    flask.g.version = pagure.__version__
Pierre-Yves Chibon bf0132
    flask.g.confirmationform = pagure.forms.ConfirmationForm()
Pierre-Yves Chibon 7694ed
    flask.g.nonce = pagure.lib.login.id_generator(
Pierre-Yves Chibon 7694ed
        size=25, chars=string.ascii_letters + string.digits
Pierre-Yves Chibon 7694ed
    )
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon cd4df4
    flask.g.issues_enabled = pagure_config.get("ENABLE_TICKETS", True)
Pierre-Yves Chibon cd4df4
Pierre-Yves Chibon b130e5
    # The API namespace has its own way of getting repo and username and
Pierre-Yves Chibon b130e5
    # of handling errors
Pierre-Yves Chibon 9c2953
    if flask.request.blueprint == "api_ns":
Pierre-Yves Chibon b130e5
        return
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
    flask.g.forkbuttonform = None
Pierre-Yves Chibon b130e5
    if pagure.utils.authenticated():
Pierre-Yves Chibon b130e5
        flask.g.forkbuttonform = pagure.forms.ConfirmationForm()
Pierre-Yves Chibon b130e5
Patrick Uiterwijk ce6d6d
        # Force logout if current session started before users'
Patrick Uiterwijk ce6d6d
        # refuse_sessions_before
Patrick Uiterwijk ce6d6d
        login_time = flask.g.fas_user.login_time
Patrick Uiterwijk ce6d6d
        # This is because flask_fas_openid will store this as a posix timestamp
Patrick Uiterwijk ce6d6d
        if not isinstance(login_time, datetime.datetime):
Patrick Uiterwijk ce6d6d
            login_time = datetime.datetime.utcfromtimestamp(login_time)
Patrick Uiterwijk ce6d6d
        user = _get_user(username=flask.g.fas_user.username)
Pierre-Yves Chibon 9c2953
        if (
Pierre-Yves Chibon 9c2953
            user.refuse_sessions_before
Pierre-Yves Chibon 9c2953
            and login_time < user.refuse_sessions_before
Pierre-Yves Chibon 9c2953
        ):
Patrick Uiterwijk ce6d6d
            logout()
Pierre-Yves Chibon 9c2953
            return flask.redirect(flask.url_for("ui_ns.index"))
Patrick Uiterwijk ce6d6d
Pierre-Yves Chibon 9c2953
    flask.g.justlogedout = flask.session.get("_justloggedout", False)
Pierre-Yves Chibon b130e5
    if flask.g.justlogedout:
Pierre-Yves Chibon 9c2953
        flask.session["_justloggedout"] = None
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
    flask.g.new_user = False
Pierre-Yves Chibon 9c2953
    if flask.session.get("_new_user"):
Pierre-Yves Chibon b130e5
        flask.g.new_user = True
Pierre-Yves Chibon 9c2953
        flask.session["_new_user"] = False
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon de8a0a
    flask.g.authenticated = pagure.utils.authenticated()
Pierre-Yves Chibon b130e5
    flask.g.admin = pagure.utils.is_admin()
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
    # Retrieve the variables in the URL
Pierre-Yves Chibon b130e5
    args = flask.request.view_args or {}
Pierre-Yves Chibon b130e5
    # Check if there is a `repo` and an `username`
Pierre-Yves Chibon 9c2953
    repo = args.get("repo")
Pierre-Yves Chibon 9c2953
    username = args.get("username")
Pierre-Yves Chibon 9c2953
    namespace = args.get("namespace")
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
    # If there isn't a `repo` in the URL path, or if there is but the
Pierre-Yves Chibon b130e5
    # endpoint called is part of the API, just don't do anything
Pierre-Yves Chibon b130e5
    if repo:
Pierre-Yves Chibon 930073
        flask.g.repo = pagure.lib.query.get_authorized_project(
Pierre-Yves Chibon 9c2953
            flask.g.session, repo, user=username, namespace=namespace
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon de8a0a
        if flask.g.authenticated:
Pierre-Yves Chibon 930073
            flask.g.repo_forked = pagure.lib.query.get_authorized_project(
Pierre-Yves Chibon 9c2953
                flask.g.session,
Pierre-Yves Chibon 9c2953
                repo,
Pierre-Yves Chibon b130e5
                user=flask.g.fas_user.username,
Pierre-Yves Chibon 9c2953
                namespace=namespace,
Pierre-Yves Chibon 9c2953
            )
Pierre-Yves Chibon 930073
            flask.g.repo_starred = pagure.lib.query.has_starred(
Pierre-Yves Chibon 9c2953
                flask.g.session, flask.g.repo, user=flask.g.fas_user.username
Pierre-Yves Chibon b130e5
            )
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon 4cfa1e
            # Block all POST request from blocked users
Pierre-Yves Chibon 4cfa1e
            if flask.g.repo and flask.request.method != "GET":
Pierre-Yves Chibon 4cfa1e
                if flask.g.fas_user.username in flask.g.repo.block_users:
Pierre-Yves Chibon c6cc5c
                    flask.abort(
Pierre-Yves Chibon c6cc5c
                        403,
Pierre-Yves Chibon c6cc5c
                        description="You have been blocked from this project",
Pierre-Yves Chibon c6cc5c
                    )
Pierre-Yves Chibon 4cfa1e
Pierre-Yves Chibon 9c2953
        if (
Pierre-Yves Chibon 9c2953
            not flask.g.repo
Pierre-Yves Chibon 9c2953
            and namespace
Pierre-Yves Chibon 9c2953
            and pagure_config.get("OLD_VIEW_COMMIT_ENABLED", False)
Pierre-Yves Chibon 9c2953
            and len(repo) == 40
Pierre-Yves Chibon 9c2953
        ):
Pierre-Yves Chibon 9c2953
            return flask.redirect(
Pierre-Yves Chibon 9c2953
                flask.url_for(
Pierre-Yves Chibon 9c2953
                    "ui_ns.view_commit",
Pierre-Yves Chibon 9c2953
                    repo=namespace,
Pierre-Yves Chibon 9c2953
                    commitid=repo,
Pierre-Yves Chibon 9c2953
                    username=username,
Pierre-Yves Chibon 9c2953
                    namespace=None,
Pierre-Yves Chibon 9c2953
                )
Pierre-Yves Chibon 9c2953
            )
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
        if flask.g.repo is None:
Pierre-Yves Chibon c6cc5c
            flask.abort(404, description="Project not found")
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon cd4df4
        # If issues are not globally enabled, there is no point in continuing
Pierre-Yves Chibon cd4df4
        if flask.g.issues_enabled:
Pierre-Yves Chibon cd4df4
Pierre-Yves Chibon cd4df4
            ticket_namespaces = pagure_config.get("ENABLE_TICKETS_NAMESPACE")
Pierre-Yves Chibon cd4df4
Pierre-Yves Chibon cd4df4
            if ticket_namespaces and flask.g.repo.namespace:
Pierre-Yves Chibon cd4df4
                if flask.g.repo.namespace in (ticket_namespaces or []):
Pierre-Yves Chibon cd4df4
                    # If the namespace is in the allowed list
Pierre-Yves Chibon cd4df4
                    # issues are enabled
Pierre-Yves Chibon cd4df4
                    flask.g.issues_enabled = True
Pierre-Yves Chibon cd4df4
                else:
Pierre-Yves Chibon cd4df4
                    # If the namespace isn't in the list of namespaces
Pierre-Yves Chibon cd4df4
                    # issues are disabled
Pierre-Yves Chibon cd4df4
                    flask.g.issues_enabled = False
Pierre-Yves Chibon cd4df4
Pierre-Yves Chibon cd4df4
            flask.g.issues_project_disabled = False
Pierre-Yves Chibon cd4df4
            if not flask.g.repo.settings.get("issue_tracker", True):
Pierre-Yves Chibon cd4df4
                # If the project specifically disabled its issue tracker,
Pierre-Yves Chibon cd4df4
                # disable issues
Pierre-Yves Chibon cd4df4
                flask.g.issues_project_disabled = True
Pierre-Yves Chibon cd4df4
                flask.g.issues_enabled = False
Pierre-Yves Chibon cd4df4
Pierre-Yves Chibon b130e5
        flask.g.reponame = get_repo_path(flask.g.repo)
Pierre-Yves Chibon b130e5
        flask.g.repo_obj = pygit2.Repository(flask.g.reponame)
Pierre-Yves Chibon b130e5
        flask.g.repo_admin = pagure.utils.is_repo_admin(flask.g.repo)
Pierre-Yves Chibon b130e5
        flask.g.repo_committer = pagure.utils.is_repo_committer(flask.g.repo)
Pierre-Yves Chibon b130e5
        flask.g.repo_user = pagure.utils.is_repo_user(flask.g.repo)
Pierre-Yves Chibon b130e5
        flask.g.branches = sorted(flask.g.repo_obj.listall_branches())
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
        repouser = flask.g.repo.user.user if flask.g.repo.is_fork else None
Pierre-Yves Chibon b130e5
        fas_user = flask.g.fas_user if pagure.utils.authenticated() else None
Pierre-Yves Chibon 930073
        flask.g.repo_watch_levels = pagure.lib.query.get_watch_level_on_repo(
Pierre-Yves Chibon 9c2953
            flask.g.session,
Pierre-Yves Chibon 9c2953
            fas_user,
Pierre-Yves Chibon 9c2953
            flask.g.repo.name,
Pierre-Yves Chibon 9c2953
            repouser=repouser,
Pierre-Yves Chibon 9c2953
            namespace=namespace,
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon 9c2953
    items_per_page = pagure_config["ITEM_PER_PAGE"]
Pierre-Yves Chibon b130e5
    flask.g.offset = 0
Pierre-Yves Chibon b130e5
    flask.g.page = 1
Pierre-Yves Chibon b130e5
    flask.g.limit = items_per_page
Pierre-Yves Chibon 9c2953
    page = flask.request.args.get("page")
Pierre-Yves Chibon 9c2953
    limit = flask.request.args.get("n")
Pierre-Yves Chibon b130e5
    if limit:
Pierre-Yves Chibon b130e5
        try:
Pierre-Yves Chibon b130e5
            limit = int(limit)
Pierre-Yves Chibon b130e5
        except ValueError:
Pierre-Yves Chibon b130e5
            limit = 10
Pierre-Yves Chibon b130e5
        if limit > 500 or limit <= 0:
Pierre-Yves Chibon b130e5
            limit = items_per_page
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
        flask.g.limit = limit
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
    if page:
Pierre-Yves Chibon b130e5
        try:
Pierre-Yves Chibon b130e5
            page = abs(int(page))
Pierre-Yves Chibon b130e5
        except ValueError:
Pierre-Yves Chibon b130e5
            page = 1
Pierre-Yves Chibon b130e5
        if page <= 0:
Pierre-Yves Chibon b130e5
            page = 1
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
        flask.g.page = page
Pierre-Yves Chibon b130e5
        flask.g.offset = (page - 1) * flask.g.limit
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
def auth_login():  # pragma: no cover
Pierre-Yves Chibon b130e5
    """ Method to log into the application using FAS OpenID. """
Pierre-Yves Chibon 9c2953
    return_point = flask.url_for("ui_ns.index")
Pierre-Yves Chibon 9c2953
    if "next" in flask.request.args:
Pierre-Yves Chibon 9c2953
        if pagure.utils.is_safe_url(flask.request.args["next"]):
Pierre-Yves Chibon 9c2953
            return_point = flask.request.args["next"]
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
    authenticated = pagure.utils.authenticated()
Pierre-Yves Chibon 9c2953
    auth = pagure_config.get("PAGURE_AUTH", None)
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon 9c2953
    if not authenticated and auth == "oidc":
Pierre-Yves Chibon b130e5
        from pagure.ui.oidc_login import oidc, fas_user_from_oidc, set_user
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon b130e5
        # If oidc is used and user hits this endpoint, it will redirect
Pierre-Yves Chibon b130e5
        # to IdP with destination=<pagure>/login?next=<location></location></pagure>
Pierre-Yves Chibon b130e5
        # After confirming user identity, the IdP will redirect user here
Pierre-Yves Chibon b130e5
        # again, but this time oidc.user_loggedin will be True and thus
Pierre-Yves Chibon b130e5
        # execution will go through the else clause, making the Pagure
Pierre-Yves Chibon b130e5
        # authentication machinery pick the user up
Pierre-Yves Chibon b130e5
        if not oidc.user_loggedin:
Pierre-Yves Chibon b130e5
            return oidc.redirect_to_auth_server(flask.request.url)
Pierre-Yves Chibon b130e5
        else:
Pierre-Yves Chibon 9c2953
            flask.session["oidc_logintime"] = time.time()
Pierre-Yves Chibon b130e5
            fas_user_from_oidc()
Pierre-Yves Chibon b130e5
            authenticated = pagure.utils.authenticated()
Pierre-Yves Chibon b130e5
            set_user()
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
    if authenticated:
Pierre-Yves Chibon b130e5
        return flask.redirect(return_point)
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon 9c2953
    admins = pagure_config["ADMIN_GROUP"]
Pierre-Yves Chibon c35959
    if admins:
Pierre-Yves Chibon c35959
        if isinstance(admins, list):
Pierre-Yves Chibon c35959
            admins = set(admins)
Pierre-Yves Chibon c35959
        else:  # pragma: no cover
Pierre-Yves Chibon c35959
            admins = set([admins])
Pierre-Yves Chibon c35959
    else:
Pierre-Yves Chibon c35959
        admins = set()
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon 9c2953
    if auth in ["fas", "openid"]:
Pierre-Yves Chibon b130e5
        from pagure.ui.fas_login import FAS
Pierre-Yves Chibon 9c2953
Pierre-Yves Chibon b130e5
        groups = set()
Pierre-Yves Chibon 9c2953
        if not pagure_config.get("ENABLE_GROUP_MNGT", False):
Pierre-Yves Chibon b130e5
            groups = [
Pierre-Yves Chibon b130e5
                group.group_name
Pierre-Yves Chibon 930073
                for group in pagure.lib.query.search_groups(
Pierre-Yves Chibon 9c2953
                    flask.g.session, group_type="user"
Pierre-Yves Chibon 9c2953
                )
Pierre-Yves Chibon b130e5
            ]
Pierre-Yves Chibon b130e5
        groups = set(groups).union(admins)
Pierre-Yves Chibon 9c2953
        ext_committer = set(pagure_config.get("EXTERNAL_COMMITTER", {}))
Pierre-Yves Chibon b130e5
        groups = set(groups).union(ext_committer)
Julen Landa Alustiza 6ec259
        flask.g.unsafe_javascript = True
Pierre-Yves Chibon b130e5
        return FAS.login(return_url=return_point, groups=groups)
Pierre-Yves Chibon 9c2953
    elif auth == "local":
Pierre-Yves Chibon b130e5
        form = pagure.login_forms.LoginForm()
Pierre-Yves Chibon b130e5
        return flask.render_template(
Pierre-Yves Chibon 9c2953
            "login/login.html", next_url=return_point, form=form
Pierre-Yves Chibon b130e5
        )
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
def auth_logout():  # pragma: no cover
Pierre-Yves Chibon b130e5
    """ Method to log out from the application. """
Pierre-Yves Chibon 9c2953
    return_point = flask.url_for("ui_ns.index")
Pierre-Yves Chibon 9c2953
    if "next" in flask.request.args:
Pierre-Yves Chibon 9c2953
        if pagure.utils.is_safe_url(flask.request.args["next"]):
Pierre-Yves Chibon 9c2953
            return_point = flask.request.args["next"]
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
    if not pagure.utils.authenticated():
Pierre-Yves Chibon b130e5
        return flask.redirect(return_point)
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
    logout()
Pierre-Yves Chibon b130e5
    flask.flash("You have been logged out")
Pierre-Yves Chibon 9c2953
    flask.session["_justloggedout"] = True
Pierre-Yves Chibon b130e5
    return flask.redirect(return_point)
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
Patrick Uiterwijk 5ff6c0
# pylint: disable=unused-argument
Patrick Uiterwijk 5ff6c0
def end_request(exception=None):
Pierre-Yves Chibon b130e5
    """ This method is called at the end of each request.
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
    Remove the DB session at the end of each request.
Pierre-Yves Chibon b130e5
    Runs a garbage collection to get rid of any open pygit2 handles.
Pierre-Yves Chibon b130e5
        Details: https://pagure.io/pagure/issue/2302
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon b130e5
    """
Pierre-Yves Chibon b130e5
    flask.g.session.remove()
Pierre-Yves Chibon b130e5
    gc.collect()
Pierre-Yves Chibon b130e5
Pierre-Yves Chibon a04944
Pierre-Yves Chibon d5a31d
def after_request(response):
Pierre-Yves Chibon d5a31d
    """ After request callback, adjust the headers returned """
Pierre-Yves Chibon d5a31d
    csp_headers = pagure_config["CSP_HEADERS"]
Pierre-Yves Chibon d5a31d
    try:
Julen Landa Alustiza 6ec259
        style_csp = "nonce-" + flask.g.nonce
Julen Landa Alustiza 6ec259
        script_csp = (
Julen Landa Alustiza 6ec259
            "unsafe-inline"
Julen Landa Alustiza 6ec259
            if "unsafe_javascript" in flask.g and flask.g.unsafe_javascript
Julen Landa Alustiza 6ec259
            else "nonce-" + flask.g.nonce
Julen Landa Alustiza 6ec259
        )
Julen Landa Alustiza 6ec259
        csp_headers = csp_headers.format(
Julen Landa Alustiza 6ec259
            nonce_script=script_csp, nonce_style=style_csp
Julen Landa Alustiza 6ec259
        )
Pierre-Yves Chibon d5a31d
    except (KeyError, IndexError):
Pierre-Yves Chibon d5a31d
        pass
Pierre-Yves Chibon f7ca8c
    response.headers.set(str("Content-Security-Policy"), csp_headers)
Pierre-Yves Chibon d5a31d
    return response
Pierre-Yves Chibon d5a31d
Pierre-Yves Chibon d5a31d
Pierre-Yves Chibon a04944
def _get_user(username):
Pierre-Yves Chibon a04944
    """ Check if user exists or not
Pierre-Yves Chibon a04944
    """
Pierre-Yves Chibon a04944
    try:
Pierre-Yves Chibon 930073
        return pagure.lib.query.get_user(flask.g.session, username)
Pierre-Yves Chibon a04944
    except pagure.exceptions.PagureException as e:
Pierre-Yves Chibon c6cc5c
        flask.abort(404, description="%s" % e)