95 lines
4.3 KiB
Markdown
95 lines
4.3 KiB
Markdown
# SPEC — Users, Access Matrix, registration, sessions and security
|
||
|
||
**Date:** 2026-09-19
|
||
**Status:** hardened Users + Access Matrix; **SMTP configurado e validado (2026-09-19)**.
|
||
**Module:** `backend/app/modules/users` · Admin UI `/users` · `/users/access`
|
||
|
||
## Design references (not a framework swap)
|
||
|
||
Patterns adapted from production FastAPI auth libraries (kept in-house RBAC matrix):
|
||
|
||
- [fastapi-fullauth](https://github.com/mdfarhankc/fastapi-fullauth) — lockout, hashed reset tokens, email verification/reset, RBAC permissions, anti-enumeration
|
||
- [AuthWarden](https://github.com/timihack/authwarden) — single-use reset tokens, OTP attempt limits, brute-force lockout
|
||
- OWASP Authentication / Session / Password-reset cheat sheets
|
||
|
||
RentCharge keeps its own SQLite models, cookie sessions and **permissions-first Access Matrix** (`module.view.action`).
|
||
|
||
## Functional scope (both sides)
|
||
|
||
### A — Self-service (`/login`, `/signup`, `/reset-password`, `/account`)
|
||
|
||
1. Public registration with strong password + email OTP verification.
|
||
2. Forgot-password → single-use email token (SHA-256 stored, 1 hour).
|
||
3. Account profile, email verification, password change with strength meter.
|
||
4. Forced password change when `must_change_password` is set.
|
||
|
||
### B — Admin Users + Access Matrix (`/users`, `/users/access`)
|
||
|
||
1. Create staff logins with **generated strong passwords** (not shared starter defaults).
|
||
2. Edit login (username, nickname, display name, recovery email).
|
||
3. Set password (modal + strength meter) **or** email a reset token.
|
||
4. Require password change on first login / after admin reset.
|
||
5. Role assignment + full Role × Permission matrix with dirty-state and default-drift highlight.
|
||
6. Sessions revoke + security activity audit.
|
||
|
||
## Security controls
|
||
|
||
- **Password rules are chosen by Admin** under `/users/password-policy` (not hard-coded).
|
||
Presets: PIN 6+ digits · alphanumeric · strong · complex · custom toggles
|
||
(uppercase / lowercase / digit / special / min length / block nick-email / block common).
|
||
- Passwords: bcrypt cost 12; validation always follows the saved admin policy.
|
||
- Account lockout after 5 consecutive failed logins (15 minutes) + IP/identity rate limits.
|
||
- Reset tokens: 256-bit random, stored as SHA-256 only, single-use, invalidate previous tokens.
|
||
- Registration OTP: HMAC-SHA256, 10 min, max 5 attempts.
|
||
- Sessions: JWT + server-side session id; HttpOnly Secure SameSite=Strict cookie; idle + absolute timeouts.
|
||
- Logout / password change / reset / disable revoke sessions.
|
||
- Generic public responses to reduce account enumeration.
|
||
- Passwords and raw tokens never written to `auth_events`.
|
||
|
||
## New / updated API
|
||
|
||
| Method | Path | Purpose |
|
||
|--------|------|---------|
|
||
| GET | `/api/auth/password-policy` | Policy hint for UIs |
|
||
| POST | `/api/auth/password-strength` | Strength score helper |
|
||
| POST | `/api/users/{id}/send-password-reset` | Admin emails recovery token |
|
||
| GET | `/api/users/generate-password` | Cryptographically strong starter |
|
||
| PUT | `/api/users/{id}` | Now accepts `username` (edit login) |
|
||
| POST | `/api/users` | `must_change_password`, `send_invite_email` |
|
||
| POST | `/api/users/{id}/set-password` | `must_change_password` flag |
|
||
|
||
## Database (additive)
|
||
|
||
`users` columns:
|
||
|
||
- `must_change_password`
|
||
- `failed_login_count`
|
||
- `locked_until`
|
||
|
||
|
||
## Mail transport (VM500)
|
||
|
||
**Validated 2026-09-19** — OTP verification and password-reset emails delivered.
|
||
|
||
| Setting | Value |
|
||
|---------|--------|
|
||
| Host | `mail.myvexx.com` |
|
||
| Port | `465` (SMTP SSL) |
|
||
| User | `admin@myvexx.com` |
|
||
| From | `Rent & Charge <admin@myvexx.com>` |
|
||
| Config | `/home/mini/rentcharge/config/rentcharge.env` (symlink `/etc/rentcharge.env`) |
|
||
| Also | Postfix on VM500 relays `[mail.myvexx.com]:465` with SASL |
|
||
|
||
App flags: `SMTP_SSL=true`, `SMTP_STARTTLS=false`. Credentials stay only in env (never in Git).
|
||
|
||
E2E checks: `POST /api/auth/forgot-password` → HTTP 200; inbox received **Rent & Charge password reset** and **Your Rent & Charge verification code**.
|
||
|
||
## Verification checklist
|
||
|
||
- [ ] Weak passwords rejected on register / change / admin set
|
||
- [ ] Generate strong password fills create form
|
||
- [ ] Edit login updates username + email
|
||
- [x] Email reset from Users works when SMTP configured
|
||
- [ ] Forced change redirects to `/account`
|
||
- [ ] Access matrix dirty badge + amber default-drift cells
|
||
- [ ] Backend tests: `python -m unittest backend.tests.test_auth_users`
|