Files
zip/actingas_test.go
zooqueenandHanzo Dev fc957a2bdb ActingAs does not carry OrgAdmin to the org acted for
Administering one's own org says nothing about the target, so the derived
caller reads and forwards OrgAdmin as false. Owner and Admin, the principal's
own platform authority, are unchanged.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-24 11:23:38 -07:00

177 lines
6.1 KiB
Go

package zip
import (
"context"
"net/http/httptest"
"testing"
)
// The security property, stated as tests: an impersonation is derivable only
// from an authenticated identity, and is distinguishable from one at every hop.
// It cannot invent a caller. This is the laundering guard, unchanged.
func TestActingAs_RefusesWithNothingAuthenticated(t *testing.T) {
_, err := ActingAs(context.Background(), "victim-org")
if err == nil {
t.Fatal("ActingAs minted a caller out of nothing — that is the laundering hole")
}
if CallerOf(context.Background()).Org != "" {
t.Error("a refused ActingAs still altered the caller")
}
}
// It re-points an authenticated one, and records who is acting.
func TestActingAs_CarriesBothWhoActsAndOnWhoseBehalf(t *testing.T) {
admin := Caller{User: "u-admin", Org: "platform", Owner: "platform", Admin: true}
ctx := WithCaller(context.Background(), admin)
acting, err := ActingAs(ctx, "acme")
if err != nil {
t.Fatalf("ActingAs: %v", err)
}
got := CallerOf(acting)
if got.Org != "acme" {
t.Errorf("Org = %q, want acme — the call must reach the target's ledger", got.Org)
}
if got.ActedBy != "u-admin" {
t.Errorf("ActedBy = %q, want u-admin — an audit row that omits the actor records the wrong one", got.ActedBy)
}
if got.User != "u-admin" {
t.Errorf("User = %q; the acting principal must not be erased", got.User)
}
// The original context is untouched.
if CallerOf(ctx).Org != "platform" {
t.Error("ActingAs mutated the context it derived from")
}
}
// A chain of hops cannot quietly reassign responsibility.
func TestActingAs_OriginalActorSurvivesAChain(t *testing.T) {
ctx := WithCaller(context.Background(), Caller{User: "u-admin", Org: "platform"})
one, err := ActingAs(ctx, "acme")
if err != nil {
t.Fatal(err)
}
two, err := ActingAs(one, "globex")
if err != nil {
t.Fatal(err)
}
got := CallerOf(two)
if got.Org != "globex" {
t.Errorf("Org = %q, want globex", got.Org)
}
if got.ActedBy != "u-admin" {
t.Errorf("ActedBy = %q after two hops, want the ORIGINAL actor u-admin", got.ActedBy)
}
}
// The impersonation travels, or the next hop sees a clean call from the target
// org and the audit trail ends at this process.
func TestActingAs_TravelsOnTheWire(t *testing.T) {
ctx := WithCaller(context.Background(), Caller{User: "u-admin", Org: "platform"})
acting, err := ActingAs(ctx, "acme")
if err != nil {
t.Fatal(err)
}
h := CallerOf(acting).headers()
if h[HeaderOrg] != "acme" {
t.Errorf("forwarded org = %q, want acme — this is the wrong-ledger bug", h[HeaderOrg])
}
if h[HeaderActedBy] != "u-admin" {
t.Errorf("forwarded %s = %q, want u-admin", HeaderActedBy, h[HeaderActedBy])
}
var forwarded bool
for _, name := range identityHeaders {
if name == HeaderActedBy {
forwarded = true
}
}
if !forwarded {
t.Error("X-Acted-By is not in the forwarded identity set, so the impersonation stops at this hop")
}
}
// The project is scoped to the original org, so it must not ride along.
func TestActingAs_DoesNotCarryTheOriginalProject(t *testing.T) {
ctx := WithCaller(context.Background(), Caller{User: "u", Org: "platform", Project: "p-platform"})
acting, _ := ActingAs(ctx, "acme")
if got := CallerOf(acting).Project; got != "" {
t.Errorf("Project = %q; a project belongs to the org it was scoped to", got)
}
}
// The derived caller is what an outbound call carries, from a request-backed context
// and from a stated one alike, and it is written whole: the target org, the actor,
// the original principal and its claim, and never the original org's project.
//
// Mutation proof: drop the acting branch from forwardIdentity and the request row
// forwards the request's own org and project with no X-Acted-By, and the stated row
// forwards the original org.
func TestActingAs_IsWhatAnOutboundCallCarries(t *testing.T) {
var got map[string]string
app := New(Config{AppName: "front", DisableStartupMessage: true})
app.Raw("GET", "/ask", func(c *Ctx) error {
ctx, err := ActingAs(c.Forward(), "globex")
if err != nil {
return err
}
got = forwarded(ctx)
return c.JSON(200, map[string]string{"ok": "1"})
})
req := httptest.NewRequest("GET", "/ask", nil)
for k, v := range map[string]string{
HeaderOrg: "platform", HeaderProject: "p-platform", HeaderUser: "u-admin",
HeaderUserName: "ada", HeaderUserOwner: "admin", HeaderUserAdmin: "true",
HeaderAccount: "person:admin/ada", HeaderRequestID: "req-9",
} {
req.Header.Set(k, v)
}
resp, err := app.Fiber().Test(req)
if err != nil {
t.Fatalf("GET: %v", err)
}
_ = resp.Body.Close()
want := map[string]string{
HeaderOrg: "globex", HeaderActedBy: "u-admin", HeaderUser: "u-admin",
HeaderUserName: "ada", HeaderUserOwner: "admin", HeaderUserAdmin: "true",
HeaderAccount: "person:admin/ada", HeaderRequestID: "req-9",
}
for k, v := range want {
if got[k] != v {
t.Errorf("a request-backed ActingAs forwarded %s = %q, want %q (%v)", k, got[k], v, got)
}
}
if v, ok := got[HeaderProject]; ok {
t.Errorf("a request-backed ActingAs forwarded the original org's project %q", v)
}
stated := WithCaller(context.Background(), Caller{User: "u-admin", Org: "platform"})
acting, err := ActingAs(stated, "acme")
if err != nil {
t.Fatal(err)
}
if h := forwarded(acting); h[HeaderOrg] != "acme" || h[HeaderActedBy] != "u-admin" {
t.Errorf("a stated ActingAs forwarded org %q acted-by %q, want acme and u-admin", h[HeaderOrg], h[HeaderActedBy])
}
}
// Administering one's own org is not administering the org acted for, so the derived
// caller does not carry OrgAdmin there, on the context or on the next hop.
func TestActingAs_DoesNotCarryOrgAdminToTheTarget(t *testing.T) {
ctx := WithCaller(context.Background(), Caller{User: "u", Org: "acme", Owner: "acme", OrgAdmin: true})
acting, err := ActingAs(ctx, "globex")
if err != nil {
t.Fatal(err)
}
if CallerOf(acting).OrgAdmin {
t.Error("an org admin of acme reads as an org admin of globex")
}
if h := forwarded(acting); h[HeaderUserOrgAdmin] != "" {
t.Errorf("the next hop receives %s = %q for globex", HeaderUserOrgAdmin, h[HeaderUserOrgAdmin])
}
if !CallerOf(ctx).OrgAdmin {
t.Error("ActingAs changed the caller it derived from")
}
}