From 2eb5c513538930abd2bb4d4f0755c63edb981e7e Mon Sep 17 00:00:00 2001 From: Adriano Dal Pastro Date: Fri, 5 Jun 2026 08:24:20 +0000 Subject: [PATCH] fix(auth): role-aware post-login landing to avoid 403 for non-MeasurementTec Login always redirected to measure.select_recipe, which is gated by @role_required("MeasurementTec"). A Maker-only (or Metrologist-only) user was therefore bounced straight to a 403 right after a successful login. Add _post_login_landing() which selects the landing endpoint from the user's roles (MeasurementTec -> measure, Maker -> maker recipes, Metrologist -> statistics, admin -> users, else profile). Used for both fresh login and the already-authenticated short-circuit. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/frontend/flask_app/blueprints/auth.py | 34 ++++++++++++++++------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/frontend/flask_app/blueprints/auth.py b/src/frontend/flask_app/blueprints/auth.py index 8534a56..387d38c 100644 --- a/src/frontend/flask_app/blueprints/auth.py +++ b/src/frontend/flask_app/blueprints/auth.py @@ -39,15 +39,32 @@ def role_required(*roles): return decorator +def _post_login_landing(): + """Pick the landing endpoint based on the logged-in user's roles. + + A Maker-only user must not be bounced to /measure (MeasurementTec-only), + which would raise a 403 right after login. Order of preference follows the + primary operator flow first, then editing, analysis, admin. + """ + user = session.get("user", {}) + roles = user.get("roles", []) + if "MeasurementTec" in roles: + return url_for("measure.select_recipe") + if "Maker" in roles: + return url_for("maker.recipe_list") + if "Metrologist" in roles: + return url_for("statistics.dashboard") + if user.get("is_admin"): + return url_for("admin.user_list") + return url_for("auth.profile") + + @auth_bp.route("/login", methods=["GET", "POST"]) def login(): """Login page and form handler.""" - # Se già loggato, redirect a home + # Se già loggato, redirect alla home in base al ruolo if session.get("api_key"): - try: - return redirect(url_for("measure.select_recipe")) - except Exception: - return redirect(url_for("auth.profile")) + return redirect(_post_login_landing()) if request.method == "POST": username = request.form.get("username", "").strip() @@ -91,11 +108,8 @@ def login(): flash(_("Benvenuto, %(name)s!", name=user.get("display_name", username)), "success") - # Redirect dopo login - try: - return redirect(url_for("measure.select_recipe")) - except Exception: - return redirect(url_for("auth.profile")) + # Redirect dopo login (in base al ruolo) + return redirect(_post_login_landing()) except Exception as e: flash(_("Errore di connessione al server: %(error)s", error=str(e)), "error")