docs re-org
This commit is contained in:
79
docs/00-SYSTEM/MULTITENANCY.md
Normal file
79
docs/00-SYSTEM/MULTITENANCY.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user