0102afa06e
Mueve el issue a completed/ y actualiza el índice. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
124 lines
4.8 KiB
Markdown
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.
|