CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-djangorestframework

Web APIs for Django, made easy.

Pending
Overview
Eval results
Files

auth-permissions.mddocs/

Authentication and Permissions

Security framework providing authentication backends for user identification and permission classes for access control. Supports multiple authentication methods and granular permission systems.

Capabilities

Authentication Classes

Authentication classes identify users making requests to the API.

class BaseAuthentication:
    """
    Base class for all authentication backends.
    """
    def authenticate(self, request):
        """
        Authenticate the request and return user/auth tuple.
        
        Args:
            request: DRF Request object
            
        Returns:
            tuple or None: (user, auth) tuple or None if not authenticated
        """
    
    def authenticate_header(self, request):
        """
        Return string for WWW-Authenticate header when authentication fails.
        
        Args:
            request: DRF Request object
            
        Returns:
            str or None: Authentication header value
        """

class BasicAuthentication(BaseAuthentication):
    """
    HTTP Basic authentication using username and password.
    """
    www_authenticate_realm = 'api'
    
    def authenticate(self, request):
        """Authenticate using HTTP Basic credentials."""
    
    def authenticate_credentials(self, userid, password, request=None):
        """
        Authenticate credentials against user database.
        
        Args:
            userid (str): Username
            password (str): Password
            request: HTTP request
            
        Returns:
            tuple: (user, None) if valid
            
        Raises:
            AuthenticationFailed: If credentials invalid
        """

class SessionAuthentication(BaseAuthentication):
    """
    Django session-based authentication.
    """
    def authenticate(self, request):
        """Authenticate using Django session."""
    
    def enforce_csrf(self, request):
        """
        Enforce CSRF validation for authenticated requests.
        
        Raises:
            PermissionDenied: If CSRF validation fails
        """

class TokenAuthentication(BaseAuthentication):
    """
    Token-based authentication using database-stored tokens.
    """
    keyword = 'Token'  # Authorization header keyword
    model = Token      # Token model class
    
    def authenticate(self, request):
        """Authenticate using token from Authorization header."""
    
    def authenticate_credentials(self, key):
        """
        Authenticate token key against database.
        
        Args:
            key (str): Token key
            
        Returns:
            tuple: (user, token) if valid
            
        Raises:
            AuthenticationFailed: If token invalid
        """

class RemoteUserAuthentication(BaseAuthentication):
    """
    Authentication using REMOTE_USER header from web server.
    """
    header = 'HTTP_REMOTE_USER'  # Header name
    
    def authenticate(self, request):
        """Authenticate using remote user header."""
    
    def clean_username(self, username):
        """
        Clean username for user lookup.
        
        Args:
            username (str): Raw username
            
        Returns:
            str: Cleaned username
        """

Permission Classes

Permission classes control access to views and objects.

class BasePermission:
    """
    Base class for all permission checks with operator support.
    """
    def has_permission(self, request, view):
        """
        Check if request has permission to access view.
        
        Args:
            request: DRF Request object
            view: View being accessed
            
        Returns:
            bool: True if permission granted
        """
    
    def has_object_permission(self, request, view, obj):
        """
        Check if request has permission to access specific object.
        
        Args:
            request: DRF Request object
            view: View being accessed
            obj: Object being accessed
            
        Returns:
            bool: True if permission granted
        """
    
    def __and__(self, other):
        """Combine permissions with AND logic."""
        return OperandHolder(AND, self, other)
    
    def __or__(self, other):
        """Combine permissions with OR logic."""
        return OperandHolder(OR, self, other)
    
    def __invert__(self):
        """Negate permission."""
        return SingleOperandHolder(NOT, self)

class AllowAny(BasePermission):
    """
    Allow access to any request.
    """
    def has_permission(self, request, view):
        return True

class IsAuthenticated(BasePermission):
    """
    Allow access only to authenticated users.
    """
    def has_permission(self, request, view):
        return bool(request.user and request.user.is_authenticated)

class IsAdminUser(BasePermission):
    """
    Allow access only to admin users.
    """
    def has_permission(self, request, view):
        return bool(request.user and request.user.is_staff)

class IsAuthenticatedOrReadOnly(BasePermission):
    """
    Allow read permissions to any request, write permissions to authenticated users.
    """
    def has_permission(self, request, view):
        return (
            request.method in SAFE_METHODS or
            request.user and request.user.is_authenticated
        )

class DjangoModelPermissions(BasePermission):
    """
    Use Django's built-in model permissions system.
    """
    perms_map = {
        'GET': [],
        'OPTIONS': [],
        'HEAD': [],
        'POST': ['%(app_label)s.add_%(model_name)s'],
        'PUT': ['%(app_label)s.change_%(model_name)s'],
        'PATCH': ['%(app_label)s.change_%(model_name)s'],
        'DELETE': ['%(app_label)s.delete_%(model_name)s'],
    }
    
    def get_required_permissions(self, method, model_cls):
        """
        Get required permissions for HTTP method and model.
        
        Args:
            method (str): HTTP method
            model_cls: Django model class
            
        Returns:
            list: Required permission strings
        """

class DjangoModelPermissionsOrAnonReadOnly(DjangoModelPermissions):
    """
    Django model permissions with anonymous read access.
    """

class DjangoObjectPermissions(DjangoModelPermissions):
    """
    Use Django's object-level permissions.
    """
    def has_object_permission(self, request, view, obj):
        """Check object-level permissions using Django's system."""

Permission Operators

Combine permissions using logical operators.

class OperationHolderMixin:
    """Mixin providing permission combination operators."""
    def __and__(self, other):
        return OperandHolder(AND, self, other)
    
    def __or__(self, other):
        return OperandHolder(OR, self, other)
    
    def __invert__(self):
        return SingleOperandHolder(NOT, self)

class SingleOperandHolder(OperationHolderMixin):
    """Hold single operand for unary operations like NOT."""
    def __init__(self, operator_class, op1_class):
        self.operator_class = operator_class
        self.op1_class = op1_class

class OperandHolder(OperationHolderMixin):
    """Hold two operands for binary operations like AND/OR."""
    def __init__(self, operator_class, op1_class, op2_class):
        self.operator_class = operator_class
        self.op1_class = op1_class
        self.op2_class = op2_class

class AND:
    """Logical AND operation for permissions."""
    def has_permission(self, request, view):
        return (
            self.op1.has_permission(request, view) and
            self.op2.has_permission(request, view)
        )

class OR:
    """Logical OR operation for permissions."""
    def has_permission(self, request, view):
        return (
            self.op1.has_permission(request, view) or
            self.op2.has_permission(request, view)
        )

class NOT:
    """Logical NOT operation for permissions."""
    def has_permission(self, request, view):
        return not self.op1.has_permission(request, view)

Usage Examples

Basic Authentication Setup

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
}

# View-level configuration
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated

class BookViewSet(ModelViewSet):
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated]
    queryset = Book.objects.all()
    serializer_class = BookSerializer

Token Authentication Usage

# Create token for user
from rest_framework.authtoken.models import Token

user = User.objects.get(username='testuser')
token, created = Token.objects.get_or_create(user=user)
print(f"Token: {token.key}")

# Client request with token
import requests

headers = {'Authorization': f'Token {token.key}'}
response = requests.get('http://api.example.com/books/', headers=headers)

Custom Permission Class

from rest_framework.permissions import BasePermission

class IsOwnerOrReadOnly(BasePermission):
    """
    Custom permission to only allow owners to edit objects.
    """
    def has_object_permission(self, request, view, obj):
        # Read permissions for any request
        if request.method in ['GET', 'HEAD', 'OPTIONS']:
            return True
        
        # Write permissions only to owner
        return obj.owner == request.user

# Usage in view
class BookViewSet(ModelViewSet):
    permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
    queryset = Book.objects.all()
    serializer_class = BookSerializer

Permission Operators

from rest_framework.permissions import IsAuthenticated, IsAdminUser

# Combine permissions with operators
class MyView(APIView):
    # Must be admin OR (authenticated AND owner)
    permission_classes = [IsAdminUser | (IsAuthenticated & IsOwnerOrReadOnly)]
    
    # Must NOT be anonymous (same as IsAuthenticated) 
    permission_classes = [~AllowAny]

Function-Based View Authentication

from rest_framework.decorators import api_view, authentication_classes, permission_classes
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated

@api_view(['GET'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def protected_view(request):
    return Response({'message': f'Hello {request.user.username}!'})

Custom Authentication Backend

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from django.contrib.auth import get_user_model

User = get_user_model()

class CustomTokenAuthentication(BaseAuthentication):
    def authenticate(self, request):
        token = request.META.get('HTTP_X_API_KEY')
        if not token:
            return None
        
        try:
            user = User.objects.get(api_key=token)
        except User.DoesNotExist:
            raise AuthenticationFailed('Invalid API key')
        
        return (user, token)

Throttling

Rate limiting classes for controlling request frequency and preventing API abuse. Throttling works alongside authentication and permissions to provide comprehensive access control.

Throttling Classes

class BaseThrottle:
    """
    Base class for all throttling implementations.
    """
    def allow_request(self, request, view):
        """
        Return True if request should be allowed, False otherwise.
        
        Args:
            request: DRF Request object
            view: API view being accessed
            
        Returns:
            bool: Whether request is allowed
        """
    
    def wait(self):
        """
        Return recommended number of seconds to wait before next request.
        
        Returns:
            int or None: Seconds to wait, or None if not available
        """

class SimpleRateThrottle(BaseThrottle):
    """
    Cache-based rate throttling with configurable rates.
    """
    cache = None  # Cache backend
    timer = None  # Time function
    rate = None   # Rate string like "100/day"
    scope = None  # Throttle scope for settings lookup
    
    def get_cache_key(self, request, view):
        """
        Return unique cache key for throttling.
        Must be overridden.
        
        Returns:
            str or None: Cache key or None to skip throttling
        """

class AnonRateThrottle(SimpleRateThrottle):
    """
    Throttle anonymous requests by IP address.
    """
    scope = 'anon'

class UserRateThrottle(SimpleRateThrottle):
    """
    Throttle authenticated requests by user.
    """
    scope = 'user'

class ScopedRateThrottle(SimpleRateThrottle):
    """
    Throttle requests based on view-specific scope.
    """
    scope_attr = 'throttle_scope'

Throttling Configuration

Configure throttling in Django settings:

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle'
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/day',
        'user': '1000/day',
        'burst': '60/min',
        'sustained': '1000/day'
    }
}

View-Level Throttling

from rest_framework.throttling import UserRateThrottle, AnonRateThrottle

class BookViewSet(ModelViewSet):
    throttle_classes = [UserRateThrottle, AnonRateThrottle]
    queryset = Book.objects.all()
    serializer_class = BookSerializer

# Custom throttle scope
class ContactViewSet(ModelViewSet):
    throttle_classes = [ScopedRateThrottle]
    throttle_scope = 'contacts'
    queryset = Contact.objects.all()
    serializer_class = ContactSerializer

Custom Throttle Class

from rest_framework.throttling import UserRateThrottle

class BurstRateThrottle(UserRateThrottle):
    scope = 'burst'

class LoginRateThrottle(AnonRateThrottle):
    scope = 'login'
    
    def get_cache_key(self, request, view):
        if request.user.is_authenticated:
            return None  # Only throttle anonymous users
        
        ident = self.get_ident(request)
        return self.cache_format % {
            'scope': self.scope,
            'ident': ident
        }

Function-Based View Throttling

from rest_framework.decorators import api_view, throttle_classes
from rest_framework.throttling import UserRateThrottle

@api_view(['POST'])
@throttle_classes([UserRateThrottle])
def send_message(request):
    return Response({'message': 'Message sent'})

Constants and Utilities

# Safe HTTP methods (read-only)
SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS')

# Utility functions
def get_authorization_header(request):
    """
    Extract authorization header from request.
    
    Args:
        request: Django request object
        
    Returns:
        bytes: Authorization header value
    """

Install with Tessl CLI

npx tessl i tessl/pypi-djangorestframework

docs

auth-permissions.md

content-negotiation.md

decorators.md

fields-validation.md

generic-views.md

index.md

pagination-filtering.md

request-response.md

routers-urls.md

serializers.md

status-exceptions.md

testing.md

views-viewsets.md

tile.json