package acl import ( "fmt" "sync" "testing" ) func TestGrantAndCheck(t *testing.T) { a := NewACL() id := Identity{Kind: KindOidc, ID: "tok-A", Namespace: "test"} a.Grant(id, "test", PermRead) if !a.Check(id, "test", PermRead) { t.Errorf("Check(Read) = false, want true after Grant(Read)") } if a.Check(id, "test", PermWrite) { t.Errorf("Check(Write) = true, want false (only Read granted)") } } func TestRevoke(t *testing.T) { a := NewACL() id := Identity{Kind: KindOidc, ID: "tok-A", Namespace: "test"} a.Grant(id, "test", PermRead) a.Revoke(id, "test") if a.Check(id, "test", PermRead) { t.Errorf("Check(Read) = true after Revoke, want false") } if got := a.List(); len(got) != 0 { t.Errorf("List() len = %d after Revoke, want 0", len(got)) } } func TestRevokeNonExistentNoOp(t *testing.T) { a := NewACL() id := Identity{Kind: KindOidc, ID: "tok-A", Namespace: "test"} a.Revoke(id, "ghost") if got := a.List(); len(got) != 0 { t.Errorf("List() len = %d after no-op Revoke, want 0", len(got)) } } func TestDenyByDefault(t *testing.T) { a := NewACL() id := Identity{Kind: KindOidc, ID: "tok-A", Namespace: "test"} if a.Check(id, "test", PermRead) { t.Errorf("Check on un-granted identity = true, want false (deny-by-default)") } if a.Check(id, "test", PermWrite) { t.Errorf("Check Write on un-granted identity = true, want false") } if a.Check(id, "test", PermAdmin) { t.Errorf("Check Admin on un-granted identity = true, want false") } } func TestNamespaceIsolation(t *testing.T) { a := NewACL() id := Identity{Kind: KindOidc, ID: "tok-A", Namespace: "ns-A"} a.Grant(id, "ns-A", PermRead) if !a.Check(id, "ns-A", PermRead) { t.Errorf("Check on ns-A = false, want true") } if a.Check(id, "ns-B", PermRead) { t.Errorf("Check on ns-B = true, want false (namespace isolation)") } } func TestGrantReplacesPermissions(t *testing.T) { a := NewACL() id := Identity{Kind: KindOidc, ID: "tok-A", Namespace: "test"} a.Grant(id, "test", PermRead) a.Grant(id, "test", PermWrite) if a.Check(id, "test", PermRead) { t.Errorf("Check(Read) = true after re-grant with Write-only, want false") } if !a.Check(id, "test", PermWrite) { t.Errorf("Check(Write) = false after re-grant, want true") } if got := a.List(); len(got) != 1 { t.Errorf("List() len = %d, want 1 (grant replaces, not appends)", len(got)) } } func TestSpiffeNamespace(t *testing.T) { got, err := SpiffeNamespace("spiffe://orca.local/ns/myapp/sa/svc1/alloc-123") if err != nil { t.Fatalf("SpiffeNamespace: %v", err) } if got != "myapp" { t.Errorf("SpiffeNamespace = %q, want %q", got, "myapp") } } func TestSpiffeNamespace_OtherTrustDomain(t *testing.T) { got, err := SpiffeNamespace("spiffe://example.com/ns/prod/sa/api/0") if err != nil { t.Fatalf("SpiffeNamespace: %v", err) } if got != "prod" { t.Errorf("SpiffeNamespace = %q, want %q", got, "prod") } } func TestSpiffeNamespace_Malformed(t *testing.T) { cases := []string{ "https://orca.local/ns/prod/sa/api/0", "spiffe://orca.local/ns/prod/api/0", "spiffe://orca.local/ns/prod/sa/api", "spiffe://orca.local/ns//sa/api/0", ":::not-a-uri", } for _, c := range cases { if _, err := SpiffeNamespace(c); err == nil { t.Errorf("SpiffeNamespace(%q): expected error, got nil", c) } } } func TestPermissionsDistinct(t *testing.T) { if PermRead == PermWrite || PermRead == PermAdmin || PermWrite == PermAdmin { t.Errorf("permission flags collide: read=%d write=%d admin=%d", PermRead, PermWrite, PermAdmin) } a := NewACL() id := Identity{Kind: KindOidc, ID: "tok-A", Namespace: "test"} a.Grant(id, "test", PermRead|PermWrite) if !a.Check(id, "test", PermRead) { t.Errorf("Check(Read) for read+write grant = false, want true") } if !a.Check(id, "test", PermWrite) { t.Errorf("Check(Write) for read+write grant = false, want true") } if a.Check(id, "test", PermAdmin) { t.Errorf("Check(Admin) for read+write grant = true, want false") } } func TestAdminImpliesReadAndWrite(t *testing.T) { a := NewACL() id := Identity{Kind: KindOidc, ID: "tok-A", Namespace: "test"} a.Grant(id, "test", PermAdmin) if !a.Check(id, "test", PermAdmin) { t.Errorf("Check(Admin) = false, want true") } if !a.Check(id, "test", PermRead) { t.Errorf("Check(Read) for admin grant = false, want true (admin implies read)") } if !a.Check(id, "test", PermWrite) { t.Errorf("Check(Write) for admin grant = false, want true (admin implies write)") } } func TestConcurrentAccess(t *testing.T) { a := NewACL() id := Identity{Kind: KindOidc, ID: "tok-concurrent", Namespace: "ns"} const n = 200 var wg sync.WaitGroup wg.Add(n * 3) for i := 0; i < n; i++ { go func() { defer wg.Done() a.Grant(id, "ns", PermRead|PermWrite) }() go func() { defer wg.Done() a.Check(id, "ns", PermRead) }() go func() { defer wg.Done() a.List() }() } wg.Wait() if !a.Check(id, "ns", PermRead) { t.Errorf("Check(Read) after concurrent grants = false, want true") } if got := a.List(); len(got) != 1 { t.Errorf("List() len = %d, want 1 (concurrent grants replace, not append)", len(got)) } } func TestListIsCopy(t *testing.T) { a := NewACL() id := Identity{Kind: KindOidc, ID: "tok-A", Namespace: "test"} a.Grant(id, "test", PermRead) lst := a.List() lst[0].Permissions = PermAdmin if a.Check(id, "test", PermAdmin) { t.Errorf("mutating List() result leaked into ACL: %v", a.List()) } } func TestSpiffeIdentityGrant(t *testing.T) { a := NewACL() uri := "spiffe://orca.local/ns/myapp/sa/svc1/alloc-123" ns, err := SpiffeNamespace(uri) if err != nil { t.Fatalf("SpiffeNamespace: %v", err) } id := Identity{Kind: KindSpiffe, ID: uri, Namespace: ns} a.Grant(id, ns, PermRead|PermWrite) if !a.Check(id, ns, PermRead) || !a.Check(id, ns, PermWrite) { t.Errorf("spiffe identity check failed for ns=%s", ns) } } func TestTokenAndSpiffeIdentitiesIndependent(t *testing.T) { a := NewACL() uri := "spiffe://orca.local/ns/prod/sa/api/0" spiffeID := Identity{Kind: KindSpiffe, ID: uri, Namespace: "prod"} tokenID := Identity{Kind: KindOidc, ID: "operator-1", Namespace: "prod"} a.Grant(spiffeID, "prod", PermRead) if a.Check(tokenID, "prod", PermRead) { t.Errorf("token identity matched spiffe grant (kind isolation broken)") } if !a.Check(spiffeID, "prod", PermRead) { t.Errorf("spiffe identity check failed") } if got := a.List(); len(got) != 1 { t.Errorf("List() len = %d, want 1", len(got)) } } func TestAllPermissionsConstant(t *testing.T) { if AllPermissions != PermRead|PermWrite|PermAdmin { t.Errorf("AllPermissions = %d, want %d", AllPermissions, PermRead|PermWrite|PermAdmin) } } func ExampleSpiffeNamespace() { ns, _ := SpiffeNamespace("spiffe://orca.local/ns/myapp/sa/svc1/alloc-123") fmt.Println(ns) // Output: myapp } // --- REQ-145 / F1 ACL OIDC rewrite tests --- // TestACLOidcUserGrant verifies an OIDC user (by sub) can be granted // and checked. func TestACLOidcUserGrant(t *testing.T) { a := NewACL() claims := OIDCClaims{Subject: "user-1", Groups: []string{"devs"}} a.Grant(OidcIdentity(claims), "prod", PermWrite|PermRead) if !a.CheckOidc(claims, "prod", PermWrite) { t.Error("CheckOidc should allow write") } if !a.CheckOidc(claims, "prod", PermRead) { t.Error("CheckOidc should allow read (explicit)") } if a.CheckOidc(claims, "prod", PermAdmin) { t.Error("CheckOidc should deny admin") } if a.CheckOidc(claims, "other", PermRead) { t.Error("CheckOidc should deny on wrong ns") } } // TestACLOidcGroupGrant verifies group-based grants work. func TestACLOidcGroupGrant(t *testing.T) { a := NewACL() a.Grant(OidcGroupIdentity("orca-admins"), "prod", PermAdmin) claims := OIDCClaims{Subject: "user-2", Groups: []string{"orca-admins"}} if !a.CheckOidc(claims, "prod", PermAdmin) { t.Error("admin group should have admin") } if !a.CheckOidc(claims, "prod", PermWrite) { t.Error("admin implies write") } claimsNoGroup := OIDCClaims{Subject: "user-3", Groups: []string{"devs"}} if a.CheckOidc(claimsNoGroup, "prod", PermRead) { t.Error("non-admin group should deny") } } // TestACLOidcDenyByDefault verifies an ungranted OIDC user is denied. func TestACLOidcDenyByDefault(t *testing.T) { a := NewACL() claims := OIDCClaims{Subject: "nobody"} if a.CheckOidc(claims, "prod", PermRead) { t.Error("ungranted user should deny") } } // TestACLTokenDeprecated verifies KindToken always denies (R-021). func TestACLTokenDeprecated(t *testing.T) { a := NewACL() // Even if an old acl.json has a KindToken entry, Check returns false. a.Grant(Identity{Kind: KindToken, ID: "old-token-123"}, "prod", PermAdmin) if a.Check(Identity{Kind: KindToken, ID: "old-token-123"}, "prod", PermRead) { t.Error("KindToken should always deny (R-021)") } if a.Check(Identity{Kind: KindToken, ID: "old-token-123"}, "prod", PermAdmin) { t.Error("KindToken should always deny even admin (R-021)") } }