Files
AR-Autopilot/docs/sprint-2.5-plan.md
T
alro65 13a2867ef6 sprint-2.5: RBAC 4 roles + Studio bootable + Flash Console
End-to-end implementation per docs/sprint-2.5-plan.md.

New requirement added by user mid-sprint: 4-role RBAC (Super Admin /
Engineer / Owner / User) with dual-auth for Engineer flashing firmware,
plus a "mini Arduino IDE" inside the Studio.

Tests: pytest 231/231 green (129 Sprint 2 + 102 Sprint 2.5 new).

RBAC core (arautopilot/core/):

- rbac.py: 4 roles, 12 capabilities, immutable capability matrix,
  has() / capabilities_of() / require() / requires_dual_auth() helpers.
  Engineer flashing firmware needs SA approval; everything else is
  single-factor.
- user.py: User model with PBKDF2-HMAC-SHA256 PIN hashing (200k iters,
  16-byte salt, self-describing hash format for future migrations).
  4-8 digit numeric PINs enforced.
- user_store.py: JSON-backed user database. seed_demo_users() for
  first-run UX.
- audit.py: append-only JSONL audit log. AuditEvent with timestamp,
  user_id, role, action, target, outcome, reason, secondary_user_id
  for dual-auth, optional extra payload. Crypto signing of lines
  deferred to Sprint 8.

Studio GUI (arautopilot/studio/):

- app.py: real entry point (replaces Sprint 0 stub). --seed-demo
  populates demo users without launching GUI; --data-dir overrides the
  ~/.ar-autopilot/studio/ default.
- session.py: Session + SessionHolder. check() always audits the
  decision; verify_super_admin_pin() + log_dual_auth_grant() for
  dual-auth flows.
- login_window.py: modal login dialog with user picker + PIN field.
  Audits login attempts (success and bad-PIN denials).
- main_window.py: top-level window with sidebar (user + role + caps)
  and tab area (Overview, Flash Console, Project placeholder,
  Telemetry placeholder).
- flash_console.py: the "mini Arduino IDE". Lists serial ports via
  pyserial; picks firmware variant (esp32-dev / esp32-debug); compiles
  via 'pio run'; flashes via 'pio run -t upload --upload-port <port>';
  streams pio output to a dark-themed read-only console; supports
  cancel. For Engineer flashes, asks the Super Admin for their PIN
  inline before invoking pio. Records dual-auth grant + pio exit code
  in the audit log.

Dependencies:

- New [project.optional-dependencies] group 'studio': PySide6>=6.6,
  pyserial>=3.5, platformio>=6.1. Kept optional so the core can be
  installed in lean / CI environments.

Tests (arautopilot/tests/):

- test_rbac.py: 32 tests for capability matrix, dual-auth policy,
  no-privilege-escalation invariants, partial overlap between roles.
- test_user.py: 11 tests for PIN hashing, verification, salting,
  serialisation, field validators.
- test_audit.py: 9 tests for JSONL append, immutability, round-trip,
  corrupt-line detection, dual-auth event shape, blank-line tolerance.
- test_user_store.py: 10 tests for CRUD, persistence, role filtering,
  demo seed idempotency.
- test_session.py: 9 tests for capability checks + audit side effects,
  SA PIN verification, dual-auth recording, SessionHolder lifecycle.
- test_studio_smoke.py: 5 headless tests verifying Studio modules
  import without a display server, --seed-demo works, helpers safe to
  call without hardware.

NOT in Sprint 2.5 (intentional):
  - Crypto signing of audit log lines (hash-chain) -- Sprint 8
  - HWID binding of the user store -- Sprint 8
  - Project configurator + .appack compiler -- Sprint 4
  - Flutter bridge display -- Sprint 4
  - Telemetry dashboard tab -- Sprint 4
  - Serial monitor as a separate tab -- future enhancement

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 18:04:27 -04:00

4.8 KiB

Sprint 2.5 — RBAC + Studio mínimo + Flash Console

Sprint nuevo añadido a petición del Super Admin (Álvaro). Cubre dos requisitos que no estaban en el brief original:

  1. Sistema de 4 roles (Super Admin / Engineer / Owner / User) con capabilities específicas y verificación de doble factor para operaciones críticas.
  2. Una "mini consola de Arduino" dentro del Studio que permita al Engineer flashear firmware ESP32 con aprobación del Super Admin.

Objetivo

Dejar Studio bootable con login por PIN, RBAC funcional, y la Flash Console operativa. Engineer puede flashear firmware mostrando el PIN del Super Admin en una ventana de confirmación. Super Admin (solo Álvaro) flashea directamente sin segundo factor.

Roles y capabilities

Capability Super Admin Engineer Owner User
Editar código Python del proyecto (arautopilot, tools, scripts)
Editar código C++ del firmware (firmware/**) (sólo en su sandbox)
Flashear firmware ESP32 directo con SA approval (2-of-N)
Editar gains PID base (IP del integrator)
Editar comisionado (rudder limits, calibración)
Editar preferencias operativas (rumbos fav, alarm vol, perfil)
Crear/gestionar Users del barco
Engage/disengage piloto
Leer telemetría
Acknowledge alarmas
Ver audit log completo parcial (sólo su barco)

Plan de implementación

1. RBAC core (arautopilot/core/rbac.py)

  • Enum Role: SUPER_ADMIN / ENGINEER / OWNER / USER
  • Enum Capability: cada acción gateable (FLASH_FIRMWARE, EDIT_BASE_GAINS, EDIT_COMMISSIONING, EDIT_OPERATIONAL, MANAGE_USERS, ENGAGE_PILOT, READ_TELEMETRY, ACK_ALARMS, VIEW_AUDIT_LOG_FULL)
  • Matriz inmutable _CAPABILITIES_BY_ROLE: dict[Role, frozenset[Capability]]
  • Función requires_dual_auth(cap: Capability) -> bool: True para FLASH_FIRMWARE cuando el actor es Engineer
  • has(role, capability) -> bool

2. User model (arautopilot/core/user.py)

  • Pydantic v2 User: user_id (UUID), display_name, role, pin_hash (argon2 o pbkdf2), created_at, last_login_at, active (bool)
  • set_pin(plain) / verify_pin(plain) -> bool con hashing seguro

3. Audit log (arautopilot/core/audit.py)

  • AuditEvent: timestamp UTC, user_id, action, target, outcome (success/denied/failed), reason, secondary_user_id (si dual-auth)
  • AuditLog con append-only file en JSONL
  • Cada permission check produce un evento

4. Studio mínimo (arautopilot/studio/app.py)

  • Reemplaza el stub Sprint 0
  • PySide6 QApplication + login window
  • Main window con 3 áreas: sidebar (rol + user), main area (placeholder para project editor), toolbar (acciones gateadas por rol)
  • Login = elegir user de lista + PIN
  • Persistencia local (SQLite o JSON) de la lista de users

5. Flash Console (arautopilot/studio/flash_console.py)

  • Widget dentro del Studio (no app separada)
  • Lista puertos serie (vía pyserial)
  • Combobox de variant (esp32-dev / esp32-debug)
  • Botones: "Compile only", "Compile + Flash", "Open Serial Monitor"
  • Para Engineer: al pulsar Flash, abre modal "Esperando aprobación del Super Admin" con form de PIN del SA. El SA introduce su PIN (si está presente) o el operario llama por teléfono y SA dicta un OTP que el SA generó desde otro Studio. Por simplicidad MVP: campo de PIN del SA en el modal.
  • Bajo el capó: corre pio run -t upload --upload-port COMx y stream del output al widget
  • Serial monitor: thread aparte que lee pio device monitor

6. Tests

  • test_rbac.py: cada rol tiene exactamente las capabilities esperadas; intentos de escalada rechazados; dual-auth requerido para Engineer flash.
  • test_user.py: PIN hash, verificación correcta/incorrecta, no se puede deserializar un User sin pin_hash.
  • test_audit.py: append es atómico, lectura es ordenada por timestamp.

Restricciones

  • No requiere hardware ESP32 conectado para que arranque el Studio.
  • La Flash Console no funciona sin board conectada (pero arranca, muestra "no boards found"). Funcionalidad real verificable cuando el usuario conecte hardware.
  • PIN del Super Admin se introduce en cada flash (no se cachea). Sí se puede cachear PIN del operario actual durante la sesión.
  • El binario flasheado por el Engineer debe ser uno compilado por el SA y firmado (futuro Sprint 8). Sprint 2.5 hace la versión simple: Engineer compila + flashea con PIN del SA.

Verificación

  • pytest verde (objetivo: 145+ tests)
  • python studio_main.py abre la ventana de login sin errores
  • Login con un user dummy de demo (demo_users.json) funciona
  • Flash Console widget se renderiza (incluso sin board)