Files
zeekay 8cab81b83d A standalone edge acts in the subject's org, never the application's
EdgeAuth set X-Org-Id, and pinned the billing subject, from the `owner`
claim: the org of the application a person signed in through. Every
self-serve user of the brand app therefore acted in and billed to the
brand's org at a standalone commerce (COMMERCE_EDGE_AUTH=true).

tenancy now resolves a verified token as cloud's SanitizeIdentity does:
the home org is orgs[0], the acting org is the client's selection
(X-Org-Id, or ?org on a billing path) when the signed set admits it,
else the default (authz.Claims.EffectiveOrg). A program is its
application's org and selects nothing; a person's token without a
membership set acts in no org. The billing subject follows the acting
org, and X-User-IsOrgAdmin is asked of it.

orgAdminHomeMatches compared home and acting org with EqualFold; it
compares them verbatim.
2026-10-20 07:49:25 -07:00

115 lines
3.5 KiB
Go

// Copyright (c) 2014-present Hanzo AI, Inc.
// Licensed under MIT OR Apache-2.0. See LICENSE-MIT and LICENSE-APACHE.
package iammiddleware
import (
"net/http"
"testing"
"github.com/zap-proto/zip"
"github.com/hanzoai/commerce/util/bit"
"github.com/hanzoai/commerce/util/permission"
)
// TestOrgAdminGrant_CoversOnboardingCatalog pins the self-serve onboarding
// contract: an org owner's grant satisfies the create gate of every catalog
// primitive step-1 onboarding needs — store (rest "store"/create =
// Admin|WriteStore), product, collection, variant — via the SAME intersection
// (bit.Field.Has) rest.CheckPermissions uses.
func TestOrgAdminGrant_CoversOnboardingCatalog(t *testing.T) {
need := []struct {
name string
mask bit.Mask
}{
{"WriteStore", permission.WriteStore},
{"ReadStore", permission.ReadStore},
{"Store(list)", permission.Store},
{"WriteProduct", permission.WriteProduct},
{"WriteCollection", permission.WriteCollection},
{"WriteVariant", permission.WriteVariant},
}
for _, n := range need {
if !orgAdminGrant.Has(n.mask) {
t.Errorf("orgAdminGrant lacks %s — an org owner cannot manage its own catalog", n.name)
}
}
}
// TestOrgAdminGrant_ExcludesMoneyAuthority is the security invariant: the org
// grant must NEVER carry permission.Admin (the money/platform authority credit-
// mint + card-charge + cross-org billing gates key on). Widening it here would
// let an org owner mint balance or charge cards platform-wide.
func TestOrgAdminGrant_ExcludesMoneyAuthority(t *testing.T) {
if orgAdminGrant.Has(permission.Admin) {
t.Fatal("orgAdminGrant includes permission.Admin — org owners must not gain money/platform authority")
}
for _, m := range []struct {
name string
mask bit.Mask
}{
{"Secret", permission.Secret},
{"Authorize", permission.Authorize},
{"Capture", permission.Capture},
{"Payment", permission.Payment},
} {
if orgAdminGrant.Has(m.mask) {
t.Errorf("orgAdminGrant includes %s — org owners must not gain payment/secret authority", m.name)
}
}
}
// TestIsOrgAdmin pins the trusted-header contract: the org-admin flag
// (X-User-IsOrgAdmin) or the SuperAdmin verdict (X-User-IsAdmin), exactly "true";
// absent or any other value fails closed.
func TestIsOrgAdmin(t *testing.T) {
cases := []struct {
val string
want bool
}{
{"true", true},
{"TRUE", false},
{"", false},
{"false", false},
{"1", false},
}
app := zip.New(zip.Config{DisableStartupMessage: true})
for _, tc := range cases {
c := app.TestCtx(http.MethodPost, "/v1/store")
if tc.val != "" {
c.Fiber().Request().Header.Set(HeaderUserIsAdmin, tc.val)
}
if got := isOrgAdmin(c); got != tc.want {
t.Errorf("isOrgAdmin(%q)=%v, want %v", tc.val, got, tc.want)
}
}
}
// The org-admin grant holds in the caller's home org only, and the home org is
// compared with the effective org verbatim: "Acme" is not "acme", and a padded name
// is no org at all.
func TestOrgAdminHomeMatches_IsVerbatim(t *testing.T) {
app := zip.New(zip.Config{DisableStartupMessage: true})
for _, tc := range []struct {
home, eff string
want bool
}{
{"acme", "acme", true},
{"Acme", "acme", false},
{"acme", "ACME", false},
{" acme", "acme", false},
{"acme", "acme ", false},
{"", "", false},
{"acme", "", false},
} {
c := app.TestCtx(http.MethodPost, "/v1/store")
if tc.home != "" {
c.Fiber().Request().Header.Set(HeaderUserOwner, tc.home)
}
if got := orgAdminHomeMatches(c, tc.eff); got != tc.want {
t.Errorf("orgAdminHomeMatches(home %q, effective %q) = %v, want %v", tc.home, tc.eff, got, tc.want)
}
}
}