Switch to standard auth

This commit is contained in:
Drew DeVault 2018-07-11 07:58:23 -04:00
parent c17cc80b77
commit c565373f49
5 changed files with 52 additions and 149 deletions

View File

@ -1,51 +1,20 @@
from flask import render_template, request
from flask_login import LoginManager, current_user
from jinja2 import Markup
import locale
import urllib
from srht.config import cfg, cfgi, load_config
from srht.flask import SrhtFlask
from srht.config import cfg, load_config
load_config("todo")
from srht.database import DbSession
db = DbSession(cfg("sr.ht", "connection-string"))
from todosrht.types import User, TicketAccess, TicketStatus, TicketResolution, TicketSeen
from todosrht.types import User
from todosrht.types import TicketAccess, TicketStatus, TicketResolution
from todosrht.types import TicketSeen
db.init()
from srht.flask import SrhtFlask
app = SrhtFlask("todo", __name__)
app.url_map.strict_slashes = False
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')
except:
pass
def oauth_url(return_to):
return "{}/oauth/authorize?client_id={}&scopes=profile&state={}".format(
meta_sr_ht, meta_client_id, urllib.parse.quote_plus(return_to))
from todosrht.blueprints.html import html
from todosrht.blueprints.auth import auth
from todosrht.blueprints.tracker import tracker
from todosrht.blueprints.ticket import ticket
app.register_blueprint(html)
app.register_blueprint(auth)
app.register_blueprint(tracker)
app.register_blueprint(ticket)
meta_sr_ht = cfg("network", "meta")
meta_client_id = cfg("meta.sr.ht", "oauth-client-id")
def tracker_name(tracker, full=False):
split = tracker.name.split("/")
user = tracker.owner.canonical_name()
@ -80,15 +49,46 @@ def render_status(ticket, access):
else:
return "<span>{}</span>".format(ticket.status.name)
@app.context_processor
def inject():
return {
"oauth_url": oauth_url(request.full_path),
"current_user": User.query.filter(User.id == current_user.id).first() \
if current_user else None,
"format_tracker_name": tracker_name,
"render_status": render_status,
"TicketAccess": TicketAccess,
"TicketStatus": TicketStatus,
"TicketResolution": TicketResolution
}
class TodoApp(SrhtFlask):
def __init__(self):
super().__init__("todo", __name__)
self.url_map.strict_slashes = False
self.register_blueprint(html)
self.register_blueprint(tracker)
self.register_blueprint(ticket)
meta_client_id = cfg("meta.sr.ht", "oauth-client-id")
meta_client_secret = cfg("meta.sr.ht", "oauth-client-secret")
self.configure_meta_auth(meta_client_id, meta_client_secret)
@self.context_processor
def inject():
return {
"format_tracker_name": tracker_name,
"render_status": render_status,
"TicketAccess": TicketAccess,
"TicketStatus": TicketStatus,
"TicketResolution": TicketResolution
}
@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.oauth_token = exchange["token"]
user.oauth_token_expires = exchange["expires"]
user.oauth_token_scopes = scopes
db.session.commit()
return user
app = TodoApp()

View File

@ -1,81 +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 todosrht.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:
return render_template("oauth-error.html",
details="todo.sr.ht requires profile and key access at a mininum to function correctly. " +
"To use todo.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.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 "/")

View File

@ -3,7 +3,6 @@ import string
from flask import Blueprint, render_template, request, url_for, abort, redirect
from flask import session
from flask_login import current_user
from todosrht.decorators import loginrequired
from todosrht.access import get_tracker, get_ticket
from todosrht.types import Tracker, User, Ticket, TicketStatus, TicketAccess
from todosrht.types import TicketComment, TicketResolution, TicketSeen
@ -12,6 +11,7 @@ from todosrht.types import Event, EventType, EventNotification
from todosrht.email import notify
from srht.config import cfg
from srht.database import db
from srht.flask import loginrequired
from srht.validation import Validation
from datetime import datetime

View File

@ -5,14 +5,13 @@ from flask import Blueprint, render_template, request, url_for, abort, redirect
from flask import session
from flask_login import current_user
from todosrht.access import get_tracker
from todosrht.decorators import loginrequired
from todosrht.email import notify
from todosrht.types import Tracker, User, Ticket, TicketStatus, TicketAccess
from todosrht.types import TicketComment, TicketResolution, TicketSubscription
from todosrht.types import TicketSeen, Event, EventType, EventNotification
from srht.config import cfg
from srht.database import db
from srht.flask import paginate_query
from srht.flask import paginate_query, loginrequired
from srht.validation import Validation
from datetime import datetime

View File

@ -1,15 +0,0 @@
from flask import redirect, request, abort
from flask_login import current_user
from functools import wraps
from todosrht.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