CreaRack-SL

Modal de Configuración de Dispositivo — credenciales SNMP/SSH y grupos

Descripción

Sesión 115: Nueva interfaz modal compartida para editar configuración de perfiles de dispositivo detectados (DeviceProfile) en las páginas de monitorización. Permite cambiar:

  • Credenciales SNMP: versión (v2c/v3), community, usuario v3, protocolos y claves.
  • Credenciales SSH: usuario y contraseña (cifradas con Fernet en backend).
  • Asignación a grupos: vincular el dispositivo a uno o más RackGroup.

NO edita IP ni nombre — esos valores vienen de Auto-Provision y se modifican allí.

Motivación

Antes, solo los dispositivos racks.Device (manuales en rack editor) podían ser configurados con credenciales. Los dispositivos descubiertos automáticamente (Wireless AP, UPS, Digital Signage, etc., como DeviceProfile) no tenían forma fácil de modificar estas credenciales desde el interfaz de monitorización. Esto fuerza a los operadores a ir a Auto-Provision cada vez que quieren cambiar una contraseña o agrupar un dispositivo.

Alcance

Disponible en:

  • Observatory (todos los DeviceProfile en el sidebar)
  • Wireless
  • UPS
  • Digital Signage

Cada página importa el servicio modal centralizado y añade un botón “Config” en la pestaña o detalle del dispositivo.

Flujo

  1. Usuario clica “Config” en un dispositivo de monitorización (se muestra su hostname/IP).
  2. Modal carga configuración actual con GET /api/network/auto-provision/profiles/{profile_id}/config.
    • Los secrets (claves SNMPv3, password SSH) NO se retornan; solo flags has_snmp_v3_keys e has_stored_ssh.
    • El usuario ve placeholders: “leave blank to keep unchanged”.
  3. Usuario edita formulario: versión SNMP, credenciales, asignación a grupos (checkboxes).
  4. Clica “Save” → PUT /api/network/auto-provision/profiles/{profile_id}/config.
  5. Backend cifra el password SSH con CredentialManager.encrypt_credential() y valida grupos por org (scope).
  6. Modal cierra, lista de dispositivos se recarga.

Seguridad

  • Multi-tenancy: Endpoint valida que profile.organization == org y que group_ids pertenecen a la org.
  • Cifrado: SSH password siempre cifrado con Fernet (AES) antes de almacenamiento; GET nunca lo retorna.
  • XSS: Modal construido sin inyección; datos sensibles vía JSON.
  • Secretos en GET: Las claves SNMP v3 y el password SSH no se retornan. Solo indica si existen (has_* flags).

Cambios principales

Backend

Endpoint POST asignación de tipos (existente):

  • POST /api/network/auto-provision/assign-types — cambia device_type de múltiples perfiles.

Nuevos endpoints:

  • GET /api/network/auto-provision/profiles/{profile_id}/config — obtiene config actual (sin secretos).
  • PUT /api/network/auto-provision/profiles/{profile_id}/config — actualiza credenciales y grupos.

Schemas (network/api/assign.py):

  • DeviceProfileConfigIn — entrada para PUT: campos opcionales (exclude_unset=True), solo se aplican los enviados.
  • _SNMP_PLAIN_FIELDS — tuple con nombres de campos SNMP a actualizar directamente.

Lógica:

  • SNMP: texto plano (como en racks.Device); si snmp_community presente → supports_snmp = True.
  • SSH: password enviado en texto plano; se cifra con CredentialManager.encrypt_credential().
  • Grupos: solo se usan los que existen en la org; profile.groups.set(valid_group_ids).

Frontend

Servicio reutilizable (static/js/services/DeviceConfigModal.js):

  • Modal overlay dinámico con CSS variables (--card-bg, --border-color, --text-primary, etc.).
  • Campos SNMP con toggle v2c ↔ v3 (muestra/oculta protocolos).
  • Campos SSH con hint “A password is stored. Leave blank to keep it unchanged.”
  • Carga grupos con checkboxes desde GET /api/racks/groups.
  • Valida presencia de credenciales (SSH: username + password_encrypted).
  • Envía solo campos presentes (exclude_unset); secretos solo si escritos.

Integraciones por página:

  • observatory.js: Método openDeviceConfig(profileId, name) + decodificación de PROFILE_ID_OFFSET (1e9).
  • ObservatoryTabs.js: Botón “Config” en pestaña de DeviceProfile (no en manual targets).
  • WirelessDetail.js, UpsDetail.js, SignageDetail.js: Botones “Config” + listeners.

Fixes simultáneos

  1. Colisión de IDs en Observatory: Los DeviceProfile se renderizaban con id negativo (-profile_id), pero los “manual targets” ya usaban negativos. Fix: desplazar id a PROFILE_ID_OFFSET + profile_id (positivo grande). Frontend decodifica: profile_id = id - PROFILE_ID_OFFSET.

  2. XSS en Wireless/UPS/Digital Signage: Datos inyectados con {{ *_json|safe }} en <script>. Fix: usar json_script (escape seguro) + parsear desde DOM con JSON.parse(document.getElementById(...).textContent).

Pruebas

Añadido tests/api/test_network_profile_config.py (8 test cases):

  • GET sin org → 404.
  • GET config con flags secretos ocultados.
  • PUT con validación de org y grupos.
  • PUT actualización parcial (campos presentes).
  • Cifrado SSH.
  • Asignación de grupos.

Interconexiones

  • DeviceProfile (network/models.py): Nuevos campos SNMP v3, SSH cifrado, relación M2M a RackGroup.
  • RackGroup (racks/models.py): Relación inversa desde DeviceProfile.
  • CredentialManager (core/security/credential_manager.py): Cifrado Fernet de credenciales.
  • Observatory, Wireless, UPS, Digital Signage (vistas + templates): Botones + modal.

Notas de implementación

  • Offset grande (1e9): Evita colisiones. En el futuro, si la DB crece, considerar un offset mayor o scheme distinto.
  • SNMP v2c ↔ v3: Toggle en frontend cambia visibilidad de campos; backend valida según lo enviado.
  • Grupos opcionales: Si group_ids: null en payload, no toca grupos existentes. Si [], los quita todos.

Véase también

  • [[entity—network—endpoint—profile-config]]
  • [[entity—static—service—device-config-modal]]
  • [[entity—network—model—device-profile]]
  • [[concept—monitoring—auto-provision]]
  • [[feature—monitoring—observatory-sidebar-unificado]]