mirror of
https://github.com/hanzoai/account.git
synced 2026-09-30 20:38:12 +00:00
4fa4ef983f05ba7c26e07e45b4db6a771441e0ce
The org selected in the switcher was not the org that paid. Three layers each derived it from a different place: the edge minted X-Org-Id from the `owner` claim, cloud honored a membership-gated selection, and ai read user.Owner and could not see a switch at all. A gate could therefore read one ledger while the debit landed on another — the same split the two deleted billing allowlists caused on the person axis, recurring on the org axis. Add the two predicates the three layers can share, as pure functions in the leaf module all three already import: EffectiveOrg(owner, orgs, requested) — which org the request ACTS IN. The ask is honored iff it byte-equals an entry of the SIGNED `orgs` claim; anything else is home, silently. Membership never comes from the request, because believing a raw client org is a cross-tenant read. The comparison is verbatim — no trim, no fold — since "acme" and "ACME" are distinct orgs in IAM, and a looser rule could only ever admit a switch this one refuses. LedgerOrg(effective, home, sudo) — which org PAYS. Two branches, not one expression: sudo bills home, because an admin acting on a customer must never spend the customer's money; everyone else bills the org they selected, which is the whole feature. OrgRef carries IAM's exact wire tags so cloud, gateway and ai can alias one decode type instead of maintaining three hand-copies that drift a field at a time. Adopting this is a no-op for every principal who is not switching — which is every principal today, since no client sends a selection yet. TestNoSwitchIsANoOp proves it by crossing every owner kind against every claim-set shape and every refused ask, and asserting the resolved account is byte-identical to what Payer returned from the owner claim alone. Co-authored-by: Hanzo Dev <[email protected]>
Description
pull mirror of github.com/hanzoai/account
166 KiB
Languages
Go
100%