Files
agents_and_robots/dev/issues/completed/0024-centralized-security-groups.md
T
egutierrez c5748212f8 chore: cerrar issue 0024c — security integration + docs
- Actualiza docs/security.md: nueva sección "Sistema de grupos centralizados"
  con estructura de los 3 YAML, acciones disponibles, campos deprecados
- Actualiza .claude/CLAUDE.md: añade security/ en la estructura del proyecto
- Mueve 0024 y 0024c a dev/issues/completed/
- Actualiza dev/issues/README.md: marca 0024, 0024a, 0024b, 0024c como completado

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-08 20:57:28 +00:00

200 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 0024 — Sistema centralizado de grupos y permisos
## Objetivo
Reemplazar los controles de acceso por agente (`security.roles`, `matrix.filters.allowed_users`) con un sistema centralizado en una carpeta `security/` donde se definen grupos de usuarios, grupos de agentes, y una política de permisos que los vincula. Esto elimina la necesidad de configurar permisos en cada agente individualmente.
## Contexto
- Actualmente cada agente tiene su propio bloque `security.roles` en `config.yaml` y `matrix.filters.allowed_users` en `matrix.filters`. Añadir un usuario a varios agentes requiere editar múltiples archivos.
- El módulo `pkg/acl/` existe y está completo: resuelve ACLs puras dado un mapa de roles. Lo reutilizamos como motor de evaluación.
- La nueva capa `pkg/security/` se apoya en `pkg/acl/` para producir `acl.ACL` por agente a partir de la política centralizada.
- La carpeta `security/` en la raíz del proyecto contiene los YAML de grupos y permisos. El launcher los carga una vez y distribuye la ACL resuelta a cada agente.
- Se elimina `matrix.filters.allowed_users` y `security.roles` del schema de config de agente una vez que todos los agentes usan la política centralizada.
**Dependencias:** ninguna (issue autocontenido en 3 fases).
## Arquitectura
```
pkg/security/ NEW — tipos puros + resolución ACL
groups.go NEW — UserGroup, AgentGroup
policy.go NEW — Permission, AgentPolicy, SecurityPolicy
resolver.go NEW — ResolveACL(agentID, policy) → acl.ACL
security_test.go NEW — tests de resolución
security/ NEW — configs centralizados (raíz del proyecto)
user-groups.yaml NEW — definición de grupos de usuarios
agent-groups.yaml NEW — definición de grupos de agentes
permissions.yaml NEW — políticas: qué grupos de usuarios tienen qué permisos en qué grupos de agentes
shell/security/ NEW — loader impuro
loader.go NEW — carga los 3 YAML y construye SecurityPolicy
loader_test.go NEW — tests con YAML de ejemplo
cmd/launcher/main.go MODIFIED — carga security/ al inicio, pasa acl.ACL resuelta a cada Agent
agents/runtime.go MODIFIED — acepta acl.ACL pre-resuelta en lugar de RoleCfg
internal/config/schema.go MODIFIED — marcar security.roles y matrix.filters.allowed_users como deprecated
agents/assistant-bot/config.yaml MODIFIED — eliminar security.roles y allowed_users
agents/asistente-2/config.yaml MODIFIED — eliminar security.roles y allowed_users
docs/security.md MODIFIED — documentar nuevo sistema
CLAUDE.md MODIFIED — mencionar security/ en estructura
```
### Patron pure core / impure shell
- `pkg/security/`**puro**: tipos (`UserGroup`, `AgentGroup`, `SecurityPolicy`) y función `ResolveACL()`. Cero I/O.
- `shell/security/`**impuro**: lee YAML del filesystem y construye `SecurityPolicy`.
- `cmd/launcher/`**impuro**: llama al loader, resuelve ACL por agente, inyecta en `Agent{}`.
- `agents/runtime.go`**composición**: recibe `acl.ACL` ya resuelta, la usa en `shouldHandle()` y en la evaluación de permisos.
## Tareas
### Fase 1: Pure core — pkg/security/
- [ ] **1.1** Crear `pkg/security/groups.go` con tipos `UserGroup{Name, Members []string}` y `AgentGroup{Name, Agents []string}`
- [ ] **1.2** Crear `pkg/security/policy.go` con tipos `Permission{UserGroup, Actions []string}`, `AgentPolicy{AgentGroup, Permissions []Permission}`, `SecurityPolicy{UserGroups, AgentGroups, Policies}`
- [ ] **1.3** Crear `pkg/security/resolver.go` con `ResolveACL(agentID string, p SecurityPolicy) acl.ACL`: expande grupos de agentes que incluyan `agentID` o `"*"`, expande grupos de usuarios a `acl.Role` list, construye `acl.ACL` vía `acl.FromRoles()`
- [ ] **1.4** Soporte de wildcard: `AgentGroup.Agents = ["*"]` aplica a todos los agentes; `UserGroup.Members = ["*"]` aplica a todos los usuarios
- [ ] **1.5** Crear `pkg/security/security_test.go` con casos: sin política (ACL vacía), agente en grupo, agente no en grupo, wildcard de agente, wildcard de usuario, múltiples políticas acumulativas
### Fase 2: Config files + Shell loader
- [ ] **2.1** Crear `security/user-groups.yaml` con ejemplo: grupos `admins` y `everyone` (members: `["*"]`)
- [ ] **2.2** Crear `security/agent-groups.yaml` con ejemplo: grupo `assistants` con los agentes actuales (`assistant-bot`, `asistente-2`), grupo `all` con `agents: ["*"]`
- [ ] **2.3** Crear `security/permissions.yaml` con ejemplo: grupo `all` da acción `"ask"` a `everyone`; grupo `all` da `"*"` a `admins`
- [ ] **2.4** Crear `shell/security/loader.go` con `Load(dir string) (security.SecurityPolicy, error)` que lee los 3 YAML del directorio y construye el struct. Si el directorio no existe, devuelve `SecurityPolicy{}` vacía (sin error: backward compat).
- [ ] **2.5** Crear `shell/security/loader_test.go` con tests: dir vacío → policy vacía, YAMLs válidos → policy correcta, YAML malformado → error claro
### Fase 3: Integración en launcher y runtime
- [ ] **3.1** En `cmd/launcher/main.go`: llamar `shell/security.Load("security/")` al inicio; para cada agente llamar `security.ResolveACL(cfg.Agent.ID, policy)` y pasar la `acl.ACL` resultante a `agents.New()`
- [ ] **3.2** En `agents/runtime.go`: añadir campo `acl acl.ACL` en `Agent{}`. Extender `agents.New()` para aceptar `acl.ACL` como parámetro adicional (o via `Option`). Usar `a.acl.CanDo()` en `shouldHandle()` y en evaluación de permisos de comandos/tools
- [ ] **3.3** En `shell/matrix/listener.go`: eliminar el chequeo de `AllowedUsers` (líneas 285-301 aprox.); el control de acceso ahora está en runtime via `acl.ACL`
- [ ] **3.4** En `internal/config/schema.go`: deprecar campos `security.roles` (añadir comentario `// Deprecated: usar security/ centralizado`) y `matrix.filters.allowed_users` (mismo comentario). No eliminar todavía — backward compat.
- [ ] **3.5** Actualizar `agents/assistant-bot/config.yaml` y `agents/asistente-2/config.yaml`: eliminar bloques `security.roles` y `matrix.filters.allowed_users` (ahora gestionados centralmente)
- [ ] **3.6** Actualizar `security/permissions.yaml` con los permisos reales de los agentes actuales (extraídos de sus configs antes de borrarlos)
### Fase 4: Tests de integración
- [ ] **4.1** `go build -tags goolm ./...` compila sin errores
- [ ] **4.2** `go test -tags goolm ./pkg/security/...` pasa
- [ ] **4.3** `go test -tags goolm ./shell/security/...` pasa
- [ ] **4.4** `go test -tags goolm ./...` pasa completo (sin romper tests existentes de pkg/acl)
### Fase 5: Cleanup y docs
- [ ] **5.1** Actualizar `docs/security.md` — documentar el sistema de grupos, estructura de los 3 YAML, campos disponibles en cada uno, cómo se resuelven las ACLs
- [ ] **5.2** Actualizar `CLAUDE.md` — añadir `security/` en la sección de estructura del proyecto
- [ ] **5.3** Añadir `.gitignore` entry si aplica (los YAML de `security/` SÍ se commitean — son config, no secrets)
- [ ] **5.4** Evaluar si eliminar definitivamente los campos deprecated del schema en este issue o dejarlo para un issue de limpieza posterior
---
## Desglose multi-issue
Este issue se implementa en 3 sub-issues independientes.
| Sub-issue | Rama | Alcance | Estado |
|-----------|------|---------|--------|
| 0024a-security-types | issue/0024a-security-types | pkg/security/ tipos puros + resolver + tests | pendiente |
| 0024b-security-loader | issue/0024b-security-loader | security/ YAML files + shell/security/ loader + tests | pendiente |
| 0024c-security-integration | issue/0024c-security-integration | Wiring en launcher+runtime, cleanup config schema, update agent configs, docs | pendiente |
### Feature flag
Nombre: `centralized-security-groups`
Se activa en el último sub-issue (0024c) una vez que todos los agentes usan la política centralizada y se han eliminado los controles per-agente.
### Progreso por tarea
- [ ] **1.1** UserGroup, AgentGroup types — 0024a
- [ ] **1.2** Permission, AgentPolicy, SecurityPolicy types — 0024a
- [ ] **1.3** ResolveACL() function — 0024a
- [ ] **1.4** Wildcard support — 0024a
- [ ] **1.5** Tests pkg/security/ — 0024a
- [ ] **2.1** security/user-groups.yaml — 0024b
- [ ] **2.2** security/agent-groups.yaml — 0024b
- [ ] **2.3** security/permissions.yaml — 0024b
- [ ] **2.4** shell/security/loader.go — 0024b
- [ ] **2.5** Tests shell/security/ — 0024b
- [ ] **3.1** Launcher wiring — 0024c
- [ ] **3.2** Runtime ACL field + New() — 0024c
- [ ] **3.3** Remove AllowedUsers from listener — 0024c
- [ ] **3.4** Deprecar campos schema — 0024c
- [ ] **3.5** Update agent configs — 0024c
- [ ] **3.6** Populate permissions.yaml con datos reales — 0024c
- [ ] **4.14.4** Tests completos — 0024c
- [ ] **5.15.4** Cleanup y docs — 0024c
---
## Ejemplo de uso
**Estructura de archivos resultante:**
```
security/
user-groups.yaml
agent-groups.yaml
permissions.yaml
```
**security/user-groups.yaml:**
```yaml
groups:
admins:
members:
- "@alice:matrix-af2f3d.organic-machine.com"
- "@bob:matrix-af2f3d.organic-machine.com"
developers:
members:
- "@carol:matrix-af2f3d.organic-machine.com"
everyone:
members: ["*"]
```
**security/agent-groups.yaml:**
```yaml
groups:
assistants:
agents:
- assistant-bot
- asistente-2
all:
agents: ["*"]
```
**security/permissions.yaml:**
```yaml
policies:
- agent_group: all
permissions:
- user_group: admins
actions: ["*"]
- user_group: developers
actions: ["ask", "command:help", "command:ping", "tool:*"]
- user_group: everyone
actions: ["ask"]
```
**Resultado:** Al arrancar, el launcher lee `security/`, resuelve la ACL de cada agente, y se la inyecta. Los agentes ya no tienen `security.roles` ni `allowed_users` en su config individual. Para dar permisos a un nuevo usuario en todos los agentes, basta editar `security/user-groups.yaml`.
## Decisiones de diseño
- **Reutilizar pkg/acl/ como motor**: `pkg/security/` no reemplaza `pkg/acl/`, lo usa. `ResolveACL()` produce `acl.ACL` que los agentes ya saben consumir. Mínimo cambio en runtime.
- **3 YAML separados vs 1 solo archivo**: separar grupos de usuarios, grupos de agentes, y permisos mantiene cada archivo enfocado. Los grupos son estables; los permisos cambian más frecuentemente.
- **Backward compat en schema**: deprecar pero no eliminar `security.roles` y `allowed_users` en 0024c. Eliminarlos definitivamente sería un issue de limpieza posterior.
- **Loader devuelve policy vacía si no existe security/**: no rompe agentes existentes si el directorio no existe. La ACL vacía equivale a "sin restricciones" (comportamiento actual).
- **ACL inyectada via parámetro en agents.New()**: alternativa a `Option{}` para mantener la firma explícita. Más simple y sin abstracción innecesaria.
## Prerequisitos
- `pkg/acl/` funcionando (completado en issue 0010)
- Agentes compilando con `-tags goolm` (ya funciona)
## Riesgos
- **Permisos actuales en config.yaml**: antes de eliminar `security.roles` de los configs de agente, leer y migrar todos los roles a `security/permissions.yaml`. Si se olvida alguno, el agente queda sin restricciones o con más acceso del esperado. Mitigación: hacer la migración explícitamente en tarea 3.6 antes de borrar en 3.5.
- **Orden de carga en launcher**: si el loader falla, los agentes arrancan sin ACL (acceso abierto). Mitigación: loguear WARNING claro en ese caso; considerar modo estricto (fail-fast) como opción de config futura.
- **acl.FromRoles() API**: verificar que `pkg/acl/` expone una función que acepte `[]acl.Role` directamente (no solo `map[string]RoleDef`). Si no existe, añadirla en 0024a.