c5748212f8
- 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>
200 lines
12 KiB
Markdown
200 lines
12 KiB
Markdown
# 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.1–4.4** Tests completos — 0024c
|
||
- [ ] **5.1–5.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.
|