05/06 Fix some notable and quality issues

This commit is contained in:
2026-05-06 11:47:20 -04:00
parent 830bb51f24
commit 02b3e1c45d
11 changed files with 125 additions and 46 deletions
+4 -21
View File
@@ -1,10 +1,9 @@
from flask import Blueprint, render_template, redirect, url_for, flash, request, abort
from flask_login import login_user, logout_user, login_required, current_user
from urllib.parse import urlparse
from app import db, limiter
from app.models.user import User
from app.utils.forms import LoginForm, UserForm, ProfileForm
from app.utils.decorators import admin_required, supervisor_required
from app.utils.decorators import admin_required, supervisor_required, safe_redirect_url
import logging
from app.utils.audit import log_action, ACTION_CREATE, ACTION_UPDATE, ACTION_DELETE, ACTION_LOGIN, ACTION_LOGOUT
@@ -13,22 +12,6 @@ logger = logging.getLogger(__name__)
bp = Blueprint('auth', __name__, url_prefix='/auth')
def _safe_next(next_url: str | None) -> str:
"""
Validate that the redirect target is a relative URL on this host.
Returns the safe URL, or the dashboard index if the URL is external/invalid.
This prevents open-redirect attacks where an attacker crafts a login link
containing next=https://evil.com to hijack post-login redirects.
"""
if not next_url:
return url_for('dashboard.index')
parsed = urlparse(next_url)
# Reject any URL that specifies a network location (external host) or scheme
if parsed.netloc or parsed.scheme:
return url_for('dashboard.index')
return next_url
@bp.route('/login', methods=['GET', 'POST'])
@limiter.limit('20 per minute; 5 per second')
def login():
@@ -52,7 +35,7 @@ def login():
return render_template('auth/login.html', form=form)
login_user(user, remember=form.remember_me.data)
# Use validated next URL — never redirect blindly to request.args['next']
next_page = _safe_next(request.args.get('next'))
next_page = safe_redirect_url(request.args.get('next'))
log_action(ACTION_LOGIN, 'User', user.id, user.username)
flash(f'Welcome back, {user.username}!', 'success')
return redirect(next_page)
@@ -282,7 +265,7 @@ def toggle_active(user_id):
f'account {action_label} by {current_user.username}',
)
flash(f'User {user.username} has been {action_label}.', 'success')
return redirect(_safe_next(request.referrer) or url_for('auth.list_users'))
return redirect(safe_redirect_url(request.referrer, fallback=url_for('auth.list_users')))
# ── Notification Matrix ───────────────────────────────────────────────────────
@@ -334,4 +317,4 @@ def notification_matrix():
matrix_roles = MATRIX_ROLES,
defaults = MATRIX_DEFAULTS,
state = state,
)
)