CreaRack-SL

Multi-tenancy en CreaRack Pro

Conceptoactiveverificado 2026-04-23#saas#multi-tenancy#rls#security

Multi-tenancy en CreaRack Pro

Qué es

CreaRack Pro es una aplicación SaaS multi-tenant donde Organization actúa como tenant raíz. Cada User pertenece a exactamente una Organization mediante FK, y todos los recursos principales (Rack, Blueprint, Stencil, SystemLog) llevan un campo organization que los ancla al tenant propietario. El aislamiento se aplica en tres capas: filtros ORM en cada servicio, middleware de Row-Level Security (RLS) que instruye a PostgreSQL con una variable de transacción, y middleware de module-gating que controla qué funcionalidades puede usar el tenant según su plan.

Por qué existe

Sin aislamiento explícito, un bug en cualquier query podría devolver datos de otro cliente. La regulación de datos y los SLAs de cada plan exigen que un tenant nunca acceda ni afecte los recursos de otro. Adicionalmente, los tenants en planes inferiores no deben poder saturar la cola de tareas en background (“noisy neighbor problem”).

Componentes

  • Organization (core/models.py): tenant raíz. Campos relevantes: is_active, deleted_at (soft-delete), plan, extra_modules. get_active_modules() combina módulos core siempre-activos y los extra del tenant.
  • User (core/models.py): extiende AbstractUser, FK a Organization, campo role (admin, operator, readonly).
  • ModuleGatingMiddleware (core/middleware/module_gating.py): resuelve la org del usuario, verifica is_active y is_deleted, bloquea con HTTP 403 cualquier path cuyo módulo requerido no esté en el set activo. Superusuarios bypassean.
  • TenantRLSMiddleware (core/middleware/tenant_rls.py): activa transacción atómica por request y ejecuta SET LOCAL app.current_org_id = <id> en PostgreSQL. Las políticas RLS leen esta variable para restringir filas como red de seguridad. Compatible con pgBouncer transaction pooling porque SET LOCAL se limpia al commit.
  • tenant_throttle (core/tenant_fairness.py): decorador para tareas Huey. Usa Django cache para contar tareas concurrentes por org, limitando según plan (starter: 3, pro: 10, custom: 25). Backoff de 5s si supera.
  • Services de apps (racks/services/racks.py, blueprints/services/blueprints.py): cada método recibe organization explícito y aplica .filter(organization=...) en querysets.

Flujos

Request HTTP normal:

  1. AuthenticationMiddleware identifica User y carga Organization via FK.
  2. ModuleGatingMiddleware verifica que el path esté cubierto por el plan. Si no, 403.
  3. TenantRLSMiddleware abre transacción atómica y ejecuta SET LOCAL app.current_org_id = <org.id>. Políticas RLS activas.
  4. La view llama al servicio (RackService.list_racks(organization=request.user.organization)) que filtra en ORM. Si el filtro falla, RLS bloquea filas de otros tenants a nivel BD.

Soft-delete de Organization:

  1. Admin setea org.deleted_at = now() y guarda.
  2. Siguiente request: ModuleGatingMiddleware detecta org.is_deleted == True y retorna 403 con errors/account_paused.html (o JSON si /api/*).
  3. Datos permanecen aislados y recuperables limpiando deleted_at.

Tarea background con fairness:

  1. View encola tarea Huey decorada con @tenant_throttle() (sin límite fijo, usa el del plan).
  2. Decorador consulta cache tenant_tasks:<org_id>. Si supera el límite del plan, espera 5s y reintenta.
  3. Incrementa contador antes de ejecutar, decrementa en finally.
  • entity--core--model--organization — tenant raíz.
  • entity--core--model--user — FK a Organization.
  • concept--saas--module-gating — lógica de planes y acceso granular.

Véase también

  • [[entity—core—model—organization]]
  • [[entity—core—model—user]]
  • [[concept—saas—module-gating]]
  • [[decision—20260101—django-ninja-vs-drf]] — Framework REST que expone las APIs multi-tenant