diff --git a/files/copr/fe/hotfix/__init__.py b/files/copr/fe/hotfix/__init__.py deleted file mode 100644 index 23a2202dda..0000000000 --- a/files/copr/fe/hotfix/__init__.py +++ /dev/null @@ -1,54 +0,0 @@ -from __future__ import with_statement - -import os -import flask - -from flask.ext.sqlalchemy import SQLAlchemy -from flask.ext.openid import OpenID -from flask.ext.whooshee import Whooshee - -app = flask.Flask(__name__) - -if "COPRS_ENVIRON_PRODUCTION" in os.environ: - app.config.from_object("coprs.config.ProductionConfig") -elif "COPRS_ENVIRON_UNITTEST" in os.environ: - app.config.from_object("coprs.config.UnitTestConfig") -else: - app.config.from_object("coprs.config.DevelopmentConfig") -if os.environ.get("COPR_CONFIG"): - app.config.from_envvar("COPR_CONFIG") -else: - app.config.from_pyfile("/etc/copr/copr.conf", silent=True) - - -oid = OpenID(app, safe_roots=[], app.config["OPENID_STORE"]) -db = SQLAlchemy(app) -whooshee = Whooshee(app) - -import coprs.filters -import coprs.log -import coprs.models -import coprs.whoosheers - -from coprs.views import admin_ns -from coprs.views.admin_ns import admin_general -from coprs.views import api_ns -from coprs.views.api_ns import api_general -from coprs.views import coprs_ns -from coprs.views.coprs_ns import coprs_builds -from coprs.views.coprs_ns import coprs_general -from coprs.views.coprs_ns import coprs_chroots -from coprs.views import backend_ns -from coprs.views.backend_ns import backend_general -from coprs.views import misc -from coprs.views import status_ns -from coprs.views.status_ns import status_general - -app.register_blueprint(api_ns.api_ns) -app.register_blueprint(admin_ns.admin_ns) -app.register_blueprint(coprs_ns.coprs_ns) -app.register_blueprint(misc.misc) -app.register_blueprint(backend_ns.backend_ns) -app.register_blueprint(status_ns.status_ns) - -app.add_url_rule("/", "coprs_ns.coprs_show", coprs_general.coprs_show) diff --git a/files/copr/fe/hotfix/misc.py b/files/copr/fe/hotfix/misc.py new file mode 100644 index 0000000000..5823347e28 --- /dev/null +++ b/files/copr/fe/hotfix/misc.py @@ -0,0 +1,153 @@ +import base64 +import datetime +import functools + +import flask + +from coprs import app +from coprs import db +from coprs import helpers +from coprs import models +from coprs import oid + + +@app.before_request +def lookup_current_user(): + flask.g.user = None + if "openid" in flask.session: + flask.g.user = models.User.query.filter( + models.User.openid_name == flask.session["openid"]).first() + + +@app.errorhandler(404) +def page_not_found(message): + return flask.render_template("404.html", message=message), 404 + + +misc = flask.Blueprint("misc", __name__) + + +@misc.route("/login/", methods=["GET"]) +@oid.loginhandler +def login(): + if flask.g.user is not None: + return flask.redirect("https://copr.fedoraproject.org") + else: + return oid.try_login("https://id.fedoraproject.org/", + ask_for=["email", "timezone"]) + + +@oid.after_login +def create_or_login(resp): + flask.session["openid"] = resp.identity_url + fasusername = resp.identity_url.replace( + ".id.fedoraproject.org/", "").replace("http://", "") + + # kidding me.. or not + if fasusername and ((app.config["USE_ALLOWED_USERS"] + and fasusername in app.config["ALLOWED_USERS"]) + or not app.config["USE_ALLOWED_USERS"]): + + user = models.User.query.filter( + models.User.openid_name == resp.identity_url).first() + if not user: # create if not created already + expiration_date_token = datetime.date.today() + \ + datetime.timedelta( + days=flask.current_app.config["API_TOKEN_EXPIRATION"]) + + copr64 = base64.b64encode("copr") + "##" + user = models.User(openid_name=resp.identity_url, mail=resp.email, + timezone=resp.timezone, + api_login=copr64 + helpers.generate_api_token( + app.config["API_TOKEN_LENGTH"] - len(copr64)), + api_token=helpers.generate_api_token( + app.config["API_TOKEN_LENGTH"]), + api_token_expiration=expiration_date_token) + else: + user.mail = resp.email + user.timezone = resp.timezone + + db.session.add(user) + db.session.commit() + flask.flash(u"Welcome, {0}".format(user.name)) + flask.g.user = user + + if flask.request.url_root == oid.get_next_url(): + return flask.redirect(flask.url_for("coprs_ns.coprs_by_owner", + username=user.name)) + return flask.redirect("https://copr.fedoraproject.org") + else: + flask.flash("User '{0}' is not allowed".format(user.name)) + return flask.redirect("https://copr.fedoraproject.org") + + +@misc.route("/logout/") +def logout(): + flask.session.pop("openid", None) + flask.flash(u"You were signed out") + return flask.redirect("https://copr.fedoraproject.org") + + +def api_login_required(f): + @functools.wraps(f) + def decorated_function(*args, **kwargs): + token = None + username = None + if "Authorization" in flask.request.headers: + base64string = flask.request.headers["Authorization"] + base64string = base64string.split()[1].strip() + userstring = base64.b64decode(base64string) + (username, token) = userstring.split(":") + token_auth = False + if token and username: + user = models.User.query.filter( + models.User.api_login == username).first() + if (user and user.api_token == token and + user.api_token_expiration >= datetime.date.today()): + + token_auth = True + flask.g.user = user + if not token_auth: + output = {"output": "notok", "error": "Login invalid/expired"} + jsonout = flask.jsonify(output) + jsonout.status_code = 500 + return jsonout + return f(*args, **kwargs) + return decorated_function + + +def login_required(role=helpers.RoleEnum("user")): + def view_wrapper(f): + @functools.wraps(f) + def decorated_function(*args, **kwargs): + if flask.g.user is None: + return flask.redirect(flask.url_for("misc.login", + next=flask.request.url)) + + if role == helpers.RoleEnum("admin") and not flask.g.user.admin: + flask.flash("You are not allowed to access admin section.") + return flask.redirect(flask.url_for("coprs_ns.coprs_show")) + + return f(*args, **kwargs) + return decorated_function + # hack: if login_required is used without params, the "role" parameter + # is in fact the decorated function, so we need to return + # the wrapped function, not the wrapper + # proper solution would be to use login_required() with parentheses + # everywhere, even if they"re empty - TODO + if callable(role): + return view_wrapper(role) + else: + return view_wrapper + + +# backend authentication +def backend_authenticated(f): + @functools.wraps(f) + def decorated_function(*args, **kwargs): + auth = flask.request.authorization + if not auth or auth.password != app.config["BACKEND_PASSWORD"]: + return "You have to provide the correct password", 401 + + return f(*args, **kwargs) + return decorated_function diff --git a/playbooks/hosts/copr-fe.cloud.fedoraproject.org.yml b/playbooks/hosts/copr-fe.cloud.fedoraproject.org.yml index ded159b90a..ca88dfac7e 100644 --- a/playbooks/hosts/copr-fe.cloud.fedoraproject.org.yml +++ b/playbooks/hosts/copr-fe.cloud.fedoraproject.org.yml @@ -61,8 +61,8 @@ - name: HOTFIX install a patch to mitigate the Covert Redirect vuln copy: > - src="{{ files }}/copr/fe/hotfix/__init__.py" - dest=/usr/share/copr/coprs_frontend/coprs/__init__.py + src="{{ files }}/copr/fe/hotfix/misc.py" + dest=/usr/share/copr/coprs_frontend/coprs/views/misc.py notify: - restart httpd tags: