Files
igny8/docs/00-SYSTEM/MULTITENANCY.md
IGNY8 VPS (Salman) 6a4f95c35a docs re-org
2025-12-09 13:26:35 +00:00

80 lines
6.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 users 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 serializers 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.