""" routes/auth.py — Authentication routes: login, logout, change-password. """ import logging from flask import Blueprint, render_template, request, session, redirect, url_for, flash from models import authenticate, check_login_allowed, change_password, log_action from utils.decorators import login_required logger = logging.getLogger("routes.auth") auth_bp = Blueprint("auth", __name__) @auth_bp.route("/login", methods=["GET", "POST"]) def login(): if "user" in session: return redirect(url_for("index")) error = None if request.method == "POST": username = request.form.get("username", "").strip() password = request.form.get("password", "") ip = request.remote_addr allowed, seconds_remaining = check_login_allowed(username) if not allowed: mins = seconds_remaining // 60 secs = seconds_remaining % 60 error = f"Account locked. Try again in {mins}m {secs}s." logger.warning(f"Login blocked for '{username}' — still locked ({seconds_remaining}s remaining).") else: user = authenticate(username, password) if user: session.permanent = True # Store a safe subset — never store the password hash in session session["user"] = { "id": user["id"], "username": user["username"], "full_name": user.get("full_name") or user["username"], "role": user["role"], "email": user.get("email", ""), } logger.info(f"User '{username}' logged in from {ip}.") flash(f"You have been signed in. Welcome, {session['user']['full_name']}!", "success") if user["role"] == "admin": return redirect(url_for("admin_dashboard.dashboard")) return redirect(url_for("user_dashboard.my_shifts")) else: error = "Invalid username or password." return render_template("login.html", error=error) @auth_bp.route("/logout") @login_required def logout(): user = session.get("user", {}) if user: log_action(user["id"], "LOGOUT", "users", user["id"], f"User '{user['username']}' logged out.") logger.info(f"User '{user['username']}' logged out.") session.clear() flash("You have been signed out.", "info") return redirect(url_for("auth.login")) @auth_bp.route("/change-password", methods=["GET", "POST"]) @login_required def change_password_view(): user = session["user"] success = None error = None if request.method == "POST": old_pw = request.form.get("old_password", "") new_pw = request.form.get("new_password", "") confirm = request.form.get("confirm_password", "") if new_pw != confirm: error = "New password and confirmation do not match." else: ok, msg = change_password(user["id"], old_pw, new_pw) if ok: success = msg else: error = msg return render_template("change_password.html", success=success, error=error)