mirror of
https://github.com/hanzoai/commerce.git
synced 2026-10-30 15:08:42 +00:00
Multi-tenant commerce and billing: orders, subscriptions, invoices, plans, usage metering and a credit ledger. One Go binary. MIT OR Apache-2.0. Claude-Session: https://claude.ai/code/session_01SAtaDFm8VDiWrhYHNNtxMw
87 lines
2.7 KiB
Go
87 lines
2.7 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: only X-User-IsAdmin=="true"
|
|
// (case-insensitive) marks an org admin; absent or any other value fails closed.
|
|
func TestIsOrgAdmin(t *testing.T) {
|
|
cases := []struct {
|
|
val string
|
|
want bool
|
|
}{
|
|
{"true", true},
|
|
{"TRUE", true},
|
|
{"", 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)
|
|
}
|
|
}
|
|
}
|