|
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)
|