import { SmrtClassOptions } from '@happyvertical/smrt-core'; import { DatabaseInterface } from '@happyvertical/sql'; import { Membership } from '../models/Membership.js'; import { PermissionResolver } from './PermissionResolver.js'; import { SessionContext, SessionService } from './SessionService.js'; interface QueryableDatabase extends DatabaseInterface { beginTransaction?: () => Promise; url: string; } interface TransactionDatabase extends QueryableDatabase { commit: () => Promise; isActive: () => boolean; rollback: () => Promise; } export interface SessionPermissionRuntimeContext { database?: QueryableDatabase; permissions: string[]; permissionSet: Set; membership: SessionContext['membership']; postgresRls: boolean; session: SessionContext | null; sessionId: string | null; superAdminBypass: boolean; systemContext: boolean; tenantId: string | null; user: SessionContext['user'] | null; userId: string | null; } export interface SessionPermissionRuntimeOptions extends SmrtClassOptions { enterTenantContext?: boolean; postgresRls?: boolean; sessionId?: string | null; sessionService?: SessionService; superAdminBypass?: boolean; systemContext?: boolean; } /** * Options for {@link withPrincipalPermissionContext}. * * Where {@link SessionPermissionRuntimeOptions} sources the principal from a * loaded session, this sources it directly from a `(userId, tenantId)` pair — * used to run work AS a bound principal (e.g. an agent persona's `runAsUserId`) * with that principal's *live* resolved permissions. */ export interface PrincipalPermissionRuntimeOptions extends SmrtClassOptions { /** Enter tenant context so tenant auto-filtering applies on every adapter. */ enterTenantContext?: boolean; /** * Raw membership row for the `(userId, tenantId)` pair, forwarded to * {@link PermissionResolver.resolvePermissions}. Only consulted when * `permissions` is not supplied. Pass `null` to assert "no direct membership" * (enabling opt-in ancestor inheritance); leave `undefined` to let the * resolver look it up. */ membership?: Membership | null; /** * Pre-resolved permission slugs. When omitted, the principal's permissions * are resolved LIVE via {@link PermissionResolver.resolvePermissions} so role * changes reflect on the next call. */ permissions?: string[]; /** Opt into Postgres RLS transaction wrapping (defaults to package config). */ postgresRls?: boolean; /** Reuse an initialized resolver instead of creating one per call. */ resolver?: PermissionResolver; /** Tenant the principal acts within. `null` resolves to no permissions. */ tenantId: string | null; /** The user whose live permissions bound this execution. */ userId: string; } declare global { var __smrtGetRequestPermissionContext: (() => SessionPermissionRuntimeContext | undefined) | undefined; } export declare function getCurrentSessionPermissionContext(): SessionPermissionRuntimeContext | undefined; export declare function getRequestScopedDatabase(): QueryableDatabase | undefined; export declare function withSessionPermissionContext(options: SessionPermissionRuntimeOptions, fn: (context: SessionPermissionRuntimeContext) => Promise): Promise; /** * Run `fn` AS a bound principal — a `(userId, tenantId)` pair rather than a * session — inside a session-permission context carrying that principal's * **live** resolved permissions. * * This is the door-agnostic mechanism behind "execute as principal": it does * NOT introduce a new authorization layer. It resolves the principal's * permissions through the standard {@link PermissionResolver} cascade (unless * the caller passes a pre-resolved set) and publishes `(smrt.user_id, * smrt.tenant_id, smrt.permissions[])` onto the DB session exactly like * {@link withSessionPermissionContext}, so the manifest-derived Postgres RLS * policies bound every query on that session per-`(table, action)`. * * A principal run never bypasses: `super_admin_bypass` and `system_context` * are always published as `false`. Resolution runs on the base connection * BEFORE any RLS transaction opens (identical ordering to session loading), so * the trusted cascade is never bounded by the not-yet-published principal. * * With RLS off (SQLite/dev) the same permission set is still resolved and * carried, but the DB has no per-operation teeth — callers relying on this on a * non-Postgres adapter must additionally assert the catalog permission for the * `(collection, action)` at their tool/exec seam (see * `assertOperationPermission`). */ export declare function withPrincipalPermissionContext(options: PrincipalPermissionRuntimeOptions, fn: (context: SessionPermissionRuntimeContext) => Promise): Promise; export {}; //# sourceMappingURL=SessionPermissionContext.d.ts.map