Commit Graph
3 Commits
Author SHA1 Message Date
Mumuni (Hermes) 106699ac5e feat: apply Wahab Abdul's 2026-09-28 directives (roster, sub-contractors, penthouses)
Three client decisions for Denya OneCare / Pavilion Accra:

1. Technician roster converges to exactly 5 named techs
   - Samuel Shang, Desmond Afful, Desmond Odekyi, Francis Norgbey, Nicholas Nartey
   - New app/services/roster.py: converge_tech_roster() runs at startup and is
     idempotent; off-roster techs are DEACTIVATED, never deleted, so ticket
     history keeps a valid assignee reference
   - seed.py SEED_USERS_DATA updated; placeholder emails until client confirms

2. Sub-contractors appear in the "Assign to" list alongside technicians
   - New canonical role "Sub-contractor" (ASSIGNEE_POOL_ROLES = Tech + Sub-contractor)
   - New GET /api/auth/assignees endpoint returns active pool members only
   - Server-side _validate_assignee gate in ticket service rejects off-pool
     or deactivated assignees (400/404)
   - "Assign To" dropdown added to the new-ticket form; assigning at creation
     auto-advances Logged -> Assigned
   - base.html isTech() includes Sub-contractor (portal UX, tracked "under tech")

3. Penthouse units selectable when raising a ticket
   - apartment_mapping.json: PH1E-/PH1W-/PH2E-/PH2W- -> clean codes
   - seed_units self-heals legacy malformed codes on existing DBs and sets floors
   - Penthouse units added to the built-in fallback seed

Tests: new tests/test_wahab_directives_20260928.py (8 tests); updated the
stale East unit count in test_categories_and_units.py (60 -> 62 with penthouses).
Full suite green.
2026-09-28 21:42:56 +00:00
root 4e8b96ed0a fix(whatsapp,roles): self-heal legacy whatsapp_log schema; map underscore role aliases
CT115 (Mumuni relay #747) — two live-instance defects after PR #12:

1. GET /api/whatsapp/mock-log 500'd with a valid admin token:
   'no such column: whatsapp_log.message_text'. The model gained
   message_text/wa_message_id/ticket_number (and dropped command) in
   4afdc36 with no migration, so legacy DBs keep the (command, ...) shape.
   ensure_legacy_schema (startup, app/main.py) now adds the three missing
   columns idempotently and backfills legacy command bodies into
   message_text before dropping the obsolete NOT NULL command column, so
   both the mock-log read path and the ORM write path work on healed DBs.
   New producer/consumer regression (tests/test_whatsapp_log_legacy_heal.py)
   reproduces the exact OperationalError, then asserts 200 + data.

2. ROLE_ALIASES gap: underscore legacy roles (cs_rep, cs_manager,
   fm_dispatcher) were not mapped, so normalize_legacy_user_roles could not
   converge rows like user 18 (test@denya.com, role 'cs_rep') and the
   frontend stranded them on /tickets. Added the underscore aliases; tests
   assert normalize_role('cs_rep') == 'CS Rep' and a cs_rep row converges
   and authenticates.
2026-09-09 12:52:07 +00:00
root 3ae1062d65 P0 security batch: admin-only user mgmt, unified role model, login rate limiting, webhook secret, security headers, pagination caps
- Remove POST /api/auth/register (404); no sign-up UI; users are admin-managed
- Add admin-only POST/PATCH/DELETE /api/auth/users (forced canonical roles,
  self-lockout + reference guards)
- Unify role model in app/core/roles.py; reject unknown roles at creation and
  at login/JWT validation; startup normalizes unambiguous legacy aliases
- Login rate limiting ~5 fails/15 min per IP+email -> 429 (in-process, tunable)
- WhatsApp webhook requires X-Webhook-Secret; fail-closed when env unset;
  GET handshake uses constant-time verify token (403 on mismatch)
- GET /api/whatsapp/mock-log now requires auth
- Security headers middleware: X-Frame-Options DENY, nosniff, CSP on HTML,
  HSTS behind TLS
- Pagination: limit alias for page_size, hard cap enforced, both -> 422
2026-09-08 10:36:16 +00:00