""" Custom Decorators ================= Reusable decorators for access control and validation. """ from functools import wraps from flask import abort, flash, redirect, url_for from flask_login import current_user def admin_required(f): """ Decorator that requires user to be logged in AND be an admin. Usage: @bp.route('/admin/users') @login_required @admin_required def admin_users(): ... Note: Always use @login_required BEFORE @admin_required """ @wraps(f) def decorated_function(*args, **kwargs): if not current_user.is_authenticated: return redirect(url_for('auth.login')) if not current_user.is_admin: flash('Brak uprawnień administratora.', 'error') return redirect(url_for('public.index')) return f(*args, **kwargs) return decorated_function def verified_required(f): """ Decorator that requires user to have verified email. Usage: @bp.route('/forum/new') @login_required @verified_required def new_topic(): ... """ @wraps(f) def decorated_function(*args, **kwargs): if not current_user.is_authenticated: return redirect(url_for('auth.login')) if not current_user.is_verified: flash('Musisz zweryfikować swój email, aby wykonać tę akcję.', 'warning') return redirect(url_for('auth.resend_verification')) return f(*args, **kwargs) return decorated_function def company_owner_or_admin(f): """ Decorator for routes that accept company_id. Allows access only if user is admin OR owns the company. Usage: @bp.route('/company//edit') @login_required @company_owner_or_admin def edit_company(company_id): ... """ @wraps(f) def decorated_function(*args, **kwargs): if not current_user.is_authenticated: return redirect(url_for('auth.login')) company_id = kwargs.get('company_id') if company_id is None: abort(400) if current_user.is_admin: return f(*args, **kwargs) if current_user.company_id == company_id: return f(*args, **kwargs) flash('Nie masz uprawnień do tej firmy.', 'error') return redirect(url_for('public.index')) return decorated_function