Files
TimeTracker/app/utils/permissions.py
T
Dries Peeters 90dde470da style: standardize code formatting and normalize line endings
- Normalize line endings from CRLF to LF across all files to match .editorconfig
- Standardize quote style from single quotes to double quotes
- Normalize whitespace and formatting throughout codebase
- Apply consistent code style across 372 files including:
  * Application code (models, routes, services, utils)
  * Test files
  * Configuration files
  * CI/CD workflows

This ensures consistency with the project's .editorconfig settings and
improves code maintainability.
2025-11-28 20:05:37 +01:00

186 lines
5.0 KiB
Python

"""Utilities for permission checking and decorators"""
from functools import wraps
from flask import flash, redirect, url_for, abort
from flask_login import current_user
from flask_babel import gettext as _
def permission_required(*permissions, require_all=False):
"""
Decorator to require one or more permissions.
Args:
*permissions: Permission name(s) required
require_all: If True, user must have ALL permissions. If False, user needs ANY permission.
Example:
@permission_required('edit_projects')
def edit_project():
...
@permission_required('view_reports', 'export_reports', require_all=True)
def export_report():
...
"""
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if not current_user.is_authenticated:
flash(_("Please log in to access this page"), "error")
return redirect(url_for("auth.login"))
# Check if user has required permissions
if require_all:
has_access = current_user.has_all_permissions(*permissions)
else:
has_access = current_user.has_any_permission(*permissions)
if not has_access:
flash(_("You do not have permission to access this page"), "error")
abort(403)
return f(*args, **kwargs)
return decorated_function
return decorator
def admin_or_permission_required(*permissions):
"""
Decorator that allows access if user is an admin OR has any of the specified permissions.
This is useful for gradual migration from admin-only to permission-based access.
Example:
@admin_or_permission_required('delete_projects')
def delete_project():
...
"""
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if not current_user.is_authenticated:
flash(_("Please log in to access this page"), "error")
return redirect(url_for("auth.login"))
# Allow access if user is admin or has any of the required permissions
if current_user.is_admin or current_user.has_any_permission(*permissions):
return f(*args, **kwargs)
flash(_("You do not have permission to access this page"), "error")
abort(403)
return decorated_function
return decorator
def check_permission(user, permission_name):
"""
Check if a user has a specific permission.
Args:
user: User object
permission_name: Name of the permission to check
Returns:
bool: True if user has the permission, False otherwise
"""
if not user or not user.is_authenticated:
return False
return user.has_permission(permission_name)
def check_any_permission(user, *permission_names):
"""
Check if a user has any of the specified permissions.
Args:
user: User object
*permission_names: Names of permissions to check
Returns:
bool: True if user has any of the permissions, False otherwise
"""
if not user or not user.is_authenticated:
return False
return user.has_any_permission(*permission_names)
def check_all_permissions(user, *permission_names):
"""
Check if a user has all of the specified permissions.
Args:
user: User object
*permission_names: Names of permissions to check
Returns:
bool: True if user has all of the permissions, False otherwise
"""
if not user or not user.is_authenticated:
return False
return user.has_all_permissions(*permission_names)
def get_user_permissions(user):
"""
Get all permissions for a user.
Args:
user: User object
Returns:
list: List of Permission objects
"""
if not user:
return []
return user.get_all_permissions()
def get_user_permission_names(user):
"""
Get all permission names for a user.
Args:
user: User object
Returns:
list: List of permission name strings
"""
if not user:
return []
permissions = user.get_all_permissions()
return [p.name for p in permissions]
# Template helper functions (register these in app context)
def init_permission_helpers(app):
"""
Initialize permission helper functions for use in templates.
Usage in templates:
{% if has_permission('edit_projects') %}
<button>Edit Project</button>
{% endif %}
"""
@app.context_processor
def inject_permission_helpers():
return {
"has_permission": lambda perm: check_permission(current_user, perm),
"has_any_permission": lambda *perms: check_any_permission(current_user, *perms),
"has_all_permissions": lambda *perms: check_all_permissions(current_user, *perms),
"get_user_permissions": lambda: (
get_user_permission_names(current_user) if current_user.is_authenticated else []
),
}