~edwargix/git.sr.ht

2b9d1a53e25939a129dfe47fd8bd4818f0eedfa5 — Drew DeVault 8 years ago a2b30cf
Implement login via meta.sr.ht
M git/app.py => git/app.py +23 -1
@@ 1,16 1,26 @@
from flask import render_template, request 

from flask_login import LoginManager, current_user
import urllib.parse
import locale

from srht.config import cfg, cfgi, load_config
load_config("git")
from srht.database import DbSession
db = DbSession(cfg("sr.ht", "connection-string"))
from git.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)

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


@@ 18,5 28,17 @@ except:
    pass

from git.blueprints.cgit import cgit
from git.blueprints.auth import auth

app.register_blueprint(cgit)
app.register_blueprint(auth)

@app.context_processor
def inject():
    return {
        "oauth_url": "{}/oauth/authorize?client_id={}&scopes=profile,keys&state={}".format(
            cfg("network", "meta"),
            cfg("meta.sr.ht", "oauth-client-id"),
            urllib.parse.quote(request.full_path)
        )
    }

A git/blueprints/auth.py => git/blueprints/auth.py +77 -0
@@ 0,0 1,77 @@
from flask import Blueprint, Response, request, render_template, redirect
from flask_login import login_user, logout_user
from srht.config import cfg
from srht.flask import DATE_FORMAT
from srht.database import db
from git.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")
    if scopes != "profile:read,keys:read":
        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.")
    
    user = User.query.filter(User.oauth_token == token).first()
    if not user:
        user = User()
        db.session.add(user)
    json = r.json()
    user.username = json.get("username")
    user.email = json.get("email")
    user.paid = json.get("paid")
    user.oauth_token = token
    user.oauth_token_expires = expires
    db.session.commit()

    login_user(user)
    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 "/")

A git/types/__init__.py => git/types/__init__.py +3 -0
@@ 0,0 1,3 @@
from .user import User
from .repository import Repository
from .group import Group

A git/types/group.py => git/types/group.py +12 -0
@@ 0,0 1,12 @@
import sqlalchemy as sa
import sqlalchemy_utils as sau
from srht.database import Base

class Group(Base):
    __tablename__ = 'group'
    id = sa.Column(sa.Integer, primary_key=True)
    created = sa.Column(sa.DateTime, nullable=False)
    updated = sa.Column(sa.DateTime, nullable=False)
    name = sa.Column(sa.Unicode(256), nullable=False)
    owner_id = sa.Column(sa.Integer, sa.ForeignKey('user.id'), nullable=False)
    owner = sa.orm.relationship('User', backref=sa.orm.backref('groups'))

M git/types/repository.py => git/types/repository.py +5 -3
@@ 1,12 1,14 @@
import sqlalchemy as sa
import sqlalchemy_utils as sau
from git.db import Base
from srht.database import Base

class Repository(Base):
    __tablename__ = 'oauthtoken'
    __tablename__ = 'repository'
    id = sa.Column(sa.Integer, primary_key=True)
    created = sa.Column(sa.DateTime, nullable=False)
    updated = sa.Column(sa.DateTime, nullable=False)
    name = sa.Column(sa.Unicode(256), nullable=False)
    owner_id = sa.Column(sa.Integer, sa.ForeignKey('user.id'))
    owner = sa.orm.relationship('User', backref=sa.orm.backref('repos'))
    name = sa.Column(sa.Unicode(256), nullable=False)
    group_id = sa.Column(sa.Integer, sa.ForeignKey('group.id'))
    group = sa.orm.relationship('Group', backref=sa.orm.backref('repos'))

M git/types/user.py => git/types/user.py +4 -1
@@ 1,12 1,15 @@
import sqlalchemy as sa
import sqlalchemy_utils as sau
from git.db import Base
from srht.database import Base

class User(Base):
    __tablename__ = 'user'
    id = sa.Column(sa.Integer, primary_key=True)
    username = sa.Column(sa.Unicode(256))
    created = sa.Column(sa.DateTime, nullable=False)
    updated = sa.Column(sa.DateTime, nullable=False)
    oauth_token = sa.Column(sa.String(256), nullable=False)
    oauth_token_expires = sa.Column(sa.DateTime, nullable=False)
    email = sa.Column(sa.String(256), nullable=False)
    paid = sa.Column(sa.Boolean, nullable=False)


M templates/git.html => templates/git.html +1 -6
@@ 1,11 1,6 @@
{% extends "layout.html" %}
{% block body %} 
<div class="container">
  <div class="row">
    <div class="col-md-12">
      <h2>git</h2>
    </div>
  </div>
  {% block content %}{% endblock %}
</div>
{% block content %}{% endblock %}
{% endblock %}

A templates/oauth-error.html => templates/oauth-error.html +9 -0
@@ 0,0 1,9 @@
{% extends "git.html" %}
{% block content %}
<div class="row">
  <section class="col-md-12">
    <h2>Error logging in</h2>
    <p>{{ details }}</p>
  </section>
</div>
{% endblock %}