UNPKG

@accounter/server

Version:
64 lines (63 loc) 3.47 kB
import type { AuthorizedReadScope, BusinessMembership, TenantContext } from '../types/auth.js'; /** * Resolve the single write-target / owning business for a request given the * primary tenant business and the active read scope. * * Precedence: * - No scope → the primary tenant business. * - Single scoped business → that business. * - Multiple scoped businesses, primary among them → the primary (stable). * - Multiple scoped businesses, primary NOT among them → the first scoped * business (writing to the primary would violate tenant isolation since it * is outside the active scope). * * This is the single source of truth for "which business does this request own * / write to"; the auth context re-points `tenant` with it and DB consumers * reuse it rather than re-deriving the rule inline. */ export declare function resolveWriteTargetBusinessId(primaryBusinessId: string | null | undefined, activeReadScope: AuthorizedReadScope | null | undefined): string | null; /** * Adapter helpers bridging the legacy single-business `TenantContext` and the * multi-business membership / read-scope types. These let existing code keep * compiling while the migration introduces membership-aware auth, before any * provider/resolver is switched over. */ /** Build a membership from the legacy single-business tenant context. */ export declare function membershipFromTenant(tenant: TenantContext): BusinessMembership; /** Collapse a membership back into the legacy single-business tenant context. */ export declare function tenantFromMembership(membership: BusinessMembership): TenantContext; /** * Default read scope = every business the user belongs to, de-duplicated and * order-preserving. */ export declare function readScopeFromMemberships(memberships: BusinessMembership[] | undefined | null): AuthorizedReadScope; /** Whether a business id is part of an authorized read scope. */ export declare function isBusinessInScope(scope: AuthorizedReadScope | undefined | null, businessId: string): boolean; /** * Resolve the effective read scope for a request by applying the precedence * rule: GraphQL args narrow the header scope, which narrows the user's * memberships. Formally `args ⊆ header ⊆ memberships`. * * - When neither header nor args narrowing is requested, defaults to all * accessible businesses. * - The header scope must be a subset of the memberships; the args scope must * be a subset of the (already header-narrowed) scope. * - Returns `null` to signal rejection when any requested id falls outside the * scope it is narrowing — callers must reject rather than silently drop ids. * * This is the single, reusable precedence check; resolvers and the scope * provider should use it rather than re-implementing narrowing per module. */ export declare function resolveReadScopePrecedence(params: { memberships: BusinessMembership[]; headerBusinessIds?: string[]; argsBusinessIds?: string[]; }): AuthorizedReadScope | null; /** * Narrow a user's memberships to a requested set of business ids. * * Returns the requested ids (de-duplicated, request order preserved) as the * read scope, or `null` if ANY requested id is outside the user's memberships — * callers must reject such requests rather than silently dropping unknown ids. */ export declare function narrowReadScope(memberships: BusinessMembership[], requestedBusinessIds: string[]): AuthorizedReadScope | null;