|
|
|
|
@ -0,0 +1,219 @@
|
|
|
|
|
# Plan de habilitación multi‑comunidades con control de acceso
|
|
|
|
|
|
|
|
|
|
Propósito
|
|
|
|
|
- Permitir que el bot opere en varias comunidades/grupos sin perder control.
|
|
|
|
|
- Introducir un mecanismo de whitelist (allowed_groups) y un flujo de aprobación administrativa.
|
|
|
|
|
- Mantener las mismas funcionalidades en todos los grupos aprobados.
|
|
|
|
|
|
|
|
|
|
Resumen de estrategia
|
|
|
|
|
- Instancia única multi‑comunidad con “gating” por grupo permitido.
|
|
|
|
|
- Descubrimiento seguro: registrar grupos desconocidos como pending y no operar hasta aprobación.
|
|
|
|
|
- Comandos/admin y notificaciones para aprobar/bloquear grupos.
|
|
|
|
|
|
|
|
|
|
Estructura del plan por etapas
|
|
|
|
|
Cada etapa indica objetivos y archivos a tocar. La implementación es incremental y desplegable paso a paso.
|
|
|
|
|
|
|
|
|
|
Etapa 0 — Preparación y criterios
|
|
|
|
|
Objetivos
|
|
|
|
|
- Alinear alcance y criterios de aceptación.
|
|
|
|
|
- Definir nuevas variables de entorno y defaults.
|
|
|
|
|
|
|
|
|
|
Criterios de aceptación
|
|
|
|
|
- Variables definidas:
|
|
|
|
|
- ADMIN_USERS: lista de user_ids normalizados con permisos de administración.
|
|
|
|
|
- ALLOWED_GROUPS: lista inicial (semilla) de group_ids a marcar como allowed.
|
|
|
|
|
- AUTO_APPROVE_PATTERNS (opcional): patrones de nombre de grupo confiables (con cautela).
|
|
|
|
|
- Comportamiento por defecto para grupos desconocidos: registrar como pending y no operar.
|
|
|
|
|
- Decidir si el bot responde (o guarda silencio) en grupos pending/blocked.
|
|
|
|
|
|
|
|
|
|
Archivos a tocar
|
|
|
|
|
- Documentación (docs/operations.md, docs/architecture.md) — actualización en Etapa 6.
|
|
|
|
|
|
|
|
|
|
Etapa 1 — Esquema y servicio AllowedGroups (sin gating todavía)
|
|
|
|
|
Objetivos
|
|
|
|
|
- Crear tabla allowed_groups con estados: pending | allowed | blocked.
|
|
|
|
|
- Implementar servicio con caché en memoria y API:
|
|
|
|
|
- upsertPending(groupId, label?, discoveredBy?) → pending
|
|
|
|
|
- isAllowed(groupId) → boolean
|
|
|
|
|
- setStatus(groupId, status, label?) → boolean
|
|
|
|
|
- listByStatus(status) → {group_id, label}[]
|
|
|
|
|
- seedFromEnv(ALLOWED_GROUPS)
|
|
|
|
|
- Métricas básicas por estado (gauges).
|
|
|
|
|
|
|
|
|
|
Cambios de datos
|
|
|
|
|
- Tabla allowed_groups:
|
|
|
|
|
- group_id TEXT PRIMARY KEY
|
|
|
|
|
- label TEXT NULL
|
|
|
|
|
- status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','allowed','blocked'))
|
|
|
|
|
- discovered_at TEXT NOT NULL DEFAULT now
|
|
|
|
|
- updated_at TEXT NOT NULL DEFAULT now
|
|
|
|
|
- discovered_by TEXT NULL
|
|
|
|
|
|
|
|
|
|
Archivos a tocar
|
|
|
|
|
- src/db/migrations/index.ts: añadir una nueva migración (v9) para crear allowed_groups e índices.
|
|
|
|
|
- src/services/allowed-groups.ts (nuevo): implementación del servicio con caché y métodos arriba.
|
|
|
|
|
- src/services/metrics.ts: gauges allowed_groups_total{status} (opcional en esta etapa).
|
|
|
|
|
- tests/unit/services/allowed-groups.test.ts (nuevo).
|
|
|
|
|
|
|
|
|
|
Etapa 2 — Descubrimiento seguro de grupos (registrar, no operar)
|
|
|
|
|
Objetivos
|
|
|
|
|
- Al recibir tráfico de un group_id desconocido:
|
|
|
|
|
- Insertar/actualizar en allowed_groups con status=pending y label (si disponible).
|
|
|
|
|
- No ejecutar lógica de negocio (comandos, sync, recordatorios).
|
|
|
|
|
- Métricas y logs del descubrimiento.
|
|
|
|
|
|
|
|
|
|
Archivos a tocar
|
|
|
|
|
- src/server.ts:
|
|
|
|
|
- handleMessageUpsert: antes de cualquier operación de grupo, consultar AllowedGroups.
|
|
|
|
|
- Si no existe: upsertPending y retornar sin procesar comandos.
|
|
|
|
|
- Añadir métricas: unknown_groups_discovered_total.
|
|
|
|
|
- src/services/contacts.ts:
|
|
|
|
|
- Helper opcional para obtener/actualizar el nombre del grupo si el webhook lo trae (si no, dejar label=null).
|
|
|
|
|
- src/services/metrics.ts: contador unknown_groups_discovered_total.
|
|
|
|
|
- tests/unit/server/unknown-group-discovery.test.ts (nuevo).
|
|
|
|
|
|
|
|
|
|
Etapa 3 — Gating mínimo en superficies críticas
|
|
|
|
|
Objetivos
|
|
|
|
|
- Bloquear procesamiento de comandos si el group_id no está allowed.
|
|
|
|
|
- Asegurar que sincronización de grupos/miembros solo opere sobre grupos allowed.
|
|
|
|
|
- Responder con mensaje breve o permanecer en silencio para pending/blocked (configurable).
|
|
|
|
|
|
|
|
|
|
Archivos a tocar
|
|
|
|
|
- src/services/command.ts:
|
|
|
|
|
- Al inicio de processTareaCommand cuando se esté en grupo: consultar AllowedGroups.isAllowed().
|
|
|
|
|
- Si no allowed: devolver mensaje breve o [] según política (config).
|
|
|
|
|
- Sustituir usos de GroupSyncService.isGroupActive por AllowedGroups.isAllowed para el gating de creación de tareas con group_id.
|
|
|
|
|
- src/services/group-sync.ts:
|
|
|
|
|
- Filtrar y operar únicamente sobre grupos allowed al sincronizar (scheduler + reconcile).
|
|
|
|
|
- Donde se cachean grupos activos, cruzar con allowed_groups o cambiar la fuente de verdad a AllowedGroups si procede.
|
|
|
|
|
- src/server.ts:
|
|
|
|
|
- En handleMessageUpsert, antes de encolar/ejecutar comandos, exigir allowed en caso de que sea un grupo.
|
|
|
|
|
- src/services/metrics.ts:
|
|
|
|
|
- commands_blocked_total
|
|
|
|
|
- sync_skipped_group_total (si se decide contabilizar grupos saltados por no allowed)
|
|
|
|
|
- tests/unit/services/command.gating.test.ts (nuevo).
|
|
|
|
|
- tests/unit/services/group-sync.gating.test.ts (nuevo).
|
|
|
|
|
|
|
|
|
|
Etapa 4 — Flujo de aprobación administrativa y notificaciones
|
|
|
|
|
Objetivos
|
|
|
|
|
- Comandos admin:
|
|
|
|
|
- /admin habilitar-aquí
|
|
|
|
|
- /admin deshabilitar-aquí
|
|
|
|
|
- /admin pendientes
|
|
|
|
|
- /admin allow-group <group_id>
|
|
|
|
|
- /admin block-group <group_id>
|
|
|
|
|
- Validación de permisos con ADMIN_USERS (IDs normalizados).
|
|
|
|
|
- Notificación automática a ADMIN_USERS al descubrir un grupo pending (con nombre si se conoce).
|
|
|
|
|
|
|
|
|
|
Archivos a tocar
|
|
|
|
|
- src/server.ts:
|
|
|
|
|
- Router para comandos /admin → nuevo servicio AdminService.
|
|
|
|
|
- Validación de ADMIN_USERS en validateEnv (no obligatorio pero recomendable).
|
|
|
|
|
- src/services/admin.ts (nuevo):
|
|
|
|
|
- Implementa los subcomandos y policy de permisos.
|
|
|
|
|
- src/services/allowed-groups.ts:
|
|
|
|
|
- Exponer setStatus, listByStatus, maybe get(groupId).
|
|
|
|
|
- src/services/response-queue.ts o src/services/webhook-manager.ts:
|
|
|
|
|
- Utilizar ResponseQueue para notificar a ADMIN_USERS por DM cuando se descubra un grupo pending.
|
|
|
|
|
- tests/unit/services/command.admin-approval.test.ts (nuevo).
|
|
|
|
|
- tests/unit/services/admin.test.ts (nuevo).
|
|
|
|
|
|
|
|
|
|
Etapa 5 — Integración con servicios dependientes (recordatorios, tareas, rate limit)
|
|
|
|
|
Objetivos
|
|
|
|
|
- Recordatorios: si incluyen contexto de grupo, operar solo en grupos allowed; si no, DM sin dependencia del gating de grupo.
|
|
|
|
|
- Tareas: validar group_id permitido al crear desde un grupo (ya resuelto en Etapa 3).
|
|
|
|
|
- Rate limit (opcional): aislar por grupo si se desea (clave compuesta groupId+userId).
|
|
|
|
|
|
|
|
|
|
Archivos a tocar
|
|
|
|
|
- src/services/reminders.ts:
|
|
|
|
|
- Al componer secciones por grupo (y “sin responsable”), filtrar por AllowedGroups.isAllowed(gid) si aplica.
|
|
|
|
|
- src/tasks/service.ts:
|
|
|
|
|
- Mantener la lógica de setear group_id=null si no existe/permitido, delegando la decisión al llamador (CommandService).
|
|
|
|
|
- src/services/rate-limit.ts (opcional):
|
|
|
|
|
- Añadir variantes de clave por grupo (ej. `${userId}:${groupId || 'dm'}`) si se activa por env.
|
|
|
|
|
- tests/unit/services/reminders.gating.test.ts (nuevo).
|
|
|
|
|
- tests/unit/tasks/service.gating.test.ts (nuevo).
|
|
|
|
|
|
|
|
|
|
Etapa 6 — Observabilidad y operaciones
|
|
|
|
|
Objetivos
|
|
|
|
|
- Métricas agregadas y documentación operativa.
|
|
|
|
|
- Logs claros en cambios de estado y descubrimientos.
|
|
|
|
|
|
|
|
|
|
Archivos a tocar
|
|
|
|
|
- src/services/metrics.ts:
|
|
|
|
|
- allowed_groups_total{status}, unknown_groups_discovered_total, commands_blocked_total, admin_actions_total{action}.
|
|
|
|
|
- src/server.ts:
|
|
|
|
|
- Confirmar exposición de métricas en /metrics.
|
|
|
|
|
- docs/operations.md:
|
|
|
|
|
- Documentar ADMIN_USERS, ALLOWED_GROUPS, AUTO_APPROVE_PATTERNS, comportamiento de pending/blocked, y comandos admin.
|
|
|
|
|
- docs/architecture.md:
|
|
|
|
|
- Añadir sección “Control de acceso por grupos (allowed_groups)”.
|
|
|
|
|
|
|
|
|
|
Etapa 7 — Hardening, pruebas de regresión y rollout
|
|
|
|
|
Objetivos
|
|
|
|
|
- Pruebas E2E con múltiples grupos en estados allowed/pending/blocked.
|
|
|
|
|
- Verificación de que no hay rutas sin gating (comandos, schedulers, envíos).
|
|
|
|
|
- Plan de despliegue gradual:
|
|
|
|
|
1) Activar registro (Etapas 1–2) sin gating duro.
|
|
|
|
|
2) Activar gating (Etapa 3) con política de silencio o mensaje breve.
|
|
|
|
|
3) Habilitar comandos admin (Etapa 4).
|
|
|
|
|
4) Ajustar recordatorios/tareas (Etapa 5).
|
|
|
|
|
5) Cerrar documentación y métricas (Etapa 6).
|
|
|
|
|
|
|
|
|
|
Archivos a tocar
|
|
|
|
|
- tests/** (nuevos casos integrados multi‑grupo).
|
|
|
|
|
- scripts de CI (si aplican) para asegurar la ejecución de nuevas suites.
|
|
|
|
|
|
|
|
|
|
Mapa rápido de archivos por etapa (resumen)
|
|
|
|
|
- Etapa 1:
|
|
|
|
|
- src/db/migrations/index.ts (nueva migración v9)
|
|
|
|
|
- src/services/allowed-groups.ts (nuevo)
|
|
|
|
|
- src/services/metrics.ts (gauges)
|
|
|
|
|
- Etapa 2:
|
|
|
|
|
- src/server.ts
|
|
|
|
|
- src/services/contacts.ts (opcional)
|
|
|
|
|
- src/services/metrics.ts (counter)
|
|
|
|
|
- Etapa 3:
|
|
|
|
|
- src/services/command.ts
|
|
|
|
|
- src/services/group-sync.ts
|
|
|
|
|
- src/server.ts
|
|
|
|
|
- src/services/metrics.ts
|
|
|
|
|
- Etapa 4:
|
|
|
|
|
- src/server.ts
|
|
|
|
|
- src/services/admin.ts (nuevo)
|
|
|
|
|
- src/services/allowed-groups.ts (extensiones)
|
|
|
|
|
- src/services/response-queue.ts (notificaciones a admins)
|
|
|
|
|
- Etapa 5:
|
|
|
|
|
- src/services/reminders.ts
|
|
|
|
|
- src/tasks/service.ts (validación indirecta ya cubierta por llamador)
|
|
|
|
|
- src/services/rate-limit.ts (opcional)
|
|
|
|
|
- Etapa 6:
|
|
|
|
|
- src/services/metrics.ts
|
|
|
|
|
- docs/operations.md
|
|
|
|
|
- docs/architecture.md
|
|
|
|
|
- Etapa 7:
|
|
|
|
|
- tests/** integración/regresión
|
|
|
|
|
- CI (si aplica)
|
|
|
|
|
|
|
|
|
|
Variables de entorno previstas (añadidos)
|
|
|
|
|
- ADMIN_USERS: lista separada por comas de IDs de usuario administradores (normalizados).
|
|
|
|
|
- ALLOWED_GROUPS: lista separada por comas de group_ids iniciales a permitir.
|
|
|
|
|
- AUTO_APPROVE_PATTERNS (opcional): patrones para auto‑aprobar por nombre (con cautela).
|
|
|
|
|
- MAX_MEMBERS_SNAPSHOT_AGE_MS: ya usada; garantizar coherencia en gating si se cruza con membresía.
|
|
|
|
|
- METRICS_ENABLED, NODE_ENV, TZ: respetadas.
|
|
|
|
|
|
|
|
|
|
Riesgos y mitigaciones
|
|
|
|
|
- Fuga de operaciones en grupos no permitidos: tests específicos (Etapas 3 y 7).
|
|
|
|
|
- Identificación de nombres de grupo: si no se dispone de “subject” en eventos, almacenar solo group_id; completar label más tarde desde comando admin o tras un fetch.
|
|
|
|
|
- Sobrecarga de DB por descubrimiento: caché en AllowedGroups + operaciones idempotentes; escribir solo si no existe o cambió estado/label.
|
|
|
|
|
|
|
|
|
|
Criterios de “hecho” globales
|
|
|
|
|
- El bot solo opera en grupos con status=allowed.
|
|
|
|
|
- Flujo de aprobación funciona desde grupo (/admin habilitar-aquí) y por DM (/admin allow-group <id>).
|
|
|
|
|
- Métricas y logs permiten auditar descubrimientos y cambios de estado.
|
|
|
|
|
- Documentación de operación actualizada.
|
|
|
|
|
|
|
|
|
|
Notas de implementación
|
|
|
|
|
- Mantener compatibilidad hacia atrás: en creación de tareas, si el group_id no está allowed, persistir sin group_id (null).
|
|
|
|
|
- No bloquear DMs por diseño (gating aplica solo a grupos).
|
|
|
|
|
- Reutilizar ResponseQueue para todas las notificaciones (incluyendo admins).
|