from flask import Blueprint, render_template, request, redirect, url_for, session, abort from functools import wraps from datetime import datetime from app.models import User from app.supabase_client import get_supabase auth_bp = Blueprint('auth', __name__) def log_auth_event(user_id: str, action: str, details: str = None): """Log authentication events to audit trail""" supabase = get_supabase() event_data = { 'user_id': user_id, 'action': action, 'timestamp': datetime.utcnow().isoformat(), 'details': details or '', 'ip_address': request.remote_addr } try: supabase.table('auth_audit').insert(event_data).execute() except Exception as e: print(f"Failed to log auth event: {e}") def role_required(role): """Decorator to require specific role""" def decorator(f): @wraps(f) def decorated_function(*args, **kwargs): if role == 'calibrate' and not session.get('can_calibrate'): abort(403) if role == 'review' and not session.get('can_review'): abort(403) return f(*args, **kwargs) return decorated_function return decorator @auth_bp.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': email = request.form.get('email') user = User.get_by_email(email) if user: session['user_id'] = user.id session['user_name'] = user.name session['can_calibrate'] = user.can_calibrate session['can_review'] = user.can_review log_auth_event(user.id, 'login', f'IP: {request.remote_addr}') return redirect(url_for('index')) log_auth_event('unknown', 'failed_login', f'Email: {email}, IP: {request.remote_addr}') return render_template('auth/login.html', error="Invalid user") # GET request - show login form users = User.get_all() if not users: return render_template('auth/login.html', users=[], error="No users exist in database") return render_template('auth/login.html', users=users) @auth_bp.route('/logout') def logout(): if 'user_id' in session: log_auth_event(session['user_id'], 'logout') session.clear() return redirect(url_for('auth.login'))