fa78075370
Agrega dos tests nuevos al paquete pkg/security que verifican escenarios especificos de father-bot: - TestResolveACL_FatherBotDenyByDefault: cuando el grupo admins esta vacio, nadie puede interactuar con father-bot (deny-by-default) - TestResolveACL_FatherBotMultipleAdmins: cuando hay multiples admins configurados, todos pueden interactuar; usuarios fuera del grupo no pueden Estos tests complementan el existente TestResolveACL_PrivilegedVsGeneral que ya cubria el caso basico de admin vs non-admin. Issue: 0043
301 lines
9.7 KiB
Go
301 lines
9.7 KiB
Go
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 — father-bot deny-by-default: admin group empty → no one can interact
|
|
func TestResolveACL_FatherBotDenyByDefault(t *testing.T) {
|
|
p := makePolicy(
|
|
[]security.UserGroup{
|
|
{Name: "admins", Members: []string{}}, // empty admin group
|
|
{Name: "everyone", Members: []string{"*"}},
|
|
},
|
|
[]security.AgentGroup{
|
|
{Name: "privileged", Agents: []string{"father-bot"}},
|
|
{Name: "general", Agents: []string{"assistant-bot"}},
|
|
},
|
|
[]security.AgentPolicy{
|
|
{
|
|
AgentGroup: "privileged",
|
|
Permissions: []security.Permission{{UserGroup: "admins", Actions: []string{"*"}}},
|
|
},
|
|
{
|
|
AgentGroup: "general",
|
|
Permissions: []security.Permission{
|
|
{UserGroup: "everyone", Actions: []string{"*"}},
|
|
},
|
|
},
|
|
},
|
|
)
|
|
|
|
// father-bot: admin group empty → nobody can interact
|
|
fatherACL := security.ResolveACL("father-bot", p)
|
|
if fatherACL.Empty() {
|
|
t.Fatal("father-bot ACL should not be empty (it has a policy, just no members)")
|
|
}
|
|
if fatherACL.CanDo("@admin:matrix.example.com", "ask") {
|
|
t.Fatal("no one should be able to interact with father-bot when admin group is empty")
|
|
}
|
|
if fatherACL.CanDo("@random:matrix.example.com", "ask") {
|
|
t.Fatal("non-admin should NOT be able to interact with father-bot")
|
|
}
|
|
|
|
// assistant-bot: still accessible to everyone
|
|
assistantACL := security.ResolveACL("assistant-bot", p)
|
|
if !assistantACL.CanDo("@random:matrix.example.com", "ask") {
|
|
t.Fatal("everyone should still be able to interact with assistant-bot")
|
|
}
|
|
}
|
|
|
|
// 2.9 — father-bot: multiple admins, only they can interact
|
|
func TestResolveACL_FatherBotMultipleAdmins(t *testing.T) {
|
|
p := makePolicy(
|
|
[]security.UserGroup{
|
|
{Name: "admins", Members: []string{
|
|
"@admin:matrix-af2f3d.organic-machine.com",
|
|
"@dev2:matrix-af2f3d.organic-machine.com",
|
|
}},
|
|
{Name: "everyone", Members: []string{"*"}},
|
|
},
|
|
[]security.AgentGroup{
|
|
{Name: "privileged", Agents: []string{"father-bot"}},
|
|
},
|
|
[]security.AgentPolicy{
|
|
{
|
|
AgentGroup: "privileged",
|
|
Permissions: []security.Permission{{UserGroup: "admins", Actions: []string{"*"}}},
|
|
},
|
|
},
|
|
)
|
|
|
|
fatherACL := security.ResolveACL("father-bot", p)
|
|
|
|
// Both admins can interact
|
|
if !fatherACL.CanDo("@admin:matrix-af2f3d.organic-machine.com", "ask") {
|
|
t.Fatal("first admin should be able to interact with father-bot")
|
|
}
|
|
if !fatherACL.CanDo("@dev2:matrix-af2f3d.organic-machine.com", "ask") {
|
|
t.Fatal("second admin should be able to interact with father-bot")
|
|
}
|
|
|
|
// Non-admin cannot
|
|
if fatherACL.CanDo("@random:matrix.example.com", "ask") {
|
|
t.Fatal("non-admin should NOT be able to interact with father-bot")
|
|
}
|
|
if fatherACL.CanDo("@hacker:evil.com", "ask") {
|
|
t.Fatal("unknown user should NOT be able to interact with father-bot")
|
|
}
|
|
}
|
|
|
|
// 2.10 — 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")
|
|
}
|
|
}
|