# Multitenancy Model ## Purpose Explain how tenant, site, and sector isolation is enforced across models, middleware, and viewsets, based on the current implementation. ## Code Locations (exact paths) - Tenant base models: `backend/igny8_core/auth/models.py` (`AccountBaseModel`, `SiteSectorBaseModel`) - Core entities: `backend/igny8_core/auth/models.py` (`Account`, `Plan`, `Site`, `Sector`, `Industry`, `IndustrySector`, `SeedKeyword`, `SiteUserAccess`, `User`) - Middleware for context: `backend/igny8_core/auth/middleware.py` - DRF base viewsets: `backend/igny8_core/api/base.py` - Auth utilities and JWT: `backend/igny8_core/api/authentication.py`, `backend/igny8_core/auth/utils.py` - URL routing (module mounting): `backend/igny8_core/urls.py` ## High-Level Responsibilities - Enforce per-account isolation for all models carrying an `account` FK. - Enforce per-site and per-sector scoping for content models via `SiteSectorBaseModel`. - Inject tenant context on every request (session or JWT/API key), then apply scoping in base viewsets. - Allow controlled overrides for admins, developers, and system accounts. ## Detailed Behavior - `AccountBaseModel` adds an `account` FK plus timestamps and indexes; all tenant-scoped models inherit this to guarantee account linkage. - `SiteSectorBaseModel` extends `AccountBaseModel` with `site` and `sector` FKs, indexes on `(account, site, sector)`, and a save hook that sets `account` from `site` and validates that `sector` belongs to the same `site`; raises validation errors on mismatch. - `AccountContextMiddleware` sets `request.account` by refreshing the authenticated session user (with account and plan) or by decoding JWT tokens; it rejects requests when account is missing or plan is inactive, returning structured JSON and logging out session users. It skips admin and auth endpoints to avoid interference. - JWT authentication (`api/authentication.py`) decodes tokens and sets `request.account` from the token payload; API key authentication sets both `request.account` and `request.site` for WordPress bridge calls. - `AccountModelViewSet` auto-filters querysets by `account` when models expose that field. It bypasses filtering for admins/developers/system-account users; otherwise, it uses `request.account` or falls back to the authenticated user’s account. Creates set `account` on save when present. - `SiteSectorModelViewSet` extends the above to filter by site/sector if those fields exist, using request query parameters and tenancy context. - `User` role helpers (`is_admin_or_developer`, `is_system_account_user`) and account checks gate override behavior. - `SiteUserAccess` provides explicit per-site access for non-admin roles; `User.get_accessible_sites` respects system account, developer, owner/admin, and granted access rules. ## Data Structures / Models Involved (no code) - `Account`: tenant container with plan, credits, billing fields, status, and retention settings. - `Plan`: defines limits (max users/sites/industries/author profiles), credit inclusion, Stripe IDs. - `Site`: belongs to an account, optionally an industry; includes status, hosting type, legacy WP fields, and SEO metadata. - `Sector`: belongs to a site and industry sector template; enforces account alignment and plan-based max-sector validation. - `Industry`, `IndustrySector`, `SeedKeyword`: global reference data not bound to a single account. - `SiteUserAccess`: explicit many-to-many grants between users and sites. - `User`: links to account with role-based access flags. ## Execution Flow - Request enters middleware; `AccountContextMiddleware` determines `request.account` (session or JWT/API key), validating plan status. - Viewsets inheriting from `AccountModelViewSet`/`SiteSectorModelViewSet` filter querysets by `account` (and site/sector when present) before pagination/serialization. - Object creation sets `account` automatically when the serializer’s model has that field; site/sector-based models validate alignment on save. - Admin/developer/system-account users skip account filtering; other users remain constrained. ## Cross-Module Interactions - All module viewsets depend on the base viewsets for scoping. - Automation, planner, writer, billing, linker, optimizer, publisher, and integration models inherit from the tenancy bases to enforce account/site/sector ownership. - API key flows for WordPress set `request.site`, enabling integration-specific logic to run in a site-aware context. ## State Transitions (if applicable) - Account status changes (active, suspended, trial, cancelled) and plan activation directly affect access through middleware plan validation. - Sector creation enforces plan-based limits for active sectors per site. ## Error Handling - Middleware returns JSON errors for missing account or inactive plan, with HTTP 403 or 402 semantics and logs out session users. - Base viewsets wrap CRUD operations in unified responses; validation failures or missing objects are returned in structured error payloads. - Save-time validation on `SiteSectorBaseModel` and `Sector` raises validation errors when site/sector alignment or plan limits are violated. ## Tenancy Rules - Every tenant-scoped model carries `account`; site/sector-aware models carry `site` and `sector` and must align to the same account. - Middleware populates `request.account`; base viewsets enforce filtering unless the user is an admin/developer/system-account member. - System accounts (`aws-admin`, `default-account`, `default`) and privileged roles can bypass scoping; protected from deletion via guard clauses. ## Billing Rules (if applicable) - Middleware requires an active plan before allowing requests (except auth/admin paths). Credits, charges, and plan enforcement are handled in billing modules (documented elsewhere). ## Background Tasks / Schedulers (if applicable) - Celery tasks inherit tenant context via payloads supplied by calling viewsets/services; the tenancy bases ensure stored records retain `account`/`site`/`sector`. ## Key Design Considerations - Tenancy is enforced as early as middleware to avoid leakage in view logic. - Base viewsets centralize scoping and unified responses to reduce duplication across modules. - Role-based overrides exist for ops and system accounts; safeguards prevent system account deletion. ## How Developers Should Work With This Module - Inherit from `AccountBaseModel` or `SiteSectorBaseModel` for any new tenant/site/sector data models. - Inherit viewsets from `AccountModelViewSet` or `SiteSectorModelViewSet` to get automatic scoping and unified responses. - Do not bypass `AccountContextMiddleware`; ensure new endpoints live under `/api/v1/*` and rely on the auth stack (API key → JWT → session). - Validate that new background tasks carry account/site/sector identifiers so downstream saves remain scoped.