Skip to content

Permissions in ORMBridge ​

ORMBridge provides a robust permissions system that controls data access and modification rights. The system works at multiple levels and integrates with your existing authentication system.

Key Concepts ​

Permission Levels ​

ORMBridge's permission system operates at four levels:

  1. Model-level: Controls which models a user can access
  2. Action-level: Defines what operations (read, create, update, delete) a user can perform
  3. Object-level: Specifies which instances a user can access
  4. Field-level: Determines which fields a user can view or modify

Note: If a model field is enabled that points to a non-ORMBridge model, this will raise an error during app initialization. You must either:

  • Register the related model with ORMBridge
  • Restrict access to the field that references the unregistered model

Example error:

ValueError: Model 'django_app.orderitem' exposes relation 'user' to unregistered model 'auth.user' through visible fields. Please register 'auth.user' with ORMBridge or restrict access to this field.

Setting Up Permissions ​

Registering Permissions ​

Permission classes must be registered with ORMBridge to take effect:

python
# Django example
from ormbridge.adaptors.django.config import registry
from myapp.permissions import RoleBasedPermission

# Register permissions for a specific model
registry.register(Product, permissions=[RoleBasedPermission])

Multiple permission classes can be registered for a model. ORMBridge takes the union of allowed actions and fields from all applicable permission classes.

The AbstractPermission Interface ​

Custom permissions in ORMBridge implement the AbstractPermission interface:

python
from ormbridge.core.interfaces import AbstractPermission
from ormbridge.core.types import ActionType


class MyCustomPermission(AbstractPermission):
    def filter_queryset(self, request, queryset):
        """Filter the queryset to objects the user can access"""
        pass
        
    def allowed_actions(self, request, model):
        """Determine what actions a user can perform on a model class"""
        pass
        
    def allowed_object_actions(self, request, obj, model):
        """Determine what actions a user can perform on a specific object"""
        pass

    def visible_fields(self, request, model):
        """Control which fields of the model the user can see"""
        pass

    def editable_fields(self, request, model):
        """Determine which fields a user can modify when updating objects"""
        pass
    
    def create_fields(self, request, model):
        """Specify which fields a user can set when creating new objects"""
        pass

For field-level permission methods, you can use the string "__all__" to indicate all fields should be allowed.

Handling Nested Models ​

ORMBridge provides special handling for permissions with related models:

  1. Representation with depth=0: Related models are represented with their full data according to their own permissions.

  2. Expanded Representation (depth>0): Related models are fully expanded with permission rules applied to each nested model.

Key behaviors:

  • If a user can't see a relationship field, they won't see the related data at all
  • Models that are not directly accessible to a user can become visible when attached to a model the user can see
  • Always specify appropriate visible_fields permissions for all models, even those not directly accessed

Note: Because related models become visible through relationships, it's critical to properly configure visible_fields for all registered models to prevent unintended data exposure.

Simple Example ​

Note: These examples assume that you've already set up your demo models (e.g., a blog with Post and Category models) in a Django app and registered them with ORMBridge.

Here's a basic permission class that varies access based on user type:

python
class BasicPermission(AbstractPermission):
    def filter_queryset(self, request, queryset):
        if request.user.is_superuser:
            return queryset  # Superusers see everything
        return queryset.filter(is_public=True)  # Others see only public items
        
    def allowed_actions(self, request, model):
        if request.user.is_superuser:
            # Superusers can do everything
            return {ActionType.READ, ActionType.CREATE, ActionType.UPDATE, ActionType.DELETE}
        return {ActionType.READ}  # Others can only read
        
    def visible_fields(self, request, model):
        if request.user.is_superuser:
            return "__all__"  # Superusers see all fields
        return {'id', 'name', 'description', 'is_public'}  # Others see basic fields

Performance Best Practices ​

  1. Use database filters in filter_queryset instead of in-memory filtering
  2. Implement bulk_operation_allowed for more efficient bulk operations
  3. Optimize queries by using appropriate join strategies
  4. Minimize permission complexity for frequently accessed models

Client-Side Integration ​

ORMBridge automatically enforces permissions on the server, but you can reflect these permissions in your client UI:

typescript
// Check if the user can perform an action before showing UI controls
try {
  await Product.objects.get({ id: 123 });
  showEditButton();  // Show edit button since we have access
} catch (error) {
  if (error instanceof PermissionDenied) {
    hideEditButton();  // Hide edit button if permission denied
  }
}

Debugging Permissions ​

Enable detailed logging to troubleshoot permission issues:

python
# settings.py
LOGGING = {
    'loggers': {
        'ormbridge': {
            'handlers': ['console'],
            'level': 'DEBUG',
        },
    },
}

Not MIT Licensed - See Licensing section for details