Permissions¶
The core permission layer defines DRF permission classes used to protect API endpoints.
These permissions ensure that:
- requests are authenticated
- a valid organization context is present
- users have the correct type of access (internal vs customer)
They act as the entry gate before any view or service logic is executed.
Purpose¶
Permissions answer:
- Is this request allowed to proceed?
- Is the user authenticated?
- Is there a valid organization context?
They operate at the request level, not the business logic level.
Relationship to Policies¶
Permissions and policies serve different roles:
Permissions (this layer)¶
- tied to DRF
- operate on requests
- enforce authentication and context
- run before views
Policies¶
- domain-level logic
- reusable across services
- independent of HTTP
Available Permission Classes¶
HasCurrentOrg¶
Ensures that the request is scoped to a valid internal organization.
Behavior¶
- allows access if
request.orgis already set - otherwise attempts to resolve org from headers
- denies access if:
- user is not authenticated
- org cannot be resolved
- user is not a member of the org
Usage¶
Apply to all endpoints that operate on internal organization data.
HasCurrentCustomerOrg¶
Ensures that the request is scoped to a valid customer organization.
Behavior¶
- resolves
request.customer_orgfrom header: - X-CUSTOMER-ORG-ID
- validates:
- org exists and is active
- org is of type customer
- user has membership (unless superuser)
Usage¶
Apply to customer-facing endpoints.
IsInternalUser¶
Ensures that the user has at least one active internal org membership.
Behavior¶
- requires authentication
- checks:
- user has at least one active OrgMembership
Usage¶
Useful for: - general internal access endpoints - dashboards or shared resources
IsCustomerUser¶
Ensures that the user has at least one active customer org membership.
Behavior¶
- requires authentication
- checks:
- user has at least one active CustomerMembership
Usage¶
Useful for: - customer portal access - external-facing features
Permission Flow¶
flowchart TD
A[Incoming Request] --> B[Permission Check]
B --> C{Authenticated?}
C -->|No| D[Reject]
C -->|Yes| E[Resolve Org]
E --> F{Valid Context?}
F -->|No| D
F -->|Yes| G[Allow Access]
¶
flowchart TD
A[Incoming Request] --> B[Permission Check]
B --> C{Authenticated?}
C -->|No| D[Reject]
C -->|Yes| E[Resolve Org]
E --> F{Valid Context?}
F -->|No| D
F -->|Yes| G[Allow Access]Usage in Views¶
Permissions are applied at the view level.
Example:
permission_classes = [IsAuthenticated, HasCurrentOrg]
This ensures: - user is authenticated - request.org is valid and attached
Execution Order¶
Permissions run before: - serializer validation - view logic - service calls
If a permission fails: - the request is rejected immediately - no business logic is executed
Error Behavior¶
If a permission fails: - response status is typically 403 Forbidden - message explains the failure reason
Example:
{ "detail": "X-ORG-SLUG or X-ORG-ID required (and you must be a member)." }
Best Practices¶
Always use HasCurrentOrg¶
For any endpoint that interacts with internal org data.
Keep views clean¶
Do not: - manually resolve org in views - perform membership checks in views
Combine permissions when needed¶
Example:
permission_classes = [ IsAuthenticated, HasCurrentOrg, ]
Use policies for deeper checks¶
Permissions should not replace policies.
Example: - permission -> ensures request is valid - policy -> ensures user can perform specific action
Common Mistakes¶
Missing HasCurrentOrg¶
Leads to: - missing request.org - potential data leaks
Duplicating permission logic in views¶
Avoid: - manual membership checks - inline role checks
Using permissions for business rules¶
Permissions should NOT: - enforce workflow constraints - validate domain state - replace service logic
Example Flow¶
- Request hits endpoint
- DRF evaluates permissions
- HasCurrentOrg resolves org
- request.org is attached
- View executes
- Service performs business logic
Summary¶
The permission layer ensures that: - every request is authenticated - every request has a valid org context - invalid requests are rejected early
It is the first line of defense in the backend architecture.