@objectstack/plugin-security 7.3.0 → 7.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/permission-evaluator.ts","../src/rls-compiler.ts","../src/field-masker.ts","../src/errors.ts","../src/bootstrap-platform-admin.ts","../src/auto-org-admin-grant.ts","../src/manifest.ts","../src/security-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * @objectstack/plugin-security\n * \n * Security Plugin for ObjectStack\n * Provides RBAC, Row-Level Security (RLS), and Field-Level Security runtime.\n */\n\nexport { SecurityPlugin } from './security-plugin.js';\nexport { PermissionEvaluator } from './permission-evaluator.js';\nexport { RLSCompiler, RLS_DENY_FILTER } from './rls-compiler.js';\nexport { FieldMasker } from './field-masker.js';\nexport { PermissionDeniedError, isPermissionDeniedError } from './errors.js';\nexport {\n securityObjects,\n securityDefaultPermissionSets,\n securityPluginManifestHeader,\n SECURITY_PLUGIN_ID,\n SECURITY_PLUGIN_VERSION,\n} from './manifest.js';\nexport {\n reconcileOrgAdminGrant,\n backfillOrgAdminGrants,\n} from './auto-org-admin-grant.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PermissionSet, ObjectPermission, FieldPermission } from '@objectstack/spec/security';\n\n/**\n * Operation type mapping to permission checks\n */\nconst OPERATION_TO_PERMISSION: Record<string, keyof ObjectPermission> = {\n find: 'allowRead',\n findOne: 'allowRead',\n count: 'allowRead',\n aggregate: 'allowRead',\n insert: 'allowCreate',\n update: 'allowEdit',\n delete: 'allowDelete',\n};\n\n/**\n * PermissionEvaluator\n * \n * Runtime evaluator for PermissionSet definitions.\n * Resolves aggregated permissions from roles to concrete allow/deny decisions.\n */\nexport class PermissionEvaluator {\n /**\n * Check if an operation is allowed on an object for the given permission sets.\n * Uses \"most permissive\" merging: if ANY permission set allows, it's allowed.\n */\n checkObjectPermission(\n operation: string,\n objectName: string,\n permissionSets: PermissionSet[]\n ): boolean {\n const permKey = OPERATION_TO_PERMISSION[operation];\n if (!permKey) return true; // Unknown operations are allowed by default\n\n for (const ps of permissionSets) {\n // Honour the `'*'` wildcard sentinel — admin permission sets typically\n // grant blanket access via a single `objects: { '*': … }` entry rather\n // than enumerating every system object.\n const objPerm = ps.objects?.[objectName] ?? ps.objects?.['*'];\n if (objPerm) {\n // Check if modifyAllRecords is set (super-user bypass for write ops)\n if (['allowEdit', 'allowDelete'].includes(permKey) && objPerm.modifyAllRecords) {\n return true;\n }\n // Check if viewAllRecords is set (super-user bypass for read ops)\n if (permKey === 'allowRead' && (objPerm.viewAllRecords || objPerm.modifyAllRecords)) {\n return true;\n }\n // Check the specific permission\n if (objPerm[permKey]) {\n return true;\n }\n }\n }\n\n return false;\n }\n\n /**\n * Get the merged field permissions for an object.\n * Returns a map of field names to their effective permissions.\n * Uses \"most permissive\" merging.\n */\n getFieldPermissions(\n objectName: string,\n permissionSets: PermissionSet[]\n ): Record<string, FieldPermission> {\n const result: Record<string, FieldPermission> = {};\n\n for (const ps of permissionSets) {\n if (!ps.fields) continue;\n\n for (const [key, perm] of Object.entries(ps.fields)) {\n // Field keys are in format: \"object_name.field_name\"\n if (!key.startsWith(`${objectName}.`)) continue;\n const fieldName = key.substring(objectName.length + 1);\n\n if (!result[fieldName]) {\n result[fieldName] = { readable: false, editable: false };\n }\n\n // Most permissive merge\n if (perm.readable) result[fieldName].readable = true;\n if (perm.editable) result[fieldName].editable = true;\n }\n }\n\n return result;\n }\n\n /**\n * Resolve permission sets for a list of identifier names from metadata.\n *\n * Identifiers are matched to `PermissionSet.name`. The names may be\n * either role names (when `sys_role.name` is reused as a permission set\n * name — common for default admin/member/viewer roles) or explicit\n * permission set names supplied through `ExecutionContext.permissions[]`\n * (resolved by `resolveExecutionContext` from `sys_user_permission_set`\n * and `sys_role_permission_set`).\n *\n * Async because the underlying metadata service exposes `list()` as a\n * Promise — synchronous iteration would silently yield zero results\n * (the historical SecurityPlugin behaviour, masking all enforcement).\n *\n * `bootstrapPermissionSets` is a fallback list of plugin-owned permission\n * sets (typically the platform defaults: admin_full_access /\n * member_default / viewer_readonly) that are registered via\n * `manifest.register({ permissions })` but do not currently propagate\n * into the metadata service's `list()` index. Without this fallback,\n * SecurityPlugin would never resolve the defaults and all enforcement\n * would be silently disabled for authenticated requests.\n */\n async resolvePermissionSets(\n identifiers: string[],\n metadataService: any,\n bootstrapPermissionSets: PermissionSet[] = [],\n /**\n * Optional async loader for permission set names that aren't found in\n * metadata or bootstrap. Lets callers query user-defined permission\n * sets persisted in `sys_permission_set`. Failures are swallowed.\n */\n dbLoader?: (unresolved: string[]) => Promise<PermissionSet[]>\n ): Promise<PermissionSet[]> {\n if (identifiers.length === 0) return [];\n\n const result: PermissionSet[] = [];\n const seen = new Set<string>();\n\n // Get all permission sets from metadata. Support both async (Manager) and\n // sync (test stub) implementations of `list`.\n let allPermSets: any = [];\n try {\n const listed = metadataService?.list?.('permission')\n ?? metadataService?.list?.('permissions')\n ?? [];\n allPermSets = typeof (listed as any)?.then === 'function' ? await listed : listed;\n } catch {\n allPermSets = [];\n }\n if (!Array.isArray(allPermSets)) allPermSets = [];\n\n const wanted = new Set(identifiers);\n for (const ps of allPermSets) {\n if (wanted.has(ps.name) && !seen.has(ps.name)) {\n seen.add(ps.name);\n result.push(ps);\n }\n }\n\n // Fallback: any wanted name not yet matched is sourced from the\n // bootstrap list (plugin-owned defaults). Avoids silent failure when\n // permission sets are registered via `manifest.register` but the\n // metadata service hasn't indexed them.\n for (const ps of bootstrapPermissionSets) {\n if (wanted.has(ps.name) && !seen.has(ps.name)) {\n seen.add(ps.name);\n result.push(ps);\n }\n }\n\n // Last-resort: query user-defined permission sets from the database.\n // Without this, custom permission sets (created via the admin UI as\n // `sys_permission_set` rows) would be silently ignored both for CRUD\n // enforcement and for field-level masking.\n if (dbLoader) {\n const unresolved = identifiers.filter((n) => !seen.has(n));\n if (unresolved.length > 0) {\n try {\n const dbRows = await dbLoader(unresolved);\n for (const ps of dbRows ?? []) {\n if (ps?.name && !seen.has(ps.name)) {\n seen.add(ps.name);\n result.push(ps);\n }\n }\n } catch {\n // Swallow — the request shouldn't fail just because the DB\n // lookup is unavailable.\n }\n }\n }\n\n return result;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { RowLevelSecurityPolicy } from '@objectstack/spec/security';\nimport type { ExecutionContext } from '@objectstack/spec/kernel';\n\n/**\n * RLS User Context\n * Variables available for RLS expression evaluation.\n */\ninterface RLSUserContext {\n id?: string;\n /**\n * Active organization id for the request. RLS expressions reference\n * this as `current_user.organization_id`. Sourced from\n * `ExecutionContext.tenantId` (the runtime keeps the abstract\n * \"tenant\" name, but at the data/RLS layer the canonical column is\n * `organization_id` — see better-auth's organization plugin).\n */\n organization_id?: string;\n roles?: string[];\n /**\n * IDs of all users that share the active organization with the\n * current user (incl. self). Pre-resolved by the runtime so RLS can\n * scope identity tables like `sys_user` via\n * `id IN (current_user.org_user_ids)` without needing subquery\n * support in the compiler.\n */\n org_user_ids?: string[];\n [key: string]: unknown;\n}\n\n/**\n * Sentinel filter used when applicable RLS policies exist but none can\n * be compiled against the current execution context (typically because a\n * required `current_user.*` variable is missing — e.g. the user has no\n * active organization). The filter compares `id` against a non-printable\n * UUID-shaped string that no real record will ever carry, so the upstream\n * SQL layer naturally returns zero rows without raising an error. This\n * gives us **fail-closed** semantics for select/update/delete on tables\n * that the user is not entitled to see, without forcing every caller to\n * handle a thrown `PermissionDeniedError` for what is conceptually an\n * empty result set.\n *\n * Exposed for the SecurityPlugin's optional short-circuit path and for\n * tests; see {@link RLSCompiler.compileFilter}.\n */\nexport const RLS_DENY_FILTER: Record<string, unknown> = Object.freeze({\n id: '__rls_deny__:00000000-0000-0000-0000-000000000000',\n});\n\n/**\n * RLSCompiler\n * \n * Compiles Row-Level Security policy expressions into query filters.\n * Converts `using` / `check` expressions into ObjectQL-compatible filter conditions.\n */\nexport class RLSCompiler {\n /**\n * Compile RLS policies into a query filter for the given user context.\n * Multiple policies for the same object/operation are OR-combined (any match allows access).\n *\n * Return-value semantics:\n * - `null` → no policies applicable → caller applies no RLS filter.\n * - non-null → caller AND's it onto the existing where clause.\n * - {@link RLS_DENY_FILTER} → policies were defined but none could be\n * compiled (e.g. wildcard `tenant_isolation` against a user with no\n * active organization). The caller must treat this as \"deny by\n * default\" — its `id` comparison naturally yields zero rows on\n * select/update/delete, which is the safe fail-closed answer.\n */\n compileFilter(\n policies: RowLevelSecurityPolicy[],\n executionContext?: ExecutionContext\n ): Record<string, unknown> | null {\n if (policies.length === 0) return null;\n\n const userCtx: RLSUserContext = {\n id: executionContext?.userId,\n organization_id: executionContext?.tenantId,\n roles: executionContext?.roles,\n org_user_ids: (executionContext as any)?.org_user_ids,\n };\n\n const filters: Record<string, unknown>[] = [];\n\n for (const policy of policies) {\n if (!policy.using) continue;\n const filter = this.compileExpression(policy.using, userCtx);\n if (filter) {\n filters.push(filter);\n }\n }\n\n if (filters.length === 0) {\n // Policies *were* applicable but every one of them depended on a\n // `current_user.*` variable that wasn't populated (or used an\n // expression we couldn't compile). Fail closed — return a sentinel\n // filter that matches no rows. This prevents the \"user without an\n // active org sees every tenant's data\" class of bug.\n return RLS_DENY_FILTER;\n }\n if (filters.length === 1) return filters[0];\n\n // Multiple policies: OR-combine (any policy allows access)\n return { $or: filters };\n }\n\n /**\n * Compile a single RLS expression into a query filter.\n * \n * Supports simple expressions like:\n * - \"field_name = current_user.property\"\n * - \"field_name IN (current_user.array_property)\"\n * - \"field_name = 'literal_value'\"\n */\n compileExpression(\n expression: string,\n userCtx: RLSUserContext\n ): Record<string, unknown> | null {\n if (!expression) return null;\n\n // Handle simple equality: \"field = current_user.property\"\n const eqMatch = expression.match(/^\\s*(\\w+)\\s*=\\s*current_user\\.(\\w+)\\s*$/);\n if (eqMatch) {\n const [, field, prop] = eqMatch;\n const value = userCtx[prop];\n // Skip when the user-context value is missing (undefined or null).\n // A `null` `organization_id` means \"no active organization\" — applying\n // the filter as `organization_id IS NULL` would silently expose every\n // un-tenanted row across tenants and break system tables that lack the\n // column entirely. Treating null as \"skip this policy\" makes the\n // tenant_isolation rule safely opt-out for users without an active org\n // while still applying when one is set.\n if (value === undefined || value === null) return null;\n return { [field]: value };\n }\n\n // Handle literal equality: \"field = 'value'\"\n const litMatch = expression.match(/^\\s*(\\w+)\\s*=\\s*'([^']*)'\\s*$/);\n if (litMatch) {\n const [, field, value] = litMatch;\n return { [field]: value };\n }\n\n // Handle IN: \"field IN (current_user.array_property)\"\n const inMatch = expression.match(/^\\s*(\\w+)\\s+IN\\s+\\(\\s*current_user\\.(\\w+)\\s*\\)\\s*$/i);\n if (inMatch) {\n const [, field, prop] = inMatch;\n const value = userCtx[prop];\n if (!Array.isArray(value) || value.length === 0) return null;\n return { [field]: { $in: value } };\n }\n\n // Unsupported expression: return null (no additional RLS filter applied).\n // Note: callers should treat absence of RLS policies as \"allow all\" only when\n // no policies are defined. If policies exist but cannot be compiled, the caller\n // may want to deny access as a safety measure.\n return null;\n }\n\n /**\n * Get applicable RLS policies for a given object and operation.\n */\n getApplicablePolicies(\n objectName: string,\n operation: string,\n allPolicies: RowLevelSecurityPolicy[]\n ): RowLevelSecurityPolicy[] {\n // Map engine operation to RLS operation type\n const rlsOp = this.mapOperationToRLS(operation);\n\n return allPolicies.filter(policy => {\n // Check object match\n if (policy.object !== objectName && policy.object !== '*') return false;\n\n // Check operation match\n if (policy.operation === 'all') return true;\n if (policy.operation === rlsOp) return true;\n\n return false;\n });\n }\n\n private mapOperationToRLS(operation: string): string {\n switch (operation) {\n case 'find':\n case 'findOne':\n case 'count':\n case 'aggregate':\n return 'select';\n case 'insert':\n return 'insert';\n case 'update':\n return 'update';\n case 'delete':\n return 'delete';\n default:\n return 'select';\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { FieldPermission } from '@objectstack/spec/security';\n\n/**\n * FieldMasker\n * \n * Applies field-level security by stripping restricted fields from query results.\n */\nexport class FieldMasker {\n /**\n * Mask fields in query results based on field permissions.\n * Removes fields that the user does not have read access to.\n */\n maskResults(\n results: any | any[],\n fieldPermissions: Record<string, FieldPermission>,\n _objectName: string\n ): any | any[] {\n // If no field permissions defined, return results as-is\n if (Object.keys(fieldPermissions).length === 0) return results;\n\n // Get list of non-readable fields\n const hiddenFields = Object.entries(fieldPermissions)\n .filter(([, perm]) => !perm.readable)\n .map(([field]) => field);\n\n if (hiddenFields.length === 0) return results;\n\n if (Array.isArray(results)) {\n return results.map(record => this.maskRecord(record, hiddenFields));\n }\n\n return this.maskRecord(results, hiddenFields);\n }\n\n /**\n * Get non-editable fields for use in write operations.\n * Returns a list of field names that should be stripped from incoming data.\n */\n getNonEditableFields(\n fieldPermissions: Record<string, FieldPermission>\n ): string[] {\n return Object.entries(fieldPermissions)\n .filter(([, perm]) => !perm.editable)\n .map(([field]) => field);\n }\n\n /**\n * Strip non-editable fields from write data.\n */\n stripNonEditableFields(\n data: Record<string, any>,\n fieldPermissions: Record<string, FieldPermission>\n ): Record<string, any> {\n const nonEditable = this.getNonEditableFields(fieldPermissions);\n if (nonEditable.length === 0) return data;\n\n const result = { ...data };\n for (const field of nonEditable) {\n delete result[field];\n }\n return result;\n }\n\n /**\n * Detect which fields in the caller's write payload would touch a\n * field they are not allowed to edit. Returns the set of offending\n * field names (no duplicates, sorted for stable error messages).\n *\n * Used by the security middleware on insert/update to fail-closed\n * with an explicit 403 rather than silently dropping fields — a\n * silent drop hides the security boundary from honest clients\n * (their update partially \"doesn't save\") and gives an attacker no\n * negative signal that the field exists. Throwing makes the\n * boundary observable in both directions.\n *\n * `data` may be a single record or an array of records (bulk insert);\n * either way the returned list is the union across all rows.\n *\n * Fields without a permission entry pass through — permission sets\n * are an allow-list at the field level only for fields they\n * explicitly enumerate. Most objects do not declare per-field rules\n * and remain fully editable.\n */\n detectForbiddenWrites(\n data: Record<string, any> | Record<string, any>[],\n fieldPermissions: Record<string, FieldPermission>\n ): string[] {\n if (Object.keys(fieldPermissions).length === 0) return [];\n const nonEditable = new Set(this.getNonEditableFields(fieldPermissions));\n if (nonEditable.size === 0) return [];\n\n const offenders = new Set<string>();\n const rows = Array.isArray(data) ? data : [data];\n for (const row of rows) {\n if (!row || typeof row !== 'object') continue;\n for (const field of Object.keys(row)) {\n if (nonEditable.has(field)) offenders.add(field);\n }\n }\n return Array.from(offenders).sort();\n }\n\n private maskRecord(record: any, hiddenFields: string[]): any {\n if (!record || typeof record !== 'object') return record;\n\n const result = { ...record };\n for (const field of hiddenFields) {\n delete result[field];\n }\n return result;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Typed sentinel error thrown by `SecurityPlugin` when an operation is\n * denied. Caught by `@objectstack/runtime`'s HTTP dispatcher and translated\n * to HTTP 403.\n */\nexport class PermissionDeniedError extends Error {\n readonly code = 'PERMISSION_DENIED';\n readonly statusCode = 403;\n readonly details?: Record<string, unknown>;\n constructor(message: string, details?: Record<string, unknown>) {\n super(message);\n this.name = 'PermissionDeniedError';\n this.details = details;\n }\n}\n\nexport function isPermissionDeniedError(e: unknown): e is PermissionDeniedError {\n if (!e || typeof e !== 'object') return false;\n const anyE = e as any;\n return (\n anyE.name === 'PermissionDeniedError' ||\n anyE.code === 'PERMISSION_DENIED' ||\n (typeof anyE.message === 'string' && anyE.message.startsWith('[Security] Access denied'))\n );\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * bootstrapPlatformAdmin — first-boot platform admin promotion.\n *\n * Two responsibilities, both idempotent and run on `kernel:ready`:\n *\n * 1. **Seed `sys_permission_set` rows** for each `defaultPermissionSets`\n * entry (admin_full_access / member_default / viewer_readonly).\n *\n * 2. **Promote the first registered user to platform admin** by\n * inserting a `sys_user_permission_set` row that points at\n * `admin_full_access` with `organization_id = NULL` (= cross-tenant).\n * If a platform admin already exists, this is a no-op forever.\n *\n * The \"create a Default Organization for the freshly-promoted admin\"\n * behavior moved to `@objectstack/plugin-org-scoping` (see\n * `ensureDefaultOrganization`). Install that plugin to get\n * multi-tenant bootstrap.\n */\n\nimport type { PermissionSet } from '@objectstack/spec/security';\n\ninterface BootstrapOptions {\n /** Logger from PluginContext. */\n logger?: {\n info: (message: string, meta?: Record<string, any>) => void;\n warn: (message: string, meta?: Record<string, any>) => void;\n };\n}\n\nconst SYSTEM_CTX = { isSystem: true };\n\nasync function tryFind(ql: any, object: string, where: any, limit = 100): Promise<any[]> {\n try {\n const rows = await ql.find(object, { where, limit }, { context: SYSTEM_CTX });\n return Array.isArray(rows) ? rows : [];\n } catch {\n return [];\n }\n}\n\nasync function tryInsert(ql: any, object: string, data: any): Promise<any | null> {\n try {\n return await ql.insert(object, data, { context: SYSTEM_CTX });\n } catch {\n return null;\n }\n}\n\nfunction genId(prefix: string): string {\n const rand = Math.random().toString(36).slice(2, 10);\n const ts = Date.now().toString(36);\n return `${prefix}_${ts}${rand}`;\n}\n\n/**\n * Persist seed permission sets and promote the first registered user to\n * platform admin. Safe to call multiple times.\n */\nexport async function bootstrapPlatformAdmin(\n ql: any,\n bootstrapPermissionSets: PermissionSet[],\n options: BootstrapOptions = {},\n): Promise<{\n seeded: number;\n adminPromoted: boolean;\n reason?: string;\n}> {\n const logger = options.logger;\n if (!ql || typeof ql.find !== 'function' || typeof ql.insert !== 'function') {\n return { seeded: 0, adminPromoted: false, reason: 'objectql_unavailable' };\n }\n\n // 1. Seed permission set rows.\n const seeded: Record<string, string> = {};\n for (const ps of bootstrapPermissionSets) {\n if (!ps.name) continue;\n const existing = await tryFind(ql, 'sys_permission_set', { name: ps.name }, 1);\n if (existing.length > 0 && existing[0].id) {\n seeded[ps.name] = existing[0].id;\n continue;\n }\n const id = genId('ps');\n const created = await tryInsert(ql, 'sys_permission_set', {\n id,\n name: ps.name,\n label: ps.label ?? ps.name,\n description: ps.description ?? null,\n object_permissions: JSON.stringify(ps.objects ?? {}),\n field_permissions: JSON.stringify(ps.fields ?? {}),\n // Persist the remaining permset facets so the runtime resolver\n // (rest-server.ts / resolve-execution-context.ts) can hydrate\n // them back into ExecutionContext.systemPermissions etc. Without\n // these the platform-admin promotion grants the right LINK row\n // but the permission set itself carries no capabilities, so\n // `setup.access` / `studio.access` never reach the app filter\n // and the Setup app is invisible even to admin_full_access.\n system_permissions: JSON.stringify(ps.systemPermissions ?? []),\n row_level_security: JSON.stringify(ps.rowLevelSecurity ?? []),\n tab_permissions: JSON.stringify(ps.tabPermissions ?? {}),\n active: true,\n });\n if (created?.id) seeded[ps.name] = created.id;\n else if (created) seeded[ps.name] = id;\n }\n\n const seededCount = Object.keys(seeded).length;\n\n // 2. First-user platform admin promotion.\n const adminPsId = seeded['admin_full_access'];\n if (!adminPsId) {\n return { seeded: seededCount, adminPromoted: false, reason: 'admin_permission_set_missing' };\n }\n\n const existingAdminLinks = await tryFind(\n ql,\n 'sys_user_permission_set',\n { permission_set_id: adminPsId },\n 5,\n );\n if (existingAdminLinks.some((r) => !r.organization_id)) {\n return { seeded: seededCount, adminPromoted: false, reason: 'already_have_admin' };\n }\n\n const allUsers = await tryFind(ql, 'sys_user', {}, 50);\n if (allUsers.length === 0) {\n logger?.info?.('[security] no users yet — first sign-up will be promoted to platform admin');\n return { seeded: seededCount, adminPromoted: false, reason: 'no_users' };\n }\n const sorted = [...allUsers].sort((a, b) => {\n const ta = a.created_at ? new Date(a.created_at).getTime() : 0;\n const tb = b.created_at ? new Date(b.created_at).getTime() : 0;\n return ta - tb;\n });\n const target = sorted[0];\n\n const inserted = await tryInsert(ql, 'sys_user_permission_set', {\n id: genId('ups'),\n user_id: target.id,\n permission_set_id: adminPsId,\n organization_id: null,\n granted_by: null,\n });\n if (!inserted) {\n logger?.warn?.(`[security] failed to grant admin_full_access to first user ${target.email ?? target.id}`);\n return { seeded: seededCount, adminPromoted: false, reason: 'insert_failed' };\n }\n logger?.info?.(`[security] first user promoted to platform admin: ${target.email ?? target.id}`);\n\n return { seeded: seededCount, adminPromoted: true };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Auto-grant `organization_admin` to org owners/admins.\n *\n * For every `sys_member` row whose `role` contains `owner` or `admin`,\n * ensure a `sys_user_permission_set` row exists that links the user to\n * the `organization_admin` permission set scoped to that organization.\n * For members whose role no longer qualifies (demotion or membership\n * removal), revoke the matching scoped grant.\n *\n * Lifecycle hookup (wired from `security-plugin.ts`):\n *\n * - after `sys_member` insert → reconcile (user_id, organization_id)\n * - after `sys_member` update → reconcile both old and new owner pair\n * - after `sys_member` delete → reconcile to revoke\n * - on `kernel:ready` → backfill across every existing member\n *\n * All operations are idempotent and failure-isolated so a missing\n * permission-set row, schema drift, or a stale row never blocks the\n * underlying `sys_member` mutation.\n *\n * **Why this isn't done by the better-auth org plugin directly:**\n * better-auth does not know about ObjectStack permission sets — it\n * only stores membership roles. Translating \"owner/admin role on this\n * org\" into \"owns the `organization_admin` permission set scoped to\n * this org\" is platform metadata policy and belongs here, alongside\n * `bootstrapPlatformAdmin` (which does the analogous thing for\n * platform admins).\n *\n * **Anti-escalation:** `organization_admin` itself (declared in\n * `platform-objects/src/security/default-permission-sets.ts`) is\n * deliberately read-only on the global RBAC tables\n * (`sys_permission_set`, `sys_user_permission_set`, `sys_role`, …),\n * so a freshly-granted org admin cannot rebind themselves to\n * `admin_full_access`.\n */\n\nconst SYSTEM_CTX = { isSystem: true } as const;\nconst PERMISSION_SET_NAME = 'organization_admin';\n\ninterface MaybeLogger {\n info?: (message: string, meta?: Record<string, any>) => void;\n warn?: (message: string, meta?: Record<string, any>) => void;\n debug?: (message: string, meta?: Record<string, any>) => void;\n}\n\nfunction genId(prefix: string): string {\n const rand = Math.random().toString(36).slice(2, 10);\n const ts = Date.now().toString(36);\n return `${prefix}_${ts}${rand}`;\n}\n\nasync function tryFind(ql: any, object: string, where: any, limit = 50): Promise<any[]> {\n try {\n const rows = await ql.find(object, { where, limit }, { context: SYSTEM_CTX });\n return Array.isArray(rows) ? rows : Array.isArray(rows?.records) ? rows.records : [];\n } catch {\n return [];\n }\n}\n\nasync function tryInsert(ql: any, object: string, data: any): Promise<any | null> {\n try {\n return await ql.insert(object, data, { context: SYSTEM_CTX });\n } catch {\n return null;\n }\n}\n\nasync function tryDelete(ql: any, object: string, id: string): Promise<boolean> {\n try {\n await ql.delete(object, id, { context: SYSTEM_CTX });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Parse a better-auth `sys_member.role` value into a lower-cased role\n * list. better-auth stores either a single role (`\"owner\"`) or a\n * comma-separated list (`\"owner,admin\"`).\n */\nfunction parseRoles(raw: unknown): string[] {\n if (typeof raw !== 'string') return [];\n return raw\n .split(',')\n .map((s) => s.trim().toLowerCase())\n .filter((s) => s.length > 0);\n}\n\nfunction isAdminRole(raw: unknown): boolean {\n const roles = parseRoles(raw);\n return roles.includes('owner') || roles.includes('admin');\n}\n\n/**\n * Resolve the `sys_permission_set.id` for `organization_admin`. Cached\n * across calls per ObjectQL instance via a WeakMap so repeated\n * reconciliations do not re-query.\n */\nconst permissionSetIdCache = new WeakMap<object, string>();\n\nasync function resolvePermissionSetId(ql: any): Promise<string | null> {\n const cached = permissionSetIdCache.get(ql);\n if (cached) return cached;\n const rows = await tryFind(ql, 'sys_permission_set', { name: PERMISSION_SET_NAME }, 1);\n const id = rows[0]?.id;\n if (typeof id === 'string' && id.length > 0) {\n permissionSetIdCache.set(ql, id);\n return id;\n }\n return null;\n}\n\n/**\n * Ensure (or revoke) the org-scoped `organization_admin` grant for\n * `(userId, orgId)` based on the current `sys_member` rows.\n *\n * - If ANY membership row for the pair carries an owner/admin role,\n * ensure exactly one `sys_user_permission_set` row exists.\n * - Else, remove every `sys_user_permission_set` row that links the\n * pair to `organization_admin` (handles demotion and membership\n * removal symmetrically).\n *\n * Returns a structured report for observability. Never throws.\n */\nexport async function reconcileOrgAdminGrant(\n ql: any,\n userId: string,\n orgId: string,\n options: { logger?: MaybeLogger } = {},\n): Promise<{\n action: 'granted' | 'revoked' | 'noop' | 'skipped';\n reason?: string;\n}> {\n const logger = options.logger;\n if (!ql || typeof ql.find !== 'function' || typeof ql.insert !== 'function') {\n return { action: 'skipped', reason: 'objectql_unavailable' };\n }\n if (!userId || !orgId) {\n return { action: 'skipped', reason: 'missing_keys' };\n }\n\n const permSetId = await resolvePermissionSetId(ql);\n if (!permSetId) {\n // organization_admin permission set isn't seeded yet (boot ordering)\n // — caller can retry later (e.g. via kernel:ready backfill).\n return { action: 'skipped', reason: 'permission_set_missing' };\n }\n\n // 1. Determine whether the user currently holds an admin-grade role\n // in this org. Better-auth allows multiple membership rows per\n // pair under some edge cases (legacy data) — any qualifying row\n // is enough.\n const memberships = await tryFind(\n ql,\n 'sys_member',\n { user_id: userId, organization_id: orgId },\n 10,\n );\n const shouldGrant = memberships.some((m: any) => isAdminRole(m?.role));\n\n // 2. Look at existing grants for this exact pair.\n const existingGrants = await tryFind(\n ql,\n 'sys_user_permission_set',\n { user_id: userId, organization_id: orgId, permission_set_id: permSetId },\n 5,\n );\n\n if (shouldGrant) {\n if (existingGrants.length > 0) {\n // Deduplicate stale duplicates if any slipped through.\n for (const extra of existingGrants.slice(1)) {\n if (extra?.id) await tryDelete(ql, 'sys_user_permission_set', String(extra.id));\n }\n return { action: 'noop' };\n }\n const created = await tryInsert(ql, 'sys_user_permission_set', {\n id: genId('ups'),\n user_id: userId,\n permission_set_id: permSetId,\n organization_id: orgId,\n granted_by: null,\n });\n if (created) {\n logger?.info?.('[security] granted organization_admin', { userId, orgId });\n return { action: 'granted' };\n }\n return { action: 'skipped', reason: 'insert_failed' };\n }\n\n // shouldGrant === false → revoke any pre-existing scoped grant.\n if (existingGrants.length === 0) {\n return { action: 'noop' };\n }\n let removed = 0;\n for (const row of existingGrants) {\n if (row?.id && (await tryDelete(ql, 'sys_user_permission_set', String(row.id)))) {\n removed += 1;\n }\n }\n if (removed > 0) {\n logger?.info?.('[security] revoked organization_admin', { userId, orgId, removed });\n return { action: 'revoked' };\n }\n return { action: 'skipped', reason: 'delete_failed' };\n}\n\n/**\n * Reconcile every `(user_id, organization_id)` pair that has at least\n * one `sys_member` row. Used by `kernel:ready` to backfill grants for\n * memberships that pre-date this feature, and as a safety net after\n * the platform admin bootstrap auto-creates the default organization.\n */\nexport async function backfillOrgAdminGrants(\n ql: any,\n options: { logger?: MaybeLogger; limit?: number } = {},\n): Promise<{ scanned: number; granted: number; revoked: number; skipped: number }> {\n const logger = options.logger;\n const limit = options.limit ?? 5000;\n const summary = { scanned: 0, granted: 0, revoked: 0, skipped: 0 };\n if (!ql || typeof ql.find !== 'function') return summary;\n\n const permSetId = await resolvePermissionSetId(ql);\n if (!permSetId) {\n logger?.debug?.('[security] organization_admin backfill skipped — permission set missing');\n return summary;\n }\n\n const members = await tryFind(ql, 'sys_member', {}, limit);\n // De-duplicate by (user_id, organization_id) pair — a user with two\n // membership rows (e.g. legacy duplicates) only needs one reconcile.\n const seen = new Set<string>();\n for (const m of members) {\n const userId = String(m?.user_id ?? '');\n const orgId = String(m?.organization_id ?? '');\n if (!userId || !orgId) continue;\n const key = `${userId}|${orgId}`;\n if (seen.has(key)) continue;\n seen.add(key);\n summary.scanned += 1;\n const res = await reconcileOrgAdminGrant(ql, userId, orgId, { logger });\n if (res.action === 'granted') summary.granted += 1;\n else if (res.action === 'revoked') summary.revoked += 1;\n else if (res.action === 'skipped') summary.skipped += 1;\n }\n\n // Also revoke any organization_admin grant pointing at a (user, org)\n // pair with NO membership row left (orphaned grants from deletes\n // that fired before this hook existed).\n const allGrants = await tryFind(\n ql,\n 'sys_user_permission_set',\n { permission_set_id: permSetId },\n limit,\n );\n for (const g of allGrants) {\n const userId = String(g?.user_id ?? '');\n const orgId = String(g?.organization_id ?? '');\n if (!userId || !orgId) continue;\n const key = `${userId}|${orgId}`;\n if (seen.has(key)) continue;\n const res = await reconcileOrgAdminGrant(ql, userId, orgId, { logger });\n if (res.action === 'revoked') summary.revoked += 1;\n }\n\n logger?.info?.('[security] organization_admin backfill complete', summary);\n return summary;\n}\n\n/**\n * Extract (user_id, organization_id) candidate pairs from a\n * `sys_member` ObjectQL middleware context. Returns both the\n * pre-change and post-change pair so callers can reconcile each.\n */\nexport function extractMemberPairs(opCtx: any): Array<{ userId: string; orgId: string }> {\n const out = new Map<string, { userId: string; orgId: string }>();\n const add = (userId: unknown, orgId: unknown) => {\n if (typeof userId === 'string' && typeof orgId === 'string' && userId && orgId) {\n out.set(`${userId}|${orgId}`, { userId, orgId });\n }\n };\n // Post-write payload — most common case.\n add(opCtx?.result?.user_id, opCtx?.result?.organization_id);\n // Update payloads carry the new values in `data` and the prior row\n // in `before` (driver-dependent). We reconcile BOTH so a member\n // moved from org A to org B (or user changed) is handled.\n add(opCtx?.data?.user_id, opCtx?.data?.organization_id);\n add(opCtx?.before?.user_id, opCtx?.before?.organization_id);\n // For deletes the affected row is sometimes only in `existing`.\n add(opCtx?.existing?.user_id, opCtx?.existing?.organization_id);\n return Array.from(out.values());\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Canonical plugin-security manifest source.\n *\n * Both `objectstack.config.ts` (compile-time) and `security-plugin.ts`\n * (runtime `manifest.register`) import from this file so the two\n * registration paths cannot drift (D7).\n */\n\nimport {\n SysPermissionSet,\n SysRole,\n SysUserPermissionSet,\n SysRolePermissionSet,\n defaultPermissionSets,\n} from '@objectstack/platform-objects/security';\n\nexport const SECURITY_PLUGIN_ID = 'com.objectstack.plugin-security';\nexport const SECURITY_PLUGIN_VERSION = '1.0.0';\n\n/** Security objects owned by plugin-security. */\nexport const securityObjects = [\n SysRole,\n SysPermissionSet,\n SysUserPermissionSet,\n SysRolePermissionSet,\n];\n\n/** Default platform permission sets (admin / member / viewer). */\nexport const securityDefaultPermissionSets = defaultPermissionSets;\n\n/** Manifest header shared by compile-time config and runtime registration. */\nexport const securityPluginManifestHeader = {\n id: SECURITY_PLUGIN_ID,\n namespace: 'sys',\n version: SECURITY_PLUGIN_VERSION,\n type: 'plugin' as const,\n scope: 'system' as const,\n defaultDatasource: 'cloud',\n name: 'Security Plugin',\n description: 'RBAC roles and permission sets for ObjectStack (Role, PermissionSet)',\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Plugin, PluginContext } from '@objectstack/core';\nimport type { PermissionSet, RowLevelSecurityPolicy } from '@objectstack/spec/security';\nimport { PermissionEvaluator } from './permission-evaluator.js';\nimport { RLSCompiler, RLS_DENY_FILTER } from './rls-compiler.js';\nimport { FieldMasker } from './field-masker.js';\nimport { PermissionDeniedError } from './errors.js';\nimport { bootstrapPlatformAdmin } from './bootstrap-platform-admin.js';\nimport {\n backfillOrgAdminGrants,\n extractMemberPairs,\n reconcileOrgAdminGrant,\n} from './auto-org-admin-grant.js';\nimport {\n securityObjects,\n securityDefaultPermissionSets,\n securityPluginManifestHeader,\n} from './manifest.js';\n\nexport interface SecurityPluginOptions {\n /**\n * Additional permission sets to register with the metadata service on\n * plugin start. Defaults to {@link securityDefaultPermissionSets}\n * (admin_full_access / member_default / viewer_readonly).\n */\n defaultPermissionSets?: PermissionSet[];\n /**\n * Permission set name applied as an implicit baseline whenever an\n * authenticated request has no resolved permission sets (and no roles\n * that map to one). This guarantees baseline tenant/owner RLS for\n * every logged-in user even before an admin assigns explicit\n * profiles. Set to `null` to disable.\n *\n * @default 'member_default'\n */\n fallbackPermissionSet?: string | null;\n}\n\n/**\n * SecurityPlugin\n *\n * Provides RBAC, Row-Level Security, and Field-Level Security runtime.\n * Registers as an engine middleware on the ObjectQL engine.\n *\n * This plugin is fully optional — without it, the system operates\n * without permission checks (same as current behavior).\n *\n * **Multi-tenant Organization scoping is provided by the separate\n * `@objectstack/plugin-org-scoping` package** (auto-stamps\n * `organization_id` on insert, per-org seed replay, default-org\n * bootstrap). When that plugin is installed, SecurityPlugin detects\n * it via `getService('org-scoping')` and keeps the wildcard\n * `current_user.organization_id` RLS policies that ship with the\n * default permission sets. Without it, those policies are stripped so\n * single-tenant deployments don't pay the field-existence safety-net\n * cost on every find.\n *\n * Dependencies:\n * - objectql service (ObjectQL engine with middleware support)\n * - metadata service (MetadataFacade for reading permission sets and RLS policies)\n */\nexport class SecurityPlugin implements Plugin {\n name = 'com.objectstack.security';\n type = 'standard';\n version = '1.0.0';\n dependencies = ['com.objectstack.engine.objectql'];\n\n private permissionEvaluator = new PermissionEvaluator();\n private rlsCompiler = new RLSCompiler();\n private fieldMasker = new FieldMasker();\n private readonly bootstrapPermissionSets: PermissionSet[];\n private readonly fallbackPermissionSet: string | null;\n /**\n * Runtime probe — set in `start()` from\n * `ctx.getService('org-scoping')`. When `false`, wildcard RLS\n * policies that reference `current_user.organization_id` are\n * stripped from the per-request policy set (saves the\n * field-existence safety net cost on every find in single-tenant\n * deployments). When `true`, the policies apply normally.\n */\n private orgScopingEnabled = false;\n /**\n * Per-object field-name cache. Populated lazily from the metadata\n * service / ObjectQL registry on first access per object. Schemas are\n * effectively immutable for the lifetime of the kernel today (hot\n * reload tears the kernel down), so we don't bother with\n * invalidation — a kernel restart drops the cache.\n */\n private readonly fieldNamesCache = new Map<string, Set<string> | null>();\n /**\n * Per-object cache of tenancy opt-out. `true` means the schema\n * explicitly disabled multi-tenancy (`tenancy.enabled === false` or\n * `systemFields.tenant === false`). Wildcard policies that target\n * the conventional tenant column (`organization_id`) are treated as\n * *not applicable* on these tables instead of triggering the\n * field-missing deny sentinel — without this, every read of a\n * cross-org catalog (e.g. `sys_package`, the Marketplace) returns\n * zero rows.\n */\n private readonly tenancyDisabledCache = new Map<string, boolean>();\n\n constructor(options: SecurityPluginOptions = {}) {\n this.bootstrapPermissionSets =\n options.defaultPermissionSets ?? securityDefaultPermissionSets;\n this.fallbackPermissionSet =\n options.fallbackPermissionSet === undefined\n ? 'member_default'\n : options.fallbackPermissionSet;\n }\n\n async init(ctx: PluginContext): Promise<void> {\n ctx.logger.info('Initializing Security Plugin...');\n\n // Register security services\n ctx.registerService('security.permissions', this.permissionEvaluator);\n ctx.registerService('security.rls', this.rlsCompiler);\n ctx.registerService('security.fieldMasker', this.fieldMasker);\n // Bootstrap permission sets (admin_full_access, member_default,\n // viewer_readonly by default) — exposed as a service so other\n // plugins (e.g. plugin-hono-server's /me/permissions endpoint)\n // can pass them as the fallback list to\n // `PermissionEvaluator.resolvePermissionSets` without re-importing\n // the platform-objects package directly.\n ctx.registerService('security.bootstrapPermissionSets', this.bootstrapPermissionSets);\n ctx.registerService('security.fallbackPermissionSet', this.fallbackPermissionSet);\n\n ctx.getService<{ register(m: any): void }>('manifest').register({\n ...securityPluginManifestHeader,\n objects: securityObjects,\n // Permission sets ride along on the manifest so the metadata service\n // can resolve them by name when SecurityPlugin middleware queries\n // `metadata.list('permissions')`.\n permissions: this.bootstrapPermissionSets,\n });\n\n ctx.logger.info('Security Plugin initialized', {\n defaultPermissionSets: this.bootstrapPermissionSets.map((p) => p.name),\n });\n }\n\n async start(ctx: PluginContext): Promise<void> {\n ctx.logger.info('Starting Security Plugin...');\n\n // Get required services\n let ql: any;\n let metadata: any;\n\n try {\n ql = ctx.getService('objectql');\n metadata = ctx.getService('metadata');\n } catch (e) {\n ctx.logger.warn('ObjectQL or metadata service not available, security middleware not registered');\n return;\n }\n\n if (!ql || typeof ql.registerMiddleware !== 'function') {\n ctx.logger.warn('ObjectQL engine does not support middleware, security middleware not registered');\n return;\n }\n\n // Probe for OrgScopingPlugin presence. When registered, its\n // `init()` exposes itself as the `org-scoping` service. We capture\n // the boolean once at start time (plugin DI graph is static after\n // start) and let `collectRLSPolicies` consult it on every request.\n try {\n const orgScoping = ctx.getService('org-scoping');\n this.orgScopingEnabled = !!orgScoping;\n } catch {\n this.orgScopingEnabled = false;\n }\n if (this.orgScopingEnabled) {\n ctx.logger.info(\n '[security] org-scoping plugin detected — wildcard `organization_id` RLS policies will apply',\n );\n } else {\n ctx.logger.info(\n '[security] org-scoping plugin not present — wildcard `organization_id` RLS policies will be stripped (single-tenant mode)',\n );\n }\n\n // Construct a dbLoader once that lets resolvePermissionSets\n // surface user-defined permission sets from `sys_permission_set`\n // (created via the admin UI) in addition to plugin-registered\n // ones. Uses `isSystem` to bypass tenant RLS.\n const dbLoader = ql\n ? async (names: string[]) => {\n let rows: any;\n try {\n rows = await ql.find(\n 'sys_permission_set',\n { where: { name: { $in: names } }, limit: names.length },\n { context: { isSystem: true } },\n );\n } catch {\n rows = [];\n }\n const list = Array.isArray(rows) ? rows : rows?.records ?? [];\n return list.map((r: any) => ({\n name: r.name,\n label: r.label,\n objects: typeof r.object_permissions === 'string'\n ? JSON.parse(r.object_permissions || '{}')\n : r.object_permissions ?? {},\n fields: typeof r.field_permissions === 'string'\n ? JSON.parse(r.field_permissions || '{}')\n : r.field_permissions ?? {},\n }));\n }\n : undefined;\n\n // Register security middleware\n ql.registerMiddleware(async (opCtx: any, next: () => Promise<void>) => {\n // System operations bypass security\n if (opCtx.context?.isSystem) {\n return next();\n }\n\n const roles = opCtx.context?.roles ?? [];\n const explicitPermissionSets = opCtx.context?.permissions ?? [];\n\n // Skip security checks if no roles AND no explicit permission sets\n // AND no userId (anonymous/unauthenticated). The auth middleware\n // should handle authentication separately.\n if (\n roles.length === 0 &&\n explicitPermissionSets.length === 0 &&\n !opCtx.context?.userId\n ) {\n return next();\n }\n\n // 1. Resolve permission sets from BOTH role names and explicit\n // permission set names attached to the execution context.\n let permissionSets: PermissionSet[] = [];\n try {\n const requested = [...roles, ...explicitPermissionSets];\n // Implicit baseline: when an authenticated request resolved zero\n // permission sets, fall back to the configured baseline (default\n // `member_default`). This guarantees tenant + owner RLS even\n // before an admin has assigned a profile/permission set.\n if (\n requested.length === 0 &&\n opCtx.context?.userId &&\n this.fallbackPermissionSet\n ) {\n requested.push(this.fallbackPermissionSet);\n }\n permissionSets = await this.permissionEvaluator.resolvePermissionSets(\n requested,\n metadata,\n this.bootstrapPermissionSets,\n dbLoader,\n );\n // **Post-resolution fallback** — closes the fail-open hole that\n // appears when a user's `roles` array is populated (e.g. a\n // better-auth `sys_member.role` like `owner`/`admin`/`member`)\n // but no `sys_role`→`sys_permission_set` binding exists yet, so\n // resolution returns an empty array. Without this, both the\n // CRUD check (`permissionSets.length > 0`) and the RLS injection\n // (`allRlsPolicies.length > 0`) below get skipped → the user\n // sees every tenant's data. Authenticated users with no\n // resolved permission sets always inherit the configured\n // baseline (default `member_default`, which carries\n // `tenant_isolation` + `owner_only_writes`).\n if (\n permissionSets.length === 0 &&\n opCtx.context?.userId &&\n this.fallbackPermissionSet\n ) {\n const fallback = await this.permissionEvaluator.resolvePermissionSets(\n [this.fallbackPermissionSet],\n metadata,\n this.bootstrapPermissionSets,\n dbLoader,\n );\n permissionSets = fallback;\n }\n } catch (e) {\n // If metadata service is misconfigured, log and continue without permission checks\n // rather than blocking all operations\n return next();\n }\n\n // 2. CRUD permission check\n if (permissionSets.length > 0) {\n const allowed = this.permissionEvaluator.checkObjectPermission(\n opCtx.operation,\n opCtx.object,\n permissionSets\n );\n\n if (!allowed) {\n throw new PermissionDeniedError(\n `[Security] Access denied: operation '${opCtx.operation}' on object '${opCtx.object}' ` +\n `is not permitted for roles [${roles.join(', ')}]`,\n { operation: opCtx.operation, object: opCtx.object, roles, permissionSets: explicitPermissionSets },\n );\n }\n }\n\n // 2.5. Field-Level Security write enforcement.\n //\n // The client-side masker (ObjectForm / inline grid) already hides\n // non-editable fields from the UI, but that is a UX layer only —\n // a hand-crafted POST / direct ObjectQL call can still target a\n // forbidden field. We fail-closed here with an explicit 403 and\n // the offending field names, so:\n //\n // - honest clients get an actionable error (vs. silent drop,\n // which manifests as a confusing partial-save), and\n // - probing clients see that the boundary is enforced (vs.\n // getting a 200 with the field silently ignored, which\n // reveals nothing).\n //\n // Runs BEFORE the tenant/owner auto-injection (step 3.5) so the\n // system-set fields are not subject to the user's edit\n // permissions — they are populated from the execution context,\n // not from the caller's payload.\n if (\n (opCtx.operation === 'insert' || opCtx.operation === 'update') &&\n opCtx.data &&\n permissionSets.length > 0\n ) {\n const fieldPerms = this.permissionEvaluator.getFieldPermissions(\n opCtx.object,\n permissionSets,\n );\n if (Object.keys(fieldPerms).length > 0) {\n const forbidden = this.fieldMasker.detectForbiddenWrites(\n opCtx.data,\n fieldPerms,\n );\n if (forbidden.length > 0) {\n throw new PermissionDeniedError(\n `[Security] Field write denied: not permitted to edit ` +\n `[${forbidden.join(', ')}] on '${opCtx.object}'`,\n {\n operation: opCtx.operation,\n object: opCtx.object,\n roles,\n permissionSets: explicitPermissionSets,\n forbiddenFields: forbidden,\n },\n );\n }\n }\n }\n\n // 3.5. Auto-inject `owner_id` on insert from the\n // ExecutionContext. Without this, the row has `owner_id = NULL`\n // and the default `owner_only_writes` RLS policy hides it from\n // the very user who just created it.\n //\n // `organization_id` auto-injection has moved to\n // `@objectstack/plugin-org-scoping`. Install that plugin for\n // multi-tenant deployments.\n if (\n opCtx.operation === 'insert' &&\n opCtx.data &&\n typeof opCtx.data === 'object' &&\n !Array.isArray(opCtx.data) &&\n !!opCtx.context?.userId\n ) {\n const fields = await this.getObjectFieldNames(metadata, opCtx.object, ql);\n if (fields) {\n const data = opCtx.data as Record<string, unknown>;\n if (\n fields.has('owner_id') &&\n (data.owner_id == null || data.owner_id === '')\n ) {\n data.owner_id = opCtx.context!.userId;\n }\n }\n }\n\n // 3. RLS filter injection\n const allRlsPolicies = this.collectRLSPolicies(permissionSets, opCtx.object, opCtx.operation);\n if (allRlsPolicies.length > 0 && opCtx.ast) {\n // Field-existence safety: wildcard policies (`object: '*'`) target\n // fields like `organization_id` that may not exist on every object\n // (e.g. system tables, CRM apps that haven't yet adopted multi-tenancy).\n //\n // We treat such policies as a *deny* contribution rather than dropping\n // them, so they fail-closed when no per-object policy provides an\n // alternate match. Any per-object policy that DOES compile against\n // the object will OR-combine and grant access (e.g. `sys_user_self`).\n // When the schema lookup itself fails we keep all policies (drivers\n // will surface column errors clearly during compilation).\n const objectFields = await this.getObjectFieldNames(metadata, opCtx.object, ql);\n const tenancyDisabled = this.tenancyDisabledCache.get(opCtx.object) === true;\n let dropped = 0;\n const compilable = objectFields\n ? allRlsPolicies.filter((p) => {\n const targetField = this.extractTargetField(p.using);\n if (!targetField) return true;\n if (objectFields.has(targetField)) return true;\n // Schema-level opt-out: when the object explicitly\n // disabled tenancy (`tenancy.enabled === false`), the\n // wildcard `tenant_isolation` policy targeting\n // `organization_id` was never meant to apply. Treat as\n // \"not applicable\" — skip silently without contributing\n // to the deny sentinel, mirroring how the registry skips\n // injecting the column itself for these tables.\n if (tenancyDisabled && targetField === 'organization_id') {\n return false;\n }\n dropped++;\n return false;\n })\n : allRlsPolicies;\n let rlsFilter = this.rlsCompiler.compileFilter(compilable, opCtx.context);\n // If every applicable policy was dropped because of missing fields,\n // contribute the deny sentinel (zero rows) — matches the rls-compiler\n // semantics for \"policies were applicable but none compiled\".\n if (rlsFilter == null && dropped > 0) {\n rlsFilter = { ...RLS_DENY_FILTER };\n }\n if (rlsFilter) {\n if (opCtx.ast.where) {\n opCtx.ast.where = { $and: [opCtx.ast.where, rlsFilter] };\n } else {\n opCtx.ast.where = rlsFilter;\n }\n }\n }\n\n await next();\n\n // 4. Field-level security: mask restricted fields in read results\n if (opCtx.result && ['find', 'findOne'].includes(opCtx.operation)) {\n const fieldPerms = this.permissionEvaluator.getFieldPermissions(opCtx.object, permissionSets);\n if (Object.keys(fieldPerms).length > 0) {\n opCtx.result = this.fieldMasker.maskResults(opCtx.result, fieldPerms, opCtx.object);\n }\n }\n });\n\n ctx.logger.info('Security middleware registered on ObjectQL engine');\n\n // Defer platform admin bootstrap until all plugins finish starting —\n // sys_user / sys_permission_set objects must be registered (by\n // plugin-auth and platform-objects respectively) before we can\n // insert seed rows. Falls back to immediate execution when the\n // kernel does not expose `hook` (test stubs).\n let bootstrapRanOnce = false;\n const runBootstrap = async () => {\n try {\n const report = await bootstrapPlatformAdmin(ql, this.bootstrapPermissionSets, {\n logger: ctx.logger,\n });\n bootstrapRanOnce = true;\n ctx.logger.info('[security] platform bootstrap complete', report);\n return report;\n } catch (e) {\n ctx.logger.warn('[security] platform bootstrap failed', { error: (e as Error).message });\n return undefined;\n }\n };\n if (typeof (ctx as any).hook === 'function') {\n (ctx as any).hook('kernel:ready', runBootstrap);\n } else {\n void runBootstrap();\n }\n\n // Re-run bootstrap after a sys_user insert so the FIRST user that\n // signs up after boot is auto-promoted to platform admin (and, in\n // multi-tenant mode, bound to the seeded default organization)\n // without requiring a server restart. The function itself is\n // idempotent and bails out as soon as any platform admin exists.\n //\n // We deliberately do NOT auto-create a \"personal workspace\" for\n // every subsequent self-service signup. In a B2B / invitation-\n // driven product (the framework's primary target), users must\n // either accept an invitation or explicitly create their first\n // organization. The account UI's /register flow already routes\n // users with zero memberships to /organizations/new for exactly\n // this case.\n ql.registerMiddleware(async (opCtx: any, next: () => Promise<void>) => {\n await next();\n if (\n opCtx?.object === 'sys_user' &&\n (opCtx?.operation === 'create' || opCtx?.operation === 'insert')\n ) {\n if (bootstrapRanOnce) {\n await runBootstrap();\n }\n }\n });\n\n // ── Auto-grant `organization_admin` on sys_member lifecycle ─────────\n //\n // For every `sys_member` row whose role is `owner` or `admin`, keep\n // a `sys_user_permission_set` row scoped to that organization in\n // sync. See `auto-org-admin-grant.ts` for the full rationale and\n // the anti-escalation argument (org_admin is read-only on the\n // global RBAC tables, so a freshly-granted admin cannot rebind\n // themselves to `admin_full_access`).\n //\n // We register one middleware that handles insert / update / delete\n // uniformly by always reconciling every (user, org) pair touched\n // by the operation. `reconcileOrgAdminGrant` is idempotent so a\n // double-fire (e.g. better-auth followed by an org plugin\n // synchronizer) is harmless.\n ql.registerMiddleware(async (opCtx: any, next: () => Promise<void>) => {\n await next();\n if (opCtx?.object !== 'sys_member') return;\n const op = opCtx?.operation;\n if (\n op !== 'insert' &&\n op !== 'create' &&\n op !== 'update' &&\n op !== 'delete' &&\n op !== 'remove'\n ) {\n return;\n }\n const pairs = extractMemberPairs(opCtx);\n for (const { userId, orgId } of pairs) {\n try {\n await reconcileOrgAdminGrant(ql, userId, orgId, { logger: ctx.logger });\n } catch (e) {\n ctx.logger.warn?.('[security] org_admin reconcile failed', {\n userId,\n orgId,\n error: (e as Error).message,\n });\n }\n }\n });\n\n // Backfill organization_admin grants after the platform admin\n // bootstrap settles on kernel:ready. Idempotent — only inserts\n // missing rows and revokes orphaned ones, never duplicates.\n const runOrgAdminBackfill = async () => {\n try {\n await backfillOrgAdminGrants(ql, { logger: ctx.logger });\n } catch (e) {\n ctx.logger.warn?.('[security] organization_admin backfill failed', {\n error: (e as Error).message,\n });\n }\n };\n if (typeof (ctx as any).hook === 'function') {\n (ctx as any).hook('kernel:ready', runOrgAdminBackfill);\n } else {\n void runOrgAdminBackfill();\n }\n\n // Per-organization seed data replay on `sys_organization` insert\n // moved to `@objectstack/plugin-org-scoping` (along with\n // `claimOrphanOrgRows` / `cloneOrgSeedData`). Install that\n // plugin for multi-tenant deployments.\n }\n\n async destroy(): Promise<void> {\n // No cleanup needed\n }\n\n /**\n * Collect all RLS policies from permission sets applicable to the given object/operation.\n */\n private collectRLSPolicies(\n permissionSets: PermissionSet[],\n objectName: string,\n operation: string\n ): RowLevelSecurityPolicy[] {\n const allPolicies: RowLevelSecurityPolicy[] = [];\n\n for (const ps of permissionSets) {\n if (ps.rowLevelSecurity) {\n for (const policy of ps.rowLevelSecurity) {\n // When the org-scoping plugin is NOT installed, strip any\n // policy that filters on `current_user.organization_id` —\n // there is no meaningful tenant to compare against, so the\n // policy would either drop every row (when the field exists\n // on the object) or be dropped by the field-existence safety\n // net. Either way it's pure overhead. Substring match is\n // sufficient: every wildcard tenant policy in the default\n // permission sets uses exactly this token. Install\n // `@objectstack/plugin-org-scoping` to enable the\n // multi-tenant behavior.\n if (\n !this.orgScopingEnabled &&\n policy.using &&\n policy.using.includes('current_user.organization_id')\n ) {\n continue;\n }\n allPolicies.push(policy);\n }\n }\n }\n\n return this.rlsCompiler.getApplicablePolicies(objectName, operation, allPolicies);\n }\n\n /**\n * Resolve the column-name set for an object (lowercased). Returns\n * `null` if the schema can't be loaded — caller should fail-closed.\n */\n private async getObjectFieldNames(\n metadata: any,\n objectName: string,\n ql?: any,\n ): Promise<Set<string> | null> {\n if (this.fieldNamesCache.has(objectName)) {\n return this.fieldNamesCache.get(objectName) ?? null;\n }\n const result = await this.loadObjectFieldNames(metadata, objectName, ql);\n // Only cache positive resolutions — a `null` may simply mean the\n // schema isn't registered yet at boot, and we want subsequent calls\n // to retry rather than be permanently denied.\n if (result) {\n this.fieldNamesCache.set(objectName, result);\n }\n return result;\n }\n\n private async loadObjectFieldNames(\n metadata: any,\n objectName: string,\n ql?: any,\n ): Promise<Set<string> | null> {\n try {\n // Prefer ObjectQL's per-engine SchemaRegistry as the source of truth\n // for the live field set: it reflects registry-time augmentations\n // (system-field auto-injection like `organization_id`) that the\n // standalone metadata artifact loaded at boot may not include.\n // Fall back to the metadata service for objects ObjectQL doesn't\n // know about (system tables registered through other paths).\n let obj: any = typeof ql?.getSchema === 'function' ? ql.getSchema(objectName) : null;\n if (!obj || !obj.fields) {\n obj = await metadata?.get?.('object', objectName);\n }\n if (!obj || !obj.fields) return null;\n // Populate the tenancy opt-out cache alongside the field set so\n // the RLS filter pass can decide whether a wildcard\n // `organization_id` policy is genuinely \"applicable but\n // uncompilable\" (deny) versus \"not applicable on this object\"\n // (skip without contributing to the deny sentinel).\n const tenancyDisabled =\n (obj as any)?.tenancy?.enabled === false ||\n (obj as any)?.systemFields?.tenant === false;\n this.tenancyDisabledCache.set(objectName, !!tenancyDisabled);\n const set = new Set<string>(['id']);\n if (Array.isArray(obj.fields)) {\n for (const f of obj.fields) {\n if (f?.name) set.add(String(f.name));\n }\n } else if (typeof obj.fields === 'object') {\n for (const key of Object.keys(obj.fields)) {\n set.add(key);\n const v = (obj.fields as Record<string, any>)[key];\n if (v && typeof v === 'object' && v.name) set.add(String(v.name));\n }\n } else {\n return null;\n }\n return set;\n } catch {\n return null;\n }\n }\n\n /**\n * Extract the left-hand field name from a simple RLS expression like\n * `field = current_user.x` or `field IN (current_user.y)`. Returns\n * `null` for unsupported shapes (in which case we keep the policy).\n */\n private extractTargetField(using?: string): string | null {\n if (!using) return null;\n // Match `field =` or `field IN`/`in`. Note: `\\b` is omitted after `=`\n // because `=` is non-word and the next char (space) is non-word too —\n // a word boundary cannot exist between two non-word chars, so `=\\b`\n // would never match. We instead require the alternation token to be\n // followed by whitespace or `(`.\n const m = using.match(/^\\s*([a-z_][a-z0-9_]*)\\s*(?:=|IN|in)(?=\\s|\\()/);\n return m ? m[1] : null;\n }\n}\n"],"mappings":"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","names":["SYSTEM_CTX","genId","tryFind","tryInsert"]}
1
+ {"version":3,"sources":["../src/translations/en.objects.generated.ts","../src/translations/zh-CN.objects.generated.ts","../src/translations/ja-JP.objects.generated.ts","../src/translations/es-ES.objects.generated.ts","../src/translations/index.ts","../src/index.ts","../src/permission-evaluator.ts","../src/rls-compiler.ts","../src/field-masker.ts","../src/errors.ts","../src/bootstrap-platform-admin.ts","../src/auto-org-admin-grant.ts","../src/objects/sys-role.object.ts","../src/objects/sys-permission-set.object.ts","../src/objects/sys-user-permission-set.object.ts","../src/objects/sys-role-permission-set.object.ts","../src/objects/default-permission-sets.ts","../src/manifest.ts","../src/security-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Auto-generated by 'os i18n extract' for locale 'en'.\n * Edit translations in place; re-run extract (with --merge) to fill new gaps.\n * Do not hand-edit the structure — only the leaf string values.\n */\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\nexport const enObjects: NonNullable<TranslationData['objects']> = {\n sys_role: {\n label: \"Role\",\n pluralLabel: \"Roles\",\n description: \"Role definitions for RBAC access control\",\n fields: {\n label: {\n label: \"Display Name\"\n },\n name: {\n label: \"API Name\",\n help: \"Unique machine name for the role (e.g. admin, editor, viewer)\"\n },\n description: {\n label: \"Description\"\n },\n permissions: {\n label: \"Permissions\",\n help: \"JSON-serialized array of permission strings\"\n },\n active: {\n label: \"Active\"\n },\n is_default: {\n label: \"Default Role\",\n help: \"Automatically assigned to new users\"\n },\n id: {\n label: \"Role ID\"\n },\n created_at: {\n label: \"Created At\"\n },\n updated_at: {\n label: \"Updated At\"\n }\n },\n _views: {\n active: {\n label: \"Active\"\n },\n default_roles: {\n label: \"Default\"\n },\n custom: {\n label: \"Custom\"\n },\n all_roles: {\n label: \"All\"\n }\n },\n _actions: {\n activate_role: {\n label: \"Activate Role\",\n successMessage: \"Role activated\"\n },\n deactivate_role: {\n label: \"Deactivate Role\",\n confirmText: \"Deactivate this role? Users with the role keep their assignment but the role stops granting permissions until re-activated.\",\n successMessage: \"Role deactivated\"\n },\n set_default_role: {\n label: \"Set as Default\",\n confirmText: \"Make this the default role for new users? Existing users are unaffected.\",\n successMessage: \"Default role updated\"\n },\n clone_role: {\n label: \"Clone Role\",\n successMessage: \"Role cloned\"\n }\n }\n },\n sys_permission_set: {\n label: \"Permission Set\",\n pluralLabel: \"Permission Sets\",\n description: \"Named permission groupings for fine-grained access control\",\n fields: {\n label: {\n label: \"Display Name\"\n },\n name: {\n label: \"API Name\",\n help: \"Unique machine name for the permission set\"\n },\n description: {\n label: \"Description\"\n },\n object_permissions: {\n label: \"Object Permissions\",\n help: \"JSON-serialized object-level CRUD permissions\"\n },\n field_permissions: {\n label: \"Field Permissions\",\n help: \"JSON-serialized field-level read/write permissions\"\n },\n system_permissions: {\n label: \"System Permissions\",\n help: \"JSON-serialized array of system capability names (e.g. [\\\"setup.access\\\",\\\"studio.access\\\",\\\"manage_users\\\"])\"\n },\n row_level_security: {\n label: \"Row-Level Security\",\n help: \"JSON-serialized array of row-level security policies (USING/CHECK clauses)\"\n },\n tab_permissions: {\n label: \"Tab Permissions\",\n help: \"JSON-serialized map of app tab visibility (visible | hidden | default_on | default_off)\"\n },\n active: {\n label: \"Active\"\n },\n id: {\n label: \"Permission Set ID\"\n },\n created_at: {\n label: \"Created At\"\n },\n updated_at: {\n label: \"Updated At\"\n }\n },\n _views: {\n active: {\n label: \"Active\"\n },\n inactive: {\n label: \"Inactive\"\n },\n all_permsets: {\n label: \"All\"\n }\n },\n _actions: {\n activate_permission_set: {\n label: \"Activate\",\n successMessage: \"Permission set activated\"\n },\n deactivate_permission_set: {\n label: \"Deactivate\",\n confirmText: \"Deactivate this permission set? Existing assignments stay in place but stop granting access until re-activated.\",\n successMessage: \"Permission set deactivated\"\n },\n clone_permission_set: {\n label: \"Clone\",\n successMessage: \"Permission set cloned\"\n }\n }\n },\n sys_user_permission_set: {\n label: \"User Permission Set\",\n pluralLabel: \"User Permission Sets\",\n description: \"Direct assignment of a permission set to a user (optionally scoped to an organization).\",\n fields: {\n id: {\n label: \"Assignment ID\",\n help: \"UUID of the assignment.\"\n },\n user_id: {\n label: \"User\",\n help: \"Foreign key to sys_user.\"\n },\n permission_set_id: {\n label: \"Permission Set\",\n help: \"Foreign key to sys_permission_set.\"\n },\n organization_id: {\n label: \"Organization\",\n help: \"Optional organization scope. NULL = applies in every org context.\"\n },\n granted_by: {\n label: \"Granted By\",\n help: \"User who granted this permission set.\"\n },\n created_at: {\n label: \"Created At\"\n },\n updated_at: {\n label: \"Updated At\"\n }\n }\n },\n sys_role_permission_set: {\n label: \"Role Permission Set\",\n pluralLabel: \"Role Permission Sets\",\n description: \"Binds a permission set to a role.\",\n fields: {\n id: {\n label: \"Binding ID\",\n help: \"UUID of the role-permission-set binding.\"\n },\n role_id: {\n label: \"Role\",\n help: \"Foreign key to sys_role.\"\n },\n permission_set_id: {\n label: \"Permission Set\",\n help: \"Foreign key to sys_permission_set.\"\n },\n created_at: {\n label: \"Created At\"\n },\n updated_at: {\n label: \"Updated At\"\n }\n }\n }\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Auto-generated by 'os i18n extract' for locale 'zh-CN'.\n * Edit translations in place; re-run extract (with --merge) to fill new gaps.\n * Do not hand-edit the structure — only the leaf string values.\n */\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\nexport const zhCNObjects: NonNullable<TranslationData['objects']> = {\n sys_role: {\n label: \"角色\",\n pluralLabel: \"角色\",\n description: \"用于 RBAC 访问控制的角色定义\",\n fields: {\n label: {\n label: \"显示名称\"\n },\n name: {\n label: \"API 名称\",\n help: \"角色的唯一机器名称(例如 admin、editor、viewer)\"\n },\n description: {\n label: \"描述\"\n },\n permissions: {\n label: \"权限\",\n help: \"权限字符串数组的 JSON 序列化内容\"\n },\n active: {\n label: \"启用\"\n },\n is_default: {\n label: \"默认角色\",\n help: \"自动分配给新用户\"\n },\n id: {\n label: \"角色 ID\"\n },\n created_at: {\n label: \"创建时间\"\n },\n updated_at: {\n label: \"更新时间\"\n }\n },\n _views: {\n active: {\n label: \"启用\"\n },\n default_roles: {\n label: \"默认\"\n },\n custom: {\n label: \"自定义\"\n },\n all_roles: {\n label: \"全部\"\n }\n },\n _actions: {\n activate_role: {\n label: \"激活角色\",\n successMessage: \"角色已激活\"\n },\n deactivate_role: {\n label: \"停用角色\",\n confirmText: \"确定要停用此角色吗?拥有该角色的用户仍保留其分配,但在重新激活之前该角色将不再授予权限。\",\n successMessage: \"角色已停用\"\n },\n set_default_role: {\n label: \"设为默认\",\n confirmText: \"将此角色设为新用户的默认角色吗?现有用户不受影响。\",\n successMessage: \"已更新默认角色\"\n },\n clone_role: {\n label: \"克隆角色\",\n successMessage: \"已克隆角色\"\n }\n }\n },\n sys_permission_set: {\n label: \"权限集\",\n pluralLabel: \"权限集\",\n description: \"用于精细化访问控制的命名权限分组\",\n fields: {\n label: {\n label: \"显示名称\"\n },\n name: {\n label: \"API 名称\",\n help: \"权限集的唯一机器名称\"\n },\n description: {\n label: \"描述\"\n },\n object_permissions: {\n label: \"对象权限\",\n help: \"对象级 CRUD 权限的 JSON 序列化内容\"\n },\n field_permissions: {\n label: \"字段权限\",\n help: \"字段级读写权限的 JSON 序列化内容\"\n },\n system_permissions: {\n label: \"系统权限\",\n help: \"系统能力名称的 JSON 序列化数组(例如 [\\\"setup.access\\\",\\\"studio.access\\\",\\\"manage_users\\\"])\"\n },\n row_level_security: {\n label: \"行级安全\",\n help: \"行级安全策略的 JSON 序列化数组(USING/CHECK 子句)\"\n },\n tab_permissions: {\n label: \"标签页权限\",\n help: \"应用标签页可见性的 JSON 序列化映射(visible | hidden | default_on | default_off)\"\n },\n active: {\n label: \"启用\"\n },\n id: {\n label: \"权限集 ID\"\n },\n created_at: {\n label: \"创建时间\"\n },\n updated_at: {\n label: \"更新时间\"\n }\n },\n _views: {\n active: {\n label: \"启用\"\n },\n inactive: {\n label: \"停用\"\n },\n all_permsets: {\n label: \"全部\"\n }\n },\n _actions: {\n activate_permission_set: {\n label: \"激活\",\n successMessage: \"权限集已激活\"\n },\n deactivate_permission_set: {\n label: \"停用\",\n confirmText: \"确定要停用此权限集吗?现有分配仍将保留,但在重新激活之前将不再授予访问权限。\",\n successMessage: \"权限集已停用\"\n },\n clone_permission_set: {\n label: \"克隆\",\n successMessage: \"已克隆权限集\"\n }\n }\n },\n sys_user_permission_set: {\n label: \"用户权限集\",\n pluralLabel: \"用户权限集\",\n description: \"将权限集直接分配给用户(可按组织范围限定)。\",\n fields: {\n id: {\n label: \"分配 ID\",\n help: \"该分配记录的 UUID。\"\n },\n user_id: {\n label: \"用户\",\n help: \"指向 sys_user 的外键。\"\n },\n permission_set_id: {\n label: \"权限集\",\n help: \"指向 sys_permission_set 的外键。\"\n },\n organization_id: {\n label: \"组织\",\n help: \"可选的组织范围。NULL = 在所有组织上下文中都生效。\"\n },\n granted_by: {\n label: \"授权人\",\n help: \"授予该权限集的用户。\"\n },\n created_at: {\n label: \"创建时间\"\n },\n updated_at: {\n label: \"更新时间\"\n }\n }\n },\n sys_role_permission_set: {\n label: \"角色权限集\",\n pluralLabel: \"角色权限集\",\n description: \"将权限集绑定到角色。\",\n fields: {\n id: {\n label: \"绑定 ID\",\n help: \"角色-权限集绑定记录的 UUID。\"\n },\n role_id: {\n label: \"角色\",\n help: \"指向 sys_role 的外键。\"\n },\n permission_set_id: {\n label: \"权限集\",\n help: \"指向 sys_permission_set 的外键。\"\n },\n created_at: {\n label: \"创建时间\"\n },\n updated_at: {\n label: \"更新时间\"\n }\n }\n }\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Auto-generated by 'os i18n extract' for locale 'ja-JP'.\n * Edit translations in place; re-run extract (with --merge) to fill new gaps.\n * Do not hand-edit the structure — only the leaf string values.\n */\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\nexport const jaJPObjects: NonNullable<TranslationData['objects']> = {\n sys_role: {\n label: \"ロール\",\n pluralLabel: \"ロール\",\n description: \"RBAC アクセス制御のためのロール定義\",\n fields: {\n label: {\n label: \"表示名\"\n },\n name: {\n label: \"API 名\",\n help: \"ロールの一意の機械名(例: admin、editor、viewer)\"\n },\n description: {\n label: \"説明\"\n },\n permissions: {\n label: \"権限\",\n help: \"権限文字列の JSON シリアライズ配列\"\n },\n active: {\n label: \"有効\"\n },\n is_default: {\n label: \"デフォルトロール\",\n help: \"新規ユーザーに自動的に割り当てられます\"\n },\n id: {\n label: \"ロール ID\"\n },\n created_at: {\n label: \"作成日時\"\n },\n updated_at: {\n label: \"更新日時\"\n }\n },\n _views: {\n active: {\n label: \"有効\"\n },\n default_roles: {\n label: \"デフォルト\"\n },\n custom: {\n label: \"カスタム\"\n },\n all_roles: {\n label: \"すべて\"\n }\n },\n _actions: {\n activate_role: {\n label: \"ロールを有効化\",\n successMessage: \"ロールが有効化されました\"\n },\n deactivate_role: {\n label: \"ロールを無効化\",\n confirmText: \"このロールを無効化しますか?このロールを持つユーザーの割り当ては維持されますが、再度有効化するまで権限の付与は停止されます。\",\n successMessage: \"ロールが無効化されました\"\n },\n set_default_role: {\n label: \"デフォルトに設定\",\n confirmText: \"このロールを新規ユーザーのデフォルトロールにしますか?既存のユーザーには影響しません。\",\n successMessage: \"デフォルトロールを更新しました\"\n },\n clone_role: {\n label: \"ロールを複製\",\n successMessage: \"ロールを複製しました\"\n }\n }\n },\n sys_permission_set: {\n label: \"権限セット\",\n pluralLabel: \"権限セット\",\n description: \"細かいアクセス制御のための権限グループ\",\n fields: {\n label: {\n label: \"表示名\"\n },\n name: {\n label: \"API 名\",\n help: \"権限セットの一意の機械名\"\n },\n description: {\n label: \"説明\"\n },\n object_permissions: {\n label: \"オブジェクト権限\",\n help: \"JSON シリアライズされたオブジェクトレベルの CRUD 権限\"\n },\n field_permissions: {\n label: \"フィールド権限\",\n help: \"JSON シリアライズされたフィールドレベルの読み取り/書き込み権限\"\n },\n system_permissions: {\n label: \"システム権限\",\n help: \"システムケーパビリティ名のJSONシリアライズ配列(例: [\\\"setup.access\\\",\\\"studio.access\\\",\\\"manage_users\\\"])\"\n },\n row_level_security: {\n label: \"行レベルセキュリティ\",\n help: \"行レベルセキュリティポリシーのJSONシリアライズ配列(USING/CHECK 句)\"\n },\n tab_permissions: {\n label: \"タブ権限\",\n help: \"アプリのタブ表示のJSONシリアライズマップ(visible | hidden | default_on | default_off)\"\n },\n active: {\n label: \"有効\"\n },\n id: {\n label: \"権限セット ID\"\n },\n created_at: {\n label: \"作成日時\"\n },\n updated_at: {\n label: \"更新日時\"\n }\n },\n _views: {\n active: {\n label: \"有効\"\n },\n inactive: {\n label: \"無効\"\n },\n all_permsets: {\n label: \"すべて\"\n }\n },\n _actions: {\n activate_permission_set: {\n label: \"有効化\",\n successMessage: \"権限セットが有効化されました\"\n },\n deactivate_permission_set: {\n label: \"無効化\",\n confirmText: \"この権限セットを無効化しますか?既存の割り当ては維持されますが、再度有効化するまでアクセスの付与は停止されます。\",\n successMessage: \"権限セットが無効化されました\"\n },\n clone_permission_set: {\n label: \"複製\",\n successMessage: \"権限セットを複製しました\"\n }\n }\n },\n sys_user_permission_set: {\n label: \"ユーザー権限セット\",\n pluralLabel: \"ユーザー権限セット\",\n description: \"ユーザーへの権限セットの直接割り当て(組織スコープ可能)。\",\n fields: {\n id: {\n label: \"割り当て ID\",\n help: \"割り当ての UUID。\"\n },\n user_id: {\n label: \"ユーザー\",\n help: \"sys_user への外部キー。\"\n },\n permission_set_id: {\n label: \"権限セット\",\n help: \"sys_permission_set への外部キー。\"\n },\n organization_id: {\n label: \"組織\",\n help: \"オプションの組織スコープ。NULL = すべての組織コンテキストで適用。\"\n },\n granted_by: {\n label: \"付与者\",\n help: \"この権限セットを付与したユーザー。\"\n },\n created_at: {\n label: \"作成日時\"\n },\n updated_at: {\n label: \"更新日時\"\n }\n }\n },\n sys_role_permission_set: {\n label: \"ロール権限セット\",\n pluralLabel: \"ロール権限セット\",\n description: \"権限セットをロールにバインドします。\",\n fields: {\n id: {\n label: \"バインド ID\",\n help: \"ロール権限セットバインドの UUID。\"\n },\n role_id: {\n label: \"ロール\",\n help: \"sys_role への外部キー。\"\n },\n permission_set_id: {\n label: \"権限セット\",\n help: \"sys_permission_set への外部キー。\"\n },\n created_at: {\n label: \"作成日時\"\n },\n updated_at: {\n label: \"更新日時\"\n }\n }\n }\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Auto-generated by 'os i18n extract' for locale 'es-ES'.\n * Edit translations in place; re-run extract (with --merge) to fill new gaps.\n * Do not hand-edit the structure — only the leaf string values.\n */\n\nimport type { TranslationData } from '@objectstack/spec/system';\n\nexport const esESObjects: NonNullable<TranslationData['objects']> = {\n sys_role: {\n label: \"Rol\",\n pluralLabel: \"Roles\",\n description: \"Definiciones de rol para el control de acceso RBAC\",\n fields: {\n label: {\n label: \"Nombre visible\"\n },\n name: {\n label: \"Nombre de API\",\n help: \"Nombre técnico único del rol (p. ej. admin, editor, viewer).\"\n },\n description: {\n label: \"Descripción\"\n },\n permissions: {\n label: \"Permisos\",\n help: \"Matriz serializada en JSON de cadenas de permisos.\"\n },\n active: {\n label: \"Activo\"\n },\n is_default: {\n label: \"Rol predeterminado\",\n help: \"Se asigna automáticamente a los nuevos usuarios.\"\n },\n id: {\n label: \"ID de rol\"\n },\n created_at: {\n label: \"Creado el\"\n },\n updated_at: {\n label: \"Actualizado el\"\n }\n },\n _views: {\n active: {\n label: \"Activo\"\n },\n default_roles: {\n label: \"Predeterminado\"\n },\n custom: {\n label: \"Personalizado\"\n },\n all_roles: {\n label: \"Todos\"\n }\n },\n _actions: {\n activate_role: {\n label: \"Activar rol\",\n successMessage: \"Rol activado\"\n },\n deactivate_role: {\n label: \"Desactivar rol\",\n confirmText: \"¿Desactivar este rol? Los usuarios con el rol conservan su asignación, pero el rol deja de otorgar permisos hasta que se vuelva a activar.\",\n successMessage: \"Rol desactivado\"\n },\n set_default_role: {\n label: \"Establecer como predeterminado\",\n confirmText: \"¿Convertir este en el rol predeterminado para los nuevos usuarios? Los usuarios existentes no se ven afectados.\",\n successMessage: \"Rol predeterminado actualizado\"\n },\n clone_role: {\n label: \"Clonar rol\",\n successMessage: \"Rol clonado\"\n }\n }\n },\n sys_permission_set: {\n label: \"Conjunto de permisos\",\n pluralLabel: \"Conjuntos de permisos\",\n description: \"Agrupaciones de permisos con nombre para un control de acceso detallado\",\n fields: {\n label: {\n label: \"Nombre visible\"\n },\n name: {\n label: \"Nombre de API\",\n help: \"Nombre técnico único del conjunto de permisos.\"\n },\n description: {\n label: \"Descripción\"\n },\n object_permissions: {\n label: \"Permisos de objeto\",\n help: \"Permisos CRUD a nivel de objeto serializados en JSON.\"\n },\n field_permissions: {\n label: \"Permisos de campo\",\n help: \"Permisos de lectura/escritura a nivel de campo serializados en JSON.\"\n },\n system_permissions: {\n label: \"Permisos del sistema\",\n help: \"Array serializado en JSON de nombres de capacidades del sistema (p. ej. [\\\"setup.access\\\",\\\"studio.access\\\",\\\"manage_users\\\"])\"\n },\n row_level_security: {\n label: \"Seguridad a nivel de fila\",\n help: \"Array serializado en JSON de políticas de seguridad a nivel de fila (cláusulas USING/CHECK)\"\n },\n tab_permissions: {\n label: \"Permisos de pestañas\",\n help: \"Mapa serializado en JSON de la visibilidad de las pestañas de la app (visible | hidden | default_on | default_off)\"\n },\n active: {\n label: \"Activo\"\n },\n id: {\n label: \"ID de conjunto de permisos\"\n },\n created_at: {\n label: \"Creado el\"\n },\n updated_at: {\n label: \"Actualizado el\"\n }\n },\n _views: {\n active: {\n label: \"Activo\"\n },\n inactive: {\n label: \"Inactivo\"\n },\n all_permsets: {\n label: \"Todos\"\n }\n },\n _actions: {\n activate_permission_set: {\n label: \"Activar\",\n successMessage: \"Conjunto de permisos activado\"\n },\n deactivate_permission_set: {\n label: \"Desactivar\",\n confirmText: \"¿Desactivar este conjunto de permisos? Las asignaciones existentes se mantienen, pero dejan de otorgar acceso hasta que se vuelva a activar.\",\n successMessage: \"Conjunto de permisos desactivado\"\n },\n clone_permission_set: {\n label: \"Clonar\",\n successMessage: \"Conjunto de permisos clonado\"\n }\n }\n },\n sys_user_permission_set: {\n label: \"Conjunto de permisos de usuario\",\n pluralLabel: \"Conjuntos de permisos de usuario\",\n description: \"Asignación directa de un conjunto de permisos a un usuario (opcionalmente con ámbito de organización).\",\n fields: {\n id: {\n label: \"ID de asignación\",\n help: \"UUID de la asignación.\"\n },\n user_id: {\n label: \"Usuario\",\n help: \"Clave foránea a sys_user.\"\n },\n permission_set_id: {\n label: \"Conjunto de permisos\",\n help: \"Clave foránea a sys_permission_set.\"\n },\n organization_id: {\n label: \"Organización\",\n help: \"Ámbito de organización opcional. NULL = se aplica en cualquier contexto de organización.\"\n },\n granted_by: {\n label: \"Concedido por\",\n help: \"Usuario que concedió este conjunto de permisos.\"\n },\n created_at: {\n label: \"Creado el\"\n },\n updated_at: {\n label: \"Actualizado el\"\n }\n }\n },\n sys_role_permission_set: {\n label: \"Conjunto de permisos de rol\",\n pluralLabel: \"Conjuntos de permisos de rol\",\n description: \"Vincula un conjunto de permisos a un rol.\",\n fields: {\n id: {\n label: \"ID de vinculación\",\n help: \"UUID de la vinculación rol-conjunto de permisos.\"\n },\n role_id: {\n label: \"Rol\",\n help: \"Clave foránea a sys_role.\"\n },\n permission_set_id: {\n label: \"Conjunto de permisos\",\n help: \"Clave foránea a sys_permission_set.\"\n },\n created_at: {\n label: \"Creado el\"\n },\n updated_at: {\n label: \"Actualizado el\"\n }\n }\n }\n};\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * SecurityTranslations — i18n bundle owned by this plugin (ADR-0029 D8).\n *\n * Object label/field/view/action translations for the sys_* objects this\n * plugin owns. Loaded at runtime via the plugin's `kernel:ready` hook\n * (`i18n.loadTranslations`). Regenerate with `os i18n extract` against\n * `scripts/i18n-extract.config.ts`.\n */\n\nimport type { TranslationBundle } from '@objectstack/spec/system';\nimport { enObjects } from './en.objects.generated.js';\nimport { zhCNObjects } from './zh-CN.objects.generated.js';\nimport { jaJPObjects } from './ja-JP.objects.generated.js';\nimport { esESObjects } from './es-ES.objects.generated.js';\n\nexport const SecurityTranslations: TranslationBundle = {\n en: { objects: enObjects },\n 'zh-CN': { objects: zhCNObjects },\n 'ja-JP': { objects: jaJPObjects },\n 'es-ES': { objects: esESObjects },\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * @objectstack/plugin-security\n * \n * Security Plugin for ObjectStack\n * Provides RBAC, Row-Level Security (RLS), and Field-Level Security runtime.\n */\n\nexport { SecurityPlugin } from './security-plugin.js';\nexport { PermissionEvaluator } from './permission-evaluator.js';\nexport { RLSCompiler, RLS_DENY_FILTER } from './rls-compiler.js';\nexport { FieldMasker } from './field-masker.js';\nexport { PermissionDeniedError, isPermissionDeniedError } from './errors.js';\nexport {\n securityObjects,\n securityDefaultPermissionSets,\n securityPluginManifestHeader,\n SECURITY_PLUGIN_ID,\n SECURITY_PLUGIN_VERSION,\n} from './manifest.js';\nexport {\n reconcileOrgAdminGrant,\n backfillOrgAdminGrants,\n} from './auto-org-admin-grant.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PermissionSet, ObjectPermission, FieldPermission } from '@objectstack/spec/security';\n\n/**\n * Operation type mapping to permission checks\n */\nconst OPERATION_TO_PERMISSION: Record<string, keyof ObjectPermission> = {\n find: 'allowRead',\n findOne: 'allowRead',\n count: 'allowRead',\n aggregate: 'allowRead',\n insert: 'allowCreate',\n update: 'allowEdit',\n delete: 'allowDelete',\n};\n\n/**\n * PermissionEvaluator\n * \n * Runtime evaluator for PermissionSet definitions.\n * Resolves aggregated permissions from roles to concrete allow/deny decisions.\n */\nexport class PermissionEvaluator {\n /**\n * Check if an operation is allowed on an object for the given permission sets.\n * Uses \"most permissive\" merging: if ANY permission set allows, it's allowed.\n */\n checkObjectPermission(\n operation: string,\n objectName: string,\n permissionSets: PermissionSet[]\n ): boolean {\n const permKey = OPERATION_TO_PERMISSION[operation];\n if (!permKey) return true; // Unknown operations are allowed by default\n\n for (const ps of permissionSets) {\n // Honour the `'*'` wildcard sentinel — admin permission sets typically\n // grant blanket access via a single `objects: { '*': … }` entry rather\n // than enumerating every system object.\n const objPerm = ps.objects?.[objectName] ?? ps.objects?.['*'];\n if (objPerm) {\n // Check if modifyAllRecords is set (super-user bypass for write ops)\n if (['allowEdit', 'allowDelete'].includes(permKey) && objPerm.modifyAllRecords) {\n return true;\n }\n // Check if viewAllRecords is set (super-user bypass for read ops)\n if (permKey === 'allowRead' && (objPerm.viewAllRecords || objPerm.modifyAllRecords)) {\n return true;\n }\n // Check the specific permission\n if (objPerm[permKey]) {\n return true;\n }\n }\n }\n\n return false;\n }\n\n /**\n * Get the merged field permissions for an object.\n * Returns a map of field names to their effective permissions.\n * Uses \"most permissive\" merging.\n */\n getFieldPermissions(\n objectName: string,\n permissionSets: PermissionSet[]\n ): Record<string, FieldPermission> {\n const result: Record<string, FieldPermission> = {};\n\n for (const ps of permissionSets) {\n if (!ps.fields) continue;\n\n for (const [key, perm] of Object.entries(ps.fields)) {\n // Field keys are in format: \"object_name.field_name\"\n if (!key.startsWith(`${objectName}.`)) continue;\n const fieldName = key.substring(objectName.length + 1);\n\n if (!result[fieldName]) {\n result[fieldName] = { readable: false, editable: false };\n }\n\n // Most permissive merge\n if (perm.readable) result[fieldName].readable = true;\n if (perm.editable) result[fieldName].editable = true;\n }\n }\n\n return result;\n }\n\n /**\n * Resolve permission sets for a list of identifier names from metadata.\n *\n * Identifiers are matched to `PermissionSet.name`. The names may be\n * either role names (when `sys_role.name` is reused as a permission set\n * name — common for default admin/member/viewer roles) or explicit\n * permission set names supplied through `ExecutionContext.permissions[]`\n * (resolved by `resolveExecutionContext` from `sys_user_permission_set`\n * and `sys_role_permission_set`).\n *\n * Async because the underlying metadata service exposes `list()` as a\n * Promise — synchronous iteration would silently yield zero results\n * (the historical SecurityPlugin behaviour, masking all enforcement).\n *\n * `bootstrapPermissionSets` is a fallback list of plugin-owned permission\n * sets (typically the platform defaults: admin_full_access /\n * member_default / viewer_readonly) that are registered via\n * `manifest.register({ permissions })` but do not currently propagate\n * into the metadata service's `list()` index. Without this fallback,\n * SecurityPlugin would never resolve the defaults and all enforcement\n * would be silently disabled for authenticated requests.\n */\n async resolvePermissionSets(\n identifiers: string[],\n metadataService: any,\n bootstrapPermissionSets: PermissionSet[] = [],\n /**\n * Optional async loader for permission set names that aren't found in\n * metadata or bootstrap. Lets callers query user-defined permission\n * sets persisted in `sys_permission_set`. Failures are swallowed.\n */\n dbLoader?: (unresolved: string[]) => Promise<PermissionSet[]>\n ): Promise<PermissionSet[]> {\n if (identifiers.length === 0) return [];\n\n const result: PermissionSet[] = [];\n const seen = new Set<string>();\n\n // Get all permission sets from metadata. Support both async (Manager) and\n // sync (test stub) implementations of `list`.\n let allPermSets: any = [];\n try {\n const listed = metadataService?.list?.('permission')\n ?? metadataService?.list?.('permissions')\n ?? [];\n allPermSets = typeof (listed as any)?.then === 'function' ? await listed : listed;\n } catch {\n allPermSets = [];\n }\n if (!Array.isArray(allPermSets)) allPermSets = [];\n\n const wanted = new Set(identifiers);\n for (const ps of allPermSets) {\n if (wanted.has(ps.name) && !seen.has(ps.name)) {\n seen.add(ps.name);\n result.push(ps);\n }\n }\n\n // Fallback: any wanted name not yet matched is sourced from the\n // bootstrap list (plugin-owned defaults). Avoids silent failure when\n // permission sets are registered via `manifest.register` but the\n // metadata service hasn't indexed them.\n for (const ps of bootstrapPermissionSets) {\n if (wanted.has(ps.name) && !seen.has(ps.name)) {\n seen.add(ps.name);\n result.push(ps);\n }\n }\n\n // Last-resort: query user-defined permission sets from the database.\n // Without this, custom permission sets (created via the admin UI as\n // `sys_permission_set` rows) would be silently ignored both for CRUD\n // enforcement and for field-level masking.\n if (dbLoader) {\n const unresolved = identifiers.filter((n) => !seen.has(n));\n if (unresolved.length > 0) {\n try {\n const dbRows = await dbLoader(unresolved);\n for (const ps of dbRows ?? []) {\n if (ps?.name && !seen.has(ps.name)) {\n seen.add(ps.name);\n result.push(ps);\n }\n }\n } catch {\n // Swallow — the request shouldn't fail just because the DB\n // lookup is unavailable.\n }\n }\n }\n\n return result;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { RowLevelSecurityPolicy } from '@objectstack/spec/security';\nimport type { ExecutionContext } from '@objectstack/spec/kernel';\n\n/**\n * RLS User Context\n * Variables available for RLS expression evaluation.\n */\ninterface RLSUserContext {\n id?: string;\n /**\n * Active organization id for the request. RLS expressions reference\n * this as `current_user.organization_id`. Sourced from\n * `ExecutionContext.tenantId` (the runtime keeps the abstract\n * \"tenant\" name, but at the data/RLS layer the canonical column is\n * `organization_id` — see better-auth's organization plugin).\n */\n organization_id?: string;\n roles?: string[];\n /**\n * IDs of all users that share the active organization with the\n * current user (incl. self). Pre-resolved by the runtime so RLS can\n * scope identity tables like `sys_user` via\n * `id IN (current_user.org_user_ids)` without needing subquery\n * support in the compiler.\n */\n org_user_ids?: string[];\n [key: string]: unknown;\n}\n\n/**\n * Sentinel filter used when applicable RLS policies exist but none can\n * be compiled against the current execution context (typically because a\n * required `current_user.*` variable is missing — e.g. the user has no\n * active organization). The filter compares `id` against a non-printable\n * UUID-shaped string that no real record will ever carry, so the upstream\n * SQL layer naturally returns zero rows without raising an error. This\n * gives us **fail-closed** semantics for select/update/delete on tables\n * that the user is not entitled to see, without forcing every caller to\n * handle a thrown `PermissionDeniedError` for what is conceptually an\n * empty result set.\n *\n * Exposed for the SecurityPlugin's optional short-circuit path and for\n * tests; see {@link RLSCompiler.compileFilter}.\n */\nexport const RLS_DENY_FILTER: Record<string, unknown> = Object.freeze({\n id: '__rls_deny__:00000000-0000-0000-0000-000000000000',\n});\n\n/**\n * RLSCompiler\n * \n * Compiles Row-Level Security policy expressions into query filters.\n * Converts `using` / `check` expressions into ObjectQL-compatible filter conditions.\n */\nexport class RLSCompiler {\n /**\n * Compile RLS policies into a query filter for the given user context.\n * Multiple policies for the same object/operation are OR-combined (any match allows access).\n *\n * Return-value semantics:\n * - `null` → no policies applicable → caller applies no RLS filter.\n * - non-null → caller AND's it onto the existing where clause.\n * - {@link RLS_DENY_FILTER} → policies were defined but none could be\n * compiled (e.g. wildcard `tenant_isolation` against a user with no\n * active organization). The caller must treat this as \"deny by\n * default\" — its `id` comparison naturally yields zero rows on\n * select/update/delete, which is the safe fail-closed answer.\n */\n compileFilter(\n policies: RowLevelSecurityPolicy[],\n executionContext?: ExecutionContext\n ): Record<string, unknown> | null {\n if (policies.length === 0) return null;\n\n const userCtx: RLSUserContext = {\n id: executionContext?.userId,\n organization_id: executionContext?.tenantId,\n roles: executionContext?.roles,\n org_user_ids: (executionContext as any)?.org_user_ids,\n };\n\n const filters: Record<string, unknown>[] = [];\n\n for (const policy of policies) {\n if (!policy.using) continue;\n const filter = this.compileExpression(policy.using, userCtx);\n if (filter) {\n filters.push(filter);\n }\n }\n\n if (filters.length === 0) {\n // Policies *were* applicable but every one of them depended on a\n // `current_user.*` variable that wasn't populated (or used an\n // expression we couldn't compile). Fail closed — return a sentinel\n // filter that matches no rows. This prevents the \"user without an\n // active org sees every tenant's data\" class of bug.\n return RLS_DENY_FILTER;\n }\n if (filters.length === 1) return filters[0];\n\n // Multiple policies: OR-combine (any policy allows access)\n return { $or: filters };\n }\n\n /**\n * Compile a single RLS expression into a query filter.\n * \n * Supports simple expressions like:\n * - \"field_name = current_user.property\"\n * - \"field_name IN (current_user.array_property)\"\n * - \"field_name = 'literal_value'\"\n */\n compileExpression(\n expression: string,\n userCtx: RLSUserContext\n ): Record<string, unknown> | null {\n if (!expression) return null;\n\n // Handle simple equality: \"field = current_user.property\"\n const eqMatch = expression.match(/^\\s*(\\w+)\\s*=\\s*current_user\\.(\\w+)\\s*$/);\n if (eqMatch) {\n const [, field, prop] = eqMatch;\n const value = userCtx[prop];\n // Skip when the user-context value is missing (undefined or null).\n // A `null` `organization_id` means \"no active organization\" — applying\n // the filter as `organization_id IS NULL` would silently expose every\n // un-tenanted row across tenants and break system tables that lack the\n // column entirely. Treating null as \"skip this policy\" makes the\n // tenant_isolation rule safely opt-out for users without an active org\n // while still applying when one is set.\n if (value === undefined || value === null) return null;\n return { [field]: value };\n }\n\n // Handle literal equality: \"field = 'value'\"\n const litMatch = expression.match(/^\\s*(\\w+)\\s*=\\s*'([^']*)'\\s*$/);\n if (litMatch) {\n const [, field, value] = litMatch;\n return { [field]: value };\n }\n\n // Handle IN: \"field IN (current_user.array_property)\"\n const inMatch = expression.match(/^\\s*(\\w+)\\s+IN\\s+\\(\\s*current_user\\.(\\w+)\\s*\\)\\s*$/i);\n if (inMatch) {\n const [, field, prop] = inMatch;\n const value = userCtx[prop];\n if (!Array.isArray(value) || value.length === 0) return null;\n return { [field]: { $in: value } };\n }\n\n // Unsupported expression: return null (no additional RLS filter applied).\n // Note: callers should treat absence of RLS policies as \"allow all\" only when\n // no policies are defined. If policies exist but cannot be compiled, the caller\n // may want to deny access as a safety measure.\n return null;\n }\n\n /**\n * Get applicable RLS policies for a given object and operation.\n */\n getApplicablePolicies(\n objectName: string,\n operation: string,\n allPolicies: RowLevelSecurityPolicy[]\n ): RowLevelSecurityPolicy[] {\n // Map engine operation to RLS operation type\n const rlsOp = this.mapOperationToRLS(operation);\n\n return allPolicies.filter(policy => {\n // Check object match\n if (policy.object !== objectName && policy.object !== '*') return false;\n\n // Check operation match\n if (policy.operation === 'all') return true;\n if (policy.operation === rlsOp) return true;\n\n return false;\n });\n }\n\n private mapOperationToRLS(operation: string): string {\n switch (operation) {\n case 'find':\n case 'findOne':\n case 'count':\n case 'aggregate':\n return 'select';\n case 'insert':\n return 'insert';\n case 'update':\n return 'update';\n case 'delete':\n return 'delete';\n default:\n return 'select';\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { FieldPermission } from '@objectstack/spec/security';\n\n/**\n * FieldMasker\n * \n * Applies field-level security by stripping restricted fields from query results.\n */\nexport class FieldMasker {\n /**\n * Mask fields in query results based on field permissions.\n * Removes fields that the user does not have read access to.\n */\n maskResults(\n results: any | any[],\n fieldPermissions: Record<string, FieldPermission>,\n _objectName: string\n ): any | any[] {\n // If no field permissions defined, return results as-is\n if (Object.keys(fieldPermissions).length === 0) return results;\n\n // Get list of non-readable fields\n const hiddenFields = Object.entries(fieldPermissions)\n .filter(([, perm]) => !perm.readable)\n .map(([field]) => field);\n\n if (hiddenFields.length === 0) return results;\n\n if (Array.isArray(results)) {\n return results.map(record => this.maskRecord(record, hiddenFields));\n }\n\n return this.maskRecord(results, hiddenFields);\n }\n\n /**\n * Get non-editable fields for use in write operations.\n * Returns a list of field names that should be stripped from incoming data.\n */\n getNonEditableFields(\n fieldPermissions: Record<string, FieldPermission>\n ): string[] {\n return Object.entries(fieldPermissions)\n .filter(([, perm]) => !perm.editable)\n .map(([field]) => field);\n }\n\n /**\n * Strip non-editable fields from write data.\n */\n stripNonEditableFields(\n data: Record<string, any>,\n fieldPermissions: Record<string, FieldPermission>\n ): Record<string, any> {\n const nonEditable = this.getNonEditableFields(fieldPermissions);\n if (nonEditable.length === 0) return data;\n\n const result = { ...data };\n for (const field of nonEditable) {\n delete result[field];\n }\n return result;\n }\n\n /**\n * Detect which fields in the caller's write payload would touch a\n * field they are not allowed to edit. Returns the set of offending\n * field names (no duplicates, sorted for stable error messages).\n *\n * Used by the security middleware on insert/update to fail-closed\n * with an explicit 403 rather than silently dropping fields — a\n * silent drop hides the security boundary from honest clients\n * (their update partially \"doesn't save\") and gives an attacker no\n * negative signal that the field exists. Throwing makes the\n * boundary observable in both directions.\n *\n * `data` may be a single record or an array of records (bulk insert);\n * either way the returned list is the union across all rows.\n *\n * Fields without a permission entry pass through — permission sets\n * are an allow-list at the field level only for fields they\n * explicitly enumerate. Most objects do not declare per-field rules\n * and remain fully editable.\n */\n detectForbiddenWrites(\n data: Record<string, any> | Record<string, any>[],\n fieldPermissions: Record<string, FieldPermission>\n ): string[] {\n if (Object.keys(fieldPermissions).length === 0) return [];\n const nonEditable = new Set(this.getNonEditableFields(fieldPermissions));\n if (nonEditable.size === 0) return [];\n\n const offenders = new Set<string>();\n const rows = Array.isArray(data) ? data : [data];\n for (const row of rows) {\n if (!row || typeof row !== 'object') continue;\n for (const field of Object.keys(row)) {\n if (nonEditable.has(field)) offenders.add(field);\n }\n }\n return Array.from(offenders).sort();\n }\n\n private maskRecord(record: any, hiddenFields: string[]): any {\n if (!record || typeof record !== 'object') return record;\n\n const result = { ...record };\n for (const field of hiddenFields) {\n delete result[field];\n }\n return result;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Typed sentinel error thrown by `SecurityPlugin` when an operation is\n * denied. Caught by `@objectstack/runtime`'s HTTP dispatcher and translated\n * to HTTP 403.\n */\nexport class PermissionDeniedError extends Error {\n readonly code = 'PERMISSION_DENIED';\n readonly statusCode = 403;\n readonly details?: Record<string, unknown>;\n constructor(message: string, details?: Record<string, unknown>) {\n super(message);\n this.name = 'PermissionDeniedError';\n this.details = details;\n }\n}\n\nexport function isPermissionDeniedError(e: unknown): e is PermissionDeniedError {\n if (!e || typeof e !== 'object') return false;\n const anyE = e as any;\n return (\n anyE.name === 'PermissionDeniedError' ||\n anyE.code === 'PERMISSION_DENIED' ||\n (typeof anyE.message === 'string' && anyE.message.startsWith('[Security] Access denied'))\n );\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * bootstrapPlatformAdmin — first-boot platform admin promotion.\n *\n * Two responsibilities, both idempotent and run on `kernel:ready`:\n *\n * 1. **Seed `sys_permission_set` rows** for each `defaultPermissionSets`\n * entry (admin_full_access / member_default / viewer_readonly).\n *\n * 2. **Promote the first registered user to platform admin** by\n * inserting a `sys_user_permission_set` row that points at\n * `admin_full_access` with `organization_id = NULL` (= cross-tenant).\n * If a platform admin already exists, this is a no-op forever.\n *\n * The \"create a Default Organization for the freshly-promoted admin\"\n * behavior moved to `@objectstack/plugin-org-scoping` (see\n * `ensureDefaultOrganization`). Install that plugin to get\n * multi-tenant bootstrap.\n */\n\nimport type { PermissionSet } from '@objectstack/spec/security';\nimport { SystemUserId } from '@objectstack/spec/system';\n\ninterface BootstrapOptions {\n /** Logger from PluginContext. */\n logger?: {\n info: (message: string, meta?: Record<string, any>) => void;\n warn: (message: string, meta?: Record<string, any>) => void;\n };\n}\n\nconst SYSTEM_CTX = { isSystem: true };\n\nasync function tryFind(ql: any, object: string, where: any, limit = 100): Promise<any[]> {\n try {\n const rows = await ql.find(object, { where, limit }, { context: SYSTEM_CTX });\n return Array.isArray(rows) ? rows : [];\n } catch {\n return [];\n }\n}\n\nasync function tryInsert(ql: any, object: string, data: any): Promise<any | null> {\n try {\n return await ql.insert(object, data, { context: SYSTEM_CTX });\n } catch {\n return null;\n }\n}\n\nfunction genId(prefix: string): string {\n const rand = Math.random().toString(36).slice(2, 10);\n const ts = Date.now().toString(36);\n return `${prefix}_${ts}${rand}`;\n}\n\n/**\n * Persist seed permission sets and promote the first registered user to\n * platform admin. Safe to call multiple times.\n */\nexport async function bootstrapPlatformAdmin(\n ql: any,\n bootstrapPermissionSets: PermissionSet[],\n options: BootstrapOptions = {},\n): Promise<{\n seeded: number;\n adminPromoted: boolean;\n reason?: string;\n}> {\n const logger = options.logger;\n if (!ql || typeof ql.find !== 'function' || typeof ql.insert !== 'function') {\n return { seeded: 0, adminPromoted: false, reason: 'objectql_unavailable' };\n }\n\n // 1. Seed permission set rows.\n const seeded: Record<string, string> = {};\n for (const ps of bootstrapPermissionSets) {\n if (!ps.name) continue;\n const existing = await tryFind(ql, 'sys_permission_set', { name: ps.name }, 1);\n if (existing.length > 0 && existing[0].id) {\n seeded[ps.name] = existing[0].id;\n continue;\n }\n const id = genId('ps');\n const created = await tryInsert(ql, 'sys_permission_set', {\n id,\n name: ps.name,\n label: ps.label ?? ps.name,\n description: ps.description ?? null,\n object_permissions: JSON.stringify(ps.objects ?? {}),\n field_permissions: JSON.stringify(ps.fields ?? {}),\n // Persist the remaining permset facets so the runtime resolver\n // (rest-server.ts / resolve-execution-context.ts) can hydrate\n // them back into ExecutionContext.systemPermissions etc. Without\n // these the platform-admin promotion grants the right LINK row\n // but the permission set itself carries no capabilities, so\n // `setup.access` / `studio.access` never reach the app filter\n // and the Setup app is invisible even to admin_full_access.\n system_permissions: JSON.stringify(ps.systemPermissions ?? []),\n row_level_security: JSON.stringify(ps.rowLevelSecurity ?? []),\n tab_permissions: JSON.stringify(ps.tabPermissions ?? {}),\n active: true,\n });\n if (created?.id) seeded[ps.name] = created.id;\n else if (created) seeded[ps.name] = id;\n }\n\n const seededCount = Object.keys(seeded).length;\n\n // 2. First-user platform admin promotion.\n const adminPsId = seeded['admin_full_access'];\n if (!adminPsId) {\n return { seeded: seededCount, adminPromoted: false, reason: 'admin_permission_set_missing' };\n }\n\n const existingAdminLinks = await tryFind(\n ql,\n 'sys_user_permission_set',\n { permission_set_id: adminPsId },\n 50,\n );\n // A platform admin \"already exists\" only if a *human* holds the\n // cross-tenant grant. The seed-data owner `usr_system` (provisioned by\n // the SeedLoader, see runtime/app-plugin.ts `ensureSeedIdentity`) must\n // never count — otherwise a DB where it was wrongly promoted would block\n // every real admin forever. Ignoring it here makes the bootstrap\n // self-healing on restart.\n if (existingAdminLinks.some((r) => !r.organization_id && r.user_id !== SystemUserId.SYSTEM)) {\n return { seeded: seededCount, adminPromoted: false, reason: 'already_have_admin' };\n }\n\n const allUsers = await tryFind(ql, 'sys_user', {}, 50);\n // Exclude the non-loginable system service account. It is created during\n // seed loading — *before* the first human sign-up — so without this filter\n // it is the earliest user and steals the platform-admin promotion, leaving\n // the real admin login without `setup.access` / `studio.access` (Setup and\n // Studio then stay invisible even though login succeeds).\n const humanUsers = allUsers.filter(\n (u) => u.id !== SystemUserId.SYSTEM && u.role !== 'system',\n );\n if (humanUsers.length === 0) {\n logger?.info?.('[security] no human users yet — first sign-up will be promoted to platform admin');\n return { seeded: seededCount, adminPromoted: false, reason: 'no_users' };\n }\n const sorted = [...humanUsers].sort((a, b) => {\n const ta = a.created_at ? new Date(a.created_at).getTime() : 0;\n const tb = b.created_at ? new Date(b.created_at).getTime() : 0;\n return ta - tb;\n });\n const target = sorted[0];\n\n const inserted = await tryInsert(ql, 'sys_user_permission_set', {\n id: genId('ups'),\n user_id: target.id,\n permission_set_id: adminPsId,\n organization_id: null,\n granted_by: null,\n });\n if (!inserted) {\n logger?.warn?.(`[security] failed to grant admin_full_access to first user ${target.email ?? target.id}`);\n return { seeded: seededCount, adminPromoted: false, reason: 'insert_failed' };\n }\n logger?.info?.(`[security] first user promoted to platform admin: ${target.email ?? target.id}`);\n\n return { seeded: seededCount, adminPromoted: true };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Auto-grant `organization_admin` to org owners/admins.\n *\n * For every `sys_member` row whose `role` contains `owner` or `admin`,\n * ensure a `sys_user_permission_set` row exists that links the user to\n * the `organization_admin` permission set scoped to that organization.\n * For members whose role no longer qualifies (demotion or membership\n * removal), revoke the matching scoped grant.\n *\n * Lifecycle hookup (wired from `security-plugin.ts`):\n *\n * - after `sys_member` insert → reconcile (user_id, organization_id)\n * - after `sys_member` update → reconcile both old and new owner pair\n * - after `sys_member` delete → reconcile to revoke\n * - on `kernel:ready` → backfill across every existing member\n *\n * All operations are idempotent and failure-isolated so a missing\n * permission-set row, schema drift, or a stale row never blocks the\n * underlying `sys_member` mutation.\n *\n * **Why this isn't done by the better-auth org plugin directly:**\n * better-auth does not know about ObjectStack permission sets — it\n * only stores membership roles. Translating \"owner/admin role on this\n * org\" into \"owns the `organization_admin` permission set scoped to\n * this org\" is platform metadata policy and belongs here, alongside\n * `bootstrapPlatformAdmin` (which does the analogous thing for\n * platform admins).\n *\n * **Anti-escalation:** `organization_admin` itself (declared in\n * `platform-objects/src/security/default-permission-sets.ts`) is\n * deliberately read-only on the global RBAC tables\n * (`sys_permission_set`, `sys_user_permission_set`, `sys_role`, …),\n * so a freshly-granted org admin cannot rebind themselves to\n * `admin_full_access`.\n */\n\nconst SYSTEM_CTX = { isSystem: true } as const;\nconst PERMISSION_SET_NAME = 'organization_admin';\n\ninterface MaybeLogger {\n info?: (message: string, meta?: Record<string, any>) => void;\n warn?: (message: string, meta?: Record<string, any>) => void;\n debug?: (message: string, meta?: Record<string, any>) => void;\n}\n\nfunction genId(prefix: string): string {\n const rand = Math.random().toString(36).slice(2, 10);\n const ts = Date.now().toString(36);\n return `${prefix}_${ts}${rand}`;\n}\n\nasync function tryFind(ql: any, object: string, where: any, limit = 50): Promise<any[]> {\n try {\n const rows = await ql.find(object, { where, limit }, { context: SYSTEM_CTX });\n return Array.isArray(rows) ? rows : Array.isArray(rows?.records) ? rows.records : [];\n } catch {\n return [];\n }\n}\n\nasync function tryInsert(ql: any, object: string, data: any): Promise<any | null> {\n try {\n return await ql.insert(object, data, { context: SYSTEM_CTX });\n } catch {\n return null;\n }\n}\n\nasync function tryDelete(ql: any, object: string, id: string): Promise<boolean> {\n try {\n await ql.delete(object, id, { context: SYSTEM_CTX });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Parse a better-auth `sys_member.role` value into a lower-cased role\n * list. better-auth stores either a single role (`\"owner\"`) or a\n * comma-separated list (`\"owner,admin\"`).\n */\nfunction parseRoles(raw: unknown): string[] {\n if (typeof raw !== 'string') return [];\n return raw\n .split(',')\n .map((s) => s.trim().toLowerCase())\n .filter((s) => s.length > 0);\n}\n\nfunction isAdminRole(raw: unknown): boolean {\n const roles = parseRoles(raw);\n return roles.includes('owner') || roles.includes('admin');\n}\n\n/**\n * Resolve the `sys_permission_set.id` for `organization_admin`. Cached\n * across calls per ObjectQL instance via a WeakMap so repeated\n * reconciliations do not re-query.\n */\nconst permissionSetIdCache = new WeakMap<object, string>();\n\nasync function resolvePermissionSetId(ql: any): Promise<string | null> {\n const cached = permissionSetIdCache.get(ql);\n if (cached) return cached;\n const rows = await tryFind(ql, 'sys_permission_set', { name: PERMISSION_SET_NAME }, 1);\n const id = rows[0]?.id;\n if (typeof id === 'string' && id.length > 0) {\n permissionSetIdCache.set(ql, id);\n return id;\n }\n return null;\n}\n\n/**\n * Ensure (or revoke) the org-scoped `organization_admin` grant for\n * `(userId, orgId)` based on the current `sys_member` rows.\n *\n * - If ANY membership row for the pair carries an owner/admin role,\n * ensure exactly one `sys_user_permission_set` row exists.\n * - Else, remove every `sys_user_permission_set` row that links the\n * pair to `organization_admin` (handles demotion and membership\n * removal symmetrically).\n *\n * Returns a structured report for observability. Never throws.\n */\nexport async function reconcileOrgAdminGrant(\n ql: any,\n userId: string,\n orgId: string,\n options: { logger?: MaybeLogger } = {},\n): Promise<{\n action: 'granted' | 'revoked' | 'noop' | 'skipped';\n reason?: string;\n}> {\n const logger = options.logger;\n if (!ql || typeof ql.find !== 'function' || typeof ql.insert !== 'function') {\n return { action: 'skipped', reason: 'objectql_unavailable' };\n }\n if (!userId || !orgId) {\n return { action: 'skipped', reason: 'missing_keys' };\n }\n\n const permSetId = await resolvePermissionSetId(ql);\n if (!permSetId) {\n // organization_admin permission set isn't seeded yet (boot ordering)\n // — caller can retry later (e.g. via kernel:ready backfill).\n return { action: 'skipped', reason: 'permission_set_missing' };\n }\n\n // 1. Determine whether the user currently holds an admin-grade role\n // in this org. Better-auth allows multiple membership rows per\n // pair under some edge cases (legacy data) — any qualifying row\n // is enough.\n const memberships = await tryFind(\n ql,\n 'sys_member',\n { user_id: userId, organization_id: orgId },\n 10,\n );\n const shouldGrant = memberships.some((m: any) => isAdminRole(m?.role));\n\n // 2. Look at existing grants for this exact pair.\n const existingGrants = await tryFind(\n ql,\n 'sys_user_permission_set',\n { user_id: userId, organization_id: orgId, permission_set_id: permSetId },\n 5,\n );\n\n if (shouldGrant) {\n if (existingGrants.length > 0) {\n // Deduplicate stale duplicates if any slipped through.\n for (const extra of existingGrants.slice(1)) {\n if (extra?.id) await tryDelete(ql, 'sys_user_permission_set', String(extra.id));\n }\n return { action: 'noop' };\n }\n const created = await tryInsert(ql, 'sys_user_permission_set', {\n id: genId('ups'),\n user_id: userId,\n permission_set_id: permSetId,\n organization_id: orgId,\n granted_by: null,\n });\n if (created) {\n logger?.info?.('[security] granted organization_admin', { userId, orgId });\n return { action: 'granted' };\n }\n return { action: 'skipped', reason: 'insert_failed' };\n }\n\n // shouldGrant === false → revoke any pre-existing scoped grant.\n if (existingGrants.length === 0) {\n return { action: 'noop' };\n }\n let removed = 0;\n for (const row of existingGrants) {\n if (row?.id && (await tryDelete(ql, 'sys_user_permission_set', String(row.id)))) {\n removed += 1;\n }\n }\n if (removed > 0) {\n logger?.info?.('[security] revoked organization_admin', { userId, orgId, removed });\n return { action: 'revoked' };\n }\n return { action: 'skipped', reason: 'delete_failed' };\n}\n\n/**\n * Reconcile every `(user_id, organization_id)` pair that has at least\n * one `sys_member` row. Used by `kernel:ready` to backfill grants for\n * memberships that pre-date this feature, and as a safety net after\n * the platform admin bootstrap auto-creates the default organization.\n */\nexport async function backfillOrgAdminGrants(\n ql: any,\n options: { logger?: MaybeLogger; limit?: number } = {},\n): Promise<{ scanned: number; granted: number; revoked: number; skipped: number }> {\n const logger = options.logger;\n const limit = options.limit ?? 5000;\n const summary = { scanned: 0, granted: 0, revoked: 0, skipped: 0 };\n if (!ql || typeof ql.find !== 'function') return summary;\n\n const permSetId = await resolvePermissionSetId(ql);\n if (!permSetId) {\n logger?.debug?.('[security] organization_admin backfill skipped — permission set missing');\n return summary;\n }\n\n const members = await tryFind(ql, 'sys_member', {}, limit);\n // De-duplicate by (user_id, organization_id) pair — a user with two\n // membership rows (e.g. legacy duplicates) only needs one reconcile.\n const seen = new Set<string>();\n for (const m of members) {\n const userId = String(m?.user_id ?? '');\n const orgId = String(m?.organization_id ?? '');\n if (!userId || !orgId) continue;\n const key = `${userId}|${orgId}`;\n if (seen.has(key)) continue;\n seen.add(key);\n summary.scanned += 1;\n const res = await reconcileOrgAdminGrant(ql, userId, orgId, { logger });\n if (res.action === 'granted') summary.granted += 1;\n else if (res.action === 'revoked') summary.revoked += 1;\n else if (res.action === 'skipped') summary.skipped += 1;\n }\n\n // Also revoke any organization_admin grant pointing at a (user, org)\n // pair with NO membership row left (orphaned grants from deletes\n // that fired before this hook existed).\n const allGrants = await tryFind(\n ql,\n 'sys_user_permission_set',\n { permission_set_id: permSetId },\n limit,\n );\n for (const g of allGrants) {\n const userId = String(g?.user_id ?? '');\n const orgId = String(g?.organization_id ?? '');\n if (!userId || !orgId) continue;\n const key = `${userId}|${orgId}`;\n if (seen.has(key)) continue;\n const res = await reconcileOrgAdminGrant(ql, userId, orgId, { logger });\n if (res.action === 'revoked') summary.revoked += 1;\n }\n\n logger?.info?.('[security] organization_admin backfill complete', summary);\n return summary;\n}\n\n/**\n * Extract (user_id, organization_id) candidate pairs from a\n * `sys_member` ObjectQL middleware context. Returns both the\n * pre-change and post-change pair so callers can reconcile each.\n */\nexport function extractMemberPairs(opCtx: any): Array<{ userId: string; orgId: string }> {\n const out = new Map<string, { userId: string; orgId: string }>();\n const add = (userId: unknown, orgId: unknown) => {\n if (typeof userId === 'string' && typeof orgId === 'string' && userId && orgId) {\n out.set(`${userId}|${orgId}`, { userId, orgId });\n }\n };\n // Post-write payload — most common case.\n add(opCtx?.result?.user_id, opCtx?.result?.organization_id);\n // Update payloads carry the new values in `data` and the prior row\n // in `before` (driver-dependent). We reconcile BOTH so a member\n // moved from org A to org B (or user changed) is handled.\n add(opCtx?.data?.user_id, opCtx?.data?.organization_id);\n add(opCtx?.before?.user_id, opCtx?.before?.organization_id);\n // For deletes the affected row is sometimes only in `existing`.\n add(opCtx?.existing?.user_id, opCtx?.existing?.organization_id);\n return Array.from(out.values());\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_role — System Role Object\n *\n * RBAC role definition for the ObjectStack platform.\n * Roles group permissions and are assigned to users or members.\n *\n * @namespace sys\n */\nexport const SysRole = ObjectSchema.create({\n name: 'sys_role',\n label: 'Role',\n pluralLabel: 'Roles',\n icon: 'shield',\n isSystem: true,\n managedBy: 'config',\n // ADR-0010 §3.7 — RBAC primitive; tenants may add custom rows\n // (created via UI / API) but the schema itself is locked.\n protection: {\n lock: 'no-overlay',\n reason: 'RBAC schema is platform-defined — see ADR-0010.',\n docsUrl: 'https://docs.objectstack.ai/adr/0010-metadata-protection',\n },\n description: 'Role definitions for RBAC access control',\n displayNameField: 'label',\n titleFormat: '{label}',\n compactLayout: ['label', 'name', 'active', 'is_default'],\n\n // Custom actions — system roles drive RBAC and are edited rarely but\n // require the four high-frequency sysadmin affordances every IdP\n // (Salesforce, ServiceNow, Okta) ships: activate/deactivate (lifecycle\n // without losing assignments), mark default (auto-assign to new users),\n // and clone (template for new roles). All operations hit the generic\n // data CRUD endpoint exposed by `apiEnabled` — no custom server route\n // required because `managedBy: 'config'` allows direct mutation.\n actions: [\n {\n name: 'activate_role',\n label: 'Activate Role',\n icon: 'circle-check',\n variant: 'secondary',\n mode: 'custom',\n locations: ['list_item', 'record_header'],\n type: 'api',\n method: 'PATCH',\n target: '/api/v1/data/sys_role/{id}',\n bodyExtra: { active: true },\n successMessage: 'Role activated',\n refreshAfter: true,\n },\n {\n name: 'deactivate_role',\n label: 'Deactivate Role',\n icon: 'circle-off',\n variant: 'danger',\n mode: 'custom',\n locations: ['list_item', 'record_header'],\n type: 'api',\n method: 'PATCH',\n target: '/api/v1/data/sys_role/{id}',\n bodyExtra: { active: false },\n confirmText: 'Deactivate this role? Users with the role keep their assignment but the role stops granting permissions until re-activated.',\n successMessage: 'Role deactivated',\n refreshAfter: true,\n },\n {\n name: 'set_default_role',\n label: 'Set as Default',\n icon: 'star',\n variant: 'secondary',\n mode: 'custom',\n locations: ['list_item', 'record_header'],\n type: 'api',\n method: 'PATCH',\n target: '/api/v1/data/sys_role/{id}',\n bodyExtra: { is_default: true },\n confirmText: 'Make this the default role for new users? Existing users are unaffected.',\n successMessage: 'Default role updated',\n refreshAfter: true,\n },\n {\n // Clone — POST a new sys_role row pre-filled from the source. The\n // dialog asks only for the new API name / label so the operator\n // can rename atomically; permissions JSON is copied wholesale via\n // defaultFromRow.\n name: 'clone_role',\n label: 'Clone Role',\n icon: 'copy',\n variant: 'secondary',\n mode: 'custom',\n locations: ['list_item', 'record_header'],\n type: 'api',\n method: 'POST',\n target: '/api/v1/data/sys_role',\n bodyExtra: { is_default: false, active: true },\n successMessage: 'Role cloned',\n refreshAfter: true,\n params: [\n { name: 'label', label: 'New Display Name', type: 'text', required: true },\n { name: 'name', label: 'New API Name', type: 'text', required: true, helpText: 'Unique snake_case machine name' },\n { field: 'description', defaultFromRow: true },\n { field: 'permissions', defaultFromRow: true },\n ],\n },\n ],\n\n listViews: {\n active: {\n type: 'grid',\n name: 'active',\n label: 'Active',\n data: { provider: 'object', object: 'sys_role' },\n columns: ['label', 'name', 'is_default', 'updated_at'],\n filter: [{ field: 'active', operator: 'equals', value: true }],\n sort: [{ field: 'label', order: 'asc' }],\n pagination: { pageSize: 50 },\n },\n default_roles: {\n type: 'grid',\n name: 'default_roles',\n label: 'Default',\n data: { provider: 'object', object: 'sys_role' },\n columns: ['label', 'name', 'description', 'active'],\n filter: [{ field: 'is_default', operator: 'equals', value: true }],\n sort: [{ field: 'label', order: 'asc' }],\n pagination: { pageSize: 50 },\n },\n custom: {\n type: 'grid',\n name: 'custom',\n label: 'Custom',\n data: { provider: 'object', object: 'sys_role' },\n columns: ['label', 'name', 'active', 'updated_at'],\n filter: [{ field: 'is_default', operator: 'equals', value: false }],\n sort: [{ field: 'label', order: 'asc' }],\n pagination: { pageSize: 50 },\n },\n all_roles: {\n type: 'grid',\n name: 'all_roles',\n label: 'All',\n data: { provider: 'object', object: 'sys_role' },\n columns: ['label', 'name', 'active', 'is_default', 'updated_at'],\n sort: [{ field: 'label', order: 'asc' }],\n pagination: { pageSize: 50 },\n },\n },\n\n fields: {\n // ── Identity ─────────────────────────────────────────────────\n label: Field.text({\n label: 'Display Name',\n required: true,\n searchable: true,\n maxLength: 255,\n group: 'Identity',\n }),\n\n name: Field.text({\n label: 'API Name',\n required: true,\n searchable: true,\n maxLength: 100,\n description: 'Unique machine name for the role (e.g. admin, editor, viewer)',\n group: 'Identity',\n }),\n\n description: Field.textarea({\n label: 'Description',\n required: false,\n group: 'Identity',\n }),\n\n // ── Configuration ────────────────────────────────────────────\n permissions: Field.textarea({\n label: 'Permissions',\n required: false,\n description: 'JSON-serialized array of permission strings',\n group: 'Configuration',\n }),\n\n // ── Status ───────────────────────────────────────────────────\n active: Field.boolean({\n label: 'Active',\n defaultValue: true,\n group: 'Status',\n }),\n\n is_default: Field.boolean({\n label: 'Default Role',\n defaultValue: false,\n description: 'Automatically assigned to new users',\n group: 'Status',\n }),\n\n // ── System ───────────────────────────────────────────────────\n id: Field.text({\n label: 'Role ID',\n required: true,\n readonly: true,\n group: 'System',\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n defaultValue: 'NOW()',\n readonly: true,\n group: 'System',\n }),\n\n updated_at: Field.datetime({\n label: 'Updated At',\n defaultValue: 'NOW()',\n readonly: true,\n group: 'System',\n }),\n },\n\n indexes: [\n { fields: ['name'], unique: true },\n { fields: ['active'] },\n ],\n\n enable: {\n trackHistory: true,\n searchable: true,\n apiEnabled: true,\n apiMethods: ['get', 'list', 'create', 'update', 'delete'],\n trash: true,\n mru: true,\n },\n});\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_permission_set — System Permission Set Object\n *\n * Named groupings of fine-grained permissions.\n * Permission sets can be assigned to roles or directly to users\n * for granular access control.\n *\n * @namespace sys\n */\nexport const SysPermissionSet = ObjectSchema.create({\n name: 'sys_permission_set',\n label: 'Permission Set',\n pluralLabel: 'Permission Sets',\n icon: 'lock',\n isSystem: true,\n managedBy: 'config',\n // ADR-0010 §3.7 — RBAC primitive; tenants may add custom rows\n // (created via UI / API) but the schema itself is locked.\n protection: {\n lock: 'no-overlay',\n reason: 'RBAC schema is platform-defined — see ADR-0010.',\n docsUrl: 'https://docs.objectstack.ai/adr/0010-metadata-protection',\n },\n description: 'Named permission groupings for fine-grained access control',\n displayNameField: 'label',\n titleFormat: '{label}',\n compactLayout: ['label', 'name', 'active'],\n\n // Custom actions — permission sets are templates assigned to roles or\n // users (via sys_role_permission_set / sys_user_permission_set). The\n // sysadmin operations that don't live on the parent-detail tabs are\n // lifecycle (activate/deactivate without losing assignments) and\n // clone (build a new permset by tweaking an existing one). Both hit\n // the generic data CRUD endpoint — managedBy: 'config' permits it.\n actions: [\n {\n name: 'activate_permission_set',\n label: 'Activate',\n icon: 'circle-check',\n variant: 'secondary',\n mode: 'custom',\n locations: ['list_item', 'record_header'],\n type: 'api',\n method: 'PATCH',\n target: '/api/v1/data/sys_permission_set/{id}',\n bodyExtra: { active: true },\n successMessage: 'Permission set activated',\n refreshAfter: true,\n },\n {\n name: 'deactivate_permission_set',\n label: 'Deactivate',\n icon: 'circle-off',\n variant: 'danger',\n mode: 'custom',\n locations: ['list_item', 'record_header'],\n type: 'api',\n method: 'PATCH',\n target: '/api/v1/data/sys_permission_set/{id}',\n bodyExtra: { active: false },\n confirmText: 'Deactivate this permission set? Existing assignments stay in place but stop granting access until re-activated.',\n successMessage: 'Permission set deactivated',\n refreshAfter: true,\n },\n {\n name: 'clone_permission_set',\n label: 'Clone',\n icon: 'copy',\n variant: 'secondary',\n mode: 'custom',\n locations: ['list_item', 'record_header'],\n type: 'api',\n method: 'POST',\n target: '/api/v1/data/sys_permission_set',\n bodyExtra: { active: true },\n successMessage: 'Permission set cloned',\n refreshAfter: true,\n params: [\n { name: 'label', label: 'New Display Name', type: 'text', required: true },\n { name: 'name', label: 'New API Name', type: 'text', required: true, helpText: 'Unique snake_case machine name' },\n { field: 'description', defaultFromRow: true },\n { field: 'object_permissions', defaultFromRow: true },\n { field: 'field_permissions', defaultFromRow: true },\n ],\n },\n ],\n\n listViews: {\n active: {\n type: 'grid',\n name: 'active',\n label: 'Active',\n data: { provider: 'object', object: 'sys_permission_set' },\n columns: ['label', 'name', 'description', 'updated_at'],\n filter: [{ field: 'active', operator: 'equals', value: true }],\n sort: [{ field: 'label', order: 'asc' }],\n pagination: { pageSize: 50 },\n },\n inactive: {\n type: 'grid',\n name: 'inactive',\n label: 'Inactive',\n data: { provider: 'object', object: 'sys_permission_set' },\n columns: ['label', 'name', 'updated_at'],\n filter: [{ field: 'active', operator: 'equals', value: false }],\n sort: [{ field: 'label', order: 'asc' }],\n pagination: { pageSize: 50 },\n },\n all_permsets: {\n type: 'grid',\n name: 'all_permsets',\n label: 'All',\n data: { provider: 'object', object: 'sys_permission_set' },\n columns: ['label', 'name', 'active', 'updated_at'],\n sort: [{ field: 'label', order: 'asc' }],\n pagination: { pageSize: 50 },\n },\n },\n\n fields: {\n // ── Identity ─────────────────────────────────────────────────\n label: Field.text({\n label: 'Display Name',\n required: true,\n searchable: true,\n maxLength: 255,\n group: 'Identity',\n }),\n\n name: Field.text({\n label: 'API Name',\n required: true,\n searchable: true,\n maxLength: 100,\n description: 'Unique machine name for the permission set',\n group: 'Identity',\n }),\n\n description: Field.textarea({\n label: 'Description',\n required: false,\n group: 'Identity',\n }),\n\n // ── Permissions ──────────────────────────────────────────────\n object_permissions: Field.textarea({\n label: 'Object Permissions',\n required: false,\n description: 'JSON-serialized object-level CRUD permissions',\n group: 'Permissions',\n }),\n\n field_permissions: Field.textarea({\n label: 'Field Permissions',\n required: false,\n description: 'JSON-serialized field-level read/write permissions',\n group: 'Permissions',\n }),\n\n system_permissions: Field.textarea({\n label: 'System Permissions',\n required: false,\n description: 'JSON-serialized array of system capability names (e.g. [\"setup.access\",\"studio.access\",\"manage_users\"])',\n group: 'Permissions',\n }),\n\n row_level_security: Field.textarea({\n label: 'Row-Level Security',\n required: false,\n description: 'JSON-serialized array of row-level security policies (USING/CHECK clauses)',\n group: 'Permissions',\n }),\n\n tab_permissions: Field.textarea({\n label: 'Tab Permissions',\n required: false,\n description: 'JSON-serialized map of app tab visibility (visible | hidden | default_on | default_off)',\n group: 'Permissions',\n }),\n\n // ── Status ───────────────────────────────────────────────────\n active: Field.boolean({\n label: 'Active',\n defaultValue: true,\n group: 'Status',\n }),\n\n // ── System ───────────────────────────────────────────────────\n id: Field.text({\n label: 'Permission Set ID',\n required: true,\n readonly: true,\n group: 'System',\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n defaultValue: 'NOW()',\n readonly: true,\n group: 'System',\n }),\n\n updated_at: Field.datetime({\n label: 'Updated At',\n defaultValue: 'NOW()',\n readonly: true,\n group: 'System',\n }),\n },\n\n indexes: [\n { fields: ['name'], unique: true },\n { fields: ['active'] },\n ],\n\n enable: {\n trackHistory: true,\n searchable: true,\n apiEnabled: true,\n apiMethods: ['get', 'list', 'create', 'update', 'delete'],\n trash: true,\n mru: true,\n },\n});\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_user_permission_set — User ↔ PermissionSet assignment.\n *\n * Salesforce-style additive permission grant: a user may be assigned any\n * number of `sys_permission_set` rows, optionally scoped to a specific\n * organization. The runtime resolver (`resolveExecutionContext` in\n * `@objectstack/runtime`) reads this table when building the per-request\n * `ExecutionContext.permissions[]`.\n *\n * Uniqueness is `(user_id, permission_set_id, organization_id)` so the\n * same permission set can be granted independently in each org context\n * the user belongs to.\n *\n * @namespace sys\n */\nexport const SysUserPermissionSet = ObjectSchema.create({\n name: 'sys_user_permission_set',\n label: 'User Permission Set',\n pluralLabel: 'User Permission Sets',\n icon: 'user-check',\n isSystem: true,\n managedBy: 'system',\n description: 'Direct assignment of a permission set to a user (optionally scoped to an organization).',\n titleFormat: '{user_id} → {permission_set_id}',\n compactLayout: ['user_id', 'permission_set_id', 'organization_id'],\n\n fields: {\n id: Field.text({\n label: 'Assignment ID',\n required: true,\n readonly: true,\n description: 'UUID of the assignment.',\n }),\n\n user_id: Field.lookup('sys_user', {\n label: 'User',\n required: true,\n description: 'Foreign key to sys_user.',\n }),\n\n permission_set_id: Field.lookup('sys_permission_set', {\n label: 'Permission Set',\n required: true,\n description: 'Foreign key to sys_permission_set.',\n }),\n\n organization_id: Field.lookup('sys_organization', {\n label: 'Organization',\n required: false,\n description: 'Optional organization scope. NULL = applies in every org context.',\n }),\n\n granted_by: Field.lookup('sys_user', {\n label: 'Granted By',\n required: false,\n description: 'User who granted this permission set.',\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n defaultValue: 'NOW()',\n readonly: true,\n }),\n\n updated_at: Field.datetime({\n label: 'Updated At',\n defaultValue: 'NOW()',\n readonly: true,\n }),\n },\n\n indexes: [\n { fields: ['user_id', 'permission_set_id', 'organization_id'], unique: true },\n { fields: ['user_id'] },\n { fields: ['organization_id'] },\n { fields: ['permission_set_id'] },\n ],\n\n enable: {\n trackHistory: true,\n searchable: true,\n apiEnabled: true,\n apiMethods: ['get', 'list', 'create', 'update', 'delete'],\n trash: true,\n mru: false,\n },\n});\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_role_permission_set — Role ↔ PermissionSet binding.\n *\n * Allows administrators to compose a `sys_role` from one or more\n * `sys_permission_set` rows. At request time, the runtime resolver\n * (`resolveExecutionContext`) collects every permission set bound to\n * the user's roles via this table and injects their names into\n * `ExecutionContext.permissions[]` for downstream RBAC evaluation.\n *\n * @namespace sys\n */\nexport const SysRolePermissionSet = ObjectSchema.create({\n name: 'sys_role_permission_set',\n label: 'Role Permission Set',\n pluralLabel: 'Role Permission Sets',\n icon: 'shield-plus',\n isSystem: true,\n managedBy: 'system',\n description: 'Binds a permission set to a role.',\n titleFormat: '{role_id} → {permission_set_id}',\n compactLayout: ['role_id', 'permission_set_id'],\n\n fields: {\n id: Field.text({\n label: 'Binding ID',\n required: true,\n readonly: true,\n description: 'UUID of the role-permission-set binding.',\n }),\n\n role_id: Field.lookup('sys_role', {\n label: 'Role',\n required: true,\n description: 'Foreign key to sys_role.',\n }),\n\n permission_set_id: Field.lookup('sys_permission_set', {\n label: 'Permission Set',\n required: true,\n description: 'Foreign key to sys_permission_set.',\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n defaultValue: 'NOW()',\n readonly: true,\n }),\n\n updated_at: Field.datetime({\n label: 'Updated At',\n defaultValue: 'NOW()',\n readonly: true,\n }),\n },\n\n indexes: [\n { fields: ['role_id', 'permission_set_id'], unique: true },\n { fields: ['role_id'] },\n { fields: ['permission_set_id'] },\n ],\n\n enable: {\n trackHistory: true,\n searchable: true,\n apiEnabled: true,\n apiMethods: ['get', 'list', 'create', 'update', 'delete'],\n trash: true,\n mru: false,\n },\n});\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { PermissionSetSchema, type PermissionSet } from '@objectstack/spec/security';\n\n/**\n * Identity tables managed by the better-auth plugin (see\n * `packages/platform-objects/src/identity/`). Mutations to these tables\n * MUST flow through the better-auth API endpoints (sign-up, password\n * reset, organization invite/remove-member, api-key/create, …) rather\n * than the generic CRUD pipeline so that password hashing, token\n * signing, email verification, invitation flows and scope hashing all\n * fire correctly.\n *\n * The default member/viewer permission sets therefore explicitly DENY\n * `allowCreate / allowEdit / allowDelete` on these objects while still\n * permitting reads (subject to the rest of the RLS chain). Admin\n * permission sets keep their `*` wildcard so they can rescue data\n * directly when needed.\n *\n * Each entry mirrors the `managedBy: 'better-auth'` flag declared on\n * the corresponding object schema in `packages/platform-objects/src/identity/`.\n */\nconst BETTER_AUTH_MANAGED_OBJECTS = [\n 'sys_user',\n 'sys_account',\n 'sys_session',\n 'sys_organization',\n 'sys_member',\n 'sys_invitation',\n 'sys_team',\n 'sys_team_member',\n 'sys_api_key',\n 'sys_two_factor',\n 'sys_verification',\n 'sys_jwks',\n 'sys_device_code',\n 'sys_oauth_application',\n 'sys_oauth_access_token',\n 'sys_oauth_refresh_token',\n 'sys_oauth_consent',\n] as const;\n\nconst denyWritesOnManagedObjects = (): Record<string, {\n allowRead: boolean;\n allowCreate: boolean;\n allowEdit: boolean;\n allowDelete: boolean;\n}> => Object.fromEntries(\n BETTER_AUTH_MANAGED_OBJECTS.map((name) => [\n name,\n { allowRead: true, allowCreate: false, allowEdit: false, allowDelete: false },\n ]),\n);\n\n/**\n * Default permission sets seeded by the platform.\n *\n * These are referenced by name (`admin_full_access`, `member_default`,\n * `viewer_readonly`) from `sys_role_permission_set` rows or assigned\n * directly to users via `sys_user_permission_set`.\n *\n * The runtime SecurityPlugin reads these via the metadata service when a\n * permission set name appears in the request `ExecutionContext.permissions[]`.\n *\n * Each entry is run through `PermissionSetSchema.parse(...)` so Zod fills\n * in the boolean/`priority`/`enabled` defaults — keeping the literal\n * source readable while still satisfying the strict output type.\n *\n * `objects: { '*': … }` uses the wildcard sentinel honoured by\n * `PermissionEvaluator` — admins do not need an explicit row per object.\n * Per-object entries fully override the wildcard for that object (see\n * `PermissionEvaluator.checkObjectPermission` — lookup, not merge).\n *\n * RLS policies use the canonical `current_user.*` placeholders compiled\n * by `RLSCompiler`. The active organization is exposed under\n * `current_user.organization_id` (sourced from\n * `ExecutionContext.tenantId` at request time) — there is no rewrite\n * step or `tenantField` indirection in SecurityPlugin. Schemas with a\n * different physical tenant column should fork these defaults.\n */\nexport const defaultPermissionSets: PermissionSet[] = [\n PermissionSetSchema.parse({\n name: 'admin_full_access',\n label: 'Administrator — Full Access',\n isProfile: true,\n objects: {\n '*': {\n allowRead: true,\n allowCreate: true,\n allowEdit: true,\n allowDelete: true,\n viewAllRecords: true,\n modifyAllRecords: true,\n },\n },\n systemPermissions: [\n 'manage_users',\n 'manage_metadata',\n 'manage_platform_settings',\n 'setup.access',\n 'studio.access',\n ],\n }),\n // ── Organization Administrator ──────────────────────────────────────\n //\n // Third tier between platform admin (`admin_full_access`) and rank-and-file\n // member. Lives at the *organization* scope: full CRUD on business\n // objects within their org (governed by `tenant_isolation` RLS), plus\n // `setup.access` so the Setup app shell is reachable.\n //\n // **Deliberately withheld** vs `admin_full_access`:\n // - `studio.access` — schema-design surfaces are platform-level (a\n // tenant cannot mutate the shared metadata) and Studio is hidden.\n // - `manage_metadata` — same reasoning.\n // - `manage_platform_settings` — global settings manifests\n // (mail / storage / AI / knowledge) and platform-only Setup pages\n // (sharing rules, audit logs, OAuth apps, JWKS, …) require this\n // and are hidden / 403'd for org admins. Tenant-scoped manifests\n // (`branding`, `feature_flags`) keep using `setup.access` so org\n // admins CAN configure their own org's branding.\n //\n // **Anti-escalation**: writes to the global RBAC tables\n // (`sys_role`, `sys_permission_set`, `sys_role_permission_set`,\n // `sys_user_permission_set`, `sys_user_role`) are denied. Allowing\n // them would let an org admin bind `admin_full_access` (which has no\n // RLS) to themselves and break out of tenant isolation. Reads are\n // permitted so the Roles / Permission Sets nav entries still render.\n //\n // Auto-granted to every `sys_member` whose role contains `owner` or\n // `admin` by `plugin-security/src/auto-org-admin-grant.ts`.\n PermissionSetSchema.parse({\n name: 'organization_admin',\n label: 'Organization Administrator',\n isProfile: true,\n objects: {\n '*': {\n allowRead: true,\n allowCreate: true,\n allowEdit: true,\n allowDelete: true,\n viewAllRecords: true,\n modifyAllRecords: true,\n },\n // Identity tables — go through better-auth endpoints (invite,\n // accept, remove-member, transfer, …) rather than raw CRUD.\n ...denyWritesOnManagedObjects(),\n // RBAC tables — read-only to prevent privilege escalation.\n sys_role: { allowRead: true, allowCreate: false, allowEdit: false, allowDelete: false },\n sys_permission_set: { allowRead: true, allowCreate: false, allowEdit: false, allowDelete: false },\n sys_role_permission_set: { allowRead: true, allowCreate: false, allowEdit: false, allowDelete: false },\n sys_user_permission_set: { allowRead: true, allowCreate: false, allowEdit: false, allowDelete: false },\n sys_user_role: { allowRead: true, allowCreate: false, allowEdit: false, allowDelete: false },\n },\n systemPermissions: ['manage_org_users', 'setup.access'],\n rowLevelSecurity: [\n {\n name: 'tenant_isolation',\n object: '*',\n operation: 'all',\n using: 'organization_id = current_user.organization_id',\n },\n // ── better-auth system tables that lack `organization_id` and would\n // otherwise be denied by the wildcard policy. Same self-only\n // carve-outs as `member_default` — an org admin does not get to\n // inspect cross-tenant identity rows.\n {\n name: 'sys_organization_self',\n object: 'sys_organization',\n operation: 'all',\n using: 'id = current_user.organization_id',\n },\n {\n name: 'sys_user_self',\n object: 'sys_user',\n operation: 'select',\n using: 'id = current_user.id',\n },\n {\n name: 'sys_user_org_members',\n object: 'sys_user',\n operation: 'select',\n using: 'id IN (current_user.org_user_ids)',\n },\n {\n name: 'sys_session_self',\n object: 'sys_session',\n operation: 'all',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_account_self',\n object: 'sys_account',\n operation: 'select',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_team_member_self',\n object: 'sys_team_member',\n operation: 'select',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_two_factor_self',\n object: 'sys_two_factor',\n operation: 'all',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_user_preference_self',\n object: 'sys_user_preference',\n operation: 'all',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_api_key_self',\n object: 'sys_api_key',\n operation: 'all',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_device_code_self',\n object: 'sys_device_code',\n operation: 'all',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_oauth_access_token_self',\n object: 'sys_oauth_access_token',\n operation: 'select',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_oauth_refresh_token_self',\n object: 'sys_oauth_refresh_token',\n operation: 'select',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_oauth_consent_self',\n object: 'sys_oauth_consent',\n operation: 'all',\n using: 'user_id = current_user.id',\n },\n // OAuth applications a user has registered themselves (self-service\n // developer flow exposed in the Account app's Developer section).\n // `sys_oauth_application` has no `organization_id` so the wildcard\n // `tenant_isolation` policy would otherwise deny every row.\n {\n name: 'sys_oauth_application_self',\n object: 'sys_oauth_application',\n operation: 'all',\n using: 'user_id = current_user.id',\n },\n // Org-scoped visibility for organization-owned identity-adjacent\n // tables. Org admins may inspect their own org's invitations and\n // memberships (read; writes still flow through better-auth).\n {\n name: 'sys_member_org',\n object: 'sys_member',\n operation: 'select',\n using: 'organization_id = current_user.organization_id',\n },\n {\n name: 'sys_invitation_org',\n object: 'sys_invitation',\n operation: 'select',\n using: 'organization_id = current_user.organization_id',\n },\n {\n name: 'sys_team_org',\n object: 'sys_team',\n operation: 'select',\n using: 'organization_id = current_user.organization_id',\n },\n ],\n }),\n PermissionSetSchema.parse({\n name: 'member_default',\n label: 'Member — Standard Access',\n isProfile: true,\n objects: {\n '*': {\n allowRead: true,\n allowCreate: true,\n allowEdit: true,\n allowDelete: true,\n },\n // Identity tables are managed by better-auth — no direct writes.\n ...denyWritesOnManagedObjects(),\n },\n rowLevelSecurity: [\n {\n name: 'tenant_isolation',\n object: '*',\n operation: 'all',\n using: 'organization_id = current_user.organization_id',\n },\n {\n name: 'owner_only_writes',\n object: '*',\n operation: 'update',\n using: 'owner_id = current_user.id',\n },\n {\n name: 'owner_only_deletes',\n object: '*',\n operation: 'delete',\n using: 'owner_id = current_user.id',\n },\n // ── better-auth system tables that lack `organization_id` and would\n // otherwise be left unprotected by the wildcard rule above. ────\n //\n // The security plugin's RLS injector treats wildcard policies that\n // target a missing field as `RLS_DENY_FILTER` (zero rows) unless a\n // per-object policy contributes an alternate match. Each `*_self`\n // policy below restores per-user visibility on a better-auth table\n // that has `user_id` but no `organization_id`. Tables without\n // `user_id` (`sys_verification`, `sys_jwks`, empty `sys_passkey`)\n // stay DENY for non-admins by design — only platform admins (via\n // `admin_full_access`, which has no RLS) should inspect them.\n {\n name: 'sys_organization_self',\n object: 'sys_organization',\n operation: 'all',\n using: 'id = current_user.organization_id',\n },\n {\n name: 'sys_user_self',\n object: 'sys_user',\n operation: 'select',\n using: 'id = current_user.id',\n },\n // Org collaborators: members can see other users in the same\n // organization. Without this, owner/assignee lookups, @-mention\n // suggestions, reviewer pickers and team-roster surfaces all\n // collapse to just the current user. `org_user_ids` is\n // pre-resolved by runtime/resolve-execution-context from\n // `sys_member` for the active organization. Sensitive credential\n // tables (`sys_account`, `sys_session`, `sys_api_key`, …) keep\n // their stricter self-only carve-outs above.\n {\n name: 'sys_user_org_members',\n object: 'sys_user',\n operation: 'select',\n using: 'id IN (current_user.org_user_ids)',\n },\n {\n name: 'sys_session_self',\n object: 'sys_session',\n operation: 'all',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_account_self',\n object: 'sys_account',\n operation: 'select',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_team_member_self',\n object: 'sys_team_member',\n operation: 'select',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_two_factor_self',\n object: 'sys_two_factor',\n operation: 'all',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_user_preference_self',\n object: 'sys_user_preference',\n operation: 'all',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_api_key_self',\n object: 'sys_api_key',\n operation: 'all',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_device_code_self',\n object: 'sys_device_code',\n operation: 'all',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_oauth_access_token_self',\n object: 'sys_oauth_access_token',\n operation: 'select',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_oauth_refresh_token_self',\n object: 'sys_oauth_refresh_token',\n operation: 'select',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_oauth_consent_self',\n object: 'sys_oauth_consent',\n operation: 'all',\n using: 'user_id = current_user.id',\n },\n // OAuth applications a user has registered themselves (Account →\n // Developer → OAuth Applications). `sys_oauth_application` has no\n // `organization_id`, so without this carve-out the wildcard\n // `tenant_isolation` policy returns zero rows even for the owner.\n {\n name: 'sys_oauth_application_self',\n object: 'sys_oauth_application',\n operation: 'all',\n using: 'user_id = current_user.id',\n },\n ],\n }),\n PermissionSetSchema.parse({\n name: 'viewer_readonly',\n label: 'Viewer — Read-Only',\n isProfile: true,\n objects: {\n '*': {\n allowRead: true,\n allowCreate: false,\n allowEdit: false,\n allowDelete: false,\n },\n // Belt-and-suspenders: explicit deny on managed objects even though\n // the wildcard already denies — keeps the policy readable when\n // future relaxations might widen the wildcard.\n ...denyWritesOnManagedObjects(),\n },\n rowLevelSecurity: [\n {\n name: 'tenant_isolation',\n object: '*',\n operation: 'select',\n using: 'organization_id = current_user.organization_id',\n },\n {\n name: 'sys_organization_self',\n object: 'sys_organization',\n operation: 'select',\n using: 'id = current_user.organization_id',\n },\n {\n name: 'sys_user_self',\n object: 'sys_user',\n operation: 'select',\n using: 'id = current_user.id',\n },\n // Org collaborators (read-only): see `sys_user_org_members` in\n // `member_default` for rationale.\n {\n name: 'sys_user_org_members',\n object: 'sys_user',\n operation: 'select',\n using: 'id IN (current_user.org_user_ids)',\n },\n {\n name: 'sys_session_self',\n object: 'sys_session',\n operation: 'select',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_account_self',\n object: 'sys_account',\n operation: 'select',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_team_member_self',\n object: 'sys_team_member',\n operation: 'select',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_two_factor_self',\n object: 'sys_two_factor',\n operation: 'select',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_user_preference_self',\n object: 'sys_user_preference',\n operation: 'select',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_api_key_self',\n object: 'sys_api_key',\n operation: 'select',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_device_code_self',\n object: 'sys_device_code',\n operation: 'select',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_oauth_access_token_self',\n object: 'sys_oauth_access_token',\n operation: 'select',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_oauth_refresh_token_self',\n object: 'sys_oauth_refresh_token',\n operation: 'select',\n using: 'user_id = current_user.id',\n },\n {\n name: 'sys_oauth_consent_self',\n object: 'sys_oauth_consent',\n operation: 'select',\n using: 'user_id = current_user.id',\n },\n ],\n }),\n];\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Canonical plugin-security manifest source.\n *\n * Both `objectstack.config.ts` (compile-time) and `security-plugin.ts`\n * (runtime `manifest.register`) import from this file so the two\n * registration paths cannot drift (D7).\n */\n\nimport {\n SysPermissionSet,\n SysRole,\n SysUserPermissionSet,\n SysRolePermissionSet,\n defaultPermissionSets,\n} from './objects/index.js';\n\nexport const SECURITY_PLUGIN_ID = 'com.objectstack.plugin-security';\nexport const SECURITY_PLUGIN_VERSION = '1.0.0';\n\n/** Security objects owned by plugin-security. */\nexport const securityObjects = [\n SysRole,\n SysPermissionSet,\n SysUserPermissionSet,\n SysRolePermissionSet,\n];\n\n/** Default platform permission sets (admin / member / viewer). */\nexport const securityDefaultPermissionSets = defaultPermissionSets;\n\n/** Manifest header shared by compile-time config and runtime registration. */\nexport const securityPluginManifestHeader = {\n id: SECURITY_PLUGIN_ID,\n namespace: 'sys',\n version: SECURITY_PLUGIN_VERSION,\n type: 'plugin' as const,\n scope: 'system' as const,\n defaultDatasource: 'cloud',\n name: 'Security Plugin',\n description: 'RBAC roles and permission sets for ObjectStack (Role, PermissionSet)',\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { Plugin, PluginContext } from '@objectstack/core';\nimport type { PermissionSet, RowLevelSecurityPolicy } from '@objectstack/spec/security';\nimport { PermissionEvaluator } from './permission-evaluator.js';\nimport { RLSCompiler, RLS_DENY_FILTER } from './rls-compiler.js';\nimport { FieldMasker } from './field-masker.js';\nimport { PermissionDeniedError } from './errors.js';\nimport { bootstrapPlatformAdmin } from './bootstrap-platform-admin.js';\nimport {\n backfillOrgAdminGrants,\n extractMemberPairs,\n reconcileOrgAdminGrant,\n} from './auto-org-admin-grant.js';\nimport {\n securityObjects,\n securityDefaultPermissionSets,\n securityPluginManifestHeader,\n} from './manifest.js';\n\nexport interface SecurityPluginOptions {\n /**\n * Additional permission sets to register with the metadata service on\n * plugin start. Defaults to {@link securityDefaultPermissionSets}\n * (admin_full_access / member_default / viewer_readonly).\n */\n defaultPermissionSets?: PermissionSet[];\n /**\n * Permission set name applied as an implicit baseline whenever an\n * authenticated request has no resolved permission sets (and no roles\n * that map to one). This guarantees baseline tenant/owner RLS for\n * every logged-in user even before an admin assigns explicit\n * profiles. Set to `null` to disable.\n *\n * @default 'member_default'\n */\n fallbackPermissionSet?: string | null;\n}\n\n/**\n * SecurityPlugin\n *\n * Provides RBAC, Row-Level Security, and Field-Level Security runtime.\n * Registers as an engine middleware on the ObjectQL engine.\n *\n * This plugin is fully optional — without it, the system operates\n * without permission checks (same as current behavior).\n *\n * **Multi-tenant Organization scoping is provided by the separate\n * `@objectstack/plugin-org-scoping` package** (auto-stamps\n * `organization_id` on insert, per-org seed replay, default-org\n * bootstrap). When that plugin is installed, SecurityPlugin detects\n * it via `getService('org-scoping')` and keeps the wildcard\n * `current_user.organization_id` RLS policies that ship with the\n * default permission sets. Without it, those policies are stripped so\n * single-tenant deployments don't pay the field-existence safety-net\n * cost on every find.\n *\n * Dependencies:\n * - objectql service (ObjectQL engine with middleware support)\n * - metadata service (MetadataFacade for reading permission sets and RLS policies)\n */\nexport class SecurityPlugin implements Plugin {\n name = 'com.objectstack.security';\n type = 'standard';\n version = '1.0.0';\n dependencies = ['com.objectstack.engine.objectql'];\n\n private permissionEvaluator = new PermissionEvaluator();\n private rlsCompiler = new RLSCompiler();\n private fieldMasker = new FieldMasker();\n private readonly bootstrapPermissionSets: PermissionSet[];\n private readonly fallbackPermissionSet: string | null;\n /**\n * Runtime probe — set in `start()` from\n * `ctx.getService('org-scoping')`. When `false`, wildcard RLS\n * policies that reference `current_user.organization_id` are\n * stripped from the per-request policy set (saves the\n * field-existence safety net cost on every find in single-tenant\n * deployments). When `true`, the policies apply normally.\n */\n private orgScopingEnabled = false;\n /**\n * Per-object field-name cache. Populated lazily from the metadata\n * service / ObjectQL registry on first access per object. Schemas are\n * effectively immutable for the lifetime of the kernel today (hot\n * reload tears the kernel down), so we don't bother with\n * invalidation — a kernel restart drops the cache.\n */\n private readonly fieldNamesCache = new Map<string, Set<string> | null>();\n /**\n * Per-object cache of tenancy opt-out. `true` means the schema\n * explicitly disabled multi-tenancy (`tenancy.enabled === false` or\n * `systemFields.tenant === false`). Wildcard policies that target\n * the conventional tenant column (`organization_id`) are treated as\n * *not applicable* on these tables instead of triggering the\n * field-missing deny sentinel — without this, every read of a\n * cross-org catalog (e.g. `sys_package`, the Marketplace) returns\n * zero rows.\n */\n private readonly tenancyDisabledCache = new Map<string, boolean>();\n\n constructor(options: SecurityPluginOptions = {}) {\n this.bootstrapPermissionSets =\n options.defaultPermissionSets ?? securityDefaultPermissionSets;\n this.fallbackPermissionSet =\n options.fallbackPermissionSet === undefined\n ? 'member_default'\n : options.fallbackPermissionSet;\n }\n\n async init(ctx: PluginContext): Promise<void> {\n ctx.logger.info('Initializing Security Plugin...');\n\n // Register security services\n ctx.registerService('security.permissions', this.permissionEvaluator);\n ctx.registerService('security.rls', this.rlsCompiler);\n ctx.registerService('security.fieldMasker', this.fieldMasker);\n // Bootstrap permission sets (admin_full_access, member_default,\n // viewer_readonly by default) — exposed as a service so other\n // plugins (e.g. plugin-hono-server's /me/permissions endpoint)\n // can pass them as the fallback list to\n // `PermissionEvaluator.resolvePermissionSets` without re-importing\n // the platform-objects package directly.\n ctx.registerService('security.bootstrapPermissionSets', this.bootstrapPermissionSets);\n ctx.registerService('security.fallbackPermissionSet', this.fallbackPermissionSet);\n\n ctx.getService<{ register(m: any): void }>('manifest').register({\n ...securityPluginManifestHeader,\n objects: securityObjects,\n // Permission sets ride along on the manifest so the metadata service\n // can resolve them by name when SecurityPlugin middleware queries\n // `metadata.list('permissions')`.\n permissions: this.bootstrapPermissionSets,\n // ADR-0029 D7 — contribute the RBAC entries into the Setup app's\n // `group_access_control` slot. This plugin owns these objects (K2), so it\n // ships their menu too; when the plugin is absent the entries don't appear.\n navigationContributions: [\n {\n app: 'setup',\n group: 'group_access_control',\n priority: 100,\n items: [\n { id: 'nav_roles', type: 'object', label: 'Roles', objectName: 'sys_role', icon: 'shield-check' },\n { id: 'nav_permission_sets', type: 'object', label: 'Permission Sets', objectName: 'sys_permission_set', icon: 'lock' },\n ],\n },\n ],\n });\n\n // ADR-0029 D8 — contribute this plugin's object translations to the i18n\n // service on kernel:ready (the i18n plugin may register after this one).\n if (typeof (ctx as any).hook === 'function') {\n (ctx as any).hook('kernel:ready', async () => {\n try {\n const i18n = ctx.getService<any>('i18n');\n if (i18n && typeof i18n.loadTranslations === 'function') {\n const { SecurityTranslations } = await import('./translations/index.js');\n for (const [locale, data] of Object.entries(SecurityTranslations)) {\n i18n.loadTranslations(locale, data as Record<string, unknown>);\n }\n }\n } catch { /* i18n optional */ }\n });\n }\n\n ctx.logger.info('Security Plugin initialized', {\n defaultPermissionSets: this.bootstrapPermissionSets.map((p) => p.name),\n });\n }\n\n async start(ctx: PluginContext): Promise<void> {\n ctx.logger.info('Starting Security Plugin...');\n\n // Get required services\n let ql: any;\n let metadata: any;\n\n try {\n ql = ctx.getService('objectql');\n metadata = ctx.getService('metadata');\n } catch (e) {\n ctx.logger.warn('ObjectQL or metadata service not available, security middleware not registered');\n return;\n }\n\n if (!ql || typeof ql.registerMiddleware !== 'function') {\n ctx.logger.warn('ObjectQL engine does not support middleware, security middleware not registered');\n return;\n }\n\n // Probe for OrgScopingPlugin presence. When registered, its\n // `init()` exposes itself as the `org-scoping` service. We capture\n // the boolean once at start time (plugin DI graph is static after\n // start) and let `collectRLSPolicies` consult it on every request.\n try {\n const orgScoping = ctx.getService('org-scoping');\n this.orgScopingEnabled = !!orgScoping;\n } catch {\n this.orgScopingEnabled = false;\n }\n if (this.orgScopingEnabled) {\n ctx.logger.info(\n '[security] org-scoping plugin detected — wildcard `organization_id` RLS policies will apply',\n );\n } else {\n ctx.logger.info(\n '[security] org-scoping plugin not present — wildcard `organization_id` RLS policies will be stripped (single-tenant mode)',\n );\n }\n\n // Construct a dbLoader once that lets resolvePermissionSets\n // surface user-defined permission sets from `sys_permission_set`\n // (created via the admin UI) in addition to plugin-registered\n // ones. Uses `isSystem` to bypass tenant RLS.\n const dbLoader = ql\n ? async (names: string[]) => {\n let rows: any;\n try {\n rows = await ql.find(\n 'sys_permission_set',\n { where: { name: { $in: names } }, limit: names.length },\n { context: { isSystem: true } },\n );\n } catch {\n rows = [];\n }\n const list = Array.isArray(rows) ? rows : rows?.records ?? [];\n return list.map((r: any) => ({\n name: r.name,\n label: r.label,\n objects: typeof r.object_permissions === 'string'\n ? JSON.parse(r.object_permissions || '{}')\n : r.object_permissions ?? {},\n fields: typeof r.field_permissions === 'string'\n ? JSON.parse(r.field_permissions || '{}')\n : r.field_permissions ?? {},\n }));\n }\n : undefined;\n\n // Register security middleware\n ql.registerMiddleware(async (opCtx: any, next: () => Promise<void>) => {\n // System operations bypass security\n if (opCtx.context?.isSystem) {\n return next();\n }\n\n const roles = opCtx.context?.roles ?? [];\n const explicitPermissionSets = opCtx.context?.permissions ?? [];\n\n // Skip security checks if no roles AND no explicit permission sets\n // AND no userId (anonymous/unauthenticated). The auth middleware\n // should handle authentication separately.\n if (\n roles.length === 0 &&\n explicitPermissionSets.length === 0 &&\n !opCtx.context?.userId\n ) {\n return next();\n }\n\n // 1. Resolve permission sets from BOTH role names and explicit\n // permission set names attached to the execution context.\n let permissionSets: PermissionSet[] = [];\n try {\n const requested = [...roles, ...explicitPermissionSets];\n // Implicit baseline: when an authenticated request resolved zero\n // permission sets, fall back to the configured baseline (default\n // `member_default`). This guarantees tenant + owner RLS even\n // before an admin has assigned a profile/permission set.\n if (\n requested.length === 0 &&\n opCtx.context?.userId &&\n this.fallbackPermissionSet\n ) {\n requested.push(this.fallbackPermissionSet);\n }\n permissionSets = await this.permissionEvaluator.resolvePermissionSets(\n requested,\n metadata,\n this.bootstrapPermissionSets,\n dbLoader,\n );\n // **Post-resolution fallback** — closes the fail-open hole that\n // appears when a user's `roles` array is populated (e.g. a\n // better-auth `sys_member.role` like `owner`/`admin`/`member`)\n // but no `sys_role`→`sys_permission_set` binding exists yet, so\n // resolution returns an empty array. Without this, both the\n // CRUD check (`permissionSets.length > 0`) and the RLS injection\n // (`allRlsPolicies.length > 0`) below get skipped → the user\n // sees every tenant's data. Authenticated users with no\n // resolved permission sets always inherit the configured\n // baseline (default `member_default`, which carries\n // `tenant_isolation` + `owner_only_writes`).\n if (\n permissionSets.length === 0 &&\n opCtx.context?.userId &&\n this.fallbackPermissionSet\n ) {\n const fallback = await this.permissionEvaluator.resolvePermissionSets(\n [this.fallbackPermissionSet],\n metadata,\n this.bootstrapPermissionSets,\n dbLoader,\n );\n permissionSets = fallback;\n }\n } catch (e) {\n // If metadata service is misconfigured, log and continue without permission checks\n // rather than blocking all operations\n return next();\n }\n\n // 2. CRUD permission check\n if (permissionSets.length > 0) {\n const allowed = this.permissionEvaluator.checkObjectPermission(\n opCtx.operation,\n opCtx.object,\n permissionSets\n );\n\n if (!allowed) {\n throw new PermissionDeniedError(\n `[Security] Access denied: operation '${opCtx.operation}' on object '${opCtx.object}' ` +\n `is not permitted for roles [${roles.join(', ')}]`,\n { operation: opCtx.operation, object: opCtx.object, roles, permissionSets: explicitPermissionSets },\n );\n }\n }\n\n // 2.5. Field-Level Security write enforcement.\n //\n // The client-side masker (ObjectForm / inline grid) already hides\n // non-editable fields from the UI, but that is a UX layer only —\n // a hand-crafted POST / direct ObjectQL call can still target a\n // forbidden field. We fail-closed here with an explicit 403 and\n // the offending field names, so:\n //\n // - honest clients get an actionable error (vs. silent drop,\n // which manifests as a confusing partial-save), and\n // - probing clients see that the boundary is enforced (vs.\n // getting a 200 with the field silently ignored, which\n // reveals nothing).\n //\n // Runs BEFORE the tenant/owner auto-injection (step 3.5) so the\n // system-set fields are not subject to the user's edit\n // permissions — they are populated from the execution context,\n // not from the caller's payload.\n if (\n (opCtx.operation === 'insert' || opCtx.operation === 'update') &&\n opCtx.data &&\n permissionSets.length > 0\n ) {\n const fieldPerms = this.permissionEvaluator.getFieldPermissions(\n opCtx.object,\n permissionSets,\n );\n if (Object.keys(fieldPerms).length > 0) {\n const forbidden = this.fieldMasker.detectForbiddenWrites(\n opCtx.data,\n fieldPerms,\n );\n if (forbidden.length > 0) {\n throw new PermissionDeniedError(\n `[Security] Field write denied: not permitted to edit ` +\n `[${forbidden.join(', ')}] on '${opCtx.object}'`,\n {\n operation: opCtx.operation,\n object: opCtx.object,\n roles,\n permissionSets: explicitPermissionSets,\n forbiddenFields: forbidden,\n },\n );\n }\n }\n }\n\n // 3.5. Auto-inject `owner_id` on insert from the\n // ExecutionContext. Without this, the row has `owner_id = NULL`\n // and the default `owner_only_writes` RLS policy hides it from\n // the very user who just created it.\n //\n // `organization_id` auto-injection has moved to\n // `@objectstack/plugin-org-scoping`. Install that plugin for\n // multi-tenant deployments.\n if (\n opCtx.operation === 'insert' &&\n opCtx.data &&\n typeof opCtx.data === 'object' &&\n !Array.isArray(opCtx.data) &&\n !!opCtx.context?.userId\n ) {\n const fields = await this.getObjectFieldNames(metadata, opCtx.object, ql);\n if (fields) {\n const data = opCtx.data as Record<string, unknown>;\n if (\n fields.has('owner_id') &&\n (data.owner_id == null || data.owner_id === '')\n ) {\n data.owner_id = opCtx.context!.userId;\n }\n }\n }\n\n // 3. RLS filter injection\n const allRlsPolicies = this.collectRLSPolicies(permissionSets, opCtx.object, opCtx.operation);\n if (allRlsPolicies.length > 0 && opCtx.ast) {\n // Field-existence safety: wildcard policies (`object: '*'`) target\n // fields like `organization_id` that may not exist on every object\n // (e.g. system tables, CRM apps that haven't yet adopted multi-tenancy).\n //\n // We treat such policies as a *deny* contribution rather than dropping\n // them, so they fail-closed when no per-object policy provides an\n // alternate match. Any per-object policy that DOES compile against\n // the object will OR-combine and grant access (e.g. `sys_user_self`).\n // When the schema lookup itself fails we keep all policies (drivers\n // will surface column errors clearly during compilation).\n const objectFields = await this.getObjectFieldNames(metadata, opCtx.object, ql);\n const tenancyDisabled = this.tenancyDisabledCache.get(opCtx.object) === true;\n let dropped = 0;\n const compilable = objectFields\n ? allRlsPolicies.filter((p) => {\n const targetField = this.extractTargetField(p.using);\n if (!targetField) return true;\n if (objectFields.has(targetField)) return true;\n // Schema-level opt-out: when the object explicitly\n // disabled tenancy (`tenancy.enabled === false`), the\n // wildcard `tenant_isolation` policy targeting\n // `organization_id` was never meant to apply. Treat as\n // \"not applicable\" — skip silently without contributing\n // to the deny sentinel, mirroring how the registry skips\n // injecting the column itself for these tables.\n if (tenancyDisabled && targetField === 'organization_id') {\n return false;\n }\n dropped++;\n return false;\n })\n : allRlsPolicies;\n let rlsFilter = this.rlsCompiler.compileFilter(compilable, opCtx.context);\n // If every applicable policy was dropped because of missing fields,\n // contribute the deny sentinel (zero rows) — matches the rls-compiler\n // semantics for \"policies were applicable but none compiled\".\n if (rlsFilter == null && dropped > 0) {\n rlsFilter = { ...RLS_DENY_FILTER };\n }\n if (rlsFilter) {\n if (opCtx.ast.where) {\n opCtx.ast.where = { $and: [opCtx.ast.where, rlsFilter] };\n } else {\n opCtx.ast.where = rlsFilter;\n }\n }\n }\n\n await next();\n\n // 4. Field-level security: mask restricted fields in read results\n if (opCtx.result && ['find', 'findOne'].includes(opCtx.operation)) {\n const fieldPerms = this.permissionEvaluator.getFieldPermissions(opCtx.object, permissionSets);\n if (Object.keys(fieldPerms).length > 0) {\n opCtx.result = this.fieldMasker.maskResults(opCtx.result, fieldPerms, opCtx.object);\n }\n }\n });\n\n ctx.logger.info('Security middleware registered on ObjectQL engine');\n\n // Defer platform admin bootstrap until all plugins finish starting —\n // sys_user / sys_permission_set objects must be registered (by\n // plugin-auth and platform-objects respectively) before we can\n // insert seed rows. Falls back to immediate execution when the\n // kernel does not expose `hook` (test stubs).\n let bootstrapRanOnce = false;\n const runBootstrap = async () => {\n try {\n const report = await bootstrapPlatformAdmin(ql, this.bootstrapPermissionSets, {\n logger: ctx.logger,\n });\n bootstrapRanOnce = true;\n ctx.logger.info('[security] platform bootstrap complete', report);\n return report;\n } catch (e) {\n ctx.logger.warn('[security] platform bootstrap failed', { error: (e as Error).message });\n return undefined;\n }\n };\n if (typeof (ctx as any).hook === 'function') {\n (ctx as any).hook('kernel:ready', runBootstrap);\n } else {\n void runBootstrap();\n }\n\n // Re-run bootstrap after a sys_user insert so the FIRST user that\n // signs up after boot is auto-promoted to platform admin (and, in\n // multi-tenant mode, bound to the seeded default organization)\n // without requiring a server restart. The function itself is\n // idempotent and bails out as soon as any platform admin exists.\n //\n // We deliberately do NOT auto-create a \"personal workspace\" for\n // every subsequent self-service signup. In a B2B / invitation-\n // driven product (the framework's primary target), users must\n // either accept an invitation or explicitly create their first\n // organization. The account UI's /register flow already routes\n // users with zero memberships to /organizations/new for exactly\n // this case.\n ql.registerMiddleware(async (opCtx: any, next: () => Promise<void>) => {\n await next();\n if (\n opCtx?.object === 'sys_user' &&\n (opCtx?.operation === 'create' || opCtx?.operation === 'insert')\n ) {\n if (bootstrapRanOnce) {\n await runBootstrap();\n }\n }\n });\n\n // ── Auto-grant `organization_admin` on sys_member lifecycle ─────────\n //\n // For every `sys_member` row whose role is `owner` or `admin`, keep\n // a `sys_user_permission_set` row scoped to that organization in\n // sync. See `auto-org-admin-grant.ts` for the full rationale and\n // the anti-escalation argument (org_admin is read-only on the\n // global RBAC tables, so a freshly-granted admin cannot rebind\n // themselves to `admin_full_access`).\n //\n // We register one middleware that handles insert / update / delete\n // uniformly by always reconciling every (user, org) pair touched\n // by the operation. `reconcileOrgAdminGrant` is idempotent so a\n // double-fire (e.g. better-auth followed by an org plugin\n // synchronizer) is harmless.\n ql.registerMiddleware(async (opCtx: any, next: () => Promise<void>) => {\n await next();\n if (opCtx?.object !== 'sys_member') return;\n const op = opCtx?.operation;\n if (\n op !== 'insert' &&\n op !== 'create' &&\n op !== 'update' &&\n op !== 'delete' &&\n op !== 'remove'\n ) {\n return;\n }\n const pairs = extractMemberPairs(opCtx);\n for (const { userId, orgId } of pairs) {\n try {\n await reconcileOrgAdminGrant(ql, userId, orgId, { logger: ctx.logger });\n } catch (e) {\n ctx.logger.warn?.('[security] org_admin reconcile failed', {\n userId,\n orgId,\n error: (e as Error).message,\n });\n }\n }\n });\n\n // Backfill organization_admin grants after the platform admin\n // bootstrap settles on kernel:ready. Idempotent — only inserts\n // missing rows and revokes orphaned ones, never duplicates.\n const runOrgAdminBackfill = async () => {\n try {\n await backfillOrgAdminGrants(ql, { logger: ctx.logger });\n } catch (e) {\n ctx.logger.warn?.('[security] organization_admin backfill failed', {\n error: (e as Error).message,\n });\n }\n };\n if (typeof (ctx as any).hook === 'function') {\n (ctx as any).hook('kernel:ready', runOrgAdminBackfill);\n } else {\n void runOrgAdminBackfill();\n }\n\n // Per-organization seed data replay on `sys_organization` insert\n // moved to `@objectstack/plugin-org-scoping` (along with\n // `claimOrphanOrgRows` / `cloneOrgSeedData`). Install that\n // plugin for multi-tenant deployments.\n }\n\n async destroy(): Promise<void> {\n // No cleanup needed\n }\n\n /**\n * Collect all RLS policies from permission sets applicable to the given object/operation.\n */\n private collectRLSPolicies(\n permissionSets: PermissionSet[],\n objectName: string,\n operation: string\n ): RowLevelSecurityPolicy[] {\n const allPolicies: RowLevelSecurityPolicy[] = [];\n\n for (const ps of permissionSets) {\n if (ps.rowLevelSecurity) {\n for (const policy of ps.rowLevelSecurity) {\n // When the org-scoping plugin is NOT installed, strip any\n // policy that filters on `current_user.organization_id` —\n // there is no meaningful tenant to compare against, so the\n // policy would either drop every row (when the field exists\n // on the object) or be dropped by the field-existence safety\n // net. Either way it's pure overhead. Substring match is\n // sufficient: every wildcard tenant policy in the default\n // permission sets uses exactly this token. Install\n // `@objectstack/plugin-org-scoping` to enable the\n // multi-tenant behavior.\n if (\n !this.orgScopingEnabled &&\n policy.using &&\n policy.using.includes('current_user.organization_id')\n ) {\n continue;\n }\n allPolicies.push(policy);\n }\n }\n }\n\n return this.rlsCompiler.getApplicablePolicies(objectName, operation, allPolicies);\n }\n\n /**\n * Resolve the column-name set for an object (lowercased). Returns\n * `null` if the schema can't be loaded — caller should fail-closed.\n */\n private async getObjectFieldNames(\n metadata: any,\n objectName: string,\n ql?: any,\n ): Promise<Set<string> | null> {\n if (this.fieldNamesCache.has(objectName)) {\n return this.fieldNamesCache.get(objectName) ?? null;\n }\n const result = await this.loadObjectFieldNames(metadata, objectName, ql);\n // Only cache positive resolutions — a `null` may simply mean the\n // schema isn't registered yet at boot, and we want subsequent calls\n // to retry rather than be permanently denied.\n if (result) {\n this.fieldNamesCache.set(objectName, result);\n }\n return result;\n }\n\n private async loadObjectFieldNames(\n metadata: any,\n objectName: string,\n ql?: any,\n ): Promise<Set<string> | null> {\n try {\n // Prefer ObjectQL's per-engine SchemaRegistry as the source of truth\n // for the live field set: it reflects registry-time augmentations\n // (system-field auto-injection like `organization_id`) that the\n // standalone metadata artifact loaded at boot may not include.\n // Fall back to the metadata service for objects ObjectQL doesn't\n // know about (system tables registered through other paths).\n let obj: any = typeof ql?.getSchema === 'function' ? ql.getSchema(objectName) : null;\n if (!obj || !obj.fields) {\n obj = await metadata?.get?.('object', objectName);\n }\n if (!obj || !obj.fields) return null;\n // Populate the tenancy opt-out cache alongside the field set so\n // the RLS filter pass can decide whether a wildcard\n // `organization_id` policy is genuinely \"applicable but\n // uncompilable\" (deny) versus \"not applicable on this object\"\n // (skip without contributing to the deny sentinel).\n const tenancyDisabled =\n (obj as any)?.tenancy?.enabled === false ||\n (obj as any)?.systemFields?.tenant === false;\n this.tenancyDisabledCache.set(objectName, !!tenancyDisabled);\n const set = new Set<string>(['id']);\n if (Array.isArray(obj.fields)) {\n for (const f of obj.fields) {\n if (f?.name) set.add(String(f.name));\n }\n } else if (typeof obj.fields === 'object') {\n for (const key of Object.keys(obj.fields)) {\n set.add(key);\n const v = (obj.fields as Record<string, any>)[key];\n if (v && typeof v === 'object' && v.name) set.add(String(v.name));\n }\n } else {\n return null;\n }\n return set;\n } catch {\n return null;\n }\n }\n\n /**\n * Extract the left-hand field name from a simple RLS expression like\n * `field = current_user.x` or `field IN (current_user.y)`. Returns\n * `null` for unsupported shapes (in which case we keep the policy).\n */\n private extractTargetField(using?: string): string | null {\n if (!using) return null;\n // Match `field =` or `field IN`/`in`. Note: `\\b` is omitted after `=`\n // because `=` is non-word and the next char (space) is non-word too —\n // a word boundary cannot exist between two non-word chars, so `=\\b`\n // would never match. We instead require the alternation token to be\n // followed by whitespace or `(`.\n const m = using.match(/^\\s*([a-z_][a-z0-9_]*)\\s*(?:=|IN|in)(?=\\s|\\()/);\n return m ? m[1] : null;\n }\n}\n"],"mappings":"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","names":["SYSTEM_CTX","genId","tryFind","tryInsert","import_data","import_data","import_data","SecurityTranslations"]}