diff --git a/README.md b/README.md index a1af264..01d27ac 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ # 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 +- **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 - **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 @@ -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 - **Locations** - Location management with OSM search integration, category filtering, frequent locations - **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) +- **Admin Portal** - User management (create, delete, activate/deactivate, role assignment, password reset), system configuration (open registration, MFA enforcement), audit log viewer ## 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 | | State | TanStack Query v5, React Router v6 | | Backend | FastAPI, Python 3.12, Pydantic v2 | -| Database | PostgreSQL 16, SQLAlchemy 2.0 (async), Alembic (25 migrations) | -| Auth | Username/password with Argon2id hashing, DB-backed sessions (signed cookies), optional TOTP MFA | +| Database | PostgreSQL 16, SQLAlchemy 2.0 (async), Alembic (37 migrations) | +| Auth | Argon2id hashing, DB-backed sessions (signed httpOnly cookies), TOTP MFA, CSRF middleware, role-based access control | | Scheduler | APScheduler (async) for ntfy notification dispatch | | 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** - 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 @@ -108,18 +110,25 @@ A self-hosted personal life administration app with a dark-themed UI. Manage you ### 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 - **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) - **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 +- **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) - **CSP headers** — Content-Security-Policy on all responses, scoped for Google Fonts - **CORS** — configurable origins with explicit method/header allowlists - **API docs disabled in production** — Swagger/ReDoc/OpenAPI only available when `ENVIRONMENT=development` - **Argon2id password hashing** with transparent bcrypt migration on first login -- **DB-backed sessions** — revocable, with signed itsdangerous cookies -- **Optional TOTP MFA** — authenticator app support with backup codes +- **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, admin-enforced MFA for new users ### Production Hardening @@ -150,12 +159,13 @@ Additionally for production: ## 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 | |-----------------------|-------------| | `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/events/*` | Calendar events CRUD (incl. recurring) | | `/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/people/*` | People CRUD | | `/api/locations/*` | Locations CRUD + search | -| `/api/settings/*` | App settings + password change + ntfy config | +| `/api/settings/*` | App settings + ntfy config | | `/api/dashboard` | Dashboard aggregation | | `/api/upcoming` | Unified upcoming items feed | | `/api/weather/*` | Weather data proxy | @@ -209,15 +219,15 @@ umbra/ │ ├── Dockerfile │ ├── requirements.txt │ ├── alembic.ini -│ ├── alembic/versions/ # 25 migrations (001–025) +│ ├── alembic/versions/ # 37 migrations (001–037) │ └── 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.) │ ├── database.py # Async SQLAlchemy engine + session factory -│ ├── models/ # 17 SQLAlchemy ORM models -│ ├── schemas/ # Pydantic v2 request/response schemas -│ ├── routers/ # 14 API route handlers -│ ├── services/ # Auth (Argon2id), recurrence, TOTP, ntfy +│ ├── models/ # 18 SQLAlchemy ORM models (incl. User, UserSession, SystemConfig, AuditLog) +│ ├── schemas/ # 13 Pydantic v2 request/response schema modules (incl. admin) +│ ├── routers/ # 14 API route handlers (incl. auth, admin, totp) +│ ├── services/ # Auth (Argon2id), recurrence, TOTP, ntfy, audit │ └── jobs/ # APScheduler notification dispatch └── frontend/ ├── Dockerfile @@ -225,15 +235,16 @@ umbra/ ├── proxy-params.conf # Shared proxy settings (DRY include) ├── package.json └── src/ - ├── App.tsx # Routes and auth guard - ├── lib/ # api.ts, date-utils.ts, utils.ts - ├── hooks/ # useAuth, useSettings, useTheme, useCalendars, useConfirmAction, useCategoryOrder, useTableVisibility + ├── App.tsx # Routes, ProtectedRoute, AdminRoute auth guards + ├── lib/ # api.ts (axios + 401 interceptor), date-utils.ts, utils.ts + ├── hooks/ # useAuth, useAdmin, useSettings, useTheme, useCalendars, useConfirmAction, useCategoryOrder, useTableVisibility ├── types/ # TypeScript interfaces └── 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 ├── layout/ # AppLayout, Sidebar, LockOverlay ├── auth/ # LockScreen, AmbientBackground + ├── admin/ # AdminPortal, IAMPage, ConfigPage, AdminDashboardPage, CreateUserDialog, UserActionsMenu, UserDetailSection ├── dashboard/ # DashboardPage + 8 widgets ├── calendar/ # CalendarPage, CalendarSidebar, CalendarForm, EventForm, TemplateForm ├── todos/ # TodosPage, TodoList, TodoItem, TodoForm