package security_test import ( "testing" "github.com/enmanuel/agents/pkg/security" ) // helpers func makePolicy(userGroups []security.UserGroup, agentGroups []security.AgentGroup, policies []security.AgentPolicy) security.SecurityPolicy { return security.SecurityPolicy{ UserGroups: userGroups, AgentGroups: agentGroups, Policies: policies, } } // 2.1 — sin política → ACL vacía → todo permitido (acl.Empty() == true) func TestResolveACL_EmptyPolicy(t *testing.T) { a := security.ResolveACL("assistant-bot", security.SecurityPolicy{}) if !a.Empty() { t.Fatal("expected empty ACL for empty policy") } if !a.CanDo("@anyone:matrix.org", "anything") { t.Fatal("empty ACL should allow everything") } } // 2.2 — agente en grupo → recibe los permisos del grupo func TestResolveACL_AgentInGroup(t *testing.T) { p := makePolicy( []security.UserGroup{{Name: "admins", Members: []string{"@alice:matrix.org"}}}, []security.AgentGroup{{Name: "bots", Agents: []string{"assistant-bot"}}}, []security.AgentPolicy{{ AgentGroup: "bots", Permissions: []security.Permission{ {UserGroup: "admins", Actions: []string{"ask"}}, }, }}, ) a := security.ResolveACL("assistant-bot", p) if a.Empty() { t.Fatal("ACL should not be empty") } if !a.CanDo("@alice:matrix.org", "ask") { t.Fatal("alice should be able to ask") } if a.CanDo("@bob:matrix.org", "ask") { t.Fatal("bob should not be able to ask") } } // 2.3 — agente NO en ningún grupo → ACL vacía func TestResolveACL_AgentNotInGroup(t *testing.T) { p := makePolicy( []security.UserGroup{{Name: "admins", Members: []string{"@alice:matrix.org"}}}, []security.AgentGroup{{Name: "bots", Agents: []string{"other-bot"}}}, []security.AgentPolicy{{ AgentGroup: "bots", Permissions: []security.Permission{{UserGroup: "admins", Actions: []string{"ask"}}}, }}, ) a := security.ResolveACL("assistant-bot", p) if !a.Empty() { t.Fatal("expected empty ACL for agent not in any group") } } // 2.4 — wildcard de agente "*" → todos los agentes reciben los permisos func TestResolveACL_AgentWildcard(t *testing.T) { p := makePolicy( []security.UserGroup{{Name: "everyone", Members: []string{"*"}}}, []security.AgentGroup{{Name: "all", Agents: []string{"*"}}}, []security.AgentPolicy{{ AgentGroup: "all", Permissions: []security.Permission{{UserGroup: "everyone", Actions: []string{"ask"}}}, }}, ) for _, agentID := range []string{"assistant-bot", "asistente-2", "any-random-bot"} { a := security.ResolveACL(agentID, p) if a.Empty() { t.Fatalf("ACL for %q should not be empty", agentID) } if !a.CanDo("@whoever:matrix.org", "ask") { t.Fatalf("any user should be able to ask via agent %q", agentID) } } } // 2.5 — wildcard de usuario "*" → todos los usuarios reciben la acción func TestResolveACL_UserWildcard(t *testing.T) { p := makePolicy( []security.UserGroup{{Name: "everyone", Members: []string{"*"}}}, []security.AgentGroup{{Name: "bots", Agents: []string{"assistant-bot"}}}, []security.AgentPolicy{{ AgentGroup: "bots", Permissions: []security.Permission{{UserGroup: "everyone", Actions: []string{"ask"}}}, }}, ) a := security.ResolveACL("assistant-bot", p) for _, user := range []string{"@alice:matrix.org", "@bob:example.com", "@unknown:other.server"} { if !a.CanDo(user, "ask") { t.Fatalf("user %q should be able to ask (wildcard user group)", user) } } } // 2.6 — múltiples grupos que incluyen al agente → permisos acumulados func TestResolveACL_AccumulatedPermissions(t *testing.T) { p := makePolicy( []security.UserGroup{ {Name: "admins", Members: []string{"@alice:matrix.org"}}, {Name: "users", Members: []string{"@bob:matrix.org"}}, }, []security.AgentGroup{ {Name: "premium", Agents: []string{"assistant-bot"}}, {Name: "all", Agents: []string{"*"}}, }, []security.AgentPolicy{ { AgentGroup: "premium", Permissions: []security.Permission{{UserGroup: "admins", Actions: []string{"deploy"}}}, }, { AgentGroup: "all", Permissions: []security.Permission{{UserGroup: "users", Actions: []string{"ask"}}}, }, }, ) a := security.ResolveACL("assistant-bot", p) if !a.CanDo("@alice:matrix.org", "deploy") { t.Fatal("alice should be able to deploy (via premium group)") } if !a.CanDo("@bob:matrix.org", "ask") { t.Fatal("bob should be able to ask (via all group)") } if a.CanDo("@bob:matrix.org", "deploy") { t.Fatal("bob should not be able to deploy") } } // 2.7 — privileged vs general: father-bot admin-only, general open to everyone func TestResolveACL_PrivilegedVsGeneral(t *testing.T) { p := makePolicy( []security.UserGroup{ {Name: "admins", Members: []string{"@admin:matrix.example.com"}}, {Name: "everyone", Members: []string{"*"}}, }, []security.AgentGroup{ {Name: "privileged", Agents: []string{"father-bot"}}, {Name: "general", Agents: []string{"assistant-bot", "test-bot"}}, }, []security.AgentPolicy{ { AgentGroup: "privileged", Permissions: []security.Permission{{UserGroup: "admins", Actions: []string{"*"}}}, }, { AgentGroup: "general", Permissions: []security.Permission{ {UserGroup: "admins", Actions: []string{"*"}}, {UserGroup: "everyone", Actions: []string{"*"}}, }, }, }, ) // father-bot: admin can interact, regular user cannot fatherACL := security.ResolveACL("father-bot", p) if fatherACL.Empty() { t.Fatal("father-bot ACL should not be empty") } if !fatherACL.CanDo("@admin:matrix.example.com", "ask") { t.Fatal("admin should be able to interact with father-bot") } if fatherACL.CanDo("@random:matrix.example.com", "ask") { t.Fatal("non-admin should NOT be able to interact with father-bot") } // assistant-bot: everyone can interact assistantACL := security.ResolveACL("assistant-bot", p) if assistantACL.Empty() { t.Fatal("assistant-bot ACL should not be empty") } if !assistantACL.CanDo("@random:matrix.example.com", "ask") { t.Fatal("everyone should be able to interact with assistant-bot") } // unknown-bot: not in any group → empty ACL (open access) unknownACL := security.ResolveACL("unknown-bot", p) if !unknownACL.Empty() { t.Fatal("unknown-bot should have empty ACL (open access)") } } // 2.8 — agente referenciado directamente por ID en AgentPolicy.AgentGroup → recibe permisos func TestResolveACL_DirectAgentID(t *testing.T) { p := makePolicy( []security.UserGroup{{Name: "admins", Members: []string{"@alice:matrix.org"}}}, nil, // no named groups []security.AgentPolicy{{ AgentGroup: "assistant-bot", // direct agent ID, no group defined Permissions: []security.Permission{{UserGroup: "admins", Actions: []string{"*"}}}, }}, ) a := security.ResolveACL("assistant-bot", p) if !a.CanDo("@alice:matrix.org", "anything") { t.Fatal("alice should have full access via direct agent ID assignment") } // other agents should not be affected b := security.ResolveACL("asistente-2", p) if !b.Empty() { t.Fatal("asistente-2 should not receive permissions from direct assignment to assistant-bot") } }