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:
parent
d269742aa2
commit
a313ce8b32
51
README.md
51
README.md
@ -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 (001–025)
|
│ ├── alembic/versions/ # 37 migrations (001–037)
|
||||||
│ └── 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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user