Skip to content

Authorization

Overview

Authorization is based on RBAC (Role-Based Access Control) with tenant isolation enforced at the database level.

Roles

Role Description
admin Full system access
operator Manage shipments and operations
courier Field operations (scan, deliver)
viewer Read-only access

Permission Matrix

See User Roles for detailed matrix.

Implementation

Decorator-based Authorization

from functools import wraps
from fastapi import HTTPException, status

def require_role(*allowed_roles):
    def decorator(func):
        @wraps(func)
        async def wrapper(*args, **kwargs):
            user = get_current_user()
            if user.role not in allowed_roles:
                raise HTTPException(status_code=403, detail="Forbidden")
            return await func(*args, **kwargs)
        return wrapper
    return decorator

@app.get("/shipments")
@require_role("admin", "operator")
async def get_shipments():
    ...

Tenant Isolation (RLS)

All tenant-scoped queries are enforced via PostgreSQL Row-Level Security:

CREATE POLICY tenant_isolation ON shipments
USING (tenant_id = current_setting('app.current_tenant')::uuid);

The application sets app.current_tenant on each database connection based on the authenticated user's tenant.

Best Practices

  1. Always check role before sensitive operations
  2. Use RLS as defense in depth (don't rely solely on application code)
  3. Audit trail - Log authorization failures for security monitoring
  4. Principle of least privilege - Only grant minimum permissions needed

Tenant Isolation Rules

API endpoints enforce tenant isolation at the application level:

Entity Filter By Endpoints
Shipment tenant_id list, get, update, delete
Bag user_id list, get, update, delete, create
Flight user_id list, get, update, delete, create
# Example: List shipments filtered by tenant
@router.get("/shipments")
async def list_shipments(
    current_user=Depends(get_current_active_user),
    session: AsyncSession = Depends(get_session),
):
    repo = ShipmentRepositoryImpl(session)
    # Application-level filtering by tenant_id
    shipments = await repo.get_all(tenant_id=current_user.tenant_id)
    return shipments

Additionally, RLS policies are enforced at the PostgreSQL level for all tenant-scoped tables as a defense-in-depth measure.

Ver también: Authentication · Tenant Isolation Rules