""" Authentication routes for SneakyScanner. Provides login and logout endpoints for user authentication. """ import logging from flask import Blueprint, current_app, flash, redirect, render_template, request, url_for from flask_login import login_user, logout_user, current_user from web.auth.models import User logger = logging.getLogger(__name__) bp = Blueprint('auth', __name__) @bp.route('/login', methods=['GET', 'POST']) def login(): """ Login page and authentication endpoint. GET: Render login form POST: Authenticate user and create session Returns: GET: Rendered login template POST: Redirect to dashboard on success, login page with error on failure """ # If already logged in, redirect to dashboard if current_user.is_authenticated: return redirect(url_for('main.dashboard')) # Check if password is set if not User.has_password_set(current_app.db_session): flash('Application password not set. Please contact administrator.', 'error') logger.warning("Login attempted but no password is set") return render_template('login.html', password_not_set=True) if request.method == 'POST': password = request.form.get('password', '') # Authenticate user user = User.authenticate(password, current_app.db_session) if user: # Login successful login_user(user, remember=request.form.get('remember', False)) logger.info(f"User logged in successfully from {request.remote_addr}") # Redirect to next page or dashboard next_page = request.args.get('next') if next_page: return redirect(next_page) return redirect(url_for('main.dashboard')) else: # Login failed flash('Invalid password', 'error') logger.warning(f"Failed login attempt from {request.remote_addr}") return render_template('login.html') @bp.route('/logout') def logout(): """ Logout endpoint. Destroys the user session and redirects to login page. Returns: Redirect to login page """ if current_user.is_authenticated: logger.info(f"User logged out from {request.remote_addr}") logout_user() flash('You have been logged out successfully', 'info') return redirect(url_for('auth.login')) @bp.route('/setup', methods=['GET', 'POST']) def setup(): """ Initial password setup page. Only accessible when no password is set. Allows setting the application password. Returns: GET: Rendered setup template POST: Redirect to login page on success """ # If password already set, redirect to login if User.has_password_set(current_app.db_session): flash('Password already set. Please login.', 'info') return redirect(url_for('auth.login')) if request.method == 'POST': password = request.form.get('password', '') confirm_password = request.form.get('confirm_password', '') # Validate passwords if not password: flash('Password is required', 'error') elif len(password) < 8: flash('Password must be at least 8 characters', 'error') elif password != confirm_password: flash('Passwords do not match', 'error') else: # Set password from web.utils.settings import PasswordManager, SettingsManager settings_manager = SettingsManager(current_app.db_session) PasswordManager.set_app_password(settings_manager, password) logger.info(f"Application password set from {request.remote_addr}") flash('Password set successfully! You can now login.', 'success') return redirect(url_for('auth.login')) return render_template('setup.html')