docs: discovery blueprint + multi-provider design (#8)
CI / backend (push) Successful in 52s
CI / frontend (push) Successful in 14s
Deploy Staging / deploy (push) Successful in 18s
CI / backend (pull_request) Successful in 52s
CI / frontend (pull_request) Successful in 15s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 55s
Security / secrets (pull_request) Successful in 4s
Security / dependencies (pull_request) Successful in 54s
CI / backend (push) Successful in 52s
CI / frontend (push) Successful in 14s
Deploy Staging / deploy (push) Successful in 18s
CI / backend (pull_request) Successful in 52s
CI / frontend (pull_request) Successful in 15s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 55s
Security / secrets (pull_request) Successful in 4s
Security / dependencies (pull_request) Successful in 54s
This commit was merged in pull request #8.
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
# 06 — Security Model (Part 5)
|
||||
|
||||
Builds on the existing hardening (read-only scope, encrypted tokens, IDOR global query
|
||||
filters, SSRF egress guard, non-root containers, confirmed destructive actions) and adds
|
||||
what multi-user + admin + multi-provider require.
|
||||
|
||||
## RBAC
|
||||
- Roles: **Admin** · **Member** (small-team, one org). First user bootstraps as Admin.
|
||||
- Enforced by **policy-based authorization** at the API (ASP.NET Core policies), not in the UI.
|
||||
|
||||
| Capability | Member | Admin |
|
||||
|------------|:------:|:-----:|
|
||||
| Read/manage **own** mail & accounts | ✅ | ✅ |
|
||||
| Own user settings | ✅ | ✅ |
|
||||
| Link/unlink **own** provider accounts | ✅ | ✅ |
|
||||
| View/manage **other users** | ❌ | ✅ |
|
||||
| Toggle **feature flags** (incl. AI global) | ❌ | ✅ |
|
||||
| Enable/disable **providers** | ❌ | ✅ |
|
||||
| **Maintenance mode**, system settings | ❌ | ✅ |
|
||||
| View **audit log** & monitoring | ❌ | ✅ |
|
||||
- **No cross-user data access, ever** — Admin manages *accounts/flags/system*, **not** other
|
||||
users' email contents (privacy). Admin power is over the *platform*, not people's inboxes.
|
||||
|
||||
## OAuth token storage
|
||||
- Refresh/access tokens **encrypted at rest** with the Data Protection API (AES); keys persist
|
||||
to the mounted `/keys` volume (existing). Decrypted **only in-memory** for the moment of an
|
||||
API call. **Never logged, never sent to the browser.**
|
||||
- 1:1 `provider_tokens` per account; rotation timestamped; a compromised/rotated token is
|
||||
replaced atomically. Token columns are `bytea` ciphertext, not readable in DB dumps.
|
||||
|
||||
## Session handling
|
||||
- Opaque **server-side sessions** (DB-backed) + HttpOnly/Secure/SameSite cookie; **id rotated
|
||||
on login** (anti-fixation); idle + absolute expiry; server-side **revocation** (logout,
|
||||
sign-out-everywhere, admin revoke, role change). CSRF via SameSite + token.
|
||||
|
||||
## Admin access protection
|
||||
- Admin routes require the **Admin policy**; sensitive mutations (toggle AI global, disable a
|
||||
provider, suspend a user, enter maintenance) require a **confirmation / step-up** and are
|
||||
**rate-limited**.
|
||||
- **Every admin action is audit-logged** (`audit_logs`: actor, action, target, metadata, ip,
|
||||
time) — append-only.
|
||||
- First-admin bootstrap is one-time; afterwards admin is grant-only by an existing Admin
|
||||
(logged). Guard against privilege escalation: role changes are Admin-only + audited + force
|
||||
session refresh.
|
||||
|
||||
## API security boundaries
|
||||
- **Per-user isolation** via EF **global query filters** (extended to `account_id`/`user_id`)
|
||||
so a query can *never* return another user's rows — the IDOR safeguard, now multi-account.
|
||||
- **Input validation** (FluentValidation) on all DTOs; **mass-assignment safe** (explicit DTOs,
|
||||
no entity binding).
|
||||
- **Rate limiting** on auth, admin, search, and AI endpoints.
|
||||
- **SSRF egress guard** (existing) constrains all outbound calls — provider APIs, IMAP hosts,
|
||||
Ollama, and any opt-in cloud AI — to an allowlist; user-supplied IMAP hosts are validated.
|
||||
- **Security headers** (CSP, HSTS, X-Frame-Options, etc.) via the reverse proxy/API; strict CORS.
|
||||
|
||||
## Multi-provider & AI specifics
|
||||
- **Least-privilege scopes** per provider; extra scopes added per-feature with consent.
|
||||
- **Provider isolation:** disabling a provider flag revokes its use cleanly; per-account tokens
|
||||
are independent (one reauth doesn't affect others).
|
||||
- **Prompt injection:** email content is untrusted → LLM output is **advisory only, never
|
||||
triggers actions**; a human/rule confirms. AI runs **local by default**; cloud AI is explicit
|
||||
opt-in with per-feature consent + egress logging.
|
||||
- **Attachments/vision:** sandboxed parsing, size/type limits, never executed.
|
||||
|
||||
## Threat model (summary)
|
||||
| Threat | Mitigation |
|
||||
|--------|------------|
|
||||
| Account hijack via linking | Must authenticate as target user; unique `(provider, sub)`; linking an owned identity blocked |
|
||||
| Token theft / DB exposure | Encryption at rest; tokens never in logs/browser; rotation |
|
||||
| Privilege escalation | Admin-only role changes, audited, session refresh; policy checks server-side |
|
||||
| IDOR / cross-user leakage | Global query filters on user_id/account_id |
|
||||
| CSRF / session fixation | SameSite + token; session id rotation; server-side revoke |
|
||||
| SSRF (providers/IMAP/AI) | Egress allowlist guard; validate user-supplied hosts |
|
||||
| Prompt injection | AI advisory-only; never acts; local-first |
|
||||
| Mass admin abuse | Rate limit + step-up + full audit trail |
|
||||
|
||||
## Non-negotiables
|
||||
Admins manage the platform, **not** users' inboxes · tokens encrypted & browser-invisible ·
|
||||
every privileged action audited · AI never required and never acts autonomously.
|
||||
Reference in New Issue
Block a user