~edwargix/git.sr.ht

fce89b045078d2a08bdd3a6bd949e641f3f52291 — Drew DeVault 6 years ago 22a8837
Switch to standard authentication
4 files changed, 51 insertions(+), 148 deletions(-)

M gitsrht/app.py
D gitsrht/blueprints/auth.py
M gitsrht/blueprints/manage.py
D gitsrht/decorators.py
M gitsrht/app.py => gitsrht/app.py +50 -50
@@ 1,61 1,61 @@
from flask import render_template, request, session
from flask_login import LoginManager, current_user
import urllib.parse
import locale

from srht.config import cfg, cfgi, load_config
from flask import session
from srht.flask import SrhtFlask
from srht.config import cfg, load_config
load_config("git")

from srht.database import DbSession
db = DbSession(cfg("sr.ht", "connection-string"))

from gitsrht.types import User
db.init()

from srht.flask import SrhtFlask
app = SrhtFlask("git", __name__)
app.secret_key = cfg("server", "secret-key")
login_manager = LoginManager()
login_manager.init_app(app)
import gitsrht.oauth

@login_manager.user_loader
def load_user(username):
    return User.query.filter(User.username == username).first()

login_manager.anonymous_user = lambda: None

try:
    locale.setlocale(locale.LC_ALL, 'en_US')
except:
    pass

meta_sr_ht = cfg("network", "meta")
meta_client_id = cfg("meta.sr.ht", "oauth-client-id")
builds_sr_ht = cfg("builds.sr.ht", "oauth-client-id")

def oauth_url(return_to):
    return "{}/oauth/authorize?client_id={}&scopes=profile,keys{}&state={}".format(
        meta_sr_ht, meta_client_id,
        "," + builds_sr_ht + "/jobs:write" if builds_sr_ht else "",
        urllib.parse.quote_plus(return_to))

from gitsrht.blueprints.api import api
from gitsrht.blueprints.auth import auth
from gitsrht.blueprints.public import public
from gitsrht.blueprints.manage import manage

app.register_blueprint(api)
app.register_blueprint(auth)
app.register_blueprint(public)
app.register_blueprint(manage)

@app.context_processor
def inject():
    notice = session.get("notice")
    if notice:
        del session["notice"]
    return {
        "oauth_url": oauth_url(request.full_path),
        "current_user": User.query.filter(User.id == current_user.id).first() \
                if current_user else None,
        "notice": notice
    }
class GitApp(SrhtFlask):
    def __init__(self):
        super().__init__("git", __name__)

        self.register_blueprint(api)
        self.register_blueprint(public)
        self.register_blueprint(manage)

        meta_client_id = cfg("meta.sr.ht", "oauth-client-id")
        meta_client_secret = cfg("meta.sr.ht", "oauth-client-secret")
        builds_client_id = cfg("builds.sr.ht", "oauth-client-id")
        self.configure_meta_auth(meta_client_id, meta_client_secret,
                base_scopes=["profile"] + ([
                    "{}/jobs:write".format(builds_client_id)
                ] if builds_client_id else []))

        @self.context_processor
        def inject():
            notice = session.get("notice")
            if notice:
                del session["notice"]
            return {
                "notice": notice
            }

        @self.login_manager.user_loader
        def user_loader(username):
            # TODO: Switch to a session token
            return User.query.filter(User.username == username).one_or_none()

    def lookup_or_register(self, exchange, profile, scopes):
        user = User.query.filter(User.username == profile["username"]).first()
        if not user:
            user = User()
            db.session.add(user)
        user.username = profile.get("username")
        user.email = profile.get("email")
        user.paid = profile.get("paid")
        user.oauth_token = exchange["token"]
        user.oauth_token_expires = exchange["expires"]
        user.oauth_token_scopes = scopes
        db.session.commit()
        return user

app = GitApp()

D gitsrht/blueprints/auth.py => gitsrht/blueprints/auth.py +0 -82
@@ 1,82 0,0 @@
from flask import Blueprint, request, render_template, redirect
from flask_login import login_user, logout_user
from sqlalchemy import or_
from srht.config import cfg
from srht.flask import DATE_FORMAT
from srht.oauth import OAuthScope
from srht.database import db
from gitsrht.types import User
from datetime import datetime
import urllib.parse
import requests

auth = Blueprint('auth', __name__)

meta_uri = cfg("network", "meta")
client_id = cfg("meta.sr.ht", "oauth-client-id")
client_secret = cfg("meta.sr.ht", "oauth-client-secret")

@auth.route("/oauth/callback")
def oauth_callback():
    error = request.args.get("error")
    if error:
        details = request.args.get("details")
        return render_template("oauth-error.html", details=details)
    exchange = request.args.get("exchange")
    scopes = request.args.get("scopes")
    state = request.args.get("state")
    _scopes = [OAuthScope(s) for s in scopes.split(",")]
    if not OAuthScope("profile:read") in _scopes or not OAuthScope("keys:read") in _scopes:
        return render_template("oauth-error.html",
            details="git.sr.ht requires profile and key access at a mininum to function correctly. " +
                "To use git.sr.ht, try again and do not untick these permissions.")
    if not exchange:
        return render_template("oauth-error.html",
            details="Expected an exchange token from meta.sr.ht. Something odd has happened, try again.")
    r = requests.post(meta_uri + "/oauth/exchange", json={
        "client_id": client_id,
        "client_secret": client_secret,
        "exchange": exchange,
    })
    if r.status_code != 200:
        return render_template("oauth-error.html",
            details="Error occured retrieving OAuth token. Try again.")
    json = r.json()
    token = json.get("token")
    expires = json.get("expires")
    if not token or not expires:
        return render_template("oauth-error.html",
            details="Error occured retrieving OAuth token. Try again.")
    expires = datetime.strptime(expires, DATE_FORMAT)

    r = requests.get(meta_uri + "/api/user/profile", headers={
        "Authorization": "token " + token
    })
    if r.status_code != 200:
        return render_template("oauth-error.html",
            details="Error occured retrieving account info. Try again.")
    
    json = r.json()
    user = User.query.filter(or_(User.oauth_token == token,
        User.username == json["username"])).first()
    if not user:
        user = User()
        db.session.add(user)
    user.username = json.get("username")
    user.email = json.get("email")
    user.paid = json.get("paid")
    user.oauth_token = token
    user.oauth_token_expires = expires
    user.oauth_token_scopes = scopes
    db.session.commit()

    login_user(user, remember=True)
    if not state or not state.startswith("/"):
        return redirect("/")
    else:
        return redirect(urllib.parse.unquote(state))

@auth.route("/logout")
def logout():
    logout_user()
    return redirect(request.headers.get("Referer") or "/")

M gitsrht/blueprints/manage.py => gitsrht/blueprints/manage.py +1 -1
@@ 3,10 3,10 @@ from flask import redirect, session, url_for
from flask_login import current_user
from srht.config import cfg
from srht.database import db
from srht.flask import loginrequired
from srht.validation import Validation
from gitsrht.types import Repository, RepoVisibility, Redirect
from gitsrht.types import Access, AccessMode, User
from gitsrht.decorators import loginrequired
from gitsrht.access import check_access, UserAccess
from gitsrht.repos import create_repo, rename_repo, delete_repo
import shutil

D gitsrht/decorators.py => gitsrht/decorators.py +0 -15
@@ 1,15 0,0 @@
from flask import redirect, request, abort
from flask_login import current_user
from functools import wraps
from gitsrht.app import oauth_url

import urllib

def loginrequired(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        if not current_user:
            return redirect(oauth_url(request.url))
        else:
            return f(*args, **kwargs)
    return wrapper