Files
agents_and_robots/dev/issues/completed/0024b-security-loader.md
T
egutierrez 0102afa06e chore: cerrar issue 0024b-security-loader
Mueve el issue a completed/ y actualiza el índice.

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

124 lines
4.8 KiB
Markdown

# 0024b — Security loader: security/ YAML files + shell/security/ loader
> Parte b del issue [0024-centralized-security-groups.md](0024-centralized-security-groups.md).
> Requiere 0024a (pkg/security/ tipos).
## Objetivo
Crear la carpeta `security/` en la raíz del proyecto con los YAML de grupos de usuarios, grupos de agentes y permisos. Crear el loader impuro `shell/security/loader.go` que los lee y devuelve un `security.SecurityPolicy`.
## Contexto
- `pkg/security/` ya existe (0024a). Este sub-issue añade la capa de persistencia (YAML) y el loader.
- Los YAML de `security/` se commitean al repositorio — son configuración de acceso, no secrets.
- El código se mergea con feature flag = false (loader creado pero no usado todavía).
## Arquitectura
```
security/ NEW — en raíz del proyecto
user-groups.yaml NEW
agent-groups.yaml NEW
permissions.yaml NEW
shell/security/ NEW
loader.go NEW
loader_test.go NEW
```
### Patron pure core / impure shell
- `security/*.yaml` — datos de configuración (no código)
- `shell/security/loader.go`**impuro**: lee filesystem, parsea YAML, construye `security.SecurityPolicy`
## Tareas
### Fase 1: YAML files
- [ ] **1.1** Crear `security/user-groups.yaml`:
```yaml
# Grupos de usuarios del sistema
# Members: lista de Matrix user IDs, o "*" para todos los usuarios
groups:
admins:
members: [] # rellenar con los administradores reales
everyone:
members: ["*"]
```
- [ ] **1.2** Crear `security/agent-groups.yaml`:
```yaml
# Grupos de agentes del sistema
# Agents: lista de agent IDs (del campo agent.id en config.yaml), o "*" para todos
groups:
assistants:
agents:
- assistant-bot
- asistente-2
all:
agents: ["*"]
```
- [ ] **1.3** Crear `security/permissions.yaml`:
```yaml
# Políticas de permisos: para cada grupo de agentes, qué acciones tiene cada grupo de usuarios
# Actions: "*" = todo, "ask" = chat libre, "command:<name>" = comandos, "tool:<name>" = tools
policies:
- agent_group: all
permissions:
- user_group: admins
actions: ["*"]
- user_group: everyone
actions: ["ask"]
```
### Fase 2: Shell loader
- [ ] **2.1** Crear `shell/security/loader.go` con función `Load(dir string) (security.SecurityPolicy, error)`:
- Lee `<dir>/user-groups.yaml` → `[]security.UserGroup`
- Lee `<dir>/agent-groups.yaml` → `[]security.AgentGroup`
- Lee `<dir>/permissions.yaml` → `[]security.AgentPolicy`
- Si el directorio no existe o está vacío: devuelve `security.SecurityPolicy{}` sin error (backward compat)
- Si un archivo no existe individualmente: ese campo queda vacío (no es error)
- Si el YAML es inválido: devuelve error con mensaje claro indicando qué archivo falló
- [ ] **2.2** Definir structs YAML intermedios (solo para parseo) distintos de los tipos puros de `pkg/security/`. Convertir tras parsear. Esto mantiene `pkg/security/` independiente de `gopkg.in/yaml.v3`.
### Fase 3: Tests del loader
- [ ] **3.1** Test: directorio inexistente → policy vacía, sin error
- [ ] **3.2** Test: directorio vacío (sin YAML) → policy vacía, sin error
- [ ] **3.3** Test: los 3 YAML válidos → policy con todos los campos
- [ ] **3.4** Test: solo `user-groups.yaml` presente → user groups poblados, resto vacío
- [ ] **3.5** Test: YAML malformado → error con nombre de archivo en el mensaje
- [ ] **3.6** Test: `user_group: "*"` y `agent: ["*"]` parseados correctamente como strings literales
### Fase 4: Cleanup
- [ ] **4.1** `go build -tags goolm ./...` compila
- [ ] **4.2** `go test -tags goolm ./shell/security/...` pasa
- [ ] **4.3** `go test -tags goolm ./...` pasa completo
## Ejemplo de uso
```go
// En el launcher (todavía no wired — eso es 0024c)
policy, err := shellsecurity.Load("security/")
if err != nil {
log.Fatal("error loading security policy", err)
}
// policy.UserGroups, policy.AgentGroups, policy.Policies disponibles
acl := security.ResolveACL("assistant-bot", policy)
```
## Decisiones de diseño
- **Structs YAML separados de los tipos puros**: `pkg/security/` no importa `gopkg.in/yaml.v3`. El loader usa tipos intermedios locales y convierte. Mantiene el core verdaderamente puro.
- **Directorio no existente = policy vacía**: no fuerza a crear los YAML si no se necesitan (ej: agentes puramente públicos). Backward compat con configuraciones existentes.
- **3 archivos separados**: cada uno puede editarse independientemente. Los grupos son más estables que los permisos.
## Prerequisitos
- 0024a completado (`pkg/security/` con tipos y `ResolveACL`)
## Riesgos
- **Typos en user IDs de YAML**: si un Matrix ID tiene un typo, el usuario no tendrá acceso. No hay validación de formato de ID en este issue — es aceptable para MVP.