6.7 KiB
6.7 KiB
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
accountFK. - 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
AccountBaseModeladds anaccountFK plus timestamps and indexes; all tenant-scoped models inherit this to guarantee account linkage.SiteSectorBaseModelextendsAccountBaseModelwithsiteandsectorFKs, indexes on(account, site, sector), and a save hook that setsaccountfromsiteand validates thatsectorbelongs to the samesite; raises validation errors on mismatch.AccountContextMiddlewaresetsrequest.accountby 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 setsrequest.accountfrom the token payload; API key authentication sets bothrequest.accountandrequest.sitefor WordPress bridge calls. AccountModelViewSetauto-filters querysets byaccountwhen models expose that field. It bypasses filtering for admins/developers/system-account users; otherwise, it usesrequest.accountor falls back to the authenticated user’s account. Creates setaccounton save when present.SiteSectorModelViewSetextends the above to filter by site/sector if those fields exist, using request query parameters and tenancy context.Userrole helpers (is_admin_or_developer,is_system_account_user) and account checks gate override behavior.SiteUserAccessprovides explicit per-site access for non-admin roles;User.get_accessible_sitesrespects 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;
AccountContextMiddlewaredeterminesrequest.account(session or JWT/API key), validating plan status. - Viewsets inheriting from
AccountModelViewSet/SiteSectorModelViewSetfilter querysets byaccount(and site/sector when present) before pagination/serialization. - Object creation sets
accountautomatically 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
SiteSectorBaseModelandSectorraises validation errors when site/sector alignment or plan limits are violated.
Tenancy Rules
- Every tenant-scoped model carries
account; site/sector-aware models carrysiteandsectorand 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
AccountBaseModelorSiteSectorBaseModelfor any new tenant/site/sector data models. - Inherit viewsets from
AccountModelViewSetorSiteSectorModelViewSetto 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.