🏗️ Project Architecture & Philosophy: Django ReBAC
This document outlines the core philosophy behind the django-rebac package, why it was created, and the architectural patterns it leverages to provide enterprise-grade authorization for Django developers.
1. Why We Built This Project
In a standard Django application, authorization logic (checking if a user can read, edit, or delete an object) is often scattered across views, serializers, and custom permission classes. This creates a tightly coupled system where changing a security rule requires rewriting and deploying Python code.
Furthermore, as systems scale into microservices, relying solely on the Django ORM for permissions becomes impossible.
We built django-rebac to solve these problems by adopting the Google Zanzibar model via the ReBAC engines (like OpenFGA). The core philosophy is 100% Decoupling: your application should not evaluate permissions; it should merely ask a dedicated authorization engine for the answer.
2. The Core Architectural Patterns
To make this decoupling seamless and reliable, the package implements several advanced architectural patterns under the hood.
The Transactional Outbox Pattern
A major challenge in distributed systems is "Dual Writes"—saving data to your local database and simultaneously sending an HTTP request to an external service (like OpenFGA). If the network fails, your database and the ReBAC engine become out of sync.
This package solves this using the Transactional Outbox Pattern:
- When a Django model is saved or deleted, the
RebacModelSyncMixinintercepts the action. - Instead of calling the ReBAC engine directly, it generates the necessary relationship tuples (e.g., "user:bob is the owner of folder:123") and saves them to a local
RebacSyncOutboxtable within the same atomic database transaction. - Once the database commit is successful, a Celery task (
process_rebac_outbox_batch) is triggered to asynchronously sweep the outbox and push the tuples to the the ReBAC engine server.
This guarantees eventual consistency. If the the ReBAC engine server goes down, the Celery task will utilize exponential backoff to retry the batch later.
Declarative Security
The package embraces a declarative approach. Developers do not write complex logic to sync data or check permissions. Instead, they define strict, type-safe dataclass configurations (RebacModelConfig on models or RebacViewConfig on views), and the underlying mixins handle the complex graph traversals and API calls.
3. Developer Capabilities (What this enables)
By installing this package, developers gain access to a suite of highly abstracted, powerful capabilities:
- Zero-Boilerplate Synchronization: By simply adding the
RebacModelSyncMixinto a model and defining therebac_configattribute with theRebacModelConfigdataclass, the package automatically calculates tuple diffs on everysave()ordelete()and syncs them to the ReBAC engine. - Automatic Identity Extraction: The
GatewayIdentityMiddlewareautomatically intercepts internal traffic from your gateway, dynamically maps incoming headers (likeX-User-IdorX-Context-Org-Id) based on your settings, and attaches formatted ReBAC context strings (e.g.,user:123) to the request lifecycle without any hardcoded constraints. - Plug-and-Play Endpoint Protection: Developers can secure DRF views instantly using the
IsRebacAuthorizedpermission class. It automatically reads therebac_configattribute (an instance ofRebacViewConfig) to verify access before a view executes. It even supports custom ViewSet actions mapping via theaction_relationsdictionary within that configuration. - Parent Cascading Validation: When creating new objects via POST requests, the framework automatically verifies if the user holds the required role on the parent object (using
create_scope_typeandcreate_relationdefined in yourRebacViewConfig) before allowing the creation. - Secure Queryset Filtering: The
RebacViewMixin(and related mixins) automatically intercepts standard DRF list views. It asks the ReBAC engine for an array of allowed IDs and injects anid__infilter into the Django ORM queryset, ensuring users never see objects they don't have access to.