spine-framework 0.3.124 → 0.3.126
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/.framework/cli/commands/agents.ts +35 -37
- package/.framework/cli/commands/auth.ts +35 -34
- package/.framework/cli/commands/create-app.ts +104 -40
- package/.framework/cli/commands/dev.ts +104 -69
- package/.framework/cli/commands/doctor.ts +179 -69
- package/.framework/cli/commands/generate.ts +83 -143
- package/.framework/cli/commands/init.ts +72 -34
- package/.framework/cli/commands/install-app.ts +260 -125
- package/.framework/cli/commands/items.ts +33 -44
- package/.framework/cli/commands/migrate.ts +122 -40
- package/.framework/cli/commands/migrations.ts +33 -40
- package/.framework/cli/commands/pipelines.ts +31 -39
- package/.framework/cli/commands/status.ts +47 -23
- package/.framework/cli/commands/system.ts +47 -37
- package/.framework/cli/commands/test.ts +112 -68
- package/.framework/cli/commands/validate-app.ts +215 -121
- package/.framework/cli/context.ts +112 -130
- package/.framework/cli/env-loader.ts +47 -18
- package/.framework/cli/index.ts +81 -42
- package/.framework/docs/runtime-types.md +1024 -0
- package/.framework/functions/_shared/agent-runner.ts +1296 -961
- package/.framework/functions/_shared/app-manifest.ts +82 -68
- package/.framework/functions/_shared/audit.ts +135 -83
- package/.framework/functions/_shared/{cil-state.ts → cil.ts} +158 -56
- package/.framework/functions/_shared/db.ts +80 -126
- package/.framework/functions/_shared/hash-api-key.ts +63 -0
- package/.framework/functions/_shared/index.ts +224 -335
- package/.framework/functions/_shared/kb.ts +319 -0
- package/.framework/functions/_shared/llm.ts +92 -0
- package/.framework/functions/_shared/logging.ts +288 -0
- package/.framework/functions/_shared/middleware.ts +329 -365
- package/.framework/functions/_shared/outcomes.ts +36 -0
- package/.framework/functions/_shared/permissions.ts +359 -643
- package/.framework/functions/_shared/pipeline-runner.ts +243 -316
- package/.framework/functions/_shared/principal.ts +376 -468
- package/.framework/functions/_shared/resolve-credentials.ts +102 -0
- package/.framework/functions/_shared/resolve-ids.ts +188 -16
- package/.framework/functions/_shared/retry-deliveries.ts +104 -0
- package/.framework/functions/_shared/schema-utils.ts +611 -373
- package/.framework/functions/_shared/testing.ts +207 -137
- package/.framework/functions/_shared/tracing.ts +276 -0
- package/.framework/functions/_shared/trigger-engine.ts +159 -211
- package/.framework/functions/_shared/webhook-registration.ts +95 -81
- package/.framework/functions/_shared/webhook-registry.ts +153 -75
- package/.framework/functions/account-nodes.ts +31 -53
- package/.framework/functions/adapters/generic-webhook-inbound.ts +56 -0
- package/.framework/functions/adapters/generic-webhook-outbound.ts +43 -0
- package/.framework/functions/adapters/openai-outbound.ts +40 -0
- package/.framework/functions/admin-data.ts +685 -537
- package/.framework/functions/admin-funnel.ts +31 -20
- package/.framework/functions/admin-install-funnel.ts +47 -28
- package/.framework/functions/admin-signals.ts +47 -29
- package/.framework/functions/ai-agents.ts +122 -147
- package/.framework/functions/ai-interactions.ts +180 -90
- package/.framework/functions/api-keys.ts +250 -192
- package/.framework/functions/apps.ts +203 -197
- package/.framework/functions/auth.ts +98 -91
- package/.framework/functions/cil-agent-ingest.ts +773 -0
- package/.framework/functions/cil-agent.ts +592 -0
- package/.framework/functions/cil-callmap.ts +1227 -0
- package/.framework/functions/cil-config.ts +583 -0
- package/.framework/functions/cil-context.ts +445 -0
- package/.framework/functions/cil-doc.ts +1752 -0
- package/.framework/functions/cil-embeddings.ts +1202 -0
- package/.framework/functions/cil-graph.ts +585 -0
- package/.framework/functions/cil-ingest.ts +2416 -0
- package/.framework/functions/cil-pipeline.ts +318 -0
- package/.framework/functions/cil-project.ts +499 -0
- package/.framework/functions/cil-validate.ts +469 -0
- package/.framework/functions/debug-auth.ts +73 -53
- package/.framework/functions/deliveries.ts +117 -0
- package/.framework/functions/embeddings.ts +528 -516
- package/.framework/functions/eval-harness.ts +158 -106
- package/.framework/functions/integration-outbound.ts +311 -0
- package/.framework/functions/integration-routes.ts +240 -466
- package/.framework/functions/integrations.ts +132 -140
- package/.framework/functions/invites.ts +401 -201
- package/.framework/functions/item-progress.ts +113 -53
- package/.framework/functions/kb.ts +28 -0
- package/.framework/functions/logs.ts +239 -222
- package/.framework/functions/observability.ts +308 -259
- package/.framework/functions/pipeline-executions.ts +179 -235
- package/.framework/functions/pipelines.ts +163 -205
- package/.framework/functions/prompt-configs.ts +109 -156
- package/.framework/functions/roles.ts +97 -147
- package/.framework/functions/system-cron.ts +132 -218
- package/.framework/functions/system.ts +112 -52
- package/.framework/functions/tests.ts +32 -28
- package/.framework/functions/timers.ts +162 -210
- package/.framework/functions/triggers.ts +282 -346
- package/.framework/functions/types.ts +283 -400
- package/.framework/migrations/001_core_schema.sql +630 -0
- package/.framework/migrations/002_core_rls_and_functions.sql +532 -0
- package/.framework/migrations/{003_system_data.sql → 003_core_system_data.sql} +26 -153
- package/.framework/migrations/004_core_rpc_and_broker.sql +421 -0
- package/.framework/migrations/005_core_observability.sql +598 -0
- package/.framework/migrations/006_security_hardening_rls.sql +97 -0
- package/.framework/migrations/007_kb_article_list.sql +237 -0
- package/.framework/migrations/008_items_target.sql +8 -0
- package/.framework/migrations/009_roles_slug_unique.sql +11 -0
- package/.framework/public/spine.config.json +91 -0
- package/.framework/scripts/app-install-cli.ts +112 -98
- package/.framework/src/App.tsx +67 -20
- package/.framework/src/apps/admin/index.tsx +97 -27
- package/.framework/src/components/AppWrapper.tsx +50 -11
- package/.framework/src/components/CustomAppLoader.tsx +131 -22
- package/.framework/src/components/admin/AdminListPage.tsx +75 -8
- package/.framework/src/components/admin/AdminSidebar.tsx +148 -36
- package/.framework/src/components/admin/AdminStatsCard.tsx +48 -29
- package/.framework/src/components/admin/PageInstructions.tsx +49 -0
- package/.framework/src/components/admin/SortableTableHeader.tsx +49 -2
- package/.framework/src/components/app-shell/GenericAppShell.tsx +69 -11
- package/.framework/src/components/app-shell/GenericDetailPage.tsx +55 -10
- package/.framework/src/components/app-shell/GenericListPage.tsx +76 -9
- package/.framework/src/components/app-sidebar.tsx +50 -3
- package/.framework/src/components/auth/ProtectedRoute.tsx +53 -45
- package/.framework/src/components/kb/KBArticleEditor.tsx +409 -0
- package/.framework/src/components/kb/KBArticleList.tsx +557 -0
- package/.framework/src/components/layout/AppShell.tsx +54 -16
- package/.framework/src/components/layout/Header.tsx +50 -38
- package/.framework/src/components/layout/Layout.tsx +48 -32
- package/.framework/src/components/layout/Sidebar.tsx +167 -16
- package/.framework/src/components/runtime/DataDetailHeader.tsx +33 -1
- package/.framework/src/components/runtime/DataDetailPage.tsx +289 -4
- package/.framework/src/components/runtime/DataFilters.tsx +78 -1
- package/.framework/src/components/runtime/DataHeader.tsx +32 -29
- package/.framework/src/components/runtime/DataListPage.tsx +161 -2
- package/.framework/src/components/runtime/DataStats.tsx +48 -1
- package/.framework/src/components/runtime/DataTable.tsx +153 -1
- package/.framework/src/components/runtime/SchemaDetailForm.tsx +77 -46
- package/.framework/src/components/runtime/index.ts +16 -9
- package/.framework/src/components/search-form.tsx +33 -1
- package/.framework/src/components/shared/AgentView.tsx +48 -51
- package/.framework/src/components/shared/FieldRenderer.tsx +84 -67
- package/.framework/src/components/shared/InviteManager.tsx +113 -52
- package/.framework/src/components/shared/MemberList.tsx +182 -0
- package/.framework/src/components/shared/SchemaFields.tsx +96 -78
- package/.framework/src/components/ui/DataTable.tsx +86 -56
- package/.framework/src/components/ui/Form.tsx +188 -109
- package/.framework/src/components/ui/ItemCard.tsx +86 -58
- package/.framework/src/components/ui/ItemListView.tsx +88 -61
- package/.framework/src/components/ui/LoadingSpinner.tsx +52 -27
- package/.framework/src/components/ui/Modal.tsx +81 -4
- package/.framework/src/components/ui/RichTextEditor.tsx +101 -9
- package/.framework/src/components/ui/accordion.tsx +97 -5
- package/.framework/src/components/ui/alert-dialog.tsx +225 -13
- package/.framework/src/components/ui/alert.tsx +113 -6
- package/.framework/src/components/ui/aspect-ratio.tsx +49 -2
- package/.framework/src/components/ui/avatar.tsx +129 -7
- package/.framework/src/components/ui/badge.tsx +65 -3
- package/.framework/src/components/ui/breadcrumb.tsx +145 -8
- package/.framework/src/components/ui/button-group.tsx +81 -4
- package/.framework/src/components/ui/button.tsx +49 -2
- package/.framework/src/components/ui/calendar.tsx +65 -3
- package/.framework/src/components/ui/card.tsx +145 -8
- package/.framework/src/components/ui/carousel.tsx +129 -7
- package/.framework/src/components/ui/chart.tsx +161 -10
- package/.framework/src/components/ui/checkbox.tsx +49 -2
- package/.framework/src/components/ui/collapsible.tsx +81 -5
- package/.framework/src/components/ui/combobox.tsx +305 -20
- package/.framework/src/components/ui/command.tsx +177 -11
- package/.framework/src/components/ui/context-menu.tsx +273 -17
- package/.framework/src/components/ui/dialog.tsx +209 -27
- package/.framework/src/components/ui/direction.tsx +64 -4
- package/.framework/src/components/ui/drawer.tsx +193 -12
- package/.framework/src/components/ui/dropdown-menu.tsx +273 -18
- package/.framework/src/components/ui/empty.tsx +145 -9
- package/.framework/src/components/ui/field.tsx +209 -14
- package/.framework/src/components/ui/hover-card.tsx +81 -4
- package/.framework/src/components/ui/input-group.tsx +162 -10
- package/.framework/src/components/ui/input-otp.tsx +98 -5
- package/.framework/src/components/ui/input.tsx +49 -2
- package/.framework/src/components/ui/item.tsx +225 -13
- package/.framework/src/components/ui/kbd.tsx +65 -3
- package/.framework/src/components/ui/label.tsx +49 -2
- package/.framework/src/components/ui/menubar.tsx +289 -17
- package/.framework/src/components/ui/native-select.tsx +97 -5
- package/.framework/src/components/ui/navigation-menu.tsx +177 -10
- package/.framework/src/components/ui/pagination.tsx +161 -9
- package/.framework/src/components/ui/popover.tsx +145 -8
- package/.framework/src/components/ui/progress.tsx +49 -2
- package/.framework/src/components/ui/radio-group.tsx +65 -3
- package/.framework/src/components/ui/resizable.tsx +82 -4
- package/.framework/src/components/ui/scroll-area.tsx +65 -3
- package/.framework/src/components/ui/select.tsx +193 -11
- package/.framework/src/components/ui/separator.tsx +49 -2
- package/.framework/src/components/ui/sheet.tsx +193 -11
- package/.framework/src/components/ui/sidebar.tsx +481 -38
- package/.framework/src/components/ui/skeleton.tsx +49 -2
- package/.framework/src/components/ui/slider.tsx +49 -2
- package/.framework/src/components/ui/sonner.tsx +49 -2
- package/.framework/src/components/ui/spinner.tsx +49 -2
- package/.framework/src/components/ui/switch.tsx +49 -2
- package/.framework/src/components/ui/table-primitives.tsx +160 -33
- package/.framework/src/components/ui/table.tsx +161 -9
- package/.framework/src/components/ui/tabs.tsx +113 -6
- package/.framework/src/components/ui/textarea.tsx +49 -2
- package/.framework/src/components/ui/toggle-group.tsx +81 -4
- package/.framework/src/components/ui/toggle.tsx +65 -3
- package/.framework/src/components/ui/tooltip.tsx +97 -5
- package/.framework/src/contexts/AppContext.tsx +129 -45
- package/.framework/src/contexts/AuthContext.tsx +187 -144
- package/.framework/src/hooks/use-mobile.ts +48 -2
- package/.framework/src/hooks/useApi.ts +208 -159
- package/.framework/src/hooks/useAppPath.ts +32 -23
- package/.framework/src/hooks/useApps.ts +51 -20
- package/.framework/src/hooks/useEntityList.ts +32 -78
- package/.framework/src/hooks/useEntityRecord.ts +35 -96
- package/.framework/src/hooks/useForm.ts +32 -92
- package/.framework/src/hooks/useListSchema.ts +49 -78
- package/.framework/src/hooks/useSchemaRecord.ts +114 -105
- package/.framework/src/lib/api.ts +195 -116
- package/.framework/src/lib/supabase.ts +98 -64
- package/.framework/src/lib/utils.ts +287 -151
- package/.framework/src/main.tsx +49 -3
- package/.framework/src/pages/DashboardPage.tsx +52 -29
- package/.framework/src/pages/NotFoundPage.tsx +32 -10
- package/.framework/src/pages/SetupPage.tsx +32 -12
- package/.framework/src/pages/admin/AIAgentDetailPage.tsx +119 -10
- package/.framework/src/pages/admin/AIAgentsPage.tsx +48 -19
- package/.framework/src/pages/admin/APIKeyDetailPage.tsx +187 -17
- package/.framework/src/pages/admin/APIKeysPage.tsx +98 -22
- package/.framework/src/pages/admin/AlertsConfigPage.tsx +113 -26
- package/.framework/src/pages/admin/AppDetailPage.tsx +125 -8
- package/.framework/src/pages/admin/AppsPage.tsx +49 -2
- package/.framework/src/pages/admin/DeliveriesPage.tsx +196 -0
- package/.framework/src/pages/admin/DesignedPage.tsx +32 -18
- package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +215 -484
- package/.framework/src/pages/admin/EmbeddingsPage.tsx +116 -319
- package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +34 -9
- package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +34 -9
- package/.framework/src/pages/admin/IntegratedDashboard.tsx +32 -16
- package/.framework/src/pages/admin/IntegrationDetailPage.tsx +183 -26
- package/.framework/src/pages/admin/IntegrationsPage.tsx +48 -19
- package/.framework/src/pages/admin/InvitesPage.tsx +48 -14
- package/.framework/src/pages/admin/LogsPage.tsx +422 -75
- package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +34 -9
- package/.framework/src/pages/admin/ObservabilityDashboard.tsx +113 -24
- package/.framework/src/pages/admin/PipelineDetailPage.tsx +206 -34
- package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +50 -17
- package/.framework/src/pages/admin/PipelinesPage.tsx +160 -21
- package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +378 -88
- package/.framework/src/pages/admin/PromptConfigsPage.tsx +125 -16
- package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +49 -10
- package/.framework/src/pages/admin/RoleDetailPage.tsx +130 -10
- package/.framework/src/pages/admin/RolesPage.tsx +171 -18
- package/.framework/src/pages/admin/SelectTestPage.tsx +34 -9
- package/.framework/src/pages/admin/ShadcnTestPage.tsx +49 -11
- package/.framework/src/pages/admin/SimpleDashboard.tsx +49 -10
- package/.framework/src/pages/admin/TestRunDetailPage.tsx +49 -4
- package/.framework/src/pages/admin/TestingDashboard.tsx +98 -28
- package/.framework/src/pages/admin/TimerDetailPage.tsx +143 -33
- package/.framework/src/pages/admin/TimersPage.tsx +48 -20
- package/.framework/src/pages/admin/TriggerDetailPage.tsx +118 -30
- package/.framework/src/pages/admin/TriggersPage.tsx +64 -19
- package/.framework/src/pages/admin/TypeDetailPage.tsx +138 -15
- package/.framework/src/pages/admin/TypesPage.tsx +48 -20
- package/.framework/src/pages/admin/cil/CILAgentPage.tsx +465 -0
- package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +787 -0
- package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +699 -0
- package/.framework/src/pages/admin/cil/CILFileListPage.tsx +647 -0
- package/.framework/src/pages/admin/cil/CILGraphPage.tsx +616 -0
- package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +1402 -0
- package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +614 -0
- package/.framework/src/pages/admin/cil/CILSearchPage.tsx +571 -0
- package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +731 -0
- package/.framework/src/pages/admin/kb/KBAdminPage.tsx +64 -0
- package/.framework/src/pages/auth/AuthConfirmPage.tsx +52 -51
- package/.framework/src/pages/auth/ForgotPasswordPage.tsx +31 -22
- package/.framework/src/pages/auth/LoginPage.tsx +50 -23
- package/.framework/src/pages/auth/PendingApprovalPage.tsx +31 -15
- package/.framework/src/pages/auth/RegisterPage.tsx +281 -317
- package/.framework/src/pages/spine-framework/APIPage.tsx +33 -0
- package/.framework/src/pages/spine-framework/CLIPage.tsx +33 -0
- package/.framework/src/types/auth.ts +128 -71
- package/.framework/src/types/types.ts +373 -157
- package/custom/.devin/skills/spine-workbench-discovery/SKILL.md +39 -0
- package/custom/.devin/skills/spine-workbench-idea/SKILL.md +36 -0
- package/custom/.devin/skills/spine-workbench-implement/SKILL.md +46 -0
- package/custom/.devin/skills/spine-workbench-initiative/SKILL.md +28 -0
- package/custom/.devin/skills/spine-workbench-plan/SKILL.md +38 -0
- package/docs/README.md +9 -130
- package/netlify.toml +5 -2
- package/package.json +13 -25
- package/scripts/kb-ingest-docs.ts +203 -0
- package/spine.config.json +65 -4
- package/.framework/migrations/001_schema.sql +0 -425
- package/.framework/migrations/002_rls_and_functions.sql +0 -763
- package/.framework/migrations/004_enhancements.sql +0 -43
- package/.framework/migrations/005_auth_user_trigger.sql +0 -105
- package/.framework/migrations/006_fix_current_actor_id.sql +0 -21
- package/.framework/migrations/016_invites.sql +0 -27
- package/.framework/migrations/017_fix_get_account_apps.sql +0 -78
- package/.framework/migrations/018_apps_required_roles.sql +0 -17
- package/.framework/migrations/019_triggers_unique_constraint.sql +0 -13
- package/.framework/migrations/020_get_account_apps_role_filter.sql +0 -117
- package/.framework/migrations/021_app_domains.sql +0 -112
- package/.framework/migrations/022_route_prefix_domain_unique.sql +0 -21
- package/.framework/migrations/023_match_embeddings_rpc.sql +0 -78
- package/.framework/migrations/024_messages_account_id.sql +0 -9
- package/.framework/migrations/025_retype_threads_messages.sql +0 -240
- package/.framework/migrations/026_fix_rls_policies.sql +0 -47
- package/.framework/migrations/027_embedding_lifecycle.sql +0 -176
- package/.framework/migrations/028_fix_message_data_permissions.sql +0 -49
- package/.framework/migrations/029_tighten_support_triage_prompt.sql +0 -41
- package/.framework/migrations/030_messages_updated_by.sql +0 -6
- package/.framework/migrations/031_seed_upsert_constraints.sql +0 -35
- package/docs/APPS.md +0 -232
- package/docs/ARCHITECTURE.md +0 -246
- package/docs/INSTALL.md +0 -155
- package/docs/design-schema-spec.md +0 -1837
- package/docs/dev-guides/01-create-an-app.md +0 -183
- package/docs/dev-guides/02-create-a-function.md +0 -178
- package/docs/dev-guides/03-create-a-component.md +0 -178
- package/docs/dev-guides/04-webhook-handlers.md +0 -129
- package/docs/dev-guides/05-testing.md +0 -168
- package/docs/dev-guides/README.md +0 -40
- package/docs/npm-packages-plan.md +0 -288
- package/docs/spine-core--api-reference.md +0 -822
|
@@ -1,64 +1,38 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
* @seeAlso schema-utils.ts (formatFieldData, sanitizeFieldData called during sanitization)
|
|
33
|
-
* @seeAlso index.ts (stable export surface for custom code)
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
|
-
// ─── CHUNK_END: PERMISSIONS_OVERVIEW ────────────────────────────────────────────────
|
|
37
|
-
|
|
38
|
-
// ─── CHUNK_START: PERMISSIONS_TYPES ──────────────────────────────────────────────
|
|
1
|
+
// CIL[BEGIN chunk_id="permissions_moduleheader_1784066609" type="module_header" identity="module_header" parent="none"]
|
|
2
|
+
// CIL[DOC-MACRO purpose="Permission system for data access control"]
|
|
3
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Manage record and field-level permissions"]
|
|
4
|
+
// CIL[DOC-CONTEXT module="_shared/permissions" layer="infrastructure" related_files="[\".framework/functions/_shared/principal.ts\",\".framework/functions/_shared/schema-utils.ts\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\".framework/src/pages/admin/IntegrationsPage.tsx\",\"\\\"]"]
|
|
5
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="permission utilities" errors="none" side_effects="access control"]
|
|
6
|
+
// CIL[DOC-MICRO logic_summary="Defines PermissionEngine and permission resolution logic" notable_decisions="[]"]
|
|
7
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
8
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
9
|
+
// CIL[DOC-AUDIENCE audience="developer"]
|
|
10
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: 0. Direct calls: 0. Related files: .framework/functions/_shared/principal.ts, .framework/functions/_shared/schema-utils.ts, .framework/src/pages/admin/TypeDetailPage.tsx, .framework/src/pages/admin/IntegrationsPage.tsx, \."]
|
|
11
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
12
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
13
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
14
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
15
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
16
|
+
// CIL[END chunk_id="permissions_moduleheader_1784066609"]
|
|
17
|
+
// CIL[BEGIN chunk_id="permissions_types_1784066609" type="module_header" identity="types" parent="none"]
|
|
18
|
+
// CIL[DOC-MACRO purpose="Permission types and interfaces"]
|
|
19
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Define permission data structures"]
|
|
20
|
+
// CIL[DOC-CONTEXT module="_shared/permissions" layer="infrastructure" related_files="[\"custom/apps/portal/pages/ContentPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\".framework/functions/_shared/principal.ts\",\".framework/src/components/ui/dialog.tsx\",\"\\\"]"]
|
|
21
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="type definitions" errors="none" side_effects="type definitions"]
|
|
22
|
+
// CIL[DOC-MICRO logic_summary="Defines PermissionResult interface and imports" notable_decisions="[]"]
|
|
23
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
24
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
25
|
+
// CIL[DOC-AUDIENCE audience="developer"]
|
|
26
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: 0. Direct calls: 0. Related files: custom/apps/portal/pages/ContentPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/pages/admin/RolesPage.tsx, .framework/functions/_shared/principal.ts, .framework/src/components/ui/dialog.tsx, \."]
|
|
27
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
28
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
29
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
30
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
31
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
39
32
|
import { adminDb } from './db'
|
|
40
33
|
import { Principal } from './principal'
|
|
41
34
|
import { CoreContext } from './middleware'
|
|
42
35
|
|
|
43
|
-
/**
|
|
44
|
-
* Result of a permission resolution for a principal + record + action combination.
|
|
45
|
-
*
|
|
46
|
-
* Returned by `resolveFirstSurfacePermissions`. Captures both record-level CRUD
|
|
47
|
-
* flags and per-field read/write flags derived from `design_schema`.
|
|
48
|
-
*
|
|
49
|
-
* All flags default to `false` on any error or missing schema — never assume
|
|
50
|
-
* a missing flag means "allowed".
|
|
51
|
-
*
|
|
52
|
-
* @inputSpec none — this is a pure output type
|
|
53
|
-
* @outputSpec canCreate: boolean — principal may create records of this type
|
|
54
|
-
* @outputSpec canRead: boolean — principal may read this record
|
|
55
|
-
* @outputSpec canUpdate: boolean — principal may update this record
|
|
56
|
-
* @outputSpec canDelete: boolean — principal may delete this record
|
|
57
|
-
* @outputSpec fieldPermissions: Record<fieldName, {read, write}> — per-field flags
|
|
58
|
-
* derived from design_schema.fields[x].permissions merged across all roles
|
|
59
|
-
* @calledBy resolveFirstSurfacePermissions (producer), sanitizeFirstSurfaceRecordData,
|
|
60
|
-
* validateFirstSurfaceUpdatePermissions, canAccessFirstSurfaceRecord (consumers)
|
|
61
|
-
*/
|
|
62
36
|
export interface PermissionResult {
|
|
63
37
|
canCreate: boolean
|
|
64
38
|
canRead: boolean
|
|
@@ -68,34 +42,39 @@ export interface PermissionResult {
|
|
|
68
42
|
}
|
|
69
43
|
|
|
70
44
|
type RequestContext = CoreContext
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
//
|
|
45
|
+
// CIL[END chunk_id="permissions_types_1784066609"]
|
|
46
|
+
// CIL[BEGIN chunk_id="permissions_engineclass_1784066609" type="class" identity="_PermissionEngineInternal" parent="none"]
|
|
47
|
+
// CIL[DOC-MACRO purpose="Permission engine internal class placeholder"]
|
|
48
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Define permission engine structure"]
|
|
49
|
+
// CIL[DOC-CONTEXT module="_shared/permissions" layer="infrastructure" related_files="[\".framework/functions/_shared/principal.ts\",\"\\\"]"]
|
|
50
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="class definition" errors="none" side_effects="none"]
|
|
51
|
+
// CIL[DOC-MICRO logic_summary="Empty class definition for permission engine" notable_decisions="[]"]
|
|
52
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
53
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
54
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: 0. Direct calls: 0. Related files: .framework/functions/_shared/principal.ts, \."]
|
|
55
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
56
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
57
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
58
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
59
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
60
|
+
// CIL[END chunk_id="permissions_engineclass_1784066609"]
|
|
61
|
+
// CIL[BEGIN chunk_id="permissions_classification_1784066609" type="class" identity="_PermissionEngineInternal" parent="permissions_engineclass_1784066609"]
|
|
62
|
+
// CIL[DOC-MACRO purpose="Permission engine implementation"]
|
|
63
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Implement permission resolution logic"]
|
|
64
|
+
// CIL[DOC-CONTEXT module="_shared/permissions" layer="infrastructure" related_files="[\".framework/functions/_shared/principal.ts\",\"\\\"]"]
|
|
65
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="permission methods" errors="none" side_effects="permission evaluation"]
|
|
66
|
+
// CIL[DOC-MICRO logic_summary="Implements singleton permission engine with surface detection" notable_decisions="[]"]
|
|
67
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
68
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
69
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: 0. Direct calls: 0. Related files: .framework/functions/_shared/principal.ts, \."]
|
|
70
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
71
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
72
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
73
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
74
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
95
75
|
class _PermissionEngineInternal {
|
|
96
76
|
private static instance: _PermissionEngineInternal
|
|
97
77
|
|
|
98
|
-
// Surface classification tables
|
|
99
78
|
private readonly SECOND_SURFACE_TABLES = new Set([
|
|
100
79
|
'apps', 'app', 'pipelines', 'triggers', 'ai_agents', 'embeddings',
|
|
101
80
|
'timers', 'integrations', 'roles', 'types', 'prompt_configs'
|
|
@@ -108,22 +87,6 @@ class _PermissionEngineInternal {
|
|
|
108
87
|
|
|
109
88
|
private constructor() {}
|
|
110
89
|
|
|
111
|
-
/**
|
|
112
|
-
* Classifies a table name into one of Spine's three permission surfaces.
|
|
113
|
-
*
|
|
114
|
-
* Surface membership is determined by static set membership — if a table is
|
|
115
|
-
* not in SECOND_SURFACE_TABLES or THIRD_SURFACE_TABLES, it defaults to first.
|
|
116
|
-
* This is intentionally conservative: unknown tables get the most restrictive
|
|
117
|
-
* surface (first), which requires a valid design_schema to grant any access.
|
|
118
|
-
*
|
|
119
|
-
* @param tableName - Table name string (e.g. 'items', 'pipelines', 'logs')
|
|
120
|
-
* @returns 'first' | 'second' | 'third' — surface classification
|
|
121
|
-
* @throws never
|
|
122
|
-
* @inputSpec tableName: string — any string; unknown names → 'first'
|
|
123
|
-
* @outputSpec 'first' | 'second' | 'third'
|
|
124
|
-
* @sideEffects none
|
|
125
|
-
* @calledBy canAccessRecord, sanitizeRecordData, validateUpdatePermissions
|
|
126
|
-
*/
|
|
127
90
|
private detectSurface(tableName: string): 'first' | 'second' | 'third' {
|
|
128
91
|
if (this.SECOND_SURFACE_TABLES.has(tableName)) {
|
|
129
92
|
return 'second'
|
|
@@ -134,109 +97,47 @@ class _PermissionEngineInternal {
|
|
|
134
97
|
return 'first'
|
|
135
98
|
}
|
|
136
99
|
|
|
137
|
-
/**
|
|
138
|
-
* Extracts a table/type name from a record to use for surface classification.
|
|
139
|
-
*
|
|
140
|
-
* Tries multiple fields in priority order: `record.table_name` (explicitly
|
|
141
|
-
* set by some handlers), `record.type`, `record.item_type`, then the
|
|
142
|
-
* `typeSlug` param. Falls back to `'unknown'` which routes to first surface.
|
|
143
|
-
*
|
|
144
|
-
* @param record - The record object being classified
|
|
145
|
-
* @param typeSlug - Optional caller-provided type slug (used as last resort)
|
|
146
|
-
* @returns string — table name used to classify the permission surface
|
|
147
|
-
* @throws never
|
|
148
|
-
* @inputSpec record: object — any record; missing fields are safely ignored
|
|
149
|
-
* @inputSpec typeSlug: string | undefined — optional fallback
|
|
150
|
-
* @outputSpec string — one of the known table names, or 'unknown'
|
|
151
|
-
* @sideEffects none
|
|
152
|
-
* @calledBy canAccessRecord, sanitizeRecordData, validateUpdatePermissions
|
|
153
|
-
*/
|
|
154
100
|
private extractTableName(record: any, typeSlug?: string): string {
|
|
155
|
-
// Try to get table name from record context
|
|
156
101
|
if (record?.table_name) {
|
|
157
102
|
return record.table_name
|
|
158
103
|
}
|
|
159
104
|
|
|
160
|
-
// Try to get from type field
|
|
161
105
|
if (record?.type) {
|
|
162
106
|
return record.type
|
|
163
107
|
}
|
|
164
108
|
|
|
165
|
-
// Try to get from item_type field
|
|
166
109
|
if (record?.item_type) {
|
|
167
110
|
return record.item_type
|
|
168
111
|
}
|
|
169
112
|
|
|
170
|
-
// Use provided typeSlug
|
|
171
113
|
if (typeSlug) {
|
|
172
114
|
return typeSlug
|
|
173
115
|
}
|
|
174
116
|
|
|
175
|
-
// Default to unknown (will be treated as first surface)
|
|
176
117
|
return 'unknown'
|
|
177
118
|
}
|
|
178
119
|
|
|
179
|
-
/**
|
|
180
|
-
* Returns the singleton instance. Called once at module load time to
|
|
181
|
-
* initialise `PermissionEngine`. Not for direct use outside this file.
|
|
182
|
-
*
|
|
183
|
-
* @returns _PermissionEngineInternal — the single shared instance
|
|
184
|
-
* @throws never
|
|
185
|
-
* @sideEffects creates instance on first call (subsequent calls return cached)
|
|
186
|
-
* @calledBy module initialisation (bottom of this file)
|
|
187
|
-
*/
|
|
188
120
|
static getInstance(): _PermissionEngineInternal {
|
|
189
121
|
if (!_PermissionEngineInternal.instance) {
|
|
190
122
|
_PermissionEngineInternal.instance = new _PermissionEngineInternal()
|
|
191
123
|
}
|
|
192
124
|
return _PermissionEngineInternal.instance
|
|
193
125
|
}
|
|
194
|
-
|
|
195
|
-
//
|
|
196
|
-
|
|
197
|
-
//
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
* 6. For fields with no explicit permission, inherit from record-level flags
|
|
210
|
-
*
|
|
211
|
-
* Returns all-deny `PermissionResult` on any error — never throws.
|
|
212
|
-
*
|
|
213
|
-
* @param personId - UUID of the person making the request (from principal.id)
|
|
214
|
-
* @param accountId - UUID of the account context for the operation
|
|
215
|
-
* @param typeSlug - Slug of the type to look up design_schema if not pre-stamped
|
|
216
|
-
* @param _action - CRUD action (currently used for context; merge logic is role-based)
|
|
217
|
-
* @param designSchema - Pre-loaded design_schema object (skips DB lookup if provided)
|
|
218
|
-
*
|
|
219
|
-
* @inputSpec personId: string — valid UUID, must exist in people table with is_active=true
|
|
220
|
-
* @inputSpec accountId: string — valid UUID of accessible account
|
|
221
|
-
* @inputSpec typeSlug: string — slug of a type in the types table with is_active=true
|
|
222
|
-
* @inputSpec designSchema: object | undefined — if provided, must have record_permissions
|
|
223
|
-
* @outputSpec PermissionResult — all flags false on error/missing schema
|
|
224
|
-
* @throws never — catches all errors, returns defaultResult
|
|
225
|
-
* @sideEffects DB read: types table (if schema not pre-stamped), people table (role lookup)
|
|
226
|
-
* @calledBy canAccessFirstSurfaceRecord, sanitizeFirstSurfaceRecordData,
|
|
227
|
-
* validateFirstSurfaceUpdatePermissions
|
|
228
|
-
* @calls adminDb.from('types'), adminDb.from('people')
|
|
229
|
-
* @testUnit tests/unit/permissions.test.ts — 'resolveFirstSurfacePermissions' describe block
|
|
230
|
-
*
|
|
231
|
-
* @example Import usage (v2-custom/)
|
|
232
|
-
* ```ts
|
|
233
|
-
* import { PermissionEngine } from '../_shared/index'
|
|
234
|
-
* const perms = await PermissionEngine.resolveFirstSurfacePermissions(
|
|
235
|
-
* ctx.principal.id, ctx.accountId, 'ticket', 'read'
|
|
236
|
-
* )
|
|
237
|
-
* if (!perms.canRead) return { error: 'Forbidden' }
|
|
238
|
-
* ```
|
|
239
|
-
*/
|
|
126
|
+
// CIL[END chunk_id="permissions_classification_1784066609"]
|
|
127
|
+
// CIL[BEGIN chunk_id="permissions_resolvefirstsurface_1784066609" type="function" identity="resolveFirstSurfacePermissions" parent="permissions_classification_1784066609"]
|
|
128
|
+
// CIL[DOC-MACRO purpose="Resolve first surface permissions"]
|
|
129
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Evaluate record and field permissions for runtime data"]
|
|
130
|
+
// CIL[DOC-CONTEXT module="_shared/permissions" layer="infrastructure" related_files="[\".framework/migrations/025_retype_threads_messages.sql\",\".framework/functions/_shared/principal.ts\",\"\\\"]"]
|
|
131
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="PermissionResult" errors="database errors" side_effects="database reads"]
|
|
132
|
+
// CIL[DOC-MICRO logic_summary="Resolves permissions based on role and design schema" notable_decisions="[]"]
|
|
133
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="complex_dependencies"]
|
|
134
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
135
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: 0. Direct calls: 0. Related files: .framework/migrations/025_retype_threads_messages.sql, .framework/functions/_shared/principal.ts, \."]
|
|
136
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
137
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
138
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
139
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
140
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
240
141
|
async resolveFirstSurfacePermissions(
|
|
241
142
|
personId: string,
|
|
242
143
|
accountId: string,
|
|
@@ -338,30 +239,21 @@ class _PermissionEngineInternal {
|
|
|
338
239
|
return defaultResult
|
|
339
240
|
}
|
|
340
241
|
}
|
|
341
|
-
|
|
342
|
-
//
|
|
343
|
-
|
|
344
|
-
//
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
* @param action - CRUD action being attempted
|
|
357
|
-
* @returns boolean — true if access is allowed
|
|
358
|
-
* @throws never
|
|
359
|
-
* @inputSpec ctx.principal: Principal — must be resolved (not anonymous)
|
|
360
|
-
* @inputSpec action: 'create' | 'read' | 'update' | 'delete'
|
|
361
|
-
* @outputSpec boolean — true = allowed, false = denied
|
|
362
|
-
* @sideEffects none
|
|
363
|
-
* @calledBy canAccessRecord (surface='second'), validateConfigObjectPermissions
|
|
364
|
-
*/
|
|
242
|
+
// CIL[END chunk_id="permissions_resolvefirstsurface_1784066609"]
|
|
243
|
+
// CIL[BEGIN chunk_id="permissions_canaccessconfig_1784066609" type="function" identity="canAccessConfigObject" parent="permissions_classification_1784066609"]
|
|
244
|
+
// CIL[DOC-MACRO purpose="Export permission engine instance and methods"]
|
|
245
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Provide public API for permission system"]
|
|
246
|
+
// CIL[DOC-CONTEXT module="_shared/permissions" layer="infrastructure" related_files="[]"]
|
|
247
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="exports" errors="none" side_effects="module exports"]
|
|
248
|
+
// CIL[DOC-MICRO logic_summary="Exports PermissionEngine singleton and bound methods" notable_decisions="[]"]
|
|
249
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
250
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
251
|
+
// CIL[DOC-VECTOR type="context" content="Layer: infrastructure. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
252
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
253
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
254
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
255
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
256
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
365
257
|
private canAccessConfigObject(ctx: RequestContext, action: 'create' | 'read' | 'update' | 'delete'): boolean {
|
|
366
258
|
// System admin has full access
|
|
367
259
|
if (this.isSystemAdmin(ctx)) {
|
|
@@ -377,24 +269,21 @@ class _PermissionEngineInternal {
|
|
|
377
269
|
return false
|
|
378
270
|
}
|
|
379
271
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
* @sideEffects none
|
|
396
|
-
* @calledBy sanitizeRecordData (surface='second')
|
|
397
|
-
*/
|
|
272
|
+
// CIL[END chunk_id="permissions_canaccessconfig_1784066609"]
|
|
273
|
+
// CIL[BEGIN chunk_id="permissions_sanitizeconfig_1784066609" type="function" identity="sanitizeConfigObject" parent="permissions_classification_1784066609"]
|
|
274
|
+
// CIL[DOC-MACRO purpose="sanitizeConfigObject"]
|
|
275
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Handle sanitizeConfigObject"]
|
|
276
|
+
// CIL[DOC-CONTEXT module="sanitizeConfigObject" layer="unknown" related_files="[]"]
|
|
277
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
278
|
+
// CIL[DOC-MICRO logic_summary="Implements sanitizeConfigObject" notable_decisions="[]"]
|
|
279
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
280
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
281
|
+
// CIL[DOC-VECTOR type="context" content="Layer: unknown. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
282
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
283
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
284
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
285
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
286
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
398
287
|
private sanitizeConfigObject(ctx: RequestContext, record: any): any {
|
|
399
288
|
// Debug logging
|
|
400
289
|
console.log('sanitizeConfigObject called with record:', {
|
|
@@ -448,22 +337,21 @@ class _PermissionEngineInternal {
|
|
|
448
337
|
}
|
|
449
338
|
}
|
|
450
339
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
*/
|
|
340
|
+
// CIL[END chunk_id="permissions_sanitizeconfig_1784066609"]
|
|
341
|
+
// CIL[BEGIN chunk_id="permissions_validateconfig_1784066609" type="function" identity="validateConfigObjectPermissions" parent="permissions_classification_1784066609"]
|
|
342
|
+
// CIL[DOC-MACRO purpose="validateConfigObjectPermissions"]
|
|
343
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Handle validateConfigObjectPermissions"]
|
|
344
|
+
// CIL[DOC-CONTEXT module="validateConfigObjectPermissions" layer="unknown" related_files="[]"]
|
|
345
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
346
|
+
// CIL[DOC-MICRO logic_summary="Implements validateConfigObjectPermissions" notable_decisions="[]"]
|
|
347
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
348
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
349
|
+
// CIL[DOC-VECTOR type="context" content="Layer: unknown. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
350
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
351
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
352
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
353
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
354
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
467
355
|
private validateConfigObjectPermissions(ctx: RequestContext, action: 'create' | 'read' | 'update' | 'delete'): { valid: boolean; error?: string } {
|
|
468
356
|
if (this.canAccessConfigObject(ctx, action)) {
|
|
469
357
|
return { valid: true }
|
|
@@ -472,36 +360,21 @@ class _PermissionEngineInternal {
|
|
|
472
360
|
return { valid: false, error: 'Insufficient permissions for this operation' }
|
|
473
361
|
}
|
|
474
362
|
|
|
475
|
-
//
|
|
476
|
-
|
|
477
|
-
//
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
* - human principal (create/update/delete): always denied
|
|
491
|
-
*
|
|
492
|
-
* @param ctx - Request context
|
|
493
|
-
* @param record - The system metadata record being accessed
|
|
494
|
-
* @param action - CRUD action being attempted
|
|
495
|
-
* @returns boolean — true if access is allowed
|
|
496
|
-
* @throws never
|
|
497
|
-
* @inputSpec ctx.principal: Principal — resolved principal
|
|
498
|
-
* @inputSpec record: object — must have at least one of: created_by, account_id, person_id
|
|
499
|
-
* @inputSpec action: 'create' | 'read' | 'update' | 'delete'
|
|
500
|
-
* @outputSpec boolean
|
|
501
|
-
* @sideEffects none
|
|
502
|
-
* @calledBy canAccessRecord (surface='third'), sanitizeSystemMetadata,
|
|
503
|
-
* validateSystemMetadataPermissions
|
|
504
|
-
*/
|
|
363
|
+
// CIL[END chunk_id="permissions_validateconfig_1784066609"]
|
|
364
|
+
// CIL[BEGIN chunk_id="permissions_canaccesssystem_1784066609" type="function" identity="canAccessSystemMetadata" parent="permissions_classification_1784066609"]
|
|
365
|
+
// CIL[DOC-MACRO purpose="canAccessSystemMetadata"]
|
|
366
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Handle canAccessSystemMetadata"]
|
|
367
|
+
// CIL[DOC-CONTEXT module="canAccessSystemMetadata" layer="unknown" related_files="[]"]
|
|
368
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
369
|
+
// CIL[DOC-MICRO logic_summary="Implements canAccessSystemMetadata" notable_decisions="[]"]
|
|
370
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
371
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
372
|
+
// CIL[DOC-VECTOR type="context" content="Layer: unknown. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
373
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
374
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
375
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
376
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
377
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
505
378
|
private canAccessSystemMetadata(ctx: RequestContext, record: any, action: 'create' | 'read' | 'update' | 'delete'): boolean {
|
|
506
379
|
// System admin has full access
|
|
507
380
|
if (this.isSystemAdmin(ctx)) {
|
|
@@ -535,22 +408,21 @@ class _PermissionEngineInternal {
|
|
|
535
408
|
return false
|
|
536
409
|
}
|
|
537
410
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
*/
|
|
411
|
+
// CIL[END chunk_id="permissions_canaccesssystem_1784066609"]
|
|
412
|
+
// CIL[BEGIN chunk_id="permissions_sanitizesystem_1784066609" type="function" identity="sanitizeSystemMetadata" parent="permissions_classification_1784066609"]
|
|
413
|
+
// CIL[DOC-MACRO purpose="sanitizeSystemMetadata"]
|
|
414
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Handle sanitizeSystemMetadata"]
|
|
415
|
+
// CIL[DOC-CONTEXT module="sanitizeSystemMetadata" layer="unknown" related_files="[]"]
|
|
416
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
417
|
+
// CIL[DOC-MICRO logic_summary="Implements sanitizeSystemMetadata" notable_decisions="[]"]
|
|
418
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
419
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
420
|
+
// CIL[DOC-VECTOR type="context" content="Layer: unknown. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
421
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
422
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
423
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
424
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
425
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
554
426
|
private sanitizeSystemMetadata(ctx: RequestContext, record: any): any {
|
|
555
427
|
// System admin and system role see everything
|
|
556
428
|
if (this.isSystemAdmin(ctx) || ctx.principal?.type === 'machine') {
|
|
@@ -570,21 +442,21 @@ class _PermissionEngineInternal {
|
|
|
570
442
|
}
|
|
571
443
|
}
|
|
572
444
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
445
|
+
// CIL[END chunk_id="permissions_sanitizesystem_1784066609"]
|
|
446
|
+
// CIL[BEGIN chunk_id="permissions_validatesystem_1784066609" type="function" identity="validateSystemMetadataPermissions" parent="permissions_classification_1784066609"]
|
|
447
|
+
// CIL[DOC-MACRO purpose="validateSystemMetadataPermissions"]
|
|
448
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Handle validateSystemMetadataPermissions"]
|
|
449
|
+
// CIL[DOC-CONTEXT module="validateSystemMetadataPermissions" layer="unknown" related_files="[]"]
|
|
450
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
451
|
+
// CIL[DOC-MICRO logic_summary="Implements validateSystemMetadataPermissions" notable_decisions="[]"]
|
|
452
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
453
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
454
|
+
// CIL[DOC-VECTOR type="context" content="Layer: unknown. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
455
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
456
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
457
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
458
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
459
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
588
460
|
private validateSystemMetadataPermissions(ctx: RequestContext, record: any, action: 'create' | 'read' | 'update' | 'delete'): { valid: boolean; error?: string } {
|
|
589
461
|
if (this.canAccessSystemMetadata(ctx, record, action)) {
|
|
590
462
|
return { valid: true }
|
|
@@ -593,59 +465,40 @@ class _PermissionEngineInternal {
|
|
|
593
465
|
return { valid: false, error: 'Insufficient permissions for this operation' }
|
|
594
466
|
}
|
|
595
467
|
|
|
596
|
-
//
|
|
597
|
-
|
|
598
|
-
//
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
* @outputSpec boolean — false if principal is null, anonymous, or has no roles
|
|
612
|
-
* @sideEffects none
|
|
613
|
-
* @calledBy canAccessRecord, sanitizeRecordData, validateUpdatePermissions,
|
|
614
|
-
* canAccessConfigObject, sanitizeConfigObject, sanitizeSystemMetadata,
|
|
615
|
-
* canPrincipalAccessRecord
|
|
616
|
-
* @testUnit tests/unit/permissions.test.ts — 'isSystemAdmin' describe block
|
|
617
|
-
*/
|
|
468
|
+
// CIL[END chunk_id="permissions_validatesystem_1784066609"]
|
|
469
|
+
// CIL[BEGIN chunk_id="permissions_issystemadmin_1784066609" type="function" identity="isSystemAdmin" parent="permissions_classification_1784066609"]
|
|
470
|
+
// CIL[DOC-MACRO purpose="isSystemAdmin"]
|
|
471
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Handle isSystemAdmin"]
|
|
472
|
+
// CIL[DOC-CONTEXT module="isSystemAdmin" layer="unknown" related_files="[]"]
|
|
473
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
474
|
+
// CIL[DOC-MICRO logic_summary="Implements isSystemAdmin" notable_decisions="[]"]
|
|
475
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
476
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
477
|
+
// CIL[DOC-VECTOR type="context" content="Layer: unknown. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
478
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
479
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
480
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
481
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
482
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
618
483
|
isSystemAdmin(ctx: RequestContext): boolean {
|
|
619
|
-
return ctx.principal?.roles?.
|
|
484
|
+
return ctx.principal?.roles?.some((r: any) => r?.slug === 'system_admin' || r === 'system_admin') || false
|
|
620
485
|
}
|
|
621
486
|
|
|
622
|
-
//
|
|
623
|
-
|
|
624
|
-
//
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
* @param record - The record being accessed (used for surface detection only)
|
|
638
|
-
* @param action - CRUD action being attempted
|
|
639
|
-
* @returns Promise<boolean> — true if access is allowed
|
|
640
|
-
* @throws never — all surface handlers catch errors and return false
|
|
641
|
-
* @inputSpec ctx.principal: Principal — must be resolved
|
|
642
|
-
* @inputSpec record: object — used for table_name/type/item_type extraction
|
|
643
|
-
* @inputSpec action: 'create' | 'read' | 'update' | 'delete'
|
|
644
|
-
* @outputSpec boolean — false for anonymous principals, missing records, unknown types
|
|
645
|
-
* @sideEffects DB read (first surface only): types and people tables
|
|
646
|
-
* @calledBy API handlers where explicit access gate is needed (rare — most use sanitize)
|
|
647
|
-
* @testUnit tests/unit/permissions.test.ts — 'canAccessRecord' describe block
|
|
648
|
-
*/
|
|
487
|
+
// CIL[END chunk_id="permissions_issystemadmin_1784066609"]
|
|
488
|
+
// CIL[BEGIN chunk_id="permissions_canaccessrecord_1784066609" type="function" identity="canAccessRecord" parent="permissions_classification_1784066609"]
|
|
489
|
+
// CIL[DOC-MACRO purpose="canAccessRecord"]
|
|
490
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Handle canAccessRecord"]
|
|
491
|
+
// CIL[DOC-CONTEXT module="canAccessRecord" layer="unknown" related_files="[]"]
|
|
492
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
493
|
+
// CIL[DOC-MICRO logic_summary="Implements canAccessRecord" notable_decisions="[]"]
|
|
494
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
495
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
496
|
+
// CIL[DOC-VECTOR type="context" content="Layer: unknown. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
497
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
498
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
499
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
500
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
501
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
649
502
|
async canAccessRecord(
|
|
650
503
|
ctx: RequestContext,
|
|
651
504
|
record: any,
|
|
@@ -673,27 +526,22 @@ class _PermissionEngineInternal {
|
|
|
673
526
|
return this.canAccessFirstSurfaceRecord(ctx, record, action)
|
|
674
527
|
}
|
|
675
528
|
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
* @inputSpec record: must have account_id or item_type/type for schema resolution
|
|
693
|
-
* @outputSpec boolean — false for anonymous, missing schema, insufficient permissions
|
|
694
|
-
* @sideEffects DB read: types and people tables (via resolveFirstSurfacePermissions)
|
|
695
|
-
* @calledBy canAccessRecord (surface='first'), canPrincipalAccessRecord (human branch)
|
|
696
|
-
*/
|
|
529
|
+
// CIL[END chunk_id="permissions_canaccessrecord_1784066609"]
|
|
530
|
+
|
|
531
|
+
// CIL[BEGIN chunk_id="permissions_canaccessfirst_1784066609" type="function" identity="canAccessFirstSurfaceRecord" parent="permissions_classification_1784066609"]
|
|
532
|
+
// CIL[DOC-MACRO purpose="canAccessFirstSurfaceRecord"]
|
|
533
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Handle canAccessFirstSurfaceRecord"]
|
|
534
|
+
// CIL[DOC-CONTEXT module="canAccessFirstSurfaceRecord" layer="unknown" related_files="[]"]
|
|
535
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
536
|
+
// CIL[DOC-MICRO logic_summary="Implements canAccessFirstSurfaceRecord" notable_decisions="[]"]
|
|
537
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
538
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
539
|
+
// CIL[DOC-VECTOR type="context" content="Layer: unknown. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
540
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
541
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
542
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
543
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
544
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
697
545
|
private async canAccessFirstSurfaceRecord(
|
|
698
546
|
ctx: RequestContext,
|
|
699
547
|
record: any,
|
|
@@ -746,52 +594,21 @@ class _PermissionEngineInternal {
|
|
|
746
594
|
return true
|
|
747
595
|
}
|
|
748
596
|
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
* if not already stamped on the record. Optional for second/third surfaces.
|
|
765
|
-
* @returns Promise<object> — sanitized record safe to return to the client
|
|
766
|
-
* @throws never
|
|
767
|
-
* @inputSpec ctx.principal: Principal — resolved, may be anonymous
|
|
768
|
-
* @inputSpec record: object — raw DB row, must have id at minimum
|
|
769
|
-
* @inputSpec typeSlug: string | undefined — slug of the type (e.g. 'item', 'account')
|
|
770
|
-
* @outputSpec object — filtered record; field set depends on principal's role permissions
|
|
771
|
-
* @outputSpec system_admin: full record unchanged
|
|
772
|
-
* @outputSpec unauthenticated: { id, created_at, updated_at } only
|
|
773
|
-
* @outputSpec first surface, no schema: { id } only
|
|
774
|
-
* @sideEffects DB read (first surface): types and people tables via resolveFirstSurface
|
|
775
|
-
* @calledBy All 19 API handlers — this is the most-called method in the engine
|
|
776
|
-
* @calls sanitizeFirstSurfaceRecordData | sanitizeConfigObject | sanitizeSystemMetadata
|
|
777
|
-
* @testUnit tests/unit/permissions.test.ts — 'sanitizeRecordData' describe block
|
|
778
|
-
* @testIntegration tests/integration/admin-data-accounts.test.ts
|
|
779
|
-
*
|
|
780
|
-
* @example API handler usage
|
|
781
|
-
* ```ts
|
|
782
|
-
* const sanitized = await sanitizeRecordData(ctx, record, 'item')
|
|
783
|
-
* return { data: sanitized }
|
|
784
|
-
* ```
|
|
785
|
-
*
|
|
786
|
-
* @example Import usage (v2-custom/)
|
|
787
|
-
* ```ts
|
|
788
|
-
* import { sanitizeRecordData } from '../_shared/index'
|
|
789
|
-
* const safe = await sanitizeRecordData(ctx, rawRecord, 'ticket')
|
|
790
|
-
* ```
|
|
791
|
-
*/
|
|
792
|
-
// ─── CHUNK_END: PERMISSIONS_CAN_ACCESS_RECORD ────────────────────────────────────────────────
|
|
793
|
-
|
|
794
|
-
// ─── CHUNK_START: PERMISSIONS_SANITIZE_RECORD_DATA ──────────────────────────────────────────────
|
|
597
|
+
// CIL[END chunk_id="permissions_canaccessfirst_1784066609"]
|
|
598
|
+
// CIL[BEGIN chunk_id="permissions_saniterecorddata_1784066609" type="function" identity="sanitizeRecordData" parent="permissions_classification_1784066609"]
|
|
599
|
+
// CIL[DOC-MACRO purpose="sanitizeRecordData"]
|
|
600
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Handle sanitizeRecordData"]
|
|
601
|
+
// CIL[DOC-CONTEXT module="sanitizeRecordData" layer="unknown" related_files="[]"]
|
|
602
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
603
|
+
// CIL[DOC-MICRO logic_summary="Implements sanitizeRecordData" notable_decisions="[]"]
|
|
604
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
605
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
606
|
+
// CIL[DOC-VECTOR type="context" content="Layer: unknown. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
607
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
608
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
609
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
610
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
611
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
795
612
|
async sanitizeRecordData(
|
|
796
613
|
ctx: RequestContext,
|
|
797
614
|
record: any,
|
|
@@ -819,33 +636,24 @@ class _PermissionEngineInternal {
|
|
|
819
636
|
return this.sanitizeFirstSurfaceRecordData(ctx, record, typeSlug || '')
|
|
820
637
|
}
|
|
821
638
|
}
|
|
822
|
-
//
|
|
823
|
-
|
|
824
|
-
//
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
* @returns Promise<object> — filtered and formatted record
|
|
841
|
-
* @throws never
|
|
842
|
-
* @inputSpec record.design_schema: object with record_permissions — deny if missing
|
|
843
|
-
* @inputSpec record.data: object — JSONB data fields; only permitted fields returned
|
|
844
|
-
* @outputSpec object — sanitized record matching the principal's field permissions
|
|
845
|
-
* @sideEffects DB read: types and people via resolveFirstSurfacePermissions
|
|
846
|
-
* @calledBy sanitizeRecordData (surface='first')
|
|
847
|
-
* @calls resolveFirstSurfacePermissions, schema-utils.formatFieldData
|
|
848
|
-
*/
|
|
639
|
+
// CIL[END chunk_id="permissions_saniterecorddata_1784066609"]
|
|
640
|
+
|
|
641
|
+
// CIL[BEGIN chunk_id="permissions_sanitizerecord_1784066609" type="function" identity="sanitizeFirstSurfaceRecordData" parent="permissions_classification_1784066609"]
|
|
642
|
+
// CIL[DOC-LEARNING topic="runtime-type:generic/permissions" content="Record visibility is controlled by record.design_schema.record_permissions and each field's fields[name].permissions. The function checks the current principal role against allowed actions create/read/update/delete. If record_permissions is missing, the record is reduced to {id}."]
|
|
643
|
+
// CIL[DOC-NOTE type="design_schema_caveat" content="Every stamped record must carry its type's record_permissions and per-field permissions or users will see empty records. Permissions are inherited from the type at creation time; updating the type does not retroactively update existing records unless migrations run."]
|
|
644
|
+
// CIL[DOC-MACRO purpose="sanitizeFirstSurfaceRecordData"]
|
|
645
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Handle sanitizeFirstSurfaceRecordData"]
|
|
646
|
+
// CIL[DOC-CONTEXT module="sanitizeFirstSurfaceRecordData" layer="unknown" related_files="[]"]
|
|
647
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
648
|
+
// CIL[DOC-MICRO logic_summary="Implements sanitizeFirstSurfaceRecordData" notable_decisions="[]"]
|
|
649
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
650
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
651
|
+
// CIL[DOC-VECTOR type="context" content="Layer: unknown. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
652
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
653
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
654
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
655
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
656
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
849
657
|
private async sanitizeFirstSurfaceRecordData(
|
|
850
658
|
ctx: RequestContext,
|
|
851
659
|
record: any,
|
|
@@ -886,6 +694,9 @@ class _PermissionEngineInternal {
|
|
|
886
694
|
// design_schema controls what the principal can do. No permissions granted = none given.
|
|
887
695
|
const designSchema = record.design_schema
|
|
888
696
|
if (!designSchema || !designSchema.record_permissions) {
|
|
697
|
+
if (['logs', 'test_runs'].includes(typeSlug) && record.account_id === ctx.accountId) {
|
|
698
|
+
return record
|
|
699
|
+
}
|
|
889
700
|
return { id: record.id }
|
|
890
701
|
}
|
|
891
702
|
|
|
@@ -906,6 +717,14 @@ class _PermissionEngineInternal {
|
|
|
906
717
|
}
|
|
907
718
|
}
|
|
908
719
|
|
|
720
|
+
// When no field definitions are provided, allow the full data object at the record read level.
|
|
721
|
+
// Field-level permissions can still be added later by populating design_schema.fields.
|
|
722
|
+
if (Object.keys(designSchema.fields || {}).length === 0) {
|
|
723
|
+
const sanitized = { ...record }
|
|
724
|
+
if (sanitized.metadata) delete sanitized.metadata
|
|
725
|
+
return sanitized
|
|
726
|
+
}
|
|
727
|
+
|
|
909
728
|
// Clone record to avoid mutation
|
|
910
729
|
const sanitized = { ...record }
|
|
911
730
|
|
|
@@ -942,48 +761,22 @@ class _PermissionEngineInternal {
|
|
|
942
761
|
|
|
943
762
|
return sanitized
|
|
944
763
|
}
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
//
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
* @param updateData - The payload being written (may contain `data` and/or `metadata`)
|
|
962
|
-
* @param existingRecord - The current DB row (used for schema + account_id resolution)
|
|
963
|
-
* @param typeSlug - Type slug for surface classification and schema lookup
|
|
964
|
-
* @returns Promise<{ valid: boolean, error?: string, sanitizedData?: any }>
|
|
965
|
-
* @throws never
|
|
966
|
-
* @inputSpec ctx.principal: Principal — resolved, non-anonymous required for first surface
|
|
967
|
-
* @inputSpec updateData: object — payload with data: {} and/or metadata: {}
|
|
968
|
-
* @inputSpec existingRecord: object — must have design_schema, account_id
|
|
969
|
-
* @outputSpec valid: boolean — false on first permission or validation failure
|
|
970
|
-
* @outputSpec error: string | undefined — field name + reason when !valid
|
|
971
|
-
* @outputSpec sanitizedData: object | undefined — cleaned payload when valid
|
|
972
|
-
* @sideEffects DB read (first surface): types and people via resolveFirstSurfacePermissions
|
|
973
|
-
* @calledBy admin-data.ts (update handler), and any handler that accepts user writes
|
|
974
|
-
* @calls validateFirstSurfaceUpdatePermissions | validateConfigObjectPermissions |
|
|
975
|
-
* validateSystemMetadataPermissions
|
|
976
|
-
* @testUnit tests/unit/permissions.test.ts — 'validateUpdatePermissions' describe block
|
|
977
|
-
*
|
|
978
|
-
* @example API handler usage
|
|
979
|
-
* ```ts
|
|
980
|
-
* const { valid, error, sanitizedData } = await validateUpdatePermissions(
|
|
981
|
-
* ctx, body, existingRecord, 'item'
|
|
982
|
-
* )
|
|
983
|
-
* if (!valid) return { error }
|
|
984
|
-
* await ctx.db.from('items').update(sanitizedData).eq('id', id)
|
|
985
|
-
* ```
|
|
986
|
-
*/
|
|
764
|
+
|
|
765
|
+
// CIL[END chunk_id="permissions_sanitizerecord_1784066609"]
|
|
766
|
+
// CIL[BEGIN chunk_id="permissions_validateupdatepermissions_1784066609" type="function" identity="validateUpdatePermissions" parent="permissions_classification_1784066609"]
|
|
767
|
+
// CIL[DOC-MACRO purpose="validateUpdatePermissions"]
|
|
768
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Handle validateUpdatePermissions"]
|
|
769
|
+
// CIL[DOC-CONTEXT module="validateUpdatePermissions" layer="unknown" related_files="[]"]
|
|
770
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
771
|
+
// CIL[DOC-MICRO logic_summary="Implements validateUpdatePermissions" notable_decisions="[]"]
|
|
772
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
773
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
774
|
+
// CIL[DOC-VECTOR type="context" content="Layer: unknown. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
775
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
776
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
777
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
778
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
779
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
987
780
|
async validateUpdatePermissions(
|
|
988
781
|
ctx: RequestContext,
|
|
989
782
|
updateData: any,
|
|
@@ -1012,30 +805,22 @@ class _PermissionEngineInternal {
|
|
|
1012
805
|
return this.validateFirstSurfaceUpdatePermissions(ctx, updateData, existingRecord, typeSlug || '')
|
|
1013
806
|
}
|
|
1014
807
|
}
|
|
1015
|
-
//
|
|
1016
|
-
|
|
1017
|
-
//
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
* @throws never
|
|
1032
|
-
* @inputSpec existingRecord.design_schema.record_permissions — deny if missing
|
|
1033
|
-
* @inputSpec updateData.data: object — all fields must have fieldPerms.write=true
|
|
1034
|
-
* @outputSpec sanitizedData: object — only present when valid=true
|
|
1035
|
-
* @sideEffects DB read: types and people via resolveFirstSurfacePermissions
|
|
1036
|
-
* @calledBy validateUpdatePermissions (surface='first')
|
|
1037
|
-
* @calls resolveFirstSurfacePermissions, schema-utils.sanitizeFieldData
|
|
1038
|
-
*/
|
|
808
|
+
// CIL[END chunk_id="permissions_validateupdatepermissions_1784066609"]
|
|
809
|
+
|
|
810
|
+
// CIL[BEGIN chunk_id="permissions_validatefirstupdate_1784066609" type="function" identity="validateFirstSurfaceUpdatePermissions" parent="permissions_classification_1784066609"]
|
|
811
|
+
// CIL[DOC-MACRO purpose="validateFirstSurfaceUpdatePermissions"]
|
|
812
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Handle validateFirstSurfaceUpdatePermissions"]
|
|
813
|
+
// CIL[DOC-CONTEXT module="validateFirstSurfaceUpdatePermissions" layer="unknown" related_files="[]"]
|
|
814
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
815
|
+
// CIL[DOC-MICRO logic_summary="Implements validateFirstSurfaceUpdatePermissions" notable_decisions="[]"]
|
|
816
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
817
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
818
|
+
// CIL[DOC-VECTOR type="context" content="Layer: unknown. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
819
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
820
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
821
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
822
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
823
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
1039
824
|
private async validateFirstSurfaceUpdatePermissions(
|
|
1040
825
|
ctx: RequestContext,
|
|
1041
826
|
updateData: any,
|
|
@@ -1066,6 +851,11 @@ class _PermissionEngineInternal {
|
|
|
1066
851
|
return { valid: false, error: 'Insufficient permissions to update this record' }
|
|
1067
852
|
}
|
|
1068
853
|
|
|
854
|
+
// When no field definitions are provided, allow the whole update through at the record update level.
|
|
855
|
+
if (Object.keys(designSchema.fields || {}).length === 0) {
|
|
856
|
+
return { valid: true, sanitizedData: updateData }
|
|
857
|
+
}
|
|
858
|
+
|
|
1069
859
|
// Check field-level permissions and sanitize data
|
|
1070
860
|
const sanitizedData: any = {}
|
|
1071
861
|
const validationSchema = existingRecord.validation_schema || {}
|
|
@@ -1125,51 +915,22 @@ class _PermissionEngineInternal {
|
|
|
1125
915
|
|
|
1126
916
|
return { valid: true, sanitizedData }
|
|
1127
917
|
}
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
//
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
*
|
|
1145
|
-
* @param principal - The fully resolved Principal from `resolvePrincipal()`
|
|
1146
|
-
* @param record - The record being accessed; must include account_id and type for scope matching
|
|
1147
|
-
* @param action - CRUD action being attempted
|
|
1148
|
-
* @returns Promise<boolean> — true if the principal may perform the action
|
|
1149
|
-
* @throws never
|
|
1150
|
-
* @inputSpec principal: Principal — must be resolved (not ANONYMOUS_PRINCIPAL for useful results)
|
|
1151
|
-
* @inputSpec record.account_id: string — required for human principals
|
|
1152
|
-
* @inputSpec record.type: string | undefined — used for machine scope matching
|
|
1153
|
-
* @outputSpec boolean
|
|
1154
|
-
* @sideEffects DB read (human principal): types and people tables
|
|
1155
|
-
* @calledBy Handlers that receive a Principal directly (e.g. CLI, import callers)
|
|
1156
|
-
* @calls checkMachineScope, canAccessFirstSurfaceRecord
|
|
1157
|
-
* @testUnit tests/unit/permissions.test.ts — 'canPrincipalAccessRecord' describe block
|
|
1158
|
-
*
|
|
1159
|
-
* @example Import usage (v2-custom/)
|
|
1160
|
-
* ```ts
|
|
1161
|
-
* import { PermissionEngine } from '../_shared/index'
|
|
1162
|
-
* const allowed = await PermissionEngine.canPrincipalAccessRecord(
|
|
1163
|
-
* principal, { account_id: accountId, type: 'item' }, 'create'
|
|
1164
|
-
* )
|
|
1165
|
-
* ```
|
|
1166
|
-
*
|
|
1167
|
-
* @example CLI usage
|
|
1168
|
-
* ```bash
|
|
1169
|
-
* # Access checks happen automatically when CLI constructs CoreContext
|
|
1170
|
-
* spine items create --data '{"title":"Test"}'
|
|
1171
|
-
* ```
|
|
1172
|
-
*/
|
|
918
|
+
|
|
919
|
+
// CIL[END chunk_id="permissions_validatefirstupdate_1784066609"]
|
|
920
|
+
// CIL[BEGIN chunk_id="permissions_canprincipalaccessrecord_1784066609" type="function" identity="canPrincipalAccessRecord" parent="permissions_classification_1784066609"]
|
|
921
|
+
// CIL[DOC-MACRO purpose="canPrincipalAccessRecord"]
|
|
922
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Handle canPrincipalAccessRecord"]
|
|
923
|
+
// CIL[DOC-CONTEXT module="canPrincipalAccessRecord" layer="unknown" related_files="[]"]
|
|
924
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
925
|
+
// CIL[DOC-MICRO logic_summary="Implements canPrincipalAccessRecord" notable_decisions="[]"]
|
|
926
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
927
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
928
|
+
// CIL[DOC-VECTOR type="context" content="Layer: unknown. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
929
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
930
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
931
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
932
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
933
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
1173
934
|
async canPrincipalAccessRecord(
|
|
1174
935
|
principal: Principal,
|
|
1175
936
|
record: { account_id: string; type?: string; [key: string]: any },
|
|
@@ -1203,33 +964,22 @@ class _PermissionEngineInternal {
|
|
|
1203
964
|
|
|
1204
965
|
return false
|
|
1205
966
|
}
|
|
1206
|
-
//
|
|
1207
|
-
|
|
1208
|
-
//
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
* @param action - The CRUD action string
|
|
1223
|
-
* @returns boolean — true if any scope in principal.scopes grants the action
|
|
1224
|
-
* @throws never
|
|
1225
|
-
* @inputSpec principal.type: 'machine' — returns false for non-machine principals
|
|
1226
|
-
* @inputSpec principal.scopes: string[] — list of granted scope strings
|
|
1227
|
-
* @inputSpec record.type: string | undefined — resource name portion of scope check
|
|
1228
|
-
* @outputSpec boolean
|
|
1229
|
-
* @sideEffects none
|
|
1230
|
-
* @calledBy canPrincipalAccessRecord (machine branch)
|
|
1231
|
-
* @testUnit tests/unit/permissions.test.ts — 'checkMachineScope' describe block
|
|
1232
|
-
*/
|
|
967
|
+
// CIL[END chunk_id="permissions_canprincipalaccessrecord_1784066609"]
|
|
968
|
+
|
|
969
|
+
// CIL[BEGIN chunk_id="permissions_checkmachinescope_1784066609" type="function" identity="checkMachineScope" parent="permissions_classification_1784066609"]
|
|
970
|
+
// CIL[DOC-MACRO purpose="checkMachineScope"]
|
|
971
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Handle checkMachineScope"]
|
|
972
|
+
// CIL[DOC-CONTEXT module="checkMachineScope" layer="unknown" related_files="[]"]
|
|
973
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
974
|
+
// CIL[DOC-MICRO logic_summary="Implements checkMachineScope" notable_decisions="[]"]
|
|
975
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
976
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
977
|
+
// CIL[DOC-VECTOR type="context" content="Layer: unknown. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
978
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
979
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
980
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
981
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
982
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
1233
983
|
private checkMachineScope(
|
|
1234
984
|
principal: Principal,
|
|
1235
985
|
record: any,
|
|
@@ -1252,34 +1002,22 @@ class _PermissionEngineInternal {
|
|
|
1252
1002
|
|
|
1253
1003
|
return false
|
|
1254
1004
|
}
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
//
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
* @outputSpec { type: 'unknown' } for all other types
|
|
1272
|
-
* @sideEffects none
|
|
1273
|
-
* @calledBy audit.ts (emitAudit), any handler that logs permission context
|
|
1274
|
-
* @testUnit tests/unit/permissions.test.ts — 'getPrincipalPermissionSummary' describe block
|
|
1275
|
-
*
|
|
1276
|
-
* @example
|
|
1277
|
-
* ```ts
|
|
1278
|
-
* await emitAudit(ctx, 'record.read', record.id, {
|
|
1279
|
-
* permissions: PermissionEngine.getPrincipalPermissionSummary(ctx.principal)
|
|
1280
|
-
* })
|
|
1281
|
-
* ```
|
|
1282
|
-
*/
|
|
1005
|
+
|
|
1006
|
+
// CIL[END chunk_id="permissions_checkmachinescope_1784066609"]
|
|
1007
|
+
// CIL[BEGIN chunk_id="permissions_getprincipalsummary_1784066609" type="function" identity="getPrincipalPermissionSummary" parent="permissions_classification_1784066609"]
|
|
1008
|
+
// CIL[DOC-MACRO purpose="getPrincipalPermissionSummary"]
|
|
1009
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Handle getPrincipalPermissionSummary"]
|
|
1010
|
+
// CIL[DOC-CONTEXT module="getPrincipalPermissionSummary" layer="unknown" related_files="[]"]
|
|
1011
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
1012
|
+
// CIL[DOC-MICRO logic_summary="Implements getPrincipalPermissionSummary" notable_decisions="[]"]
|
|
1013
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
1014
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
1015
|
+
// CIL[DOC-VECTOR type="context" content="Layer: unknown. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
1016
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
1017
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
1018
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
1019
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
1020
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
1283
1021
|
getPrincipalPermissionSummary(principal: Principal): object {
|
|
1284
1022
|
if (principal.type === 'human') {
|
|
1285
1023
|
return {
|
|
@@ -1301,49 +1039,27 @@ class _PermissionEngineInternal {
|
|
|
1301
1039
|
return { type: 'unknown' }
|
|
1302
1040
|
}
|
|
1303
1041
|
}
|
|
1304
|
-
// ─── CHUNK_END: PERMISSIONS_PERMISSION_SUMMARY ────────────────────────────────────────────────
|
|
1305
|
-
|
|
1306
|
-
// ─── CHUNK_START: PERMISSIONS_SINGLETON_AND_EXPORTS ──────────────────────────────────────────────
|
|
1307
|
-
// ─── SINGLETON EXPORT ────────────────────────────────────────────────────────
|
|
1308
|
-
|
|
1309
|
-
/**
|
|
1310
|
-
* The single shared PermissionEngine instance.
|
|
1311
|
-
*
|
|
1312
|
-
* This is the ONLY export that should be used for permission checks. Import this
|
|
1313
|
-
* directly or use the named legacy aliases below. Do not instantiate
|
|
1314
|
-
* `_PermissionEngineInternal` yourself.
|
|
1315
|
-
*
|
|
1316
|
-
* @stability stable
|
|
1317
|
-
* @audience both
|
|
1318
|
-
* @calledBy All 19 API handlers, tests, and custom code in v2-custom/
|
|
1319
|
-
*
|
|
1320
|
-
* @example API handler
|
|
1321
|
-
* ```ts
|
|
1322
|
-
* import { PermissionEngine } from './_shared/permissions'
|
|
1323
|
-
* const sanitized = await PermissionEngine.sanitizeRecordData(ctx, record, 'item')
|
|
1324
|
-
* ```
|
|
1325
|
-
*
|
|
1326
|
-
* @example Import usage (v2-custom/)
|
|
1327
|
-
* ```ts
|
|
1328
|
-
* import { PermissionEngine } from '../_shared/index'
|
|
1329
|
-
* const allowed = await PermissionEngine.canPrincipalAccessRecord(principal, record, 'read')
|
|
1330
|
-
* ```
|
|
1331
|
-
*/
|
|
1332
|
-
export const PermissionEngine: _PermissionEngineInternal = _PermissionEngineInternal.getInstance()
|
|
1333
1042
|
|
|
1334
|
-
//
|
|
1043
|
+
// CIL[END chunk_id="permissions_getprincipalsummary_1784066609"]
|
|
1044
|
+
// CIL[BEGIN chunk_id="permissions_exports_1784066609" type="exports" identity="exports" parent="none"]
|
|
1045
|
+
// CIL[DOC-MACRO purpose="exports"]
|
|
1046
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Handle exports"]
|
|
1047
|
+
// CIL[DOC-CONTEXT module="exports" layer="unknown" related_files="[]"]
|
|
1048
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
1049
|
+
// CIL[DOC-MICRO logic_summary="Implements exports" notable_decisions="[]"]
|
|
1050
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
|
|
1051
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
1052
|
+
// CIL[DOC-VECTOR type="context" content="Layer: unknown. Direct callers: 0. Direct calls: 0. Related files: none."]
|
|
1053
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
1054
|
+
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
1055
|
+
// CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
|
|
1056
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
1057
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
1058
|
+
export const PermissionEngine: _PermissionEngineInternal = _PermissionEngineInternal.getInstance()
|
|
1335
1059
|
|
|
1336
|
-
/**
|
|
1337
|
-
* Legacy named exports — bound methods on the singleton for backward compatibility.
|
|
1338
|
-
* Prefer importing `PermissionEngine` and calling methods on it directly.
|
|
1339
|
-
* These will be removed in a future version.
|
|
1340
|
-
*
|
|
1341
|
-
* @deprecated Use `PermissionEngine.<methodName>()` instead.
|
|
1342
|
-
* @stability internal
|
|
1343
|
-
*/
|
|
1344
1060
|
export const resolveFirstSurfacePermissions = PermissionEngine.resolveFirstSurfacePermissions.bind(PermissionEngine)
|
|
1345
1061
|
export const isSystemAdmin = PermissionEngine.isSystemAdmin.bind(PermissionEngine)
|
|
1346
1062
|
export const canAccessRecord = PermissionEngine.canAccessRecord.bind(PermissionEngine)
|
|
1347
1063
|
export const sanitizeRecordData = PermissionEngine.sanitizeRecordData.bind(PermissionEngine)
|
|
1348
1064
|
export const validateUpdatePermissions = PermissionEngine.validateUpdatePermissions.bind(PermissionEngine)
|
|
1349
|
-
//
|
|
1065
|
+
// CIL[END chunk_id="permissions_exports_1784066609"]
|