Files
jcoffey-dev 3eb0f4c589 Phase 4: SSO scaffolding, RBAC enforcement, tenant-scoped dashboards, audit logging, K8s deployment
RBAC (api/internal/authz) is live on /query and /dashboards, backed by a
new enterprise/ module (session issuance, audit logging, RBAC storage,
OIDC/SAML protocol wiring) that core never imports -- only calls over
HTTP. Found and fixed a real cross-tenant vulnerability in dashboards
(no tenant_id filtering at all) while writing the threat model doc.

Two things are explicitly NOT done, documented rather than hidden:
tenant isolation for log data itself (/query still shares one ClickHouse
connection and Tantivy index across every tenant -- RBAC controls who
can query, not what a query can see), and human SSO login (protocol
wiring exists, no HTTP handler calls it yet). See
docs/security/threat-model.md and docs/phase-4-runbook.md.

Also adds deploy/ (Go Operator + Helm chart, validated offline only --
no cluster was reachable in this environment).
2026-08-13 22:16:59 -07:00

72 lines
2.4 KiB
Go

// Exercises the hand-written DeepCopy methods in zz_generated.deepcopy.go
// -- see that file's doc comment for why these aren't controller-gen
// output here. A DeepCopy that accidentally shares a slice/map with the
// original is a real, easy-to-introduce bug (client-go relies on
// DeepCopyObject returning something safe to mutate independently), so
// these tests mutate the copy and assert the original is unaffected.
package v1alpha1
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestTenantDeepCopyIsIndependent(t *testing.T) {
orig := &Tenant{
ObjectMeta: metav1.ObjectMeta{Name: "acme", Labels: map[string]string{"a": "1"}},
Spec: TenantSpec{DisplayName: "Acme", Suspended: false},
Status: TenantStatus{
Phase: PhaseActive,
Conditions: []metav1.Condition{
{Type: ConditionReady, Status: metav1.ConditionTrue, Reason: "x"},
},
},
}
cp := orig.DeepCopy()
cp.Spec.DisplayName = "Changed"
cp.Status.Conditions[0].Reason = "changed"
cp.Labels["a"] = "changed"
if orig.Spec.DisplayName != "Acme" {
t.Fatalf("mutating the copy's Spec affected the original: %q", orig.Spec.DisplayName)
}
if orig.Status.Conditions[0].Reason != "x" {
t.Fatalf("mutating the copy's Conditions affected the original: %q", orig.Status.Conditions[0].Reason)
}
// Labels comes from metav1.ObjectMeta.DeepCopyInto, which this
// package doesn't implement itself -- this assertion is really
// checking that Tenant.DeepCopyInto actually calls
// ObjectMeta.DeepCopyInto rather than doing a shallow `out.ObjectMeta
// = in.ObjectMeta`.
if orig.Labels["a"] != "1" {
t.Fatalf("mutating the copy's Labels affected the original: %q", orig.Labels["a"])
}
}
func TestTenantDeepCopyObjectPreservesData(t *testing.T) {
orig := &Tenant{ObjectMeta: metav1.ObjectMeta{Name: "acme"}, Spec: TenantSpec{DisplayName: "Acme"}}
obj := orig.DeepCopyObject()
cp, ok := obj.(*Tenant)
if !ok {
t.Fatalf("DeepCopyObject returned %T, want *Tenant", obj)
}
if cp.Name != "acme" || cp.Spec.DisplayName != "Acme" {
t.Fatalf("unexpected copy: %+v", cp)
}
}
func TestTenantListDeepCopyIsIndependent(t *testing.T) {
orig := &TenantList{Items: []Tenant{
{ObjectMeta: metav1.ObjectMeta{Name: "acme"}},
{ObjectMeta: metav1.ObjectMeta{Name: "globex"}},
}}
cp := orig.DeepCopy()
cp.Items[0].Name = "changed"
if orig.Items[0].Name != "acme" {
t.Fatalf("mutating the copy's Items affected the original: %q", orig.Items[0].Name)
}
}