🔍 Code Extractor

function require_permission

Maturity: 57

A decorator function that enforces permission-based access control by checking if a user has required permissions before executing the decorated function.

File:
/tf/active/vicechatdev/CDocs/controllers/__init__.py
Lines:
84 - 137
Complexity:
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

  • functools
  • typing
  • logging
  • CDocs.config
  • CDocs.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

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function user_has_permission_for_resource 65.7% similar

    Checks if a user has a specific permission for a resource, considering both general permissions and resource-specific ownership/creator rights.

    From: /tf/active/vicechatdev/CDocs/config/permissions.py
  • function user_has_permission 61.6% similar

    Validates whether a user has one or more specified permissions based on their assigned roles and a role-permission mapping configuration.

    From: /tf/active/vicechatdev/CDocs/config/permissions.py
  • function check_document_permission 59.2% similar

    Validates whether a user has specific permission(s) to access or modify a document based on their roles, ownership status, and configured role permissions.

    From: /tf/active/vicechatdev/CDocs/config/permissions.py
  • function require_auth 59.2% similar

    A decorator function that enforces authentication requirements on Flask route handlers by checking if a user is authenticated before allowing access to the decorated function.

    From: /tf/active/vicechatdev/vice_ai/new_app.py
  • function require_auth_v1 56.7% similar

    A Flask decorator that enforces authentication by checking if a user is authenticated before allowing access to a protected route, redirecting to login if not authenticated.

    From: /tf/active/vicechatdev/vice_ai/complex_app.py
← Back to Browse