64 lines
1.9 KiB
Python
64 lines
1.9 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# Authentication Utilities
|
|
|
|
from functools import wraps
|
|
from quart import session, request, redirect, url_for, jsonify
|
|
from typing import Optional
|
|
from models.user import User
|
|
|
|
def login_required(f):
|
|
"""Decorator to require login for a route"""
|
|
@wraps(f)
|
|
async def decorated_function(*args, **kwargs):
|
|
if 'user_id' not in session:
|
|
if request.is_json:
|
|
return jsonify({'error': 'Login required'}), 401
|
|
return redirect(url_for('auth.login'))
|
|
return await f(*args, **kwargs)
|
|
return decorated_function
|
|
|
|
def admin_required(f):
|
|
"""Decorator to require admin role for a route"""
|
|
@wraps(f)
|
|
async def decorated_function(*args, **kwargs):
|
|
if 'user_id' not in session:
|
|
if request.is_json:
|
|
return jsonify({'error': 'Login required'}), 401
|
|
return redirect(url_for('auth.login'))
|
|
|
|
user = await get_current_user()
|
|
if not user or not user.is_admin:
|
|
if request.is_json:
|
|
return jsonify({'error': 'Admin access required'}), 403
|
|
return redirect(url_for('home.index'))
|
|
|
|
return await f(*args, **kwargs)
|
|
return decorated_function
|
|
|
|
async def get_current_user() -> Optional[User]:
|
|
"""Get the currently logged-in user"""
|
|
if 'user_id' not in session:
|
|
return None
|
|
|
|
try:
|
|
user_id = session['user_id']
|
|
return await User.find_by_id(user_id)
|
|
except:
|
|
# Clear invalid session
|
|
session.pop('user_id', None)
|
|
return None
|
|
|
|
def login_user(user: User):
|
|
"""Log in a user (set session)"""
|
|
session['user_id'] = user.id
|
|
session['username'] = user.username
|
|
session['is_admin'] = user.is_admin
|
|
|
|
def logout_user():
|
|
"""Log out the current user (clear session)"""
|
|
session.pop('user_id', None)
|
|
session.pop('username', None)
|
|
session.pop('is_admin', None)
|