04/27 Updated documents
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
> **Audience:** AI assistants and developers working on this codebase.
|
||||
> **Purpose:** Authoritative reference for architecture, conventions, gotchas, and decisions.
|
||||
> **Last reviewed:** April 2026 (Phase 12 complete + post-review hardening patch)
|
||||
> **Last reviewed:** April 2026 (Phase 12 complete + post-review hardening + UX improvements)
|
||||
|
||||
---
|
||||
|
||||
@@ -195,7 +195,9 @@ users: id, username (unique, indexed), full_name, email (unique, indexed),
|
||||
|
||||
**Expired invitations:** Customer accounts with `password_set=False` and `set_password_token_expires < now_eastern()` are flagged on the `/customers` index with an inline Resend button.
|
||||
|
||||
**Token verification:** `User.verify_set_password_token()` uses `hmac.compare_digest()` as a constant-time comparison guard after the DB lookup and expiry check, preventing timing oracle attacks on the stored token value.
|
||||
**Customer self-service setup:** When a customer clicks their invitation link, `set_password.html` prompts them to choose their own **username** (replacing the auto-generated placeholder) and set a **password**. The `set_password` route saves `form.username.data` to `user.username` before committing. The `SetPasswordForm` validates username uniqueness inline via `validate_username()`.
|
||||
|
||||
**Token verification:** `User.verify_set_password_token()` guards in order: token present → DB lookup → `password_set=False` check → expiry check → `hmac.compare_digest()` constant-time comparison. The `password_set=False` guard ensures accounts already activated cannot be re-used via a stale token. `compare_digest` is wrapped in `try/except` to safely handle type mismatches.
|
||||
|
||||
### Facility / Area
|
||||
|
||||
@@ -326,10 +328,10 @@ api_device_tokens: id, user_id, device_id, apns_token, device_name, app_version
|
||||
|---|---|---|
|
||||
| `auth` | `/auth` | `/login` (rate-limited 20/min), `/logout`, `/profile`, `/users/*`, `/notification-matrix` |
|
||||
| `dashboard` | `/` | `GET /`, `/facility-trend` (AJAX) |
|
||||
| `facilities` | `/facilities` | CRUD + area management |
|
||||
| `facilities` | `/facilities` | CRUD + area management; list grouped by Contract with collapsible sections; detail page has Back button |
|
||||
| `projects` | `/projects` | CRUD + customer assignment management |
|
||||
| `customers` | `/customers` | list (expired invitation banner), invite, set-password, manage, import CSV |
|
||||
| `inspections` | `/inspections` | list (stale badge, `now` passed from route), start, execute, view, PDF export, flag-issue, save-draft (AJAX), flag-followup, reinspect |
|
||||
| `customers` | `/customers` | list (expired invitation banner), invite (name + email only), set-password (customer chooses username + password), manage, import CSV |
|
||||
| `inspections` | `/inspections` | list (stale badge, `now` passed from route), start (template + facility only — area and notes removed), execute, view, PDF export, flag-issue, save-draft (AJAX), flag-followup, reinspect |
|
||||
| `templates` | `/templates` | list, create, edit, delete, form editor, preview |
|
||||
| `issues` | `/issues` | list (SLA column, facility filter, quick-assign dropdown), view, create, update, verify, comment, follow/unfollow, verification queue, delete |
|
||||
| `issues` | `/issues` | `POST /<id>/quick-assign` — JSON AJAX, admin/director only |
|
||||
@@ -625,6 +627,10 @@ Logs: `/home/jqc/logs/gunicorn-error.log`, `/home/jqc/logs/gunicorn-access.log`
|
||||
| 23 | **`hmac.compare_digest()` for token comparison** | Prevents timing oracle attacks on `verify_set_password_token` |
|
||||
| 24 | **`get_customer_scope()` uses bulk project query** | Single `Facility.project_id.in_(project_ids)` replaces per-assignment loop |
|
||||
| 25 | **CSV exports always call `log_action(ACTION_EXPORT, ...)`** | Data exports are compliance-relevant audit events |
|
||||
| 26 | **`StartInspectionForm` has no `area_id` or `notes` fields** | Both removed from the start page; `Inspection` constructor receives `area_id=None, notes=None` explicitly |
|
||||
| 27 | **Customer set-password page collects username + password** | `SetPasswordForm` includes `username` field; route saves it to `user.username`, replacing the auto-generated placeholder |
|
||||
| 28 | **`verify_set_password_token` requires `password_set=False`** | Prevents reuse of a stale token on an already-activated account |
|
||||
| 29 | **Facilities list grouped by Contract in route, not template** | `list_facilities()` builds a `grouped` OrderedDict before rendering; template iterates `grouped`, not the flat `facilities` list |
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user