92 lines
3.0 KiB
Python
92 lines
3.0 KiB
Python
|
|
"""Authentication and authorization decorators"""
|
||
|
|
from functools import wraps
|
||
|
|
from flask import session, redirect, url_for, request, abort, current_app
|
||
|
|
from app.models.user import UserModel
|
||
|
|
|
||
|
|
|
||
|
|
def login_required(f):
|
||
|
|
"""
|
||
|
|
Decorator to require user authentication.
|
||
|
|
Redirects to login if user is not authenticated.
|
||
|
|
"""
|
||
|
|
@wraps(f)
|
||
|
|
def decorated_function(*args, **kwargs):
|
||
|
|
if 'user' not in session:
|
||
|
|
# Store the requested URL to redirect back after login
|
||
|
|
session['next'] = request.url
|
||
|
|
return redirect(url_for('auth.login'))
|
||
|
|
return f(*args, **kwargs)
|
||
|
|
return decorated_function
|
||
|
|
|
||
|
|
|
||
|
|
def admin_required(f):
|
||
|
|
"""
|
||
|
|
Decorator to require admin role.
|
||
|
|
Returns 403 if user is not an admin.
|
||
|
|
"""
|
||
|
|
@wraps(f)
|
||
|
|
def decorated_function(*args, **kwargs):
|
||
|
|
if 'user' not in session:
|
||
|
|
session['next'] = request.url
|
||
|
|
return redirect(url_for('auth.login'))
|
||
|
|
|
||
|
|
user_id = session['user'].get('id')
|
||
|
|
if not UserModel.is_admin(user_id):
|
||
|
|
current_app.logger.warning(f"User {user_id} attempted to access admin route without permission")
|
||
|
|
abort(403, description="Admin access required")
|
||
|
|
|
||
|
|
return f(*args, **kwargs)
|
||
|
|
return decorated_function
|
||
|
|
|
||
|
|
|
||
|
|
def tenant_access_required(f):
|
||
|
|
"""
|
||
|
|
Decorator to require access to the specific tenant in the URL.
|
||
|
|
User must either be an admin or have explicit access to the tenant.
|
||
|
|
Admins can see server settings, regular users cannot.
|
||
|
|
"""
|
||
|
|
@wraps(f)
|
||
|
|
def decorated_function(*args, **kwargs):
|
||
|
|
if 'user' not in session:
|
||
|
|
session['next'] = request.url
|
||
|
|
return redirect(url_for('auth.login'))
|
||
|
|
|
||
|
|
user_id = session['user'].get('id')
|
||
|
|
tenant_id = kwargs.get('tenant_id')
|
||
|
|
|
||
|
|
if not tenant_id:
|
||
|
|
# No tenant in URL, allow access (e.g., global settings for admins only)
|
||
|
|
return f(*args, **kwargs)
|
||
|
|
|
||
|
|
# Check if user has access to this tenant
|
||
|
|
if not UserModel.has_access_to_tenant(user_id, tenant_id):
|
||
|
|
current_app.logger.warning(
|
||
|
|
f"User {user_id} attempted to access tenant {tenant_id} without permission"
|
||
|
|
)
|
||
|
|
abort(403, description="You don't have access to this tenant")
|
||
|
|
|
||
|
|
return f(*args, **kwargs)
|
||
|
|
return decorated_function
|
||
|
|
|
||
|
|
|
||
|
|
def settings_access_required(f):
|
||
|
|
"""
|
||
|
|
Decorator for server settings pages - admin only.
|
||
|
|
Regular users cannot see server settings.
|
||
|
|
"""
|
||
|
|
@wraps(f)
|
||
|
|
def decorated_function(*args, **kwargs):
|
||
|
|
if 'user' not in session:
|
||
|
|
session['next'] = request.url
|
||
|
|
return redirect(url_for('auth.login'))
|
||
|
|
|
||
|
|
user_id = session['user'].get('id')
|
||
|
|
if not UserModel.is_admin(user_id):
|
||
|
|
current_app.logger.warning(
|
||
|
|
f"User {user_id} attempted to access server settings without admin permission"
|
||
|
|
)
|
||
|
|
abort(403, description="Admin access required to view server settings")
|
||
|
|
|
||
|
|
return f(*args, **kwargs)
|
||
|
|
return decorated_function
|