tasks ready
This commit is contained in:
84
docs/adr/ADR-0005-auth-rbac.md
Normal file
84
docs/adr/ADR-0005-auth-rbac.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# [DEF:ADR-0005:ADR]
|
||||
# @STATUS ACTIVE
|
||||
# @PURPOSE Define the authentication and authorization architecture for ss-tools: local auth, optional ADFS SSO federation, RBAC model, session management, and the security boundary between ss-tools (DevOps privileges) and Superset (BI privileges).
|
||||
# @RELATION DEPENDS_ON -> [ADR-0001:ADR]
|
||||
# @RELATION DEPENDS_ON -> [ADR-0003:ADR]
|
||||
# @RELATION CALLS -> [ADR-0004:ADR]
|
||||
# @RATIONALE ss-tools manages operations that can modify production Superset instances (dashboard migration, backup, deployment). Unauthenticated or under‑authorized access to these operations is a critical security risk. A dedicated RBAC system ensures that DevOps privileges (manage deployments) are cleanly separated from BI privileges (view dashboards) and that actions are auditable.
|
||||
# @RATIONALE Local auth (bcrypt + JWT) is the primary path because ss-tools must work in air‑gapped enterprise deployments where external identity providers are unavailable. ADFS SSO is an optional federation layer for organizations that already have Active Directory.
|
||||
# @RATIONALE Role‑Based Access Control (RBAC) was chosen over Attribute‑Based Access Control (ABAC) because: (a) the system has a small, well‑defined set of operations (deploy, backup, migrate, view), (b) RBAC maps naturally to organizational roles (admin, analyst, operator), (c) ABAC adds complexity without proportional benefit for this scope.
|
||||
# @REJECTED Delegating all auth to Superset — rejected because ss-tools must operate when Superset is down, and Superset's auth model is designed for BI users, not DevOps operators with cross‑environment privileges.
|
||||
# @REJECTED OAuth2 social login (Google, GitHub) as primary path — rejected because enterprise deployments require air‑gapped operation. External OAuth providers are unavailable in offline mode.
|
||||
# @REJECTED Simple API key (no RBAC) — rejected because it cannot express granular permissions (admin vs analyst vs viewer), making auditability and least‑privilege impossible.
|
||||
|
||||
## Decision
|
||||
|
||||
### Authentication Flow
|
||||
|
||||
```
|
||||
┌──────────┐ POST /api/auth/login ┌──────────────┐
|
||||
│ User │ ──────────────────────────────│ FastAPI │
|
||||
│ (Browser)│ {username, password} │ Backend │
|
||||
│ │ ◄──────────────────────────── │ │
|
||||
│ │ {access_token, refresh} │ bcrypt + │
|
||||
│ │ │ JWT (HS256) │
|
||||
└──────────┘ └──────┬───────┘
|
||||
│
|
||||
┌───────▼───────┐
|
||||
│ PostgreSQL │
|
||||
│ users table │
|
||||
│ roles table │
|
||||
└───────────────┘
|
||||
```
|
||||
|
||||
### ADFS Federation (Optional)
|
||||
|
||||
- Enabled via `config.json: auth.adfs_enabled = true`
|
||||
- SAML 2.0 flow through `python3-saml`
|
||||
- ADFS users mapped to local roles via `adfs_group → ss_tools_role` mapping table
|
||||
- Federated sessions still receive JWTs (stateless after initial SAML handshake)
|
||||
|
||||
### RBAC Model
|
||||
|
||||
| Role | Permissions |
|
||||
|------|-------------|
|
||||
| `admin` | All: manage users, manage roles, manage plugins, deploy, backup, migrate, view dashboards, view logs |
|
||||
| `analyst` | View dashboards, run LLM analysis plugins, view reports, view logs (own) |
|
||||
| `operator` | Deploy dashboards, run migrations, manage backups, view logs |
|
||||
| `viewer` | View dashboards, view reports |
|
||||
|
||||
### Token Design
|
||||
|
||||
- **Access token**: JWT (HS256), 15‑minute expiry, contains `user_id`, `roles: list[str]`
|
||||
- **Refresh token**: opaque random string (SHA‑256), 7‑day expiry, stored hashed in DB
|
||||
- **Superset API token per environment**: AES‑256‑GCM encrypted, stored in `connection_configs` table
|
||||
- **Plugin execution token**: JWT, scoped to a single `task_id`, 15‑minute expiry
|
||||
|
||||
### Security Constraints
|
||||
|
||||
1. Passwords: bcrypt with cost factor 12 (minimum).
|
||||
2. Rate limiting: 5 failed login attempts per IP per 15 minutes → temporary IP block.
|
||||
3. Token revocation: admin can revoke all sessions for a user (delete refresh tokens).
|
||||
4. Audit log: all auth events (login success/failure, role change, token revoke) written to `audit_log` table.
|
||||
5. Enterprise clean mode: local auth only, ADFS disabled, no external network calls for identity.
|
||||
|
||||
### RBAC Enforcement Pattern
|
||||
|
||||
```python
|
||||
# backend/src/api/dependencies.py
|
||||
from fastapi import Depends, HTTPException
|
||||
|
||||
def require_role(required_role: str):
|
||||
def dependency(current_user: User = Depends(get_current_user)):
|
||||
if required_role not in current_user.roles:
|
||||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||||
return current_user
|
||||
return dependency
|
||||
|
||||
# Usage in route:
|
||||
@router.post("/api/dashboards/deploy")
|
||||
async def deploy(..., user: User = Depends(require_role("operator"))):
|
||||
...
|
||||
```
|
||||
|
||||
# [/DEF:ADR-0005:ADR]
|
||||
Reference in New Issue
Block a user