organization

package
v1.49.61 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 4, 2026 License: MIT Imports: 31 Imported by: 1

Documentation

Index

Constants

View Source
const (
	DefaultAppName   = "Hanzo App"
	DefaultStoreName = "Default"
)

Variables

View Source
var ErrSecretLikeName = errors.New("organization: refusing to provision org from a bearer-shaped name")

ErrSecretLikeName is returned when an untrusted, gateway-supplied org name is actually a raw API key / bearer token rather than a real org identifier.

The predicate itself is secret.Like. It lives in package secret because the same list of credential markers also generates the DB backstop trigger on _entities — one definition behind both gates, so neither can drift out from under the other.

View Source
var OnSaved = func(name string) {}

OnSaved runs after an org is persisted, with the org's name. pkg/org registers cache invalidation here so a mutation is not masked by the resolve TTL. It is a seam rather than a direct call because pkg/org depends on this package, so this package cannot import it back.

View Source
var (
	UserNotTopLevel = errors.New("User is not in the top level namespace.")
)

Functions

func Query

Types

type ByName

type ByName []*Organization

func (ByName) Len

func (o ByName) Len() int

func (ByName) Less

func (o ByName) Less(i, j int) bool

func (ByName) Swap

func (o ByName) Swap(i, j int)

type Organization

type Organization struct {
	mixin.Model[Organization]
	mixin.AccessTokens
	wallet.WalletHolder

	Name       string   `json:"name"`
	FullName   string   `json:"fullName"`
	Owners     []string `json:"owners,omitempty" datastore:",noindex"`
	Admins     []string `json:"admins,omitempty" datastore:",noindex"`
	Moderators []string `json:"moderators,omitempty" datastore:",noindex"`
	Enabled    bool     `json:"enabled"`

	BillingEmail     string                  `json:"billingEmail,omitempty"`
	Phone            string                  `json:"phone,omitempty"`
	Address          Address                 `json:"address,omitempty"`
	SocialMedia      socialmedia.SocialMedia `json:"socialMedia,omitEmpty"`
	Websites         []website.Website       `json:"websites,omitEmpty"`
	WalletPassphrase string                  `json:"-"`

	Timezone string `json:"timezone"`

	Country string `json:"country"`
	TaxId   string `json:"taxId"`

	// Used in generating email templates
	LogoUrl string `json:"logoUrl"`

	// Where is the user's dashboard?
	DashboardUrl string `json:"dashboardUrl"`

	// Fee structure for this organization
	Fees pricing.Fees `json:"fees" datastore:",noindex"`

	// Partner fees (private, should be up to partner to disclose)
	Partners []pricing.Partner `json:"-" datastore:",noindex"`

	// Email settings
	Email email.Settings `json:"email" datastore:",noindex"`

	// Default Store
	DefaultStore string `json:"defaultStore"`

	// Default App
	DefaultApp string `json:"defaultApp"`

	// Plan settings
	Plan struct {
		PlanId    string
		StartDate time.Time
	} `json:"-"`

	// Affiliate configuration
	Affiliate integration.Affiliate `json:"-" datastore:",noindex"`

	// Signup options
	SignUpOptions struct {
		// Controls the enabled status of account after creation
		AccountsEnabledByDefault bool `json:"accountsEnabledByDefault"`

		// Allow direct affiliate sign up
		AllowAffiliateSignup bool `json:"allowAffiliateSignup"`

		// Turns off required backend checks
		NoNameRequired     bool `json:"noNameRequired"`
		NoPasswordRequired bool `json:"noPasswordRequired"`

		// Requires password set on create confirmation
		TwoStageEnabled bool `json:"twoStageEnabled"`
		ImmediateLogin  bool `json:"immediateLogin"`

		UsernameRequired bool `json:"usernameRequired"`
	} `json:"signUpOptions" datastore:",noindex"`

	// Whether we use live or test tokens, mostly applicable to stripe
	Live bool `json:"live"`

	// TODO: Remain to PaymentWhitelist for clarity
	// List of comma deliminated email globs that result in charges of 50 cents
	EmailWhitelist string `json:"emailWhitelist" datastore:",noindex"`

	// integration
	Integrations  integration.Integrations `json:"-" datastore:"-"`
	Integrations_ string                   `json:"-" datastore:",noindex"`

	// Analytics config
	Analytics analytics.Analytics `json:"analytics" datastore:",noindex"`

	// Bitcoi settings
	Bitcoin integration.Bitcoin `json:"-"`

	// Ethereum settings
	Ethereum integration.Ethereum `json:"-"`

	// Mailchimp settings
	Mailchimp integration.Mailchimp `json:"-"`

	// Mandrill settings
	Mandrill integration.Mandrill `json:"-"`

	// Mercury bank connection
	Mercury integration.Mercury `json:"-"`

	// Netlify settings
	Netlify integration.Netlify `json:"-"`

	// Paypal connection
	Paypal integration.Paypal `json:"-"`

	// Plaid connection
	Plaid integration.Plaid `json:"-"`

	// ReAmaze settings
	Reamaze integration.Reamaze `json:"-"`

	Recaptcha integration.Recaptcha `json:"-" datastore:",noindex"`

	// Salesforce settings
	Salesforce integration.Salesforce `json:"-"`

	// Shipwire settings
	Shipwire integration.Shipwire `json:"-"`

	// Square connection
	Square integration.Square `json:"-"`

	// Stripe connection
	Stripe integration.Stripe `json:"-"`

	// Wire transfer settings
	Wire integration.WireTransfer `json:"-"`

	// AuthorizeNet connection
	AuthorizeNet integration.AuthorizeNet `json:"-"`

	// Adyen connection
	Adyen integration.Adyen `json:"-"`

	// Braintree connection
	Braintree integration.Braintree `json:"-"`

	// Recurly connection
	Recurly integration.Recurly `json:"-"`

	// LemonSqueezy connection
	LemonSqueezy integration.LemonSqueezy `json:"-"`

	SecurityToken integration.SecurityToken `json:"-"`

	Currency currency.Type `json:"currency"`
}

func New

func (*Organization) AddAdmin

func (o *Organization) AddAdmin(userOrId string)

Add admin to organization

func (*Organization) AddDefaultTokens

func (o *Organization) AddDefaultTokens()

Old JWT / AccessToken AUTH

func (*Organization) AddOwner

func (o *Organization) AddOwner(userOrId string)

Add admin to organization

func (*Organization) AfterCreate

func (o *Organization) AfterCreate() error

func (*Organization) AfterUpdate added in v1.49.3

func (o *Organization) AfterUpdate(prev *Organization) error

AfterUpdate is the one place every full-entity org write passes through (orm.Model.UpdateCtx dispatches it), so invalidating here covers Update and MustUpdate from any handler without each call site having to remember.

func (Organization) AuthorizeNetToken

func (o Organization) AuthorizeNetToken(sandbox bool) integration.AuthorizeNetConnection

func (*Organization) BeforeCreate

func (o *Organization) BeforeCreate() error

Hooks

func (*Organization) Defaults

func (o *Organization) Defaults()

func (Organization) GetDefaultApp

func (o Organization) GetDefaultApp() (*app.App, error)

Return DefaultApp

func (Organization) GetDefaultStore

func (o Organization) GetDefaultStore() (*store.Store, error)

Return DefaultStore

func (*Organization) GetReferenceToken

func (o *Organization) GetReferenceToken(usr *user.User) (*oauthtoken.Token, bool, error)

func (Organization) GetStripeAccessToken

func (o Organization) GetStripeAccessToken(userId string) (string, error)

func (*Organization) Init

func (o *Organization) Init(db *datastore.Datastore)

func (Organization) IsAdmin

func (o Organization) IsAdmin(userOrId interface{}) bool

func (Organization) IsOwner

func (o Organization) IsOwner(userOrId interface{}) bool

func (Organization) IsTestEmail

func (o Organization) IsTestEmail(email string) bool

func (*Organization) Load

func (o *Organization) Load(ps []datastore.Property) (err error)

func (Organization) Namespace

func (o Organization) Namespace() string

Namespace returns the datastore namespace for this organization.

SECURITY: every org — without exception — is strictly scoped to its own name. There is NO org-name escape hatch. The legacy `Name=="platform" -> "" (global/cross-org namespace)` bypass was REMOVED: it keyed cross-org datastore access on an org-NAME string, fully decoupled from real platform-admin identity, so anyone who could land in (or forge) an org named "platform" read/wrote the global namespace where every org's records live (Red — privilege escalation via the empty namespace). Cross-org / superadmin datastore access is now gated EXCLUSIVELY on auth.IAMClaims.IsSuperAdmin() (owner=="admin") at the handler/middleware layer — never inferred from the org name here.

func (Organization) Namespaced

func (o Organization) Namespaced(ctx context.Context) context.Context

Namespaced returns a context scoped to this organization's namespace. An inbound HTTP request arrives already mint-gated (mintauth.WithGate) — the request middleware stamps the gate ONCE at the HTTP→datastore boundary, so a gated context IS the HTTP boundary signal (the single structural gate point). For such contexts we detach from the request lifecycle with context.WithoutCancel so the database context survives a browser disconnect or upstream proxy timeout mid-query — while PRESERVING the request's context values (trace and, critically, any mintauth grant applied by PlatformOnly / settled-payment middleware). Using WithoutCancel instead of context.Background() is what keeps the mint authorization (a context value) reachable through the detach.

func (Organization) Pricing

func (o Organization) Pricing() (*pricing.Fees, []pricing.Partner)

func (*Organization) ResetReferenceToken

func (o *Organization) ResetReferenceToken(usr *user.User, claims oauthtoken.Claims) (*oauthtoken.Token, error)

New JWT / OAUTH

func (*Organization) RevokeReferenceToken

func (o *Organization) RevokeReferenceToken(usr *user.User) (*oauthtoken.Token, bool, error)

func (*Organization) Save

func (o *Organization) Save() (ps []datastore.Property, err error)

func (Organization) SquareConfig

func (o Organization) SquareConfig(sandbox bool) integration.SquareConnection

func (Organization) SquareEnvironment added in v1.42.44

func (o Organization) SquareEnvironment() string

SquareEnvironment returns "sandbox" or "production" — the value the Square SDK uses to select its API base URL — derived from TestMode (one authority).

func (Organization) StripeToken

func (o Organization) StripeToken() string

func (Organization) TestMode added in v1.42.44

func (o Organization) TestMode() bool

TestMode reports whether this org transacts in TEST mode. It is the SINGLE authority for BOTH the payment environment (Square sandbox vs production) AND the ledger (trans.Test, balance bucket, pay.Live). Keeping the charge environment and the ledger on ONE authority is what prevents a sandbox charge from crediting the live (spendable) balance and a production charge from booking test (unbilled) revenue.

The authority is the ORG RECORD, and nothing else. It used to be the deployment's SQUARE_ENVIRONMENT, which consulted o.Live only when that variable was UNSET — so on any templated deploy every tenant was forced into one mode and each org's own flag was dead. That is what made a second deployment necessary to serve a sandbox merchant (commerce-api.testnet.hanzo.ai exists for exactly this reason), and why a replica was never freely interchangeable: its behaviour depended on which env block started it. Resolving per org is what lets ONE stateless replica serve a sandbox merchant and a live merchant in the same process, on the same request path.

It is also what "configured per org, not in env files" means here. The credentials were already per org — o.Square.Sandbox / o.Square.Production, o.Stripe.Test / o.Stripe.Live, KMS-backed — and StripeToken() already chose between them from o.Live alone. Only the mode stayed deployment-wide, so the two could disagree. Now both read the same per-org fact.

Still fail-CLOSED, but per tenant instead of per deployment: an org is in production only when its own record says Live, so a new, unset or half-configured org transacts in sandbox and a missing flag can never silently charge real cards. What improves over the env gate is isolation — one org's misconfiguration can no longer drag every other tenant on the pod into the wrong environment, in either direction.

func (*Organization) Validator

func (o *Organization) Validator() *val.Validator

type StripeAccessTokenNotFound

type StripeAccessTokenNotFound struct {
	UserId     string
	LiveUserId string
	TestUserId string
}

func (StripeAccessTokenNotFound) Error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL