diff --git a/dev/issues/README.md b/dev/issues/README.md index 69c3864..c26796d 100644 --- a/dev/issues/README.md +++ b/dev/issues/README.md @@ -32,7 +32,7 @@ afectados y notas de implementacion. | 22c | E2E: Tests de agentes + docs | [0022c-e2e-agent-tests.md](completed/0022c-e2e-agent-tests.md) | completado | | 23 | Seccion de tests en dashboard | [0023-dashboard-tests.md](completed/0023-dashboard-tests.md) | completado | | 24 | Grupos y permisos centralizados | [0024-centralized-security-groups.md](0024-centralized-security-groups.md) | pendiente | -| 24a | Security types: pkg/security/ | [0024a-security-types.md](0024a-security-types.md) | pendiente | +| 24a | Security types: pkg/security/ | [0024a-security-types.md](completed/0024a-security-types.md) | completado | | 24b | Security loader: shell/security/ | [0024b-security-loader.md](0024b-security-loader.md) | pendiente | | 24c | Security integration + cleanup | [0024c-security-integration.md](0024c-security-integration.md) | pendiente | | 25 | Catálogo cron + scaffolder | [0025-cron-scaffolder.md](completed/0025-cron-scaffolder.md) | completado | diff --git a/dev/issues/completed/0024a-security-types.md b/dev/issues/completed/0024a-security-types.md new file mode 100644 index 0000000..4ec4010 --- /dev/null +++ b/dev/issues/completed/0024a-security-types.md @@ -0,0 +1,107 @@ +# 0024a — Security types: pkg/security/ — tipos puros y resolución ACL + +> Parte a del issue [0024-centralized-security-groups.md](0024-centralized-security-groups.md). + +## Objetivo + +Crear el paquete puro `pkg/security/` con los tipos `UserGroup`, `AgentGroup`, `SecurityPolicy` y la función `ResolveACL(agentID, policy) → acl.ACL`. Este paquete es el núcleo de resolución del sistema centralizado de permisos. + +## Contexto + +- `pkg/acl/` ya existe con `ACL`, `Role`, `CanDo()`, `RoleFor()`. Lo reutilizamos como motor de evaluación. +- Este sub-issue no toca ningún otro archivo. Es pure core sin dependencias nuevas. +- El código se mergea con `centralized-security-groups` feature flag = false (no wired todavía). + +## Arquitectura + +``` +pkg/security/ NEW + groups.go NEW — UserGroup, AgentGroup + policy.go NEW — Permission, AgentPolicy, SecurityPolicy + resolver.go NEW — ResolveACL() + security_test.go NEW +``` + +### Patron pure core / impure shell + +- `pkg/security/` — **puro**: solo tipos y funciones de transformación. Cero I/O, cero side effects. + +## Tareas + +### Fase 1: Tipos y resolver + +- [ ] **1.1** Crear `pkg/security/groups.go`: + ```go + type UserGroup struct { Name string; Members []string } + type AgentGroup struct { Name string; Agents []string } + ``` +- [ ] **1.2** Crear `pkg/security/policy.go`: + ```go + type Permission struct { UserGroup string; Actions []string } + type AgentPolicy struct { AgentGroup string; Permissions []Permission } + type SecurityPolicy struct { UserGroups []UserGroup; AgentGroups []AgentGroup; Policies []AgentPolicy } + ``` +- [ ] **1.3** Crear `pkg/security/resolver.go` con `ResolveACL(agentID string, p SecurityPolicy) acl.ACL`: + - Iterar `p.Policies` para encontrar `AgentPolicy` cuyo `AgentGroup` sea un grupo que contenga `agentID` o `"*"`, o sea directamente el `agentID` + - Para cada `AgentPolicy` que aplique, iterar sus `Permissions` + - Resolver `Permission.UserGroup` a los `Members` del grupo correspondiente + - Construir `[]acl.Role` y devolver `acl.ACL` via `acl.FromRoles()` (verificar que esta función existe; si no, añadirla a `pkg/acl/`) +- [ ] **1.4** Soporte wildcard: `AgentGroup.Agents = ["*"]` → aplica la policy a cualquier agentID; `UserGroup.Members = ["*"]` → rol sin restricción de usuario +- [ ] **1.5** Políticas acumulativas: si un agente aparece en múltiples grupos, sus permisos se acumulan (unión de roles) + +### Fase 2: Tests + +- [ ] **2.1** Test: sin política → ACL vacía (todo permitido, comportamiento actual de acl.Empty()) +- [ ] **2.2** Test: agente en grupo → recibe los permisos del grupo +- [ ] **2.3** Test: agente NO en ningún grupo → ACL vacía +- [ ] **2.4** Test: wildcard de agente `"*"` → todos los agentes reciben los permisos +- [ ] **2.5** Test: wildcard de usuario `"*"` → todos los usuarios reciben la acción +- [ ] **2.6** Test: múltiples grupos que incluyen al agente → permisos acumulados (unión) +- [ ] **2.7** Test: agente referenciado directamente por ID en `AgentPolicy.AgentGroup` (sin definir grupo) → recibe permisos + +### Fase 3: Cleanup + +- [ ] **3.1** `go build -tags goolm ./...` compila sin errores +- [ ] **3.2** `go test -tags goolm ./pkg/security/...` pasa + +## Ejemplo de uso + +```go +policy := security.SecurityPolicy{ + UserGroups: []security.UserGroup{ + {Name: "admins", Members: []string{"@alice:matrix.org"}}, + {Name: "everyone", Members: []string{"*"}}, + }, + AgentGroups: []security.AgentGroup{ + {Name: "all", Agents: []string{"*"}}, + }, + Policies: []security.AgentPolicy{ + { + AgentGroup: "all", + Permissions: []security.Permission{ + {UserGroup: "admins", Actions: []string{"*"}}, + {UserGroup: "everyone", Actions: []string{"ask"}}, + }, + }, + }, +} + +acl := security.ResolveACL("assistant-bot", policy) +acl.CanDo("@alice:matrix.org", "tool:ssh_command") // true (admin → "*") +acl.CanDo("@unknown:matrix.org", "ask") // true (everyone → "ask") +acl.CanDo("@unknown:matrix.org", "command:deploy") // false +``` + +## Decisiones de diseño + +- **No reemplazar pkg/acl/**: este paquete produce `acl.ACL`, no lo sustituye. Máxima reutilización. +- **AgentPolicy.AgentGroup acepta nombre de grupo O ID directo de agente**: permite asignar permisos a un agente individual sin crear un grupo de un solo elemento. +- **Unión de permisos entre grupos**: si un agente está en `assistants` y en `all`, recibe la unión de sus permisos. Seguro: siempre da más acceso, nunca menos de lo esperado. + +## Prerequisitos + +- `pkg/acl/` compilando (completado en issue 0010) + +## Riesgos + +- **acl.FromRoles() puede no existir**: si `pkg/acl/` solo expone `FromMap(map[string]RoleDef)`, añadir `FromRoles([]Role) ACL` en ese paquete como parte de esta tarea. Es una adición mínima.