function require_permission
A decorator function that enforces permission-based access control by checking if a user has required permissions before executing the decorated function.
/tf/active/vicechatdev/CDocs/controllers/__init__.py
84 - 137
moderate
Purpose
This decorator provides a reusable authorization mechanism for functions that require permission checks. It supports both global permissions and resource-specific permissions, can handle single or multiple required permissions (OR logic), and optionally skips checks when no user is provided. It's designed to work with DocUser objects and integrates with a permissions system to enforce access control across an application.
Source Code
def require_permission(permission: Union[str, List[str]], resource_param: Optional[str] = None, skip_if_no_user: bool = False):
"""
Decorator to check if user has required permission.
Args:
permission: Permission or list of permissions required
resource_param: Optional parameter name for resource-specific permission check
skip_if_no_user: If True, skip permission check if no user is provided
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# Get user from args or kwargs
user = None
for arg in args:
if isinstance(arg, DocUser):
user = arg
break
if user is None and 'user' in kwargs:
user = kwargs['user']
# Skip permission check if no user and skip_if_no_user is True
if user is None:
if skip_if_no_user:
logger.debug(f"Skipping permission check for {func.__name__}: no user provided and skip_if_no_user=True")
return func(*args, **kwargs)
else:
raise PermissionError("User not provided")
# Check if any required permissions are present
required_permissions = [permission] if isinstance(permission, str) else permission
# If resource-specific permission check
if resource_param and resource_param in kwargs:
resource_id = kwargs[resource_param]
has_permission = any(
permissions.user_has_permission_for_resource(user, perm, resource_id)
for perm in required_permissions
)
else:
# Global permission check
has_permission = any(
permissions.user_has_permission(user, perm)
for perm in required_permissions
)
if not has_permission:
perm_str = ', '.join(required_permissions)
raise PermissionError(f"User {user.username} does not have required permission(s): {perm_str}")
return func(*args, **kwargs)
return wrapper
return decorator
Parameters
| Name | Type | Default | Kind |
|---|---|---|---|
permission |
Union[str, List[str]] | - | positional_or_keyword |
resource_param |
Optional[str] | None | positional_or_keyword |
skip_if_no_user |
bool | False | positional_or_keyword |
Parameter Details
permission: A single permission string (e.g., 'read_document') or a list of permission strings. If a list is provided, the user needs to have at least ONE of the permissions (OR logic). These permissions are checked against the user's granted permissions.
resource_param: Optional string specifying the name of a keyword argument in the decorated function that contains a resource ID. When provided, the decorator performs resource-specific permission checks instead of global permission checks. For example, if resource_param='document_id', the decorator will look for kwargs['document_id'] and check if the user has permission for that specific resource.
skip_if_no_user: Boolean flag that determines behavior when no user is found in the function arguments. If True, the permission check is skipped and the function executes normally. If False (default), a PermissionError is raised when no user is provided. Useful for functions that can operate in both authenticated and unauthenticated contexts.
Return Value
Returns a decorator function that wraps the target function. The wrapper function returns whatever the original decorated function returns if permission checks pass. If permission checks fail, it raises a PermissionError with a descriptive message indicating which user and which permissions were required.
Dependencies
functoolstypingloggingCDocs.configCDocs.models.user_extensions
Required Imports
from functools import wraps
from typing import Union, List, Optional
from CDocs.models.user_extensions import DocUser
from CDocs.config import permissions
import logging
Usage Example
from functools import wraps
from typing import Union, List, Optional
from CDocs.models.user_extensions import DocUser
from CDocs.config import permissions
import logging
logger = logging.getLogger(__name__)
# Example 1: Single permission check
@require_permission('read_document')
def view_document(user: DocUser, document_id: str):
return f"Viewing document {document_id}"
# Example 2: Multiple permissions (OR logic)
@require_permission(['admin', 'moderator'])
def moderate_content(user: DocUser, content_id: str):
return f"Moderating content {content_id}"
# Example 3: Resource-specific permission
@require_permission('edit_document', resource_param='doc_id')
def edit_document(user: DocUser, doc_id: str, new_content: str):
return f"Editing document {doc_id}"
# Example 4: Skip if no user provided
@require_permission('view_stats', skip_if_no_user=True)
def get_public_stats(user: DocUser = None):
if user:
return "Detailed stats for authenticated user"
return "Public stats"
# Usage
user = DocUser(username='john_doe')
try:
result = view_document(user, 'doc123')
print(result)
except PermissionError as e:
print(f"Access denied: {e}")
Best Practices
- Ensure the decorated function receives a DocUser object either as a positional argument or as a keyword argument named 'user'
- When using resource_param, make sure the specified parameter name exists in the decorated function's kwargs
- Use skip_if_no_user=True only for functions that genuinely support both authenticated and unauthenticated access
- When providing multiple permissions, remember they use OR logic - the user only needs ONE of the listed permissions
- The permissions module must implement user_has_permission(user, permission) and user_has_permission_for_resource(user, permission, resource_id) functions
- Handle PermissionError exceptions appropriately in calling code to provide user-friendly error messages
- Consider logging permission denials for security auditing purposes
- Place this decorator closest to the function definition when using multiple decorators to ensure permission checks happen first
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
function user_has_permission_for_resource 65.7% similar
-
function user_has_permission 61.6% similar
-
function check_document_permission 59.2% similar
-
function require_auth 59.2% similar
-
function require_auth_v1 56.7% similar