Update README for multi-user RBAC release

- Add multi-user RBAC, admin portal, and registration to features
- Update tech stack (37 migrations, CSRF middleware, RBAC)
- Expand security section with IDOR protection, CSRF, input validation,
  timing safety, inactive user blocking, password reuse prevention
- Update project structure (18 models, 13 schema modules, admin components)
- Add admin endpoints to API overview
- Note pentest verification (51+ test cases, 0 exploitable findings)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Kyle 2026-02-28 02:58:52 +08:00
parent d269742aa2
commit a313ce8b32

View File

@ -1,9 +1,10 @@
# UMBRA # UMBRA
A self-hosted personal life administration app with a dark-themed UI. Manage your todos, calendar events, projects, reminders, contacts, and locations from a single dashboard. A self-hosted, multi-user life administration app with a dark-themed UI and role-based access control. Manage your todos, calendar events, projects, reminders, contacts, and locations from a single dashboard.
## Features ## Features
- **Multi-user RBAC** - Admin and standard user roles, per-user data isolation, admin portal with IAM, system config, and audit logs
- **Dashboard** - Contextual greeting, week timeline, stat cards, upcoming events, weather widget, day briefing - **Dashboard** - Contextual greeting, week timeline, stat cards, upcoming events, weather widget, day briefing
- **Todos** - Task management with priorities, due dates, recurrence, and grouped sections (overdue/today/upcoming) - **Todos** - Task management with priorities, due dates, recurrence, and grouped sections (overdue/today/upcoming)
- **Calendar** - Multi-calendar system with month/week/day views, recurring events, drag-and-drop, event templates - **Calendar** - Multi-calendar system with month/week/day views, recurring events, drag-and-drop, event templates
@ -12,8 +13,9 @@ A self-hosted personal life administration app with a dark-themed UI. Manage you
- **People** - Contact directory with avatar initials, favourites, birthday tracking, category filtering - **People** - Contact directory with avatar initials, favourites, birthday tracking, category filtering
- **Locations** - Location management with OSM search integration, category filtering, frequent locations - **Locations** - Location management with OSM search integration, category filtering, frequent locations
- **Weather** - Dashboard weather widget with temperature, conditions, and contextual rain warnings - **Weather** - Dashboard weather widget with temperature, conditions, and contextual rain warnings
- **Settings** - Accent color picker (5 presets), first day of week, weather city, ntfy push notifications, TOTP two-factor auth, auto-lock, password management - **Settings** - Accent color picker (8 presets), first day of week, weather city, ntfy push notifications, TOTP two-factor auth, auto-lock, password management
- **Notifications** - ntfy push notifications for reminders (configurable per-user) - **Notifications** - ntfy push notifications for reminders (configurable per-user)
- **Admin Portal** - User management (create, delete, activate/deactivate, role assignment, password reset), system configuration (open registration, MFA enforcement), audit log viewer
## Tech Stack ## Tech Stack
@ -24,8 +26,8 @@ A self-hosted personal life administration app with a dark-themed UI. Manage you
| Fonts | Sora (headings), DM Sans (body) via Google Fonts | | Fonts | Sora (headings), DM Sans (body) via Google Fonts |
| State | TanStack Query v5, React Router v6 | | State | TanStack Query v5, React Router v6 |
| Backend | FastAPI, Python 3.12, Pydantic v2 | | Backend | FastAPI, Python 3.12, Pydantic v2 |
| Database | PostgreSQL 16, SQLAlchemy 2.0 (async), Alembic (25 migrations) | | Database | PostgreSQL 16, SQLAlchemy 2.0 (async), Alembic (37 migrations) |
| Auth | Username/password with Argon2id hashing, DB-backed sessions (signed cookies), optional TOTP MFA | | Auth | Argon2id hashing, DB-backed sessions (signed httpOnly cookies), TOTP MFA, CSRF middleware, role-based access control |
| Scheduler | APScheduler (async) for ntfy notification dispatch | | Scheduler | APScheduler (async) for ntfy notification dispatch |
| Deployment | Docker Compose (3 services), Nginx reverse proxy | | Deployment | Docker Compose (3 services), Nginx reverse proxy |
@ -66,7 +68,7 @@ A self-hosted personal life administration app with a dark-themed UI. Manage you
4. **Open the app** 4. **Open the app**
Navigate to `http://localhost` in your browser. On first launch you'll be prompted to create a username and password. Navigate to `http://localhost` in your browser. On first launch you'll be prompted to create an admin account.
## Architecture ## Architecture
@ -108,18 +110,25 @@ A self-hosted personal life administration app with a dark-themed UI. Manage you
### Hardened by default ### Hardened by default
- **Multi-user data isolation** — all resources scoped by `user_id` with per-query filtering; pentest-verified (51+ test cases, 0 exploitable IDOR findings)
- **Role-based access control**`admin` and `standard` roles with `require_admin` dependency on all admin endpoints
- **CSRF protection** — global `CSRFHeaderMiddleware` requires `X-Requested-With: XMLHttpRequest` on all mutating requests
- **Input validation**`extra="forbid"` on all Pydantic schemas prevents mass-assignment; `max_length` on all string fields; `ge=1, le=2147483647` on path IDs
- **Non-root containers** — both backend (`appuser:1000`) and frontend (`nginx-unprivileged`) run as non-root - **Non-root containers** — both backend (`appuser:1000`) and frontend (`nginx-unprivileged`) run as non-root
- **No external backend port** — port 8000 is internal-only; all traffic flows through nginx - **No external backend port** — port 8000 is internal-only; all traffic flows through nginx
- **Server version suppression**`server_tokens off` (nginx) and `--no-server-header` (uvicorn) - **Server version suppression**`server_tokens off` (nginx) and `--no-server-header` (uvicorn)
- **Rate limiting** — nginx `limit_req_zone` (10 req/min) on `/api/auth/login` (burst=5), `/verify-password` (burst=5), `/change-password` (burst=5), `/totp-verify` (burst=5), `/setup` (burst=3) - **Rate limiting** — nginx `limit_req_zone` (10 req/min) on `/api/auth/login` (burst=5), `/verify-password` (burst=5), `/change-password` (burst=5), `/totp-verify` (burst=5), `/setup` (burst=3)
- **DB-backed account lockout** — 10 failed attempts triggers 30-minute lock per account - **DB-backed account lockout** — 10 failed attempts triggers 30-minute lock per account
- **Inactive user blocking** — disabled accounts rejected at login (HTTP 403) without session creation, lockout reset, or last_login_at update
- **Timing-safe login** — dummy Argon2id hash for non-existent users prevents username enumeration
- **Password reuse prevention** — change-password endpoint rejects same password as old
- **Dotfile blocking**`/.env`, `/.git/config`, etc. return 404 (`.well-known` preserved for ACME) - **Dotfile blocking**`/.env`, `/.git/config`, etc. return 404 (`.well-known` preserved for ACME)
- **CSP headers** — Content-Security-Policy on all responses, scoped for Google Fonts - **CSP headers** — Content-Security-Policy on all responses, scoped for Google Fonts
- **CORS** — configurable origins with explicit method/header allowlists - **CORS** — configurable origins with explicit method/header allowlists
- **API docs disabled in production** — Swagger/ReDoc/OpenAPI only available when `ENVIRONMENT=development` - **API docs disabled in production** — Swagger/ReDoc/OpenAPI only available when `ENVIRONMENT=development`
- **Argon2id password hashing** with transparent bcrypt migration on first login - **Argon2id password hashing** with transparent bcrypt migration on first login
- **DB-backed sessions** — revocable, with signed itsdangerous cookies - **DB-backed sessions** — revocable, with signed itsdangerous httpOnly cookies, 7-day sliding window with 30-day hard ceiling
- **Optional TOTP MFA** — authenticator app support with backup codes - **Optional TOTP MFA** — authenticator app support with backup codes, admin-enforced MFA for new users
### Production Hardening ### Production Hardening
@ -150,12 +159,13 @@ Additionally for production:
## API Overview ## API Overview
All endpoints require authentication (signed session cookie) except auth routes and the health check. All endpoints require authentication (signed session cookie) except auth routes and the health check. Admin endpoints require the `admin` role.
| Endpoint | Description | | Endpoint | Description |
|-----------------------|-------------| |-----------------------|-------------|
| `GET /health` | Health check | | `GET /health` | Health check |
| `/api/auth/*` | Login, logout, setup, status, password change, TOTP MFA | | `/api/auth/*` | Login, logout, setup, register, status, password change, TOTP MFA |
| `/api/admin/*` | User management, system config, audit logs (admin only) |
| `/api/todos/*` | Todos CRUD + toggle completion | | `/api/todos/*` | Todos CRUD + toggle completion |
| `/api/events/*` | Calendar events CRUD (incl. recurring) | | `/api/events/*` | Calendar events CRUD (incl. recurring) |
| `/api/event-templates/*` | Event templates CRUD | | `/api/event-templates/*` | Event templates CRUD |
@ -164,7 +174,7 @@ All endpoints require authentication (signed session cookie) except auth routes
| `/api/projects/*` | Projects + nested tasks + comments CRUD | | `/api/projects/*` | Projects + nested tasks + comments CRUD |
| `/api/people/*` | People CRUD | | `/api/people/*` | People CRUD |
| `/api/locations/*` | Locations CRUD + search | | `/api/locations/*` | Locations CRUD + search |
| `/api/settings/*` | App settings + password change + ntfy config | | `/api/settings/*` | App settings + ntfy config |
| `/api/dashboard` | Dashboard aggregation | | `/api/dashboard` | Dashboard aggregation |
| `/api/upcoming` | Unified upcoming items feed | | `/api/upcoming` | Unified upcoming items feed |
| `/api/weather/*` | Weather data proxy | | `/api/weather/*` | Weather data proxy |
@ -209,15 +219,15 @@ umbra/
│ ├── Dockerfile │ ├── Dockerfile
│ ├── requirements.txt │ ├── requirements.txt
│ ├── alembic.ini │ ├── alembic.ini
│ ├── alembic/versions/ # 25 migrations (001025) │ ├── alembic/versions/ # 37 migrations (001037)
│ └── app/ │ └── app/
│ ├── main.py # FastAPI app, router registration, health endpoint │ ├── main.py # FastAPI app, CSRF middleware, router registration, health endpoint
│ ├── config.py # Pydantic BaseSettings (DATABASE_URL, SECRET_KEY, CORS, etc.) │ ├── config.py # Pydantic BaseSettings (DATABASE_URL, SECRET_KEY, CORS, etc.)
│ ├── database.py # Async SQLAlchemy engine + session factory │ ├── database.py # Async SQLAlchemy engine + session factory
│ ├── models/ # 17 SQLAlchemy ORM models │ ├── models/ # 18 SQLAlchemy ORM models (incl. User, UserSession, SystemConfig, AuditLog)
│ ├── schemas/ # Pydantic v2 request/response schemas │ ├── schemas/ # 13 Pydantic v2 request/response schema modules (incl. admin)
│ ├── routers/ # 14 API route handlers │ ├── routers/ # 14 API route handlers (incl. auth, admin, totp)
│ ├── services/ # Auth (Argon2id), recurrence, TOTP, ntfy │ ├── services/ # Auth (Argon2id), recurrence, TOTP, ntfy, audit
│ └── jobs/ # APScheduler notification dispatch │ └── jobs/ # APScheduler notification dispatch
└── frontend/ └── frontend/
├── Dockerfile ├── Dockerfile
@ -225,15 +235,16 @@ umbra/
├── proxy-params.conf # Shared proxy settings (DRY include) ├── proxy-params.conf # Shared proxy settings (DRY include)
├── package.json ├── package.json
└── src/ └── src/
├── App.tsx # Routes and auth guard ├── App.tsx # Routes, ProtectedRoute, AdminRoute auth guards
├── lib/ # api.ts, date-utils.ts, utils.ts ├── lib/ # api.ts (axios + 401 interceptor), date-utils.ts, utils.ts
├── hooks/ # useAuth, useSettings, useTheme, useCalendars, useConfirmAction, useCategoryOrder, useTableVisibility ├── hooks/ # useAuth, useAdmin, useSettings, useTheme, useCalendars, useConfirmAction, useCategoryOrder, useTableVisibility
├── types/ # TypeScript interfaces ├── types/ # TypeScript interfaces
└── components/ └── components/
├── ui/ # 18 base components (Button, Dialog, Sheet, Card, Input, Select, Switch, etc.) ├── ui/ # 17 base components (Button, Dialog, Sheet, Card, Input, Select, Switch, etc.)
├── shared/ # EntityTable, EntityDetailPanel, CategoryFilterBar, CategoryAutocomplete, CopyableField ├── shared/ # EntityTable, EntityDetailPanel, CategoryFilterBar, CategoryAutocomplete, CopyableField
├── layout/ # AppLayout, Sidebar, LockOverlay ├── layout/ # AppLayout, Sidebar, LockOverlay
├── auth/ # LockScreen, AmbientBackground ├── auth/ # LockScreen, AmbientBackground
├── admin/ # AdminPortal, IAMPage, ConfigPage, AdminDashboardPage, CreateUserDialog, UserActionsMenu, UserDetailSection
├── dashboard/ # DashboardPage + 8 widgets ├── dashboard/ # DashboardPage + 8 widgets
├── calendar/ # CalendarPage, CalendarSidebar, CalendarForm, EventForm, TemplateForm ├── calendar/ # CalendarPage, CalendarSidebar, CalendarForm, EventForm, TemplateForm
├── todos/ # TodosPage, TodoList, TodoItem, TodoForm ├── todos/ # TodosPage, TodoList, TodoItem, TodoForm