admin: user accounts and session login
The admin was one shared HTTP Basic login: no way to give someone partial access, revoke one person, or tell who changed what. Add a users table and a session login form. Passwords are scrypt hashes from Ruby's OpenSSL, so no native gem is needed. Sessions carry a per-user version that is bumped on password change, which logs that user out everywhere. ADMIN_USER/ADMIN_PASSWORD now bootstrap the first superuser on an empty table and afterwards only work while no active superuser exists, as a recovery path that re-creates one. Until per-area permissions land, non-superusers only reach the dashboard and their own password page. Assisted-by: Claude:opus-5
This commit is contained in:
parent
c2020bf903
commit
9bf40690e5
13 changed files with 436 additions and 59 deletions
|
|
@ -36,7 +36,7 @@ Banking details and registration number are deliberately not on the public page
|
|||
- **aws-sdk-s3** against MinIO on the storage stack
|
||||
- In-memory `RateLimit` (5 req/hour/IP) — see `lib/rate_limit.rb`
|
||||
- `Rack::Protection::AuthenticityToken` for CSRF (uses session-stored token)
|
||||
- `AdminAuth` Rack middleware: HTTP Basic on `/admin/*`, credentials from env. Greedy-prefix bug fixed; only `/admin` exact and `/admin/` paths are gated, so sibling assets like `/admin-invoice-form.js` stay public.
|
||||
- `AdminAuth` (`lib/admin_auth.rb`, a Sinatra extension): session login at `/admin/login` against the `users` table (`lib/user.rb`, scrypt via OpenSSL, no gem). Session holds user id + `session_version` (bump = logged out everywhere). `ADMIN_USER`/`ADMIN_PASSWORD` bootstrap the first superuser on an empty table and otherwise work only while no active superuser exists (recovery; it re-creates that superuser). Login is rate-limited per IP (`LOGIN_RATE_LIMIT`, default 10/15 min).
|
||||
- Honeypot field named `website` for spam
|
||||
- Static assets in `public/`; views in `views/`
|
||||
- Deploy: `Dockerfile` + `docker-compose.yml` (Traefik labels for asxp.io / www.asxp.io). Container joins two external Docker networks: `traefik` (ingress) and `storage` (Postgres + MinIO).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue