# Klee Labs — Diseño Completo del Sistema Administrativo

> Consola administrativa PHP/Metronic 8 que gestiona **todo** Apache Guacamole vía REST API,
> sin tocar una sola línea del código fuente de Guacamole.

---

## Índice

1. [Contexto y restricciones](#1-contexto-y-restricciones)
2. [Arquitectura general](#2-arquitectura-general)
3. [Capas de la aplicación](#3-capas-de-la-aplicación)
4. [Módulos y CRUDs](#4-módulos-y-cruds)
   - 4.1 Autenticación Klee Labs
   - 4.2 Usuarios Guacamole
   - 4.3 Grupos de usuarios
   - 4.4 Conexiones
   - 4.5 Grupos de conexión
   - 4.6 Perfiles de compartición
   - 4.7 Sesiones activas
   - 4.8 Historial y reportes
   - 4.9 Esquema / Protocolos
   - 4.10 Sedes (Klee)
   - 4.11 Laboratorios (Klee)
   - 4.12 Equipos (Klee)
   - 4.13 Reservas (Klee)
   - 4.14 Reglas de horario (Klee)
   - 4.15 Auditoría (Klee)
5. [Esquema de base de datos Klee](#5-esquema-de-base-de-datos-klee)
6. [Mapeo completo de endpoints REST](#6-mapeo-completo-de-endpoints-rest)
7. [Estructura de archivos PHP](#7-estructura-de-archivos-php)
8. [Plan de trabajo por fases](#8-plan-de-trabajo-por-fases)
9. [Reglas de oro](#9-reglas-de-oro)

---

## 1. Contexto y restricciones

| Restricción | Detalle |
|---|---|
| Guacamole intocable | No se modifica `guacd`, la webapp ni sus extensiones |
| Escritura solo por API | Toda mutación pasa por `POST/PUT/PATCH/DELETE /api/session/data/{ds}/...` |
| Lectura directa a BD | Permitida **solo** en tablas `guacamole_*` para reportes de solo lectura |
| BD propia | Tablas `klee_*` en base de datos separada (mismo PG o distinto) |
| Token de servicio | Un usuario `guacadmin` (o cuenta de servicio) autentica en Guacamole al arrancar |
| Versión de la API | Guacamole 1.5.x / 1.6.x — verificar tras cada upgrade |

---

## 2. Arquitectura general

```
┌──────────────────────────────────────────────────────────────────┐
│  Navegador                                                       │
│  Metronic 8 (HTML/JS/Ajax)                                       │
└───────────────┬──────────────────────────────────────────────────┘
                │ HTTPS
┌───────────────▼──────────────────────────────────────────────────┐
│  Klee Labs (PHP 8.1+, sin framework)                             │
│  ┌─────────────┐  ┌────────────────┐  ┌──────────────────────┐  │
│  │  public/    │  │  src/          │  │  views/              │  │
│  │  index.php  │→ │  Controllers   │→ │  Plantillas Metronic │  │
│  │  (router)   │  │  Services      │  └──────────────────────┘  │
│  └─────────────┘  │  Repositories  │                            │
│                   └──────┬─────────┘                            │
└──────────────────────────┼───────────────────────────────────────┘
                           │
              ┌────────────┴──────────────┐
              │                           │
   ┌──────────▼──────────┐   ┌────────────▼──────────┐
   │  Guacamole REST API │   │  PostgreSQL            │
   │  /api/tokens        │   │  guacamole_* (RO)      │
   │  /api/session/...   │   │  klee_* (RW)           │
   └─────────────────────┘   └───────────────────────┘
              │
   ┌──────────▼──────────┐
   │  guacd (daemon)     │
   │  (nunca se toca)    │
   └─────────────────────┘
```

---

## 3. Capas de la aplicación

### 3.1 Enrutador (`public/index.php`)

Router minimalista basado en `$_GET['route']` o PATH_INFO:

```
GET  /                        → dashboard
GET  /usuarios                → usuarios/index
GET  /usuarios/crear          → usuarios/form
POST /usuarios/crear          → usuarios/store
GET  /usuarios/{id}/editar    → usuarios/form (edit)
POST /usuarios/{id}/editar    → usuarios/update
POST /usuarios/{id}/eliminar  → usuarios/destroy
... (mismo patrón para cada módulo)
```

### 3.2 Controladores (`src/Controllers/`)

Clases PHP puras que:
- Validan CSRF y sesión Klee
- Delegan lógica a Services
- Pasan datos a las vistas

### 3.3 Services (`src/Guacamole/` y `src/Klee/`)

- `Guacamole/*Service.php` → llaman a `GuacamoleApiClient`
- `Klee/*Service.php` → llaman a repositorios PDO propios

### 3.4 Repositorios (`src/Database/`)

- `GuacamoleReadRepository.php` → consultas SELECT en tablas `guacamole_*`
- `KleeRepository.php` → CRUD en tablas `klee_*`

### 3.5 Cliente API (`src/Guacamole/GuacamoleApiClient.php`)

Única clase que habla HTTP con Guacamole. Gestiona el token de sesión.

---

## 4. Módulos y CRUDs

---

### 4.1 Módulo: Autenticación Klee Labs

**Objetivo**: sesión propia de Klee Labs (independiente de Guacamole).

#### Pantallas

| Ruta | Vista | Descripción |
|---|---|---|
| `GET /login` | `views/auth/login.php` | Formulario usuario/contraseña Klee |
| `POST /login` | — | Valida contra `klee_admins`, inicia sesión PHP |
| `POST /logout` | — | Destruye sesión, redirige a login |

#### Tabla BD Klee

```sql
CREATE TABLE klee_admins (
  id SERIAL PRIMARY KEY,
  nombre VARCHAR(120) NOT NULL,
  email VARCHAR(180) UNIQUE NOT NULL,
  password_hash VARCHAR(255) NOT NULL,   -- bcrypt
  rol VARCHAR(40) DEFAULT 'admin',       -- admin | auditor | operador
  activo BOOLEAN DEFAULT true,
  ultimo_acceso TIMESTAMPTZ,
  creado_en TIMESTAMPTZ DEFAULT NOW()
);
```

#### Roles de Klee Labs

| Rol | Puede hacer |
|---|---|
| `admin` | Todo |
| `operador` | CRUD de equipos, reservas; ver conexiones |
| `auditor` | Solo lectura; ver historial, sesiones activas |

---

### 4.2 Módulo: Usuarios Guacamole

**Objetivo**: gestión completa de usuarios de Guacamole vía API.

#### Pantallas y operaciones

| Pantalla | Ruta | Acción API |
|---|---|---|
| Lista de usuarios | `GET /guac/usuarios` | `GET /api/session/data/{ds}/users` |
| Detalle de usuario | `GET /guac/usuarios/{username}` | `GET /api/session/data/{ds}/users/{u}` |
| Formulario crear | `GET /guac/usuarios/nuevo` | — |
| Crear usuario | `POST /guac/usuarios` | `POST /api/session/data/{ds}/users` |
| Formulario editar | `GET /guac/usuarios/{u}/editar` | `GET /api/session/data/{ds}/users/{u}` |
| Editar usuario | `PUT /guac/usuarios/{u}` | `PUT /api/session/data/{ds}/users/{u}` |
| Cambiar contraseña | `POST /guac/usuarios/{u}/password` | `PUT /api/session/data/{ds}/users/{u}/password` |
| Eliminar usuario | `DELETE /guac/usuarios/{u}` | `DELETE /api/session/data/{ds}/users/{u}` |
| Permisos de usuario | `GET /guac/usuarios/{u}/permisos` | `GET /api/session/data/{ds}/users/{u}/permissions` |
| Editar permisos | `PATCH /guac/usuarios/{u}/permisos` | `PATCH /api/session/data/{ds}/users/{u}/permissions` |
| Permisos efectivos | `GET /guac/usuarios/{u}/permisos-efectivos` | `GET /api/session/data/{ds}/users/{u}/effectivePermissions` |
| Grupos del usuario | `GET /guac/usuarios/{u}/grupos` | `GET /api/session/data/{ds}/users/{u}/userGroups` |
| Historial del usuario | `GET /guac/usuarios/{u}/historial` | `GET /api/session/data/{ds}/history/users` + filtro |

#### Campos del formulario de usuario

```
username*        → texto único
password*        → (solo en creación) / campo separado para cambio
disabled         → checkbox (bloquear cuenta)
expired          → checkbox
access-window-start → hora HH:MM
access-window-end   → hora HH:MM
valid-from          → fecha YYYY-MM-DD
valid-until         → fecha YYYY-MM-DD
timezone            → select (zonas horarias)
```

#### Pantalla de permisos

Dos sub-secciones:

**Permisos de sistema** (checkboxes):
- `CREATE_USER` — Crear usuarios
- `CREATE_USER_GROUP` — Crear grupos
- `CREATE_CONNECTION` — Crear conexiones
- `CREATE_CONNECTION_GROUP` — Crear grupos de conexión
- `CREATE_SHARING_PROFILE` — Crear perfiles de compartición
- `AUDIT` — Ver historial/auditoría
- `ADMINISTER` — Administrador total

**Permisos por objeto** (tabla dinámica):
- Sobre cada Conexión: `READ | UPDATE | DELETE | ADMINISTER`
- Sobre cada Grupo de conexión: `READ | UPDATE | DELETE | ADMINISTER`
- Sobre cada Perfil de compartición: `READ | UPDATE | DELETE | ADMINISTER`
- Sobre cada Usuario: `READ | UPDATE | DELETE | ADMINISTER`
- Sobre cada Grupo de usuarios: `READ | UPDATE | DELETE | ADMINISTER`

---

### 4.3 Módulo: Grupos de Usuarios

**Objetivo**: agrupar usuarios y asignar permisos en bloque.

#### Pantallas y operaciones

| Pantalla | Ruta | Acción API |
|---|---|---|
| Lista de grupos | `GET /guac/grupos-usuario` | `GET /api/session/data/{ds}/userGroups` |
| Detalle | `GET /guac/grupos-usuario/{id}` | `GET /api/session/data/{ds}/userGroups/{id}` |
| Crear | `POST /guac/grupos-usuario` | `POST /api/session/data/{ds}/userGroups` |
| Editar | `PUT /guac/grupos-usuario/{id}` | `PUT /api/session/data/{ds}/userGroups/{id}` |
| Eliminar | `DELETE /guac/grupos-usuario/{id}` | `DELETE /api/session/data/{ds}/userGroups/{id}` |
| Miembros (usuarios) | `GET /guac/grupos-usuario/{id}/miembros` | `GET /api/session/data/{ds}/userGroups/{id}/memberUsers` |
| Editar miembros | `PATCH /guac/grupos-usuario/{id}/miembros` | `PATCH /api/session/data/{ds}/userGroups/{id}/memberUsers` |
| Sub-grupos | `GET /guac/grupos-usuario/{id}/subgrupos` | `GET /api/session/data/{ds}/userGroups/{id}/userGroups` |
| Editar sub-grupos | `PATCH /guac/grupos-usuario/{id}/subgrupos` | `PATCH /api/session/data/{ds}/userGroups/{id}/userGroups` |
| Permisos del grupo | `GET /guac/grupos-usuario/{id}/permisos` | `GET /api/session/data/{ds}/userGroups/{id}/permissions` |
| Editar permisos | `PATCH /guac/grupos-usuario/{id}/permisos` | `PATCH /api/session/data/{ds}/userGroups/{id}/permissions` |

#### Campos del formulario de grupo

```
identifier*  → nombre/ID único del grupo
disabled     → checkbox
atributos    → extensibles (dependen de extensiones instaladas)
```

---

### 4.4 Módulo: Conexiones

**Objetivo**: CRUD completo de conexiones RDP/VNC/SSH y sus parámetros.

#### Pantallas y operaciones

| Pantalla | Ruta | Acción API |
|---|---|---|
| Lista de conexiones | `GET /guac/conexiones` | `GET /api/session/data/{ds}/connections` |
| Detalle | `GET /guac/conexiones/{id}` | `GET /api/session/data/{ds}/connections/{id}` |
| Crear | `POST /guac/conexiones` | `POST /api/session/data/{ds}/connections` |
| Editar | `PUT /guac/conexiones/{id}` | `PUT /api/session/data/{ds}/connections/{id}` |
| Eliminar | `DELETE /guac/conexiones/{id}` | `DELETE /api/session/data/{ds}/connections/{id}` |
| Ver parámetros | `GET /guac/conexiones/{id}/parametros` | `GET /api/session/data/{ds}/connections/{id}/parameters` |
| Historial de uso | `GET /guac/conexiones/{id}/historial` | `GET /api/session/data/{ds}/connections/{id}/history` |
| Perfiles compartición | `GET /guac/conexiones/{id}/perfiles` | `GET /api/session/data/{ds}/connections/{id}/sharingProfiles` |

#### Formulario de conexión (campos comunes)

```
name*               → nombre visible
protocol*           → select: rdp | vnc | ssh | telnet | kubernetes | ...
parentIdentifier*   → selector de grupo padre (árbol)
max-connections     → número (atributo)
max-connections-per-user → número (atributo)
weight              → número (para balanceo en grupos BALANCING)
```

#### Parámetros por protocolo (cargados dinámicamente vía schema API)

**RDP** (`GET /api/session/data/{ds}/schema/protocols/rdp`):
```
hostname*, port (3389), username, password, domain,
security (rdp|nla|tls|vmconnect|any), ignore-cert,
width, height, color-depth, dpi,
enable-audio, audio-servername,
enable-printing, printer-name,
enable-drive, drive-name, drive-path,
enable-wallpaper, enable-font-smoothing,
enable-full-window-drag, enable-desktop-composition,
enable-menu-animations, disable-bitmap-caching,
recording-path, recording-name, recording-exclude-output,
sftp-hostname, sftp-port, sftp-username, sftp-password,
resize-method (display-update|reconnect|none)
```

**VNC** (`GET /api/session/data/{ds}/schema/protocols/vnc`):
```
hostname*, port (5900), password,
color-depth, swap-red-blue, cursor (remote|local),
read-only, autoretry, encoding,
recording-path, sftp-hostname, ...
```

**SSH** (`GET /api/session/data/{ds}/schema/protocols/ssh`):
```
hostname*, port (22), username, password, private-key, passphrase,
host-key, color-scheme, font-name, font-size,
locale, timezone, execute-command,
recording-path, sftp-root-directory, ...
```

> Los parámetros disponibles se leen dinámicamente de la API para ser agnósticos a versiones.

---

### 4.5 Módulo: Grupos de Conexión

**Objetivo**: organizar conexiones en jerarquías (carpetas o balanceadores).

#### Pantallas y operaciones

| Pantalla | Ruta | Acción API |
|---|---|---|
| Vista árbol | `GET /guac/grupos-conexion` | `GET /api/session/data/{ds}/connectionGroups/ROOT/tree` |
| Lista flat | `GET /guac/grupos-conexion/lista` | `GET /api/session/data/{ds}/connectionGroups` |
| Detalle | `GET /guac/grupos-conexion/{id}` | `GET /api/session/data/{ds}/connectionGroups/{id}` |
| Crear | `POST /guac/grupos-conexion` | `POST /api/session/data/{ds}/connectionGroups` |
| Editar | `PUT /guac/grupos-conexion/{id}` | `PUT /api/session/data/{ds}/connectionGroups/{id}` |
| Eliminar | `DELETE /guac/grupos-conexion/{id}` | `DELETE /api/session/data/{ds}/connectionGroups/{id}` |
| Árbol de sub-grupo | `GET /guac/grupos-conexion/{id}/arbol` | `GET /api/session/data/{ds}/connectionGroups/{id}/tree` |

#### Formulario de grupo de conexión

```
name*               → nombre del grupo
type*               → ORGANIZATIONAL | BALANCING
parentIdentifier    → grupo padre
enable-session-affinity → checkbox (solo BALANCING)
max-connections     → número (solo BALANCING)
max-connections-per-user → número
```

---

### 4.6 Módulo: Perfiles de Compartición

**Objetivo**: configurar sesiones multi-usuario (un host invita a un observador).

#### Pantallas y operaciones

| Pantalla | Ruta | Acción API |
|---|---|---|
| Lista | `GET /guac/perfiles-comparticion` | `GET /api/session/data/{ds}/sharingProfiles` |
| Detalle | `GET /guac/perfiles-comparticion/{id}` | `GET /api/session/data/{ds}/sharingProfiles/{id}` |
| Crear | `POST /guac/perfiles-comparticion` | `POST /api/session/data/{ds}/sharingProfiles` |
| Editar | `PUT /guac/perfiles-comparticion/{id}` | `PUT /api/session/data/{ds}/sharingProfiles/{id}` |
| Eliminar | `DELETE /guac/perfiles-comparticion/{id}` | `DELETE /api/session/data/{ds}/sharingProfiles/{id}` |
| Ver parámetros | `GET /guac/perfiles-comparticion/{id}/parametros` | `GET /api/session/data/{ds}/sharingProfiles/{id}/parameters` |

#### Formulario

```
name*                   → nombre del perfil
primaryConnectionId*    → selector de conexión base
read-only               → checkbox (observador sin control)
```

---

### 4.7 Módulo: Sesiones Activas

**Objetivo**: monitorizar y terminar conexiones en tiempo real.

#### Pantallas y operaciones

| Pantalla | Ruta | Acción API |
|---|---|---|
| Lista activas | `GET /guac/sesiones-activas` | `GET /api/session/data/{ds}/activeConnections` |
| Detalle sesión | `GET /guac/sesiones-activas/{id}` | `GET /api/session/data/{ds}/activeConnections/{id}` |
| Terminar sesión | `DELETE /guac/sesiones-activas/{id}` | `DELETE /api/session/data/{ds}/activeConnections/{id}` |
| Conexión subyacente | `GET /guac/sesiones-activas/{id}/conexion` | `GET /api/session/data/{ds}/activeConnections/{id}/connection` |

#### Columnas de la tabla de sesiones activas

```
Identificador | Usuario | Conexión | IP cliente | Fecha inicio | Duración | [Terminar]
```

> Auto-refresco cada 30 segundos vía fetch/Ajax.

---

### 4.8 Módulo: Historial y Reportes

**Objetivo**: consultar el histórico de conexiones y logins; exportar datos.

#### Sub-módulos

**4.8.1 Historial de conexiones**

| Pantalla | Ruta | Fuente |
|---|---|---|
| Lista con filtros | `GET /reportes/historial-conexiones` | API: `GET /api/session/data/{ds}/history/connections` |
| Detalle registro | `GET /reportes/historial-conexiones/{id}` | API: `GET /api/session/data/{ds}/history/connections/{id}` |
| Ver log de actividad | `GET /reportes/historial-conexiones/{id}/log/{nombre}` | API: `GET /api/session/data/{ds}/history/connections/{id}/logs/{name}` |
| Exportar CSV | `POST /reportes/historial-conexiones/exportar` | SQL RO: `guacamole_connection_history` |
| Exportar PDF | `POST /reportes/historial-conexiones/exportar-pdf` | SQL RO |

Filtros disponibles (parámetros query de la API):
```
contains={texto}   → búsqueda en nombre de conexión
order={campo}      → startDate | endDate | username | connectionName
descending=true
```

**4.8.2 Historial de usuarios (logins)**

| Pantalla | Ruta | Fuente |
|---|---|---|
| Lista con filtros | `GET /reportes/historial-usuarios` | API: `GET /api/session/data/{ds}/history/users` |
| Exportar CSV | `POST /reportes/historial-usuarios/exportar` | SQL RO: `guacamole_user_history` |

**4.8.3 Dashboard de reportes (KPIs)**

```
┌────────────────┬────────────────┬────────────────┬────────────────┐
│ Sesiones hoy   │ Usuarios       │ Conexiones     │ Equipos en uso │
│ (last 24h)     │ activos        │ configuradas   │ ahora mismo    │
└────────────────┴────────────────┴────────────────┴────────────────┘
┌──────────────────────────────────┬─────────────────────────────────┐
│ Gráfico: sesiones por día (30d)  │ Tabla: top 10 equipos más usados│
└──────────────────────────────────┴─────────────────────────────────┘
┌──────────────────────────────────┬─────────────────────────────────┐
│ Gráfico: uso por hora del día    │ Tabla: top 10 usuarios          │
└──────────────────────────────────┴─────────────────────────────────┘
```

Todas las queries del dashboard usan **SQL RO** sobre `guacamole_connection_history` y `guacamole_user_history`.

---

### 4.9 Módulo: Esquema / Protocolos

**Objetivo**: consultar las capacidades dinámicas de Guacamole.

| Pantalla | Ruta | Acción API |
|---|---|---|
| Protocolos disponibles | `GET /guac/schema/protocolos` | `GET /api/session/data/{ds}/schema/protocols` |
| Parámetros de protocolo | `GET /guac/schema/protocolos/{proto}` | `GET /api/session/data/{ds}/schema/protocols/{proto}` |
| Atributos de usuario | `GET /guac/schema/atributos-usuario` | `GET /api/session/data/{ds}/schema/userAttributes` |
| Atributos de conexión | `GET /guac/schema/atributos-conexion` | `GET /api/session/data/{ds}/schema/connectionAttributes` |
| Atributos de grupo | `GET /guac/schema/atributos-grupo-conexion` | `GET /api/session/data/{ds}/schema/connectionGroupAttributes` |
| Atributos de perfil | `GET /guac/schema/atributos-perfil` | `GET /api/session/data/{ds}/schema/sharingProfileAttributes` |

---

### 4.10 Módulo: Sedes (Klee)

**Objetivo**: capa organizativa superior (campus, edificio, institución).

#### CRUD completo

| Pantalla | Ruta | BD |
|---|---|---|
| Lista de sedes | `GET /klee/sedes` | `SELECT * FROM klee_sedes` |
| Detalle sede | `GET /klee/sedes/{id}` | — |
| Crear | `POST /klee/sedes` | `INSERT INTO klee_sedes` |
| Editar | `PUT /klee/sedes/{id}` | `UPDATE klee_sedes` |
| Desactivar | `PATCH /klee/sedes/{id}/desactivar` | `UPDATE klee_sedes SET activo=false` |
| Eliminar | `DELETE /klee/sedes/{id}` | Solo si no tiene labs hijos |

#### Campos

```
nombre*     → texto
codigo      → texto corto único (ej: "SEDE-A")
direccion   → texto
activo      → boolean
```

---

### 4.11 Módulo: Laboratorios (Klee)

**Objetivo**: salas físicas que corresponden a un Connection Group de Guacamole.

#### CRUD completo

| Pantalla | Ruta | BD / API |
|---|---|---|
| Lista | `GET /klee/laboratorios` | `klee_laboratorios JOIN klee_sedes` |
| Por sede | `GET /klee/sedes/{id}/laboratorios` | filtro |
| Crear | `POST /klee/laboratorios` | INSERT + `POST /connectionGroups` |
| Editar | `PUT /klee/laboratorios/{id}` | UPDATE + `PUT /connectionGroups/{guac_id}` |
| Desactivar | `PATCH /klee/laboratorios/{id}/desactivar` | UPDATE klee |
| Eliminar | `DELETE /klee/laboratorios/{id}` | DELETE klee + `DELETE /connectionGroups/{guac_id}` |

#### Campos

```
sede_id*                    → select de sedes
nombre*                     → texto
bloque                      → texto (ej: "Bloque B")
sala                        → texto (ej: "Sala 201")
capacidad                   → número de equipos
guac_connection_group_id    → (se rellena automáticamente al crear en Guacamole)
activo                      → boolean
```

> Al crear un laboratorio, se crea automáticamente un ConnectionGroup ORGANIZATIONAL en Guacamole
> con el nombre del laboratorio. El ID devuelto por la API se guarda en `guac_connection_group_id`.

---

### 4.12 Módulo: Equipos (Klee)

**Objetivo**: PCs/servidores de cada laboratorio, cada uno es una Connection en Guacamole.

#### CRUD completo

| Pantalla | Ruta | BD / API |
|---|---|---|
| Lista | `GET /klee/equipos` | `klee_equipos JOIN klee_laboratorios` |
| Por laboratorio | `GET /klee/laboratorios/{id}/equipos` | filtro |
| Detalle | `GET /klee/equipos/{id}` | — |
| Crear | `POST /klee/equipos` | INSERT + `POST /connections` |
| Editar | `PUT /klee/equipos/{id}` | UPDATE + `PUT /connections/{guac_id}` |
| Cambiar estado | `PATCH /klee/equipos/{id}/estado` | UPDATE klee_equipos.estado |
| Eliminar | `DELETE /klee/equipos/{id}` | DELETE klee + `DELETE /connections/{guac_id}` |
| Ver sesión activa | `GET /klee/equipos/{id}/sesion` | API: activeConnections filtrado |
| Asignar permiso | `POST /klee/equipos/{id}/permisos` | PATCH /users/{u}/permissions |

#### Campos

```
laboratorio_id*             → select de laboratorios
nombre*                     → ej: "PC-01"
hostname*                   → IP o FQDN
ip                          → IP display
protocolo*                  → rdp | vnc | ssh
puerto                      → 3389 | 5900 | 22
usuario_rdp                 → usuario Windows/Linux
password_rdp                → (cifrado en BD Klee)
dominio_rdp                 → (RDP)
seguridad_rdp               → any | rdp | nla | tls
ignorar_certificado         → boolean (RDP)
guac_connection_id          → (auto al crear en Guacamole)
estado                      → disponible | ocupado | mantenimiento
```

---

### 4.13 Módulo: Reservas (Klee)

**Objetivo**: asignar equipos a usuarios por rangos de tiempo.

#### CRUD completo

| Pantalla | Ruta | Lógica |
|---|---|---|
| Calendario de reservas | `GET /klee/reservas` | Vista FullCalendar |
| Lista tabular | `GET /klee/reservas/lista` | `klee_reservas` |
| Por equipo | `GET /klee/equipos/{id}/reservas` | filtro |
| Por usuario Guacamole | `GET /klee/reservas?usuario={u}` | filtro |
| Crear reserva | `POST /klee/reservas` | INSERT + asignar permiso Guacamole |
| Editar reserva | `PUT /klee/reservas/{id}` | UPDATE + re-asignar permiso |
| Cancelar | `PATCH /klee/reservas/{id}/cancelar` | UPDATE estado + quitar permiso |
| Activar manualmente | `PATCH /klee/reservas/{id}/activar` | UPDATE estado + asignar permiso |
| Finalizar manualmente | `PATCH /klee/reservas/{id}/finalizar` | UPDATE estado + quitar permiso |

#### Flujo de permisos al reservar

```
1. POST /klee/reservas
   → INSERT klee_reservas
   → PATCH /api/session/data/{ds}/users/{username}/permissions
     [{ op: "add", path: "/connectionPermissions/{guac_connection_id}", value: "READ" }]
2. Al cancelar/finalizar:
   → PATCH /api/session/data/{ds}/users/{username}/permissions
     [{ op: "remove", path: "/connectionPermissions/{guac_connection_id}", value: "READ" }]
```

#### Campos del formulario de reserva

```
equipo_id*              → selector (muestra lab + equipo)
username_guacamole*     → selector (lista de usuarios Guacamole)
usuario_externo         → texto libre (ID COLOSO u otro sistema)
inicio*                 → datetime
fin*                    → datetime
origen                  → klee | coloso | externo
```

#### Validaciones

- No solapamiento: verificar `klee_reservas` que no exista otra reserva activa/pendiente para el mismo equipo en el mismo rango
- Equipo disponible: `klee_equipos.estado = 'disponible'`
- Usuario existe en Guacamole: llamar a `GET /users/{username}` antes de crear

---

### 4.14 Módulo: Reglas de Horario (Klee)

**Objetivo**: definir ventanas horarias de operación por laboratorio.

#### CRUD completo

| Pantalla | Ruta |
|---|---|
| Lista por laboratorio | `GET /klee/laboratorios/{id}/horarios` |
| Crear regla | `POST /klee/laboratorios/{id}/horarios` |
| Editar regla | `PUT /klee/horarios/{id}` |
| Eliminar | `DELETE /klee/horarios/{id}` |

#### Campos

```
laboratorio_id*     → referencia
dia_semana*         → 0=Dom, 1=Lun, ..., 6=Sáb (multi-select)
hora_inicio*        → HH:MM
hora_fin*           → HH:MM
activo              → boolean
```

---

### 4.15 Módulo: Auditoría (Klee)

**Objetivo**: registrar todas las acciones administrativas realizadas por usuarios de Klee Labs.

#### Solo lectura (escritura automática)

| Pantalla | Ruta | Fuente |
|---|---|---|
| Lista de eventos | `GET /klee/auditoria` | `klee_auditoria` |
| Filtros | por usuario, acción, entidad, fechas | — |
| Exportar CSV | `POST /klee/auditoria/exportar` | — |

#### Registro automático

Toda acción que llame a la API de Guacamole o modifique tablas Klee debe registrar en `klee_auditoria`:

```php
$this->auditoria->log(
    accion: 'crear_usuario',
    entidad: 'usuario',
    entidad_id: $username,
    detalle: ['username' => $username, 'disabled' => false],
    ip: $_SERVER['REMOTE_ADDR']
);
```

---

## 5. Esquema de base de datos Klee

```sql
-- ============================================================
-- ADMINISTRACIÓN KLEE LABS
-- ============================================================

CREATE TABLE klee_admins (
  id SERIAL PRIMARY KEY,
  nombre VARCHAR(120) NOT NULL,
  email VARCHAR(180) UNIQUE NOT NULL,
  password_hash VARCHAR(255) NOT NULL,
  rol VARCHAR(40) NOT NULL DEFAULT 'admin',  -- admin | operador | auditor
  activo BOOLEAN NOT NULL DEFAULT true,
  ultimo_acceso TIMESTAMPTZ,
  creado_en TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- ============================================================
-- ESTRUCTURA ORGANIZATIVA
-- ============================================================

CREATE TABLE klee_sedes (
  id SERIAL PRIMARY KEY,
  nombre VARCHAR(120) NOT NULL,
  codigo VARCHAR(40) UNIQUE,
  direccion VARCHAR(255),
  activo BOOLEAN NOT NULL DEFAULT true,
  creado_en TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE klee_laboratorios (
  id SERIAL PRIMARY KEY,
  sede_id INT NOT NULL REFERENCES klee_sedes(id),
  nombre VARCHAR(120) NOT NULL,
  bloque VARCHAR(60),
  sala VARCHAR(60),
  capacidad INT NOT NULL DEFAULT 0,
  guac_connection_group_id VARCHAR(40) NULL,  -- ID del ConnectionGroup en Guacamole
  activo BOOLEAN NOT NULL DEFAULT true,
  creado_en TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE klee_equipos (
  id SERIAL PRIMARY KEY,
  laboratorio_id INT NOT NULL REFERENCES klee_laboratorios(id),
  nombre VARCHAR(120) NOT NULL,
  hostname VARCHAR(255) NOT NULL,
  ip VARCHAR(45),
  protocolo VARCHAR(10) NOT NULL DEFAULT 'rdp',  -- rdp | vnc | ssh
  puerto INT,
  usuario_rdp VARCHAR(120),
  password_rdp_enc TEXT,             -- cifrado con clave app
  dominio_rdp VARCHAR(120),
  seguridad_rdp VARCHAR(20) DEFAULT 'any',
  ignorar_cert BOOLEAN DEFAULT true,
  guac_connection_id VARCHAR(40) NULL,  -- ID de la Connection en Guacamole
  estado VARCHAR(20) NOT NULL DEFAULT 'disponible',  -- disponible | ocupado | mantenimiento
  creado_en TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  actualizado_en TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- ============================================================
-- RESERVAS
-- ============================================================

CREATE TABLE klee_reservas (
  id SERIAL PRIMARY KEY,
  equipo_id INT NOT NULL REFERENCES klee_equipos(id),
  guac_username VARCHAR(120),        -- username en Guacamole
  guac_entity_id INT,                -- entity_id de Guacamole (para referencia)
  usuario_externo VARCHAR(120),      -- ID en sistema externo (COLOSO, etc.)
  inicio TIMESTAMPTZ NOT NULL,
  fin TIMESTAMPTZ NOT NULL,
  origen VARCHAR(20) NOT NULL DEFAULT 'klee',  -- klee | coloso | api
  estado VARCHAR(20) NOT NULL DEFAULT 'pendiente',  -- pendiente | activa | finalizada | cancelada
  notas TEXT,
  creado_en TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  creado_por INT REFERENCES klee_admins(id),
  CONSTRAINT no_solapamiento EXCLUDE USING GIST (
    equipo_id WITH =,
    TSTZRANGE(inicio, fin, '[)') WITH &&
  ) WHERE (estado IN ('pendiente', 'activa'))
);

-- ============================================================
-- HORARIOS
-- ============================================================

CREATE TABLE klee_reglas_horario (
  id SERIAL PRIMARY KEY,
  laboratorio_id INT NOT NULL REFERENCES klee_laboratorios(id),
  dia_semana SMALLINT NOT NULL CHECK (dia_semana BETWEEN 0 AND 6),
  hora_inicio TIME NOT NULL,
  hora_fin TIME NOT NULL,
  activo BOOLEAN NOT NULL DEFAULT true,
  CONSTRAINT horario_coherente CHECK (hora_inicio < hora_fin)
);

-- ============================================================
-- AUDITORÍA
-- ============================================================

CREATE TABLE klee_auditoria (
  id BIGSERIAL PRIMARY KEY,
  admin_id INT REFERENCES klee_admins(id),
  accion VARCHAR(80) NOT NULL,        -- crear_usuario | editar_conexion | cancelar_reserva ...
  entidad VARCHAR(80),                -- usuario | conexion | reserva | equipo ...
  entidad_id VARCHAR(80),             -- identificador del objeto afectado
  detalle JSONB,                      -- payload enviado / recibido
  ip VARCHAR(45),
  resultado VARCHAR(20) DEFAULT 'ok', -- ok | error
  error_msg TEXT,
  creado_en TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Índices de rendimiento
CREATE INDEX idx_auditoria_admin ON klee_auditoria(admin_id);
CREATE INDEX idx_auditoria_entidad ON klee_auditoria(entidad, entidad_id);
CREATE INDEX idx_auditoria_fecha ON klee_auditoria(creado_en DESC);
CREATE INDEX idx_reservas_equipo_estado ON klee_reservas(equipo_id, estado);
CREATE INDEX idx_reservas_usuario ON klee_reservas(guac_username);
CREATE INDEX idx_equipos_laboratorio ON klee_equipos(laboratorio_id);
CREATE INDEX idx_labs_sede ON klee_laboratorios(sede_id);
```

---

## 6. Mapeo completo de endpoints REST

### Base: `{GUAC_URL}/api/session/data/{dataSource}`

| Módulo | Operación | Método | Endpoint Guacamole |
|---|---|---|---|
| **Sesión** | Login | POST | `/api/tokens` |
| **Sesión** | Logout | DELETE | `/api/tokens/{token}` |
| **Usuarios** | Listar | GET | `/users` |
| **Usuarios** | Obtener | GET | `/users/{id}` |
| **Usuarios** | Crear | POST | `/users` |
| **Usuarios** | Editar | PUT | `/users/{id}` |
| **Usuarios** | Eliminar | DELETE | `/users/{id}` |
| **Usuarios** | Cambiar contraseña | PUT | `/users/{id}/password` |
| **Usuarios** | Ver permisos | GET | `/users/{id}/permissions` |
| **Usuarios** | Permisos efectivos | GET | `/users/{id}/effectivePermissions` |
| **Usuarios** | Editar permisos | PATCH | `/users/{id}/permissions` |
| **Usuarios** | Grupos del usuario | GET | `/users/{id}/userGroups` |
| **Usuarios** | Historial login | GET | `/history/users` |
| **Grupos usuario** | Listar | GET | `/userGroups` |
| **Grupos usuario** | Obtener | GET | `/userGroups/{id}` |
| **Grupos usuario** | Crear | POST | `/userGroups` |
| **Grupos usuario** | Editar | PUT | `/userGroups/{id}` |
| **Grupos usuario** | Eliminar | DELETE | `/userGroups/{id}` |
| **Grupos usuario** | Miembros | GET | `/userGroups/{id}/memberUsers` |
| **Grupos usuario** | Editar miembros | PATCH | `/userGroups/{id}/memberUsers` |
| **Grupos usuario** | Sub-grupos | GET | `/userGroups/{id}/userGroups` |
| **Grupos usuario** | Editar sub-grupos | PATCH | `/userGroups/{id}/userGroups` |
| **Grupos usuario** | Permisos | GET | `/userGroups/{id}/permissions` |
| **Grupos usuario** | Editar permisos | PATCH | `/userGroups/{id}/permissions` |
| **Conexiones** | Listar | GET | `/connections` |
| **Conexiones** | Obtener | GET | `/connections/{id}` |
| **Conexiones** | Crear | POST | `/connections` |
| **Conexiones** | Editar | PUT | `/connections/{id}` |
| **Conexiones** | Eliminar | DELETE | `/connections/{id}` |
| **Conexiones** | Ver parámetros | GET | `/connections/{id}/parameters` |
| **Conexiones** | Historial uso | GET | `/connections/{id}/history` |
| **Conexiones** | Perfiles compartición | GET | `/connections/{id}/sharingProfiles` |
| **Grupos conexión** | Listar | GET | `/connectionGroups` |
| **Grupos conexión** | Árbol ROOT | GET | `/connectionGroups/ROOT/tree` |
| **Grupos conexión** | Árbol sub-grupo | GET | `/connectionGroups/{id}/tree` |
| **Grupos conexión** | Obtener | GET | `/connectionGroups/{id}` |
| **Grupos conexión** | Crear | POST | `/connectionGroups` |
| **Grupos conexión** | Editar | PUT | `/connectionGroups/{id}` |
| **Grupos conexión** | Eliminar | DELETE | `/connectionGroups/{id}` |
| **Perfiles compartición** | Listar | GET | `/sharingProfiles` |
| **Perfiles compartición** | Obtener | GET | `/sharingProfiles/{id}` |
| **Perfiles compartición** | Crear | POST | `/sharingProfiles` |
| **Perfiles compartición** | Editar | PUT | `/sharingProfiles/{id}` |
| **Perfiles compartición** | Eliminar | DELETE | `/sharingProfiles/{id}` |
| **Perfiles compartición** | Ver parámetros | GET | `/sharingProfiles/{id}/parameters` |
| **Sesiones activas** | Listar | GET | `/activeConnections` |
| **Sesiones activas** | Obtener | GET | `/activeConnections/{id}` |
| **Sesiones activas** | Terminar | DELETE | `/activeConnections/{id}` |
| **Sesiones activas** | Conexión base | GET | `/activeConnections/{id}/connection` |
| **Historial** | Historial conexiones | GET | `/history/connections` |
| **Historial** | Detalle registro | GET | `/history/connections/{id}` |
| **Historial** | Ver log | GET | `/history/connections/{id}/logs/{name}` |
| **Historial** | Historial usuarios | GET | `/history/users` |
| **Schema** | Protocolos | GET | `/schema/protocols` |
| **Schema** | Atributos usuario | GET | `/schema/userAttributes` |
| **Schema** | Atributos grupo usr | GET | `/schema/userGroupAttributes` |
| **Schema** | Atributos conexión | GET | `/schema/connectionAttributes` |
| **Schema** | Atributos grp conn | GET | `/schema/connectionGroupAttributes` |
| **Schema** | Atributos perfil | GET | `/schema/sharingProfileAttributes` |

---

## 7. Estructura de archivos PHP

```
klee-labs/
├── config/
│   └── config.php                      # Credenciales Guacamole + BD
│
├── public/
│   ├── index.php                       # Front controller + router
│   ├── login.php                       # Login Klee Labs
│   └── assets/
│       ├── metronic/                   # Metronic 8 (css, js, plugins)
│       └── app/
│           ├── css/klee.css
│           └── js/
│               ├── klee.js             # Helpers globales
│               ├── datatables-init.js
│               └── fullcalendar-init.js
│
├── src/
│   ├── Guacamole/
│   │   ├── GuacamoleApiClient.php      # Cliente HTTP base (token, request)
│   │   ├── UserService.php             # CRUD usuarios + permisos
│   │   ├── UserGroupService.php        # CRUD grupos de usuarios
│   │   ├── ConnectionService.php       # CRUD conexiones + parámetros
│   │   ├── ConnectionGroupService.php  # CRUD grupos de conexión + árbol
│   │   ├── SharingProfileService.php   # CRUD perfiles compartición
│   │   ├── ActiveConnectionService.php # Sesiones activas + terminar
│   │   ├── HistoryService.php          # Historial conexiones + usuarios
│   │   └── SchemaService.php           # Protocolos + atributos
│   │
│   ├── Klee/
│   │   ├── SedeService.php
│   │   ├── LaboratorioService.php
│   │   ├── EquipoService.php
│   │   ├── ReservaService.php
│   │   ├── HorarioService.php
│   │   └── AuditoriaService.php
│   │
│   ├── Controllers/
│   │   ├── AuthController.php
│   │   ├── DashboardController.php
│   │   ├── Guacamole/
│   │   │   ├── UserController.php
│   │   │   ├── UserGroupController.php
│   │   │   ├── ConnectionController.php
│   │   │   ├── ConnectionGroupController.php
│   │   │   ├── SharingProfileController.php
│   │   │   ├── ActiveConnectionController.php
│   │   │   ├── HistoryController.php
│   │   │   └── SchemaController.php
│   │   └── Klee/
│   │       ├── SedeController.php
│   │       ├── LaboratorioController.php
│   │       ├── EquipoController.php
│   │       ├── ReservaController.php
│   │       └── HorarioController.php
│   │
│   ├── Database/
│   │   ├── Connection.php              # PDO factory (Guac RO + Klee RW)
│   │   ├── GuacamoleReadRepository.php # SELECT en guacamole_*
│   │   ├── KleeRepository.php          # CRUD en klee_*
│   │   └── ReportRepository.php        # Queries de reportes/KPIs
│   │
│   └── Support/
│       ├── Auth.php                    # Sesión Klee + control de roles
│       ├── Csrf.php                    # Token CSRF
│       ├── Paginator.php               # Paginación de tablas
│       ├── Exporter.php                # CSV + PDF (PhpSpreadsheet, mPDF)
│       └── Encryptor.php               # Cifrado de passwords RDP
│
├── views/
│   ├── layout.php                      # Layout Metronic (header, sidebar, footer)
│   ├── partials/
│   │   ├── flash.php                   # Mensajes de éxito/error
│   │   ├── breadcrumb.php
│   │   └── pagination.php
│   ├── auth/
│   │   └── login.php
│   ├── dashboard/
│   │   └── index.php                   # KPIs + gráficos
│   ├── guacamole/
│   │   ├── usuarios/
│   │   │   ├── index.php               # DataTable
│   │   │   ├── form.php                # Crear / Editar
│   │   │   ├── show.php                # Detalle
│   │   │   ├── permisos.php            # Gestión de permisos
│   │   │   └── historial.php
│   │   ├── grupos-usuario/
│   │   │   ├── index.php
│   │   │   ├── form.php
│   │   │   ├── miembros.php
│   │   │   └── permisos.php
│   │   ├── conexiones/
│   │   │   ├── index.php
│   │   │   ├── form.php                # Dinámico por protocolo
│   │   │   ├── show.php
│   │   │   └── historial.php
│   │   ├── grupos-conexion/
│   │   │   ├── index.php               # Vista árbol + lista
│   │   │   └── form.php
│   │   ├── perfiles-comparticion/
│   │   │   ├── index.php
│   │   │   └── form.php
│   │   ├── sesiones-activas/
│   │   │   └── index.php               # Auto-refresh Ajax
│   │   └── historial/
│   │       ├── conexiones.php
│   │       └── usuarios.php
│   └── klee/
│       ├── sedes/
│       │   ├── index.php
│       │   └── form.php
│       ├── laboratorios/
│       │   ├── index.php
│       │   └── form.php
│       ├── equipos/
│       │   ├── index.php
│       │   ├── form.php
│       │   └── show.php                # Estado + sesión activa
│       ├── reservas/
│       │   ├── calendario.php          # FullCalendar
│       │   ├── lista.php
│       │   └── form.php
│       └── horarios/
│           ├── index.php
│           └── form.php
│
├── sql/
│   ├── 001_klee_schema.sql             # Tablas klee_*
│   └── 002_klee_seed.sql               # Datos iniciales (admin por defecto)
│
├── storage/
│   └── logs/
│       ├── app.log
│       └── api.log
│
├── composer.json
└── .env.example
```

---

## 8. Plan de trabajo por fases

### Fase 0 — Infraestructura base (Semana 1)

| # | Tarea | Archivos involucrados |
|---|---|---|
| 0.1 | `composer install` + autoload PSR-4 | `composer.json` |
| 0.2 | Variables de entorno con phpdotenv | `.env`, `config/config.php` |
| 0.3 | PDO factory: conexión Guacamole (RO) + Klee (RW) | `src/Database/Connection.php` |
| 0.4 | Ejecutar `sql/001_klee_schema.sql` | — |
| 0.5 | `GuacamoleApiClient`: login() + request() genérico | `src/Guacamole/GuacamoleApiClient.php` |
| 0.6 | Smoke test: `listUsers()` funciona | script temporal |
| 0.7 | Router minimalista en `public/index.php` | `public/index.php` |
| 0.8 | Layout Metronic 8 (`views/layout.php`) | `views/layout.php` |
| 0.9 | Módulo Auth Klee: login/logout, sesión PHP, CSRF | `src/Support/Auth.php`, `src/Support/Csrf.php` |
| 0.10 | Seed: crear admin por defecto (bcrypt) | `sql/002_klee_seed.sql` |

**Criterio de éxito Fase 0**: login en Klee Labs, el token de Guacamole se obtiene y `listUsers()` devuelve datos.

---

### Fase 1 — Módulos Guacamole core (Semanas 2–3)

| # | Módulo | Prioridad |
|---|---|---|
| 1.1 | **Usuarios**: lista + crear + editar + eliminar | Alta |
| 1.2 | **Usuarios**: cambiar contraseña | Alta |
| 1.3 | **Usuarios**: ver y editar permisos de sistema | Alta |
| 1.4 | **Usuarios**: asignar permisos a objetos (conexiones) | Alta |
| 1.5 | **Grupos de usuario**: lista + CRUD | Media |
| 1.6 | **Grupos de usuario**: miembros + permisos | Media |
| 1.7 | **Schema**: cargar protocolos dinámicamente | Alta |
| 1.8 | **Conexiones**: lista + crear (RDP/VNC/SSH) | Alta |
| 1.9 | **Conexiones**: editar + eliminar + ver parámetros | Alta |
| 1.10 | **Grupos de conexión**: árbol + CRUD | Media |

**Criterio de éxito Fase 1**: crear usuario en Guacamole desde Klee Labs y asignarle una conexión.

---

### Fase 2 — Módulos Guacamole avanzados (Semana 4)

| # | Módulo | Prioridad |
|---|---|---|
| 2.1 | **Sesiones activas**: monitor con auto-refresh | Alta |
| 2.2 | **Sesiones activas**: terminar sesión | Alta |
| 2.3 | **Historial de conexiones**: lista + filtros | Alta |
| 2.4 | **Historial de conexiones**: exportar CSV + PDF | Media |
| 2.5 | **Historial de usuarios**: lista + filtros | Media |
| 2.6 | **Perfiles de compartición**: CRUD | Baja |
| 2.7 | **Dashboard**: KPIs desde SQL RO | Alta |
| 2.8 | **Dashboard**: gráficos Chart.js | Media |

**Criterio de éxito Fase 2**: dashboard con KPIs reales, monitor de sesiones activas funcional.

---

### Fase 3 — Módulos Klee propios (Semanas 5–6)

| # | Módulo | Prioridad |
|---|---|---|
| 3.1 | **Sedes**: CRUD completo | Alta |
| 3.2 | **Laboratorios**: CRUD + crear ConnectionGroup en Guacamole | Alta |
| 3.3 | **Equipos**: CRUD + crear Connection en Guacamole | Alta |
| 3.4 | **Equipos**: ver estado en tiempo real (sesión activa) | Alta |
| 3.5 | **Equipos**: cambio de estado (mantenimiento) | Media |
| 3.6 | **Reglas de horario**: CRUD por laboratorio | Media |

**Criterio de éxito Fase 3**: crear un laboratorio → crear 10 equipos → verlos en Guacamole automáticamente.

---

### Fase 4 — Reservas y permisos automáticos (Semana 7)

| # | Tarea | Detalle |
|---|---|---|
| 4.1 | Calendario FullCalendar | Vista mensual de reservas por equipo |
| 4.2 | CRUD reservas | Formulario + validación de solapamiento |
| 4.3 | Asignación automática de permisos | Al crear reserva → PATCH Guacamole |
| 4.4 | Revocación automática de permisos | Al cancelar/finalizar → PATCH Guacamole |
| 4.5 | Job de activación/finalización | Cron PHP: cambiar estado según hora |
| 4.6 | Integración sistema externo (COLOSO) | Endpoint API Klee Labs para recibir reservas |

**Criterio de éxito Fase 4**: reservar equipo → usuario gana acceso en Guacamole → expirar reserva → acceso revocado.

---

### Fase 5 — Auditoría, seguridad y pulido (Semana 8)

| # | Tarea |
|---|---|
| 5.1 | Registro automático en `klee_auditoria` en todas las acciones |
| 5.2 | Vista de auditoría con filtros y exportación |
| 5.3 | Control de roles (admin / operador / auditor) en todos los controladores |
| 5.4 | Cifrado de passwords RDP en BD Klee (`src/Support/Encryptor.php`) |
| 5.5 | Rate limiting en el router (evitar abuso de API) |
| 5.6 | Manejo global de errores de API (token expirado → re-login automático) |
| 5.7 | Logs en `storage/logs/api.log` y `app.log` |
| 5.8 | Página 403 / 404 / 500 personalizadas con Metronic |
| 5.9 | Pruebas de integración básicas |

---

## 9. Reglas de oro

```
NUNCA  → Modificar guacd, la webapp Guacamole, ni sus extensiones
NUNCA  → INSERT/UPDATE/DELETE en tablas guacamole_* (solo la API REST puede hacerlo)
NUNCA  → Leer sesiones activas por SQL (no existe tabla guacamole_active_connection)
NUNCA  → Guardar authToken de Guacamole en BD (es de sesión, se regenera)
NUNCA  → Exponer passwords RDP en texto plano (cifrar en klee_equipos.password_rdp_enc)

SIEMPRE → Toda escritura va por la API REST de Guacamole
SIEMPRE → SQL directo en guacamole_* solo para SELECT (reportes/KPIs)
SIEMPRE → Registrar cada acción en klee_auditoria (antes de llamar a la API y con el resultado)
SIEMPRE → Validar token CSRF en cada formulario POST/PUT/DELETE
SIEMPRE → Verificar rol del admin de Klee antes de cualquier operación sensible
SIEMPRE → Fijar la versión de Guacamole en config y revisar la API tras cada upgrade
```

---

*Generado para Klee Labs — Consola de administración Apache Guacamole*
*Fecha de referencia: 2026-06-14 | API Guacamole 1.5.x / 1.6.x*
