squirrel/squirrel/auth.py

91 lines
2.0 KiB
Python

from flask import (
Blueprint,
request,
render_template,
session,
redirect,
url_for,
flash,
g,
)
from werkzeug.security import check_password_hash, generate_password_hash
from dataclasses import dataclass
from squirrel.conf import settings
import functools
bp = Blueprint("auth", __name__, url_prefix="/auth")
@dataclass
class User:
name: str
password: str
repos: list[str] | None = None
users = settings["users"]
@bp.route("/login", methods=("GET", "POST"))
def login():
username = ""
referer = request.args.get("referer") or request.headers.get("Referer")
if request.method == "POST":
username = request.form["username"]
password = request.form["password"]
error = None
user = users.get(username)
if user is None or not check_password_hash(user["password"], password):
error = "Incorrect username or password."
if error is None:
session.clear()
session["user"] = username
return redirect(referer or url_for("render_home"))
flash(error)
return render_template("auth/login.html", username=username, referer=referer)
@bp.route("/generate", methods=("GET", "POST"))
def generate_password():
hashed = None
if request.method == "POST":
password = request.form["password"]
hashed = generate_password_hash(password)
return render_template("auth/generate.html", hashed=hashed)
@bp.route("/logout")
def logout():
referer = request.headers.get("Referer")
session.clear()
return redirect(referer or url_for("auth.login"))
def login_required(view):
@functools.wraps(view)
def wrapped_view(**kwargs):
if g.user is None:
return redirect(url_for("auth.login"))
return view(**kwargs)
return wrapped_view
@bp.before_app_request
def load_logged_in_user():
username = session.get("user")
if username is None:
g.user = User(**users["guest"])
else:
g.user = User(**users[username])