@flowdular/sdk 0.2.4 → 0.3.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/.ai/agents/sandbox/business-manager.md +5 -1
- package/.ai/blueprints/author-spec/README.md +1 -1
- package/.ai/blueprints/author-spec/spec-requirements.yaml +44 -0
- package/.ai/blueprints/author-spec/steps.yaml +5 -5
- package/.ai/blueprints/author-spec/templates/module.yaml +99 -3
- package/.ai/blueprints/edit-module/gates.yaml +4 -0
- package/.ai/blueprints/edit-module/required-files.yaml +9 -0
- package/.ai/blueprints/new-module/gates.yaml +4 -0
- package/.ai/blueprints/new-module/spec-requirements.yaml +2 -2
- package/.ai/blueprints/release/gates.yaml +4 -0
- package/.ai/platform-capabilities.md +128 -0
- package/.ai/policies/capabilities.yaml +130 -3
- package/.ai/policies/path-ownership.yaml +5 -2
- package/.ai/policies/task-budgets.yaml +5 -3
- package/.ai/references/catalog/module.json +4 -4
- package/.ai/references/catalog/package.json +2 -2
- package/.ai/references/catalog/spec/module.yaml +5 -3
- package/.ai/references/catalog/src/platform.ts +2 -0
- package/.ai/references/catalog/src/services/catalog-service.ts +89 -1
- package/.ai/references/catalog/src/services/data-classes.ts +47 -0
- package/.ai/references/catalog/src/services/database-repository.ts +98 -1
- package/.ai/references/catalog/src/services/repository.ts +22 -1
- package/.ai/references/catalog/tests/data-classes.test.ts +157 -0
- package/.ai/references/catalog.provenance.json +12 -10
- package/.ai/skills/README.md +10 -0
- package/.ai/skills/agent-tool-design/SKILL.md +1 -2
- package/.ai/skills/auth-security-review/SKILL.md +1 -1
- package/.ai/skills/business-agent-design/SKILL.md +0 -1
- package/.ai/skills/cli-extension/SKILL.md +1 -1
- package/.ai/skills/deploy-operate/SKILL.md +119 -0
- package/.ai/skills/module-new/SKILL.md +29 -3
- package/.ai/skills/module-update/SKILL.md +9 -3
- package/.ai/skills/perf-audit/SKILL.md +0 -1
- package/.ai/skills/release-eject-pr/SKILL.md +0 -1
- package/.ai/skills/spec-interview/SKILL.md +120 -0
- package/.ai/skills/test-hardening/SKILL.md +1 -0
- package/.ai/skills/ux-design/SKILL.md +34 -3
- package/.ai/skills/variables/SKILL.md +0 -2
- package/.ai/skills/workflow-development/SKILL.md +0 -1
- package/AGENTS.md +4 -0
- package/docs/agent-contract.md +2 -2
- package/docs/design-system.md +185 -31
- package/modules/access/migrations/0001_access_core.down.sql +1 -0
- package/modules/access/migrations/0001_access_core.up.sql +28 -0
- package/modules/access/migrations/README.md +3 -0
- package/modules/access/module.json +33 -0
- package/modules/access/package.json +43 -0
- package/modules/access/spec/module.yaml +225 -0
- package/modules/access/src/acl/permissions.ts +6 -0
- package/modules/access/src/api/endpoints.ts +244 -0
- package/modules/access/src/client/ActivityView.tsrx +245 -0
- package/modules/access/src/client/AttestationsView.tsrx +176 -0
- package/modules/access/src/client/ReviewView.tsrx +586 -0
- package/modules/access/src/client/api.ts +132 -0
- package/modules/access/src/client/contribution.tsrx +41 -0
- package/modules/access/src/client/index.ts +41 -0
- package/modules/access/src/client/navigation.ts +57 -0
- package/modules/access/src/client/presentation.ts +69 -0
- package/modules/access/src/client/state.ts +72 -0
- package/modules/access/src/domain/types.ts +189 -0
- package/modules/access/src/index.ts +38 -0
- package/modules/access/src/platform.ts +60 -0
- package/modules/access/src/server/index.ts +42 -0
- package/modules/access/src/server/runtime.ts +105 -0
- package/modules/access/src/services/access-service.ts +298 -0
- package/modules/access/src/services/audit-window.ts +78 -0
- package/modules/access/src/services/auth-directory.ts +72 -0
- package/modules/access/src/services/changes.ts +91 -0
- package/modules/access/src/services/data-classes.ts +28 -0
- package/modules/access/src/services/database-repository.ts +185 -0
- package/modules/access/src/services/directory.ts +102 -0
- package/modules/access/src/services/index.ts +33 -0
- package/modules/access/src/services/list-exports.ts +150 -0
- package/modules/access/src/services/migration.ts +54 -0
- package/modules/access/src/services/repository.ts +30 -0
- package/modules/access/src/services/review.ts +178 -0
- package/modules/access/translations/en.json +155 -0
- package/modules/access/translations/pl.json +155 -0
- package/modules/agents/migrations/0022_credential_rotation_inventory.down.sql +2 -0
- package/modules/agents/migrations/0022_credential_rotation_inventory.up.sql +7 -0
- package/modules/agents/migrations/0023_agents_retention_indexes.down.sql +2 -0
- package/modules/agents/migrations/0023_agents_retention_indexes.up.sql +9 -0
- package/modules/agents/migrations/0024_agent_meter_refusals.down.sql +3 -0
- package/modules/agents/migrations/0024_agent_meter_refusals.up.sql +22 -0
- package/modules/agents/migrations/0025_agent_list_indexes.down.sql +3 -0
- package/modules/agents/migrations/0025_agent_list_indexes.up.sql +11 -0
- package/modules/agents/module.json +30 -4
- package/modules/agents/package.json +2 -1
- package/modules/agents/spec/module.yaml +63 -5
- package/modules/agents/src/api/endpoints.ts +291 -33
- package/modules/agents/src/cli/commands.json +11 -0
- package/modules/agents/src/cli/index.ts +45 -0
- package/modules/agents/src/client/AgentDashboardWidget.tsrx +19 -11
- package/modules/agents/src/client/AgentDefinitionList.tsrx +53 -10
- package/modules/agents/src/client/AgentPlaygroundView.tsrx +4 -4
- package/modules/agents/src/client/AgentProceduresView.tsrx +2 -2
- package/modules/agents/src/client/AgentRunsView.tsrx +143 -40
- package/modules/agents/src/client/AgentsView.tsrx +109 -33
- package/modules/agents/src/client/api.ts +99 -21
- package/modules/agents/src/client/state.ts +97 -0
- package/modules/agents/src/domain/types.ts +44 -4
- package/modules/agents/src/platform.ts +42 -0
- package/modules/agents/src/server/action-execution.ts +201 -106
- package/modules/agents/src/server/runtime.ts +32 -0
- package/modules/agents/src/services/agent-service.ts +158 -30
- package/modules/agents/src/services/credential-rotation.ts +185 -0
- package/modules/agents/src/services/credential-vault.ts +71 -46
- package/modules/agents/src/services/data-classes.ts +176 -0
- package/modules/agents/src/services/database-repository.ts +393 -101
- package/modules/agents/src/services/metering.ts +111 -0
- package/modules/agents/src/services/migration.ts +109 -0
- package/modules/agents/src/services/module-agent-preflight.ts +17 -9
- package/modules/agents/src/services/notifications.ts +98 -0
- package/modules/agents/src/services/provider-repository.ts +9 -18
- package/modules/agents/src/services/provider-service.ts +1 -9
- package/modules/agents/src/services/reports.ts +107 -0
- package/modules/agents/src/services/repository.ts +86 -6
- package/modules/agents/src/services/usage-service.ts +2 -0
- package/modules/agents/src/services/worker.ts +114 -4
- package/modules/agents/src/settings.ts +14 -3
- package/modules/agents/translations/en.json +14 -4
- package/modules/agents/translations/pl.json +14 -4
- package/modules/approvals/migrations/0001_approvals_core.down.sql +3 -0
- package/modules/approvals/migrations/0001_approvals_core.up.sql +78 -0
- package/modules/approvals/migrations/0002_approvals_expiry_routing_role.down.sql +2 -0
- package/modules/approvals/migrations/0002_approvals_expiry_routing_role.up.sql +19 -0
- package/modules/approvals/migrations/0003_approvals_retention_indexes.down.sql +6 -0
- package/modules/approvals/migrations/0003_approvals_retention_indexes.up.sql +14 -0
- package/modules/approvals/migrations/0004_approvals_inbox_keyset_indexes.down.sql +5 -0
- package/modules/approvals/migrations/0004_approvals_inbox_keyset_indexes.up.sql +12 -0
- package/modules/approvals/migrations/0005_approvals_grant_audit.down.sql +4 -0
- package/modules/approvals/migrations/0005_approvals_grant_audit.up.sql +20 -0
- package/modules/approvals/migrations/README.md +3 -0
- package/modules/approvals/module.json +34 -0
- package/modules/approvals/package.json +43 -0
- package/modules/approvals/spec/module.yaml +327 -0
- package/modules/approvals/src/acl/permissions.ts +7 -0
- package/modules/approvals/src/api/endpoints.ts +457 -0
- package/modules/approvals/src/client/ApprovalRequestDrawer.tsrx +138 -0
- package/modules/approvals/src/client/ApprovalsView.tsrx +562 -0
- package/modules/approvals/src/client/PendingCountWidget.tsrx +52 -0
- package/modules/approvals/src/client/api.ts +203 -0
- package/modules/approvals/src/client/approvals.css +51 -0
- package/modules/approvals/src/client/contribution.tsrx +44 -0
- package/modules/approvals/src/client/index.ts +26 -0
- package/modules/approvals/src/client/navigation.ts +39 -0
- package/modules/approvals/src/client/pending-count.ts +26 -0
- package/modules/approvals/src/client/presentation.ts +52 -0
- package/modules/approvals/src/client/state.ts +141 -0
- package/modules/approvals/src/domain/capability.ts +107 -0
- package/modules/approvals/src/domain/grant.ts +69 -0
- package/modules/approvals/src/domain/types.ts +168 -0
- package/modules/approvals/src/index.ts +61 -0
- package/modules/approvals/src/platform.ts +136 -0
- package/modules/approvals/src/server/index.ts +42 -0
- package/modules/approvals/src/server/runtime.ts +185 -0
- package/modules/approvals/src/services/approvals-service.ts +637 -0
- package/modules/approvals/src/services/callbacks.ts +75 -0
- package/modules/approvals/src/services/data-classes.ts +167 -0
- package/modules/approvals/src/services/database-repository.ts +783 -0
- package/modules/approvals/src/services/eligibility.ts +121 -0
- package/modules/approvals/src/services/expiry-runner.ts +81 -0
- package/modules/approvals/src/services/index.ts +31 -0
- package/modules/approvals/src/services/migration.ts +256 -0
- package/modules/approvals/src/services/notifications.ts +95 -0
- package/modules/approvals/src/services/repository.ts +169 -0
- package/modules/approvals/src/services/service-error.ts +70 -0
- package/modules/approvals/src/settings.ts +102 -0
- package/modules/approvals/translations/en.json +104 -0
- package/modules/approvals/translations/pl.json +104 -0
- package/modules/audit/migrations/0001_audit_core.down.sql +8 -0
- package/modules/audit/migrations/0001_audit_core.up.sql +107 -0
- package/modules/audit/migrations/0002_audit_sweep_routing_role.down.sql +2 -0
- package/modules/audit/migrations/0002_audit_sweep_routing_role.up.sql +21 -0
- package/modules/audit/migrations/0003_audit_export_request.down.sql +12 -0
- package/modules/audit/migrations/0003_audit_export_request.up.sql +40 -0
- package/modules/audit/migrations/0004_audit_hold_seal_erasure.down.sql +17 -0
- package/modules/audit/migrations/0004_audit_hold_seal_erasure.up.sql +192 -0
- package/modules/audit/migrations/0005_audit_event_format.down.sql +17 -0
- package/modules/audit/migrations/0005_audit_event_format.up.sql +60 -0
- package/modules/audit/migrations/0006_audit_own_class_walks.down.sql +3 -0
- package/modules/audit/migrations/0006_audit_own_class_walks.up.sql +14 -0
- package/modules/audit/migrations/README.md +3 -0
- package/modules/audit/module.json +32 -0
- package/modules/audit/package.json +49 -0
- package/modules/audit/spec/module.yaml +809 -0
- package/modules/audit/src/acl/permissions.ts +7 -0
- package/modules/audit/src/api/endpoints.ts +315 -0
- package/modules/audit/src/cli/commands.json +95 -0
- package/modules/audit/src/cli/index.ts +658 -0
- package/modules/audit/src/client/DataClassesView.tsrx +420 -0
- package/modules/audit/src/client/ExportsView.tsrx +256 -0
- package/modules/audit/src/client/HoldsView.tsrx +604 -0
- package/modules/audit/src/client/SweepsView.tsrx +251 -0
- package/modules/audit/src/client/api.ts +185 -0
- package/modules/audit/src/client/contribution.tsrx +52 -0
- package/modules/audit/src/client/index.ts +23 -0
- package/modules/audit/src/client/navigation.ts +74 -0
- package/modules/audit/src/client/presentation.ts +105 -0
- package/modules/audit/src/client/state.ts +86 -0
- package/modules/audit/src/domain/data-classes.ts +16 -0
- package/modules/audit/src/domain/types.ts +339 -0
- package/modules/audit/src/index.ts +76 -0
- package/modules/audit/src/platform.ts +44 -0
- package/modules/audit/src/server/index.ts +177 -0
- package/modules/audit/src/server/runtime.ts +303 -0
- package/modules/audit/src/services/anchor-key.ts +143 -0
- package/modules/audit/src/services/anchor-rotation.ts +147 -0
- package/modules/audit/src/services/audit-runners.ts +278 -0
- package/modules/audit/src/services/backup-guard.ts +95 -0
- package/modules/audit/src/services/database-repository.ts +1946 -0
- package/modules/audit/src/services/declared-classes.ts +64 -0
- package/modules/audit/src/services/erasure-port.ts +126 -0
- package/modules/audit/src/services/erasure-service.ts +770 -0
- package/modules/audit/src/services/export-directory.ts +106 -0
- package/modules/audit/src/services/export-service.ts +714 -0
- package/modules/audit/src/services/hold-service.ts +295 -0
- package/modules/audit/src/services/index.ts +17 -0
- package/modules/audit/src/services/migration.ts +658 -0
- package/modules/audit/src/services/own-classes.ts +318 -0
- package/modules/audit/src/services/platform-version.ts +23 -0
- package/modules/audit/src/services/repository.ts +495 -0
- package/modules/audit/src/services/retention-service.ts +191 -0
- package/modules/audit/src/services/seal-service.ts +769 -0
- package/modules/audit/src/services/service-error.ts +61 -0
- package/modules/audit/src/services/subject-keys.ts +90 -0
- package/modules/audit/src/services/sweep-service.ts +298 -0
- package/modules/audit/src/services/zip.ts +217 -0
- package/modules/audit/src/settings.ts +69 -0
- package/modules/audit/translations/en.json +210 -0
- package/modules/audit/translations/pl.json +210 -0
- package/modules/auth/README.md +77 -29
- package/modules/auth/migrations/0016_external_identities.up.sql +16 -0
- package/modules/auth/migrations/0017_mfa_key_id.up.sql +8 -0
- package/modules/auth/migrations/0018_retire_bundled_module_scopes.up.sql +29 -0
- package/modules/auth/migrations/0019_notifications_member_scopes.up.sql +44 -0
- package/modules/auth/migrations/0020_identity_providers.up.sql +61 -0
- package/modules/auth/migrations/0021_membership_status.up.sql +19 -0
- package/modules/auth/migrations/0022_auth_provider_scopes.up.sql +45 -0
- package/modules/auth/migrations/0023_enterprise_module_scopes.down.sql +19 -0
- package/modules/auth/migrations/0023_enterprise_module_scopes.up.sql +71 -0
- package/modules/auth/migrations/0024_approvals_member_scopes.down.sql +22 -0
- package/modules/auth/migrations/0024_approvals_member_scopes.up.sql +48 -0
- package/modules/auth/migrations/0025_audit_holds_owner_scope.down.sql +21 -0
- package/modules/auth/migrations/0025_audit_holds_owner_scope.up.sql +47 -0
- package/modules/auth/migrations/0026_auth_export_keyset_indexes.down.sql +6 -0
- package/modules/auth/migrations/0026_auth_export_keyset_indexes.up.sql +15 -0
- package/modules/auth/migrations/0027_workflow_automation_profile_scopes.down.sql +34 -0
- package/modules/auth/migrations/0027_workflow_automation_profile_scopes.up.sql +72 -0
- package/modules/auth/migrations/0028_auth_member_search_prefix_indexes.down.sql +6 -0
- package/modules/auth/migrations/0028_auth_member_search_prefix_indexes.up.sql +21 -0
- package/modules/auth/migrations/0029_reports_exports_access_scopes.down.sql +22 -0
- package/modules/auth/migrations/0029_reports_exports_access_scopes.up.sql +56 -0
- package/modules/auth/migrations/0030_auth_membership_keyset_index.down.sql +4 -0
- package/modules/auth/migrations/0030_auth_membership_keyset_index.up.sql +10 -0
- package/modules/auth/migrations/0031_membership_scope_backfills_under_rls.down.sql +4 -0
- package/modules/auth/migrations/0031_membership_scope_backfills_under_rls.up.sql +31 -0
- package/modules/auth/migrations/0032_audit_actor_service.down.sql +9 -0
- package/modules/auth/migrations/0032_audit_actor_service.up.sql +10 -0
- package/modules/auth/migrations/0033_auth_account_keyset_indexes.down.sql +5 -0
- package/modules/auth/migrations/0033_auth_account_keyset_indexes.up.sql +23 -0
- package/modules/auth/module.json +3 -3
- package/modules/auth/package.json +2 -1
- package/modules/auth/spec/module.yaml +372 -3
- package/modules/auth/src/acl/scopes.ts +50 -6
- package/modules/auth/src/cli/commands.json +11 -0
- package/modules/auth/src/cli/greenfield.ts +2 -2
- package/modules/auth/src/cli/index.ts +14 -0
- package/modules/auth/src/cli/secrets.ts +114 -0
- package/modules/auth/src/client/AuthenticationCore.tsrx +61 -8
- package/modules/auth/src/client/api.ts +72 -6
- package/modules/auth/src/client/contribution.tsrx +23 -0
- package/modules/auth/src/client/index.ts +6 -0
- package/modules/auth/src/client/providers/IdentityProviderForm.tsrx +191 -0
- package/modules/auth/src/client/providers/IdentityProvidersView.tsrx +358 -0
- package/modules/auth/src/client/providers/api.ts +128 -0
- package/modules/auth/src/client/providers/provider-columns.tsrx +60 -0
- package/modules/auth/src/client/providers/state.ts +35 -0
- package/modules/auth/src/client/security/SecurityView.tsrx +9 -1
- package/modules/auth/src/client/state.ts +15 -1
- package/modules/auth/src/domain/types.ts +55 -0
- package/modules/auth/src/index.ts +11 -1
- package/modules/auth/src/middleware/authentication.ts +4 -0
- package/modules/auth/src/server/endpoints.ts +467 -171
- package/modules/auth/src/server/http.ts +3 -2
- package/modules/auth/src/server/index.ts +22 -1
- package/modules/auth/src/server/membership-endpoints.ts +62 -0
- package/modules/auth/src/server/mfa-enforcement.ts +156 -0
- package/modules/auth/src/server/oidc.ts +389 -0
- package/modules/auth/src/server/provider-endpoints.ts +246 -0
- package/modules/auth/src/server/provider-host.ts +77 -0
- package/modules/auth/src/server/runtime.ts +231 -55
- package/modules/auth/src/services/auth-service-error.ts +7 -2
- package/modules/auth/src/services/auth-service.ts +948 -51
- package/modules/auth/src/services/data-classes.ts +236 -0
- package/modules/auth/src/services/database-repository.ts +853 -71
- package/modules/auth/src/services/identity-provider-service.ts +571 -0
- package/modules/auth/src/services/mail-delivery.ts +16 -16
- package/modules/auth/src/services/mail-message.ts +54 -0
- package/modules/auth/src/services/mail-port.ts +24 -0
- package/modules/auth/src/services/mail-smtp.ts +85 -0
- package/modules/auth/src/services/mfa-rotation.ts +144 -0
- package/modules/auth/src/services/migration.ts +810 -0
- package/modules/auth/src/services/password.ts +14 -0
- package/modules/auth/src/services/provider-secret-rotation.ts +169 -0
- package/modules/auth/src/services/provider-secrets.ts +136 -0
- package/modules/auth/src/services/repository.ts +321 -5
- package/modules/auth/src/services/session-sweep-runner.ts +73 -0
- package/modules/auth/src/services/settings-store.ts +10 -83
- package/modules/auth/src/services/totp.ts +88 -62
- package/modules/auth/src/services/validation.ts +194 -7
- package/modules/auth/src/settings.ts +18 -0
- package/modules/auth/translations/en.json +84 -1
- package/modules/auth/translations/pl.json +84 -1
- package/modules/automations/migrations/0005_secret_rotation_inventory.down.sql +1 -0
- package/modules/automations/migrations/0005_secret_rotation_inventory.up.sql +6 -0
- package/modules/automations/migrations/0006_automations_list_sort_indexes.down.sql +4 -0
- package/modules/automations/migrations/0006_automations_list_sort_indexes.up.sql +11 -0
- package/modules/automations/module.json +24 -6
- package/modules/automations/package.json +4 -1
- package/modules/automations/spec/module.yaml +81 -7
- package/modules/automations/src/api/endpoints.ts +202 -28
- package/modules/automations/src/cli/commands.json +17 -0
- package/modules/automations/src/cli/index.ts +114 -0
- package/modules/automations/src/client/AutomationScheduleForm.tsrx +77 -19
- package/modules/automations/src/client/AutomationSchedulesView.tsrx +144 -40
- package/modules/automations/src/client/AutomationTriggersView.tsrx +131 -32
- package/modules/automations/src/client/api.ts +62 -19
- package/modules/automations/src/client/presentation.ts +20 -2
- package/modules/automations/src/client/state.ts +84 -0
- package/modules/automations/src/domain/cadence.ts +99 -3
- package/modules/automations/src/domain/cron.ts +366 -0
- package/modules/automations/src/domain/time-zone.ts +61 -0
- package/modules/automations/src/platform.ts +38 -4
- package/modules/automations/src/server/index.ts +11 -0
- package/modules/automations/src/server/runtime.ts +46 -22
- package/modules/{automations-workflows-integration/src/server/adapter.ts → automations/src/server/workflow-target.ts} +13 -14
- package/modules/{automations-workflows-integration/src/server/tools.ts → automations/src/server/workflow-tools.ts} +0 -0
- package/modules/automations/src/services/data-classes.ts +62 -0
- package/modules/automations/src/services/database-repository.ts +152 -38
- package/modules/automations/src/services/migration.ts +119 -9
- package/modules/automations/src/services/repository.ts +52 -1
- package/modules/automations/src/services/schedule-runner.ts +84 -0
- package/modules/automations/src/services/schedule-service.ts +152 -26
- package/modules/automations/src/services/secret-rotation.ts +176 -0
- package/modules/automations/src/services/secret-vault.ts +68 -41
- package/modules/automations/src/services/trigger-service.ts +13 -10
- package/modules/automations/translations/en.json +15 -2
- package/modules/automations/translations/pl.json +15 -2
- package/modules/connectors/migrations/0001_connectors_core.down.sql +3 -0
- package/modules/connectors/migrations/0001_connectors_core.up.sql +72 -0
- package/modules/connectors/migrations/0002_connectors_call_keys.down.sql +3 -0
- package/modules/connectors/migrations/0002_connectors_call_keys.up.sql +18 -0
- package/modules/connectors/migrations/0003_connectors_rotation_inventory.down.sql +2 -0
- package/modules/connectors/migrations/0003_connectors_rotation_inventory.up.sql +19 -0
- package/modules/connectors/migrations/README.md +3 -0
- package/modules/connectors/module.json +32 -0
- package/modules/{automations-workflows-integration → connectors}/package.json +28 -26
- package/modules/connectors/spec/module.yaml +448 -0
- package/modules/connectors/src/acl/permissions.ts +6 -0
- package/modules/connectors/src/agent/tools.ts +180 -0
- package/modules/connectors/src/api/endpoints.ts +572 -0
- package/modules/connectors/src/cli/commands.json +17 -0
- package/modules/connectors/src/cli/index.ts +126 -0
- package/modules/connectors/src/client/ConnectorCallsView.tsrx +358 -0
- package/modules/connectors/src/client/ConnectorForm.tsrx +424 -0
- package/modules/connectors/src/client/ConnectorsView.tsrx +711 -0
- package/modules/connectors/src/client/api.ts +281 -0
- package/modules/connectors/src/client/consent.ts +48 -0
- package/modules/connectors/src/client/contribution.tsrx +37 -0
- package/modules/connectors/src/client/index.ts +22 -0
- package/modules/connectors/src/client/navigation.ts +42 -0
- package/modules/connectors/src/client/presentation.ts +38 -0
- package/modules/connectors/src/client/state.ts +175 -0
- package/modules/connectors/src/domain/calls.ts +70 -0
- package/modules/connectors/src/domain/definitions.ts +190 -0
- package/modules/connectors/src/domain/http-json.ts +68 -0
- package/modules/connectors/src/domain/types.ts +185 -0
- package/modules/connectors/src/index.ts +64 -0
- package/modules/connectors/src/platform.ts +56 -0
- package/modules/connectors/src/server/index.ts +66 -0
- package/modules/connectors/src/server/runtime.ts +163 -0
- package/modules/connectors/src/services/call-service.ts +903 -0
- package/modules/connectors/src/services/connectors-service.ts +653 -0
- package/modules/connectors/src/services/credential-rotation.ts +221 -0
- package/modules/connectors/src/services/credential-vault.ts +194 -0
- package/modules/connectors/src/services/data-classes.ts +158 -0
- package/modules/connectors/src/services/database-repository.ts +897 -0
- package/modules/connectors/src/services/egress.ts +261 -0
- package/modules/connectors/src/services/index.ts +19 -0
- package/modules/connectors/src/services/migration.ts +202 -0
- package/modules/connectors/src/services/repository.ts +175 -0
- package/modules/connectors/src/services/service-error.ts +46 -0
- package/modules/connectors/src/settings.ts +69 -0
- package/modules/connectors/translations/en.json +191 -0
- package/modules/connectors/translations/pl.json +191 -0
- package/modules/directory/migrations/0001_directory_core.down.sql +5 -0
- package/modules/directory/migrations/0001_directory_core.up.sql +111 -0
- package/modules/directory/migrations/README.md +3 -0
- package/modules/directory/module.json +27 -0
- package/modules/directory/package.json +46 -0
- package/modules/directory/spec/module.yaml +351 -0
- package/modules/directory/src/acl/permissions.ts +7 -0
- package/modules/directory/src/api/endpoints.ts +478 -0
- package/modules/directory/src/api/scim-endpoints.ts +516 -0
- package/modules/directory/src/client/GroupMappingsView.tsrx +430 -0
- package/modules/directory/src/client/ProvisioningLogView.tsrx +248 -0
- package/modules/directory/src/client/ScimTokensView.tsrx +587 -0
- package/modules/directory/src/client/api.ts +222 -0
- package/modules/directory/src/client/contribution.tsrx +46 -0
- package/modules/directory/src/client/index.ts +22 -0
- package/modules/directory/src/client/navigation.ts +57 -0
- package/modules/directory/src/client/presentation.ts +143 -0
- package/modules/directory/src/client/state.ts +173 -0
- package/modules/directory/src/domain/scim.ts +346 -0
- package/modules/directory/src/domain/types.ts +166 -0
- package/modules/directory/src/index.ts +44 -0
- package/modules/directory/src/platform.ts +43 -0
- package/modules/directory/src/server/index.ts +17 -0
- package/modules/directory/src/server/runtime.ts +124 -0
- package/modules/directory/src/services/auth-port.ts +127 -0
- package/modules/directory/src/services/data-classes.ts +41 -0
- package/modules/directory/src/services/database-repository.ts +1067 -0
- package/modules/directory/src/services/directory-service.ts +311 -0
- package/modules/directory/src/services/index.ts +43 -0
- package/modules/directory/src/services/migration.ts +146 -0
- package/modules/directory/src/services/provisioning-service.ts +1420 -0
- package/modules/directory/src/services/rate-limiter.ts +103 -0
- package/modules/directory/src/services/repository.ts +208 -0
- package/modules/directory/src/services/service-error.ts +42 -0
- package/modules/directory/src/services/token-service.ts +215 -0
- package/modules/directory/src/settings.ts +86 -0
- package/modules/directory/translations/en.json +185 -0
- package/modules/directory/translations/pl.json +185 -0
- package/modules/documents/migrations/0001_documents_core.down.sql +1 -0
- package/modules/documents/migrations/0001_documents_core.up.sql +35 -0
- package/modules/documents/migrations/0002_documents_files_page.down.sql +1 -0
- package/modules/documents/migrations/0002_documents_files_page.up.sql +6 -0
- package/modules/documents/migrations/0003_documents_rotation_inventory.down.sql +2 -0
- package/modules/documents/migrations/0003_documents_rotation_inventory.up.sql +18 -0
- package/modules/documents/migrations/README.md +3 -0
- package/modules/documents/module.json +32 -0
- package/modules/documents/package.json +50 -0
- package/modules/documents/spec/module.yaml +248 -0
- package/modules/documents/src/acl/permissions.ts +6 -0
- package/modules/documents/src/api/endpoints.ts +401 -0
- package/modules/documents/src/cli/commands.json +17 -0
- package/modules/documents/src/cli/index.ts +145 -0
- package/modules/documents/src/client/DocumentUploader.tsrx +148 -0
- package/modules/documents/src/client/DocumentsView.tsrx +540 -0
- package/modules/documents/src/client/api.ts +209 -0
- package/modules/documents/src/client/contribution.tsrx +29 -0
- package/modules/documents/src/client/download.ts +70 -0
- package/modules/documents/src/client/index.ts +22 -0
- package/modules/documents/src/client/navigation.ts +25 -0
- package/modules/documents/src/client/presentation.ts +110 -0
- package/modules/documents/src/client/state.ts +117 -0
- package/modules/documents/src/domain/attachments.ts +84 -0
- package/modules/documents/src/domain/types.ts +80 -0
- package/modules/documents/src/index.ts +46 -0
- package/modules/documents/src/platform.ts +61 -0
- package/modules/documents/src/server/index.ts +30 -0
- package/modules/documents/src/server/runtime.ts +107 -0
- package/modules/documents/src/services/attachments.ts +39 -0
- package/modules/documents/src/services/data-classes.ts +25 -0
- package/modules/documents/src/services/database-repository.ts +270 -0
- package/modules/documents/src/services/documents-service.ts +590 -0
- package/modules/documents/src/services/index.ts +18 -0
- package/modules/documents/src/services/migration.ts +117 -0
- package/modules/documents/src/services/repository.ts +58 -0
- package/modules/documents/src/services/storage-rotation.ts +157 -0
- package/modules/documents/src/settings.ts +107 -0
- package/modules/documents/translations/en.json +111 -0
- package/modules/documents/translations/pl.json +111 -0
- package/modules/exports/migrations/0001_exports_core.down.sql +2 -0
- package/modules/exports/migrations/0001_exports_core.up.sql +28 -0
- package/modules/exports/migrations/0002_exports_job_routing_role.down.sql +4 -0
- package/modules/exports/migrations/0002_exports_job_routing_role.up.sql +21 -0
- package/modules/exports/migrations/0003_exports_rotation_inventory.down.sql +1 -0
- package/modules/exports/migrations/0003_exports_rotation_inventory.up.sql +9 -0
- package/modules/exports/migrations/README.md +3 -0
- package/modules/exports/module.json +32 -0
- package/modules/exports/package.json +45 -0
- package/modules/exports/spec/module.yaml +230 -0
- package/modules/exports/src/acl/permissions.ts +6 -0
- package/modules/exports/src/api/endpoints.ts +211 -0
- package/modules/exports/src/cli/commands.json +17 -0
- package/modules/exports/src/cli/index.ts +145 -0
- package/modules/exports/src/client/ExportsView.tsrx +327 -0
- package/modules/exports/src/client/api.ts +133 -0
- package/modules/exports/src/client/contribution.tsrx +31 -0
- package/modules/exports/src/client/index.ts +20 -0
- package/modules/exports/src/client/navigation.ts +25 -0
- package/modules/exports/src/client/presentation.ts +53 -0
- package/modules/exports/src/client/state.ts +56 -0
- package/modules/exports/src/domain/lists.ts +29 -0
- package/modules/exports/src/domain/types.ts +120 -0
- package/modules/exports/src/index.ts +41 -0
- package/modules/exports/src/platform.ts +80 -0
- package/modules/exports/src/server/index.ts +50 -0
- package/modules/exports/src/server/runtime.ts +160 -0
- package/modules/exports/src/services/data-classes.ts +59 -0
- package/modules/exports/src/services/database-repository.ts +403 -0
- package/modules/exports/src/services/export-runner.ts +123 -0
- package/modules/exports/src/services/export-service.ts +356 -0
- package/modules/exports/src/services/index.ts +6 -0
- package/modules/exports/src/services/list-registry.ts +105 -0
- package/modules/exports/src/services/migration.ts +116 -0
- package/modules/exports/src/services/repository.ts +96 -0
- package/modules/exports/src/services/storage-rotation.ts +138 -0
- package/modules/exports/src/settings.ts +70 -0
- package/modules/exports/translations/en.json +69 -0
- package/modules/exports/translations/pl.json +69 -0
- package/modules/import/migrations/0001_import_core.down.sql +6 -0
- package/modules/import/migrations/0001_import_core.up.sql +61 -0
- package/modules/import/migrations/0002_import_job_routing_role.down.sql +4 -0
- package/modules/import/migrations/0002_import_job_routing_role.up.sql +20 -0
- package/modules/import/migrations/0003_import_jobs_export_index.down.sql +1 -0
- package/modules/import/migrations/0003_import_jobs_export_index.up.sql +7 -0
- package/modules/import/migrations/0004_import_jobs_traceparent.down.sql +1 -0
- package/modules/import/migrations/0004_import_jobs_traceparent.up.sql +7 -0
- package/modules/import/migrations/README.md +3 -0
- package/modules/import/module.json +37 -0
- package/modules/import/package.json +50 -0
- package/modules/import/spec/module.yaml +335 -0
- package/modules/import/src/acl/permissions.ts +6 -0
- package/modules/import/src/api/endpoints.ts +400 -0
- package/modules/import/src/client/ImportJobDrawer.tsrx +229 -0
- package/modules/import/src/client/ImportsView.tsrx +455 -0
- package/modules/import/src/client/NewImportForm.tsrx +274 -0
- package/modules/import/src/client/api.ts +243 -0
- package/modules/import/src/client/contribution.tsrx +29 -0
- package/modules/import/src/client/index.ts +23 -0
- package/modules/import/src/client/navigation.ts +25 -0
- package/modules/import/src/client/presentation.ts +59 -0
- package/modules/import/src/client/state.ts +43 -0
- package/modules/import/src/domain/csv.ts +258 -0
- package/modules/import/src/domain/ports.ts +157 -0
- package/modules/import/src/domain/types.ts +179 -0
- package/modules/import/src/index.ts +58 -0
- package/modules/import/src/platform.ts +60 -0
- package/modules/import/src/server/index.ts +49 -0
- package/modules/import/src/server/runtime.ts +158 -0
- package/modules/import/src/services/csv-source.ts +121 -0
- package/modules/import/src/services/data-classes.ts +63 -0
- package/modules/import/src/services/database-repository.ts +780 -0
- package/modules/import/src/services/import-runner.ts +127 -0
- package/modules/import/src/services/import-service.ts +872 -0
- package/modules/import/src/services/index.ts +6 -0
- package/modules/import/src/services/migration.ts +156 -0
- package/modules/import/src/services/port-registry.ts +158 -0
- package/modules/import/src/services/repository.ts +147 -0
- package/modules/import/src/settings.ts +70 -0
- package/modules/import/translations/en.json +175 -0
- package/modules/import/translations/pl.json +175 -0
- package/modules/metering/migrations/0001_metering_core.down.sql +6 -0
- package/modules/metering/migrations/0001_metering_core.up.sql +119 -0
- package/modules/metering/migrations/0002_metering_bucket_export.down.sql +3 -0
- package/modules/metering/migrations/0002_metering_bucket_export.up.sql +5 -0
- package/modules/metering/migrations/README.md +3 -0
- package/modules/metering/module.json +46 -0
- package/modules/metering/package.json +50 -0
- package/modules/metering/spec/module.yaml +269 -0
- package/modules/metering/src/acl/permissions.ts +5 -0
- package/modules/metering/src/api/endpoints.ts +104 -0
- package/modules/metering/src/cli/commands.json +28 -0
- package/modules/metering/src/cli/index.ts +246 -0
- package/modules/metering/src/client/LimitsView.tsrx +170 -0
- package/modules/metering/src/client/UsageSummaryWidget.tsrx +63 -0
- package/modules/metering/src/client/UsageView.tsrx +205 -0
- package/modules/metering/src/client/api.ts +91 -0
- package/modules/metering/src/client/contribution.tsrx +42 -0
- package/modules/metering/src/client/index.ts +26 -0
- package/modules/metering/src/client/navigation.ts +54 -0
- package/modules/metering/src/client/presentation.ts +63 -0
- package/modules/metering/src/client/state.ts +31 -0
- package/modules/metering/src/domain/meters.ts +100 -0
- package/modules/metering/src/domain/types.ts +71 -0
- package/modules/metering/src/index.ts +51 -0
- package/modules/metering/src/platform.ts +84 -0
- package/modules/metering/src/server/index.ts +40 -0
- package/modules/metering/src/server/runtime.ts +165 -0
- package/modules/metering/src/services/data-classes.ts +34 -0
- package/modules/metering/src/services/database-repository.ts +574 -0
- package/modules/metering/src/services/index.ts +9 -0
- package/modules/metering/src/services/meter-registry.ts +145 -0
- package/modules/metering/src/services/metering-service.ts +441 -0
- package/modules/metering/src/services/migration.ts +183 -0
- package/modules/metering/src/services/notifications.ts +75 -0
- package/modules/metering/src/services/reports.ts +105 -0
- package/modules/metering/src/services/repository.ts +132 -0
- package/modules/metering/src/services/service-error.ts +46 -0
- package/modules/metering/src/settings.ts +47 -0
- package/modules/metering/translations/en.json +68 -0
- package/modules/metering/translations/pl.json +68 -0
- package/modules/notifications/migrations/0001_notifications_core.down.sql +1 -0
- package/modules/notifications/migrations/0001_notifications_core.up.sql +26 -0
- package/modules/notifications/migrations/0002_notifications_preferences.down.sql +1 -0
- package/modules/notifications/migrations/0002_notifications_preferences.up.sql +17 -0
- package/modules/notifications/migrations/0003_notifications_webhook_subscriptions.down.sql +1 -0
- package/modules/notifications/migrations/0003_notifications_webhook_subscriptions.up.sql +30 -0
- package/modules/notifications/migrations/0004_notifications_deliveries.down.sql +1 -0
- package/modules/notifications/migrations/0004_notifications_deliveries.up.sql +35 -0
- package/modules/notifications/migrations/0005_notifications_delivery_routing_role.down.sql +2 -0
- package/modules/notifications/migrations/0005_notifications_delivery_routing_role.up.sql +17 -0
- package/modules/notifications/migrations/0006_notifications_delivery_title.down.sql +1 -0
- package/modules/notifications/migrations/0006_notifications_delivery_title.up.sql +11 -0
- package/modules/notifications/migrations/0007_notifications_secret_rotation_inventory.down.sql +2 -0
- package/modules/notifications/migrations/0007_notifications_secret_rotation_inventory.up.sql +19 -0
- package/modules/notifications/migrations/0008_notifications_delivery_claim.down.sql +8 -0
- package/modules/notifications/migrations/0008_notifications_delivery_claim.up.sql +19 -0
- package/modules/notifications/migrations/0009_notifications_kind_approvals.down.sql +16 -0
- package/modules/notifications/migrations/0009_notifications_kind_approvals.up.sql +18 -0
- package/modules/notifications/migrations/0010_notifications_kind_meter_threshold.down.sql +16 -0
- package/modules/notifications/migrations/0010_notifications_kind_meter_threshold.up.sql +18 -0
- package/modules/notifications/migrations/0011_notifications_retention_keyset.down.sql +5 -0
- package/modules/notifications/migrations/0011_notifications_retention_keyset.up.sql +15 -0
- package/modules/notifications/migrations/0012_notifications_email_channel.down.sql +12 -0
- package/modules/notifications/migrations/0012_notifications_email_channel.up.sql +38 -0
- package/modules/notifications/migrations/0013_notifications_list_pages.down.sql +5 -0
- package/modules/notifications/migrations/0013_notifications_list_pages.up.sql +13 -0
- package/modules/notifications/migrations/README.md +3 -0
- package/modules/notifications/module.json +32 -0
- package/modules/notifications/package.json +49 -0
- package/modules/notifications/spec/module.yaml +718 -0
- package/modules/notifications/src/acl/permissions.ts +11 -0
- package/modules/notifications/src/api/endpoints.ts +814 -0
- package/modules/notifications/src/cli/commands.json +17 -0
- package/modules/notifications/src/cli/index.ts +126 -0
- package/modules/notifications/src/client/DeliveriesView.tsrx +483 -0
- package/modules/notifications/src/client/FactList.tsrx +24 -0
- package/modules/notifications/src/client/NotificationPreferencesView.tsrx +240 -0
- package/modules/notifications/src/client/NotificationsInboxView.tsrx +592 -0
- package/modules/notifications/src/client/UnreadCountWidget.tsrx +62 -0
- package/modules/notifications/src/client/WebhookForm.tsrx +297 -0
- package/modules/notifications/src/client/WebhooksView.tsrx +637 -0
- package/modules/notifications/src/client/api.ts +382 -0
- package/modules/notifications/src/client/contribution.tsrx +83 -0
- package/modules/notifications/src/client/inbox.ts +41 -0
- package/modules/notifications/src/client/index.ts +29 -0
- package/modules/notifications/src/client/navigation.ts +92 -0
- package/modules/notifications/src/client/notifications.css +70 -0
- package/modules/notifications/src/client/presentation.ts +81 -0
- package/modules/notifications/src/client/state.ts +246 -0
- package/modules/notifications/src/client/unread-count.ts +26 -0
- package/modules/notifications/src/domain/locale.ts +47 -0
- package/modules/notifications/src/domain/publish.ts +46 -0
- package/modules/notifications/src/domain/types.ts +186 -0
- package/modules/notifications/src/index.ts +62 -0
- package/modules/notifications/src/platform.ts +85 -0
- package/modules/notifications/src/server/index.ts +82 -0
- package/modules/notifications/src/server/runtime.ts +251 -0
- package/modules/notifications/src/services/data-classes.ts +139 -0
- package/modules/notifications/src/services/database-repository.ts +1334 -0
- package/modules/notifications/src/services/delivery-payload.ts +93 -0
- package/modules/notifications/src/services/delivery-runner.ts +170 -0
- package/modules/notifications/src/services/delivery-service.ts +811 -0
- package/modules/notifications/src/services/egress.ts +239 -0
- package/modules/notifications/src/services/email-channel.ts +93 -0
- package/modules/notifications/src/services/index.ts +23 -0
- package/modules/notifications/src/services/migration.ts +572 -0
- package/modules/notifications/src/services/notifications-service.ts +297 -0
- package/modules/notifications/src/services/paging.ts +60 -0
- package/modules/notifications/src/services/publish-service.ts +133 -0
- package/modules/notifications/src/services/repository.ts +342 -0
- package/modules/notifications/src/services/secret-rotation.ts +177 -0
- package/modules/notifications/src/services/secret-vault.ts +168 -0
- package/modules/notifications/src/services/service-error.ts +67 -0
- package/modules/notifications/src/services/signature.ts +81 -0
- package/modules/notifications/src/services/webhook-service.ts +307 -0
- package/modules/notifications/src/settings.ts +130 -0
- package/modules/notifications/translations/en.json +255 -0
- package/modules/notifications/translations/pl.json +255 -0
- package/modules/profile/module.json +3 -3
- package/modules/profile/package.json +1 -1
- package/modules/profile/spec/module.yaml +3 -3
- package/modules/profile/src/services/database-repository.ts +3 -13
- package/modules/reports/module.json +28 -0
- package/modules/reports/package.json +42 -0
- package/modules/reports/spec/module.yaml +231 -0
- package/modules/reports/src/acl/permissions.ts +5 -0
- package/modules/reports/src/api/endpoints.ts +72 -0
- package/modules/reports/src/client/ReportsSummaryWidget.tsrx +52 -0
- package/modules/reports/src/client/ReportsView.tsrx +246 -0
- package/modules/reports/src/client/api.ts +65 -0
- package/modules/reports/src/client/contribution.tsrx +36 -0
- package/modules/reports/src/client/index.ts +18 -0
- package/modules/reports/src/client/navigation.ts +39 -0
- package/modules/reports/src/client/presentation.ts +123 -0
- package/modules/reports/src/client/state.ts +28 -0
- package/modules/reports/src/domain/providers.ts +115 -0
- package/modules/reports/src/domain/types.ts +44 -0
- package/modules/reports/src/index.ts +47 -0
- package/modules/reports/src/platform.ts +37 -0
- package/modules/reports/src/server/index.ts +21 -0
- package/modules/reports/src/server/runtime.ts +40 -0
- package/modules/reports/src/services/index.ts +17 -0
- package/modules/reports/src/services/provider-registry.ts +144 -0
- package/modules/reports/src/services/range.ts +65 -0
- package/modules/reports/src/services/reports-service.ts +316 -0
- package/modules/reports/src/services/service-error.ts +26 -0
- package/modules/reports/src/settings.ts +49 -0
- package/modules/reports/translations/en.json +35 -0
- package/modules/reports/translations/pl.json +35 -0
- package/modules/sandbox/module.json +4 -4
- package/modules/sandbox/package.json +1 -1
- package/modules/sandbox/spec/module.yaml +3 -3
- package/modules/sandbox/src/services/database-repository.ts +14 -23
- package/modules/sandbox/src/services/directory.ts +16 -0
- package/modules/sandbox/src/services/sandbox-service.ts +13 -11
- package/modules/search/migrations/0001_search_core.down.sql +1 -0
- package/modules/search/migrations/0001_search_core.up.sql +22 -0
- package/modules/search/migrations/0002_search_recent_export.down.sql +3 -0
- package/modules/search/migrations/0002_search_recent_export.up.sql +5 -0
- package/modules/search/migrations/README.md +3 -0
- package/modules/search/module.json +28 -0
- package/modules/search/package.json +43 -0
- package/modules/search/spec/module.yaml +177 -0
- package/modules/search/src/acl/permissions.ts +5 -0
- package/modules/search/src/api/endpoints.ts +221 -0
- package/modules/search/src/client/SearchView.tsrx +346 -0
- package/modules/search/src/client/api.ts +157 -0
- package/modules/search/src/client/contribution.tsrx +31 -0
- package/modules/search/src/client/index.ts +17 -0
- package/modules/search/src/client/navigation.ts +97 -0
- package/modules/search/src/client/search.css +56 -0
- package/modules/search/src/client/state.ts +113 -0
- package/modules/search/src/domain/data-classes.ts +73 -0
- package/modules/search/src/domain/providers.ts +88 -0
- package/modules/search/src/domain/types.ts +56 -0
- package/modules/search/src/index.ts +46 -0
- package/modules/search/src/platform.ts +42 -0
- package/modules/search/src/server/index.ts +25 -0
- package/modules/search/src/server/runtime.ts +123 -0
- package/modules/search/src/services/database-repository.ts +163 -0
- package/modules/search/src/services/index.ts +21 -0
- package/modules/search/src/services/migration.ts +65 -0
- package/modules/search/src/services/provider-registry.ts +137 -0
- package/modules/search/src/services/repository.ts +43 -0
- package/modules/search/src/services/search-service.ts +442 -0
- package/modules/search/src/services/service-error.ts +35 -0
- package/modules/search/src/settings.ts +68 -0
- package/modules/search/translations/en.json +45 -0
- package/modules/search/translations/pl.json +45 -0
- package/modules/system/module.json +2 -2
- package/modules/system/package.json +1 -1
- package/modules/system/spec/module.yaml +30 -3
- package/modules/system/src/client/FlagsPanel.tsrx +127 -0
- package/modules/system/src/client/ModuleSettingRow.tsrx +17 -7
- package/modules/system/src/client/ModuleSettingsSection.tsrx +10 -71
- package/modules/system/src/client/ModulesView.tsrx +91 -65
- package/modules/system/src/client/flags.ts +35 -0
- package/modules/system/src/client/index.ts +8 -0
- package/modules/system/src/client/navigation.ts +8 -0
- package/modules/system/src/client/settings-state.ts +159 -0
- package/modules/system/src/client/state.ts +2 -0
- package/modules/system/src/domain/time-zone.ts +105 -0
- package/modules/system/src/index.ts +12 -0
- package/modules/system/src/platform.ts +5 -1
- package/modules/system/src/server/endpoints.ts +72 -21
- package/modules/system/src/settings.ts +28 -0
- package/modules/system/translations/en.json +17 -0
- package/modules/system/translations/pl.json +17 -0
- package/modules/users/module.json +30 -4
- package/modules/users/package.json +4 -1
- package/modules/users/spec/module.yaml +290 -7
- package/modules/users/src/api/endpoints.ts +228 -15
- package/modules/users/src/client/InviteMemberForm.tsrx +2 -2
- package/modules/users/src/client/MemberDrawer.tsrx +142 -26
- package/modules/users/src/client/UserAccountForm.tsrx +2 -2
- package/modules/users/src/client/UsersView.tsrx +432 -71
- package/modules/users/src/client/api.ts +106 -12
- package/modules/users/src/client/contribution.tsrx +5 -0
- package/modules/users/src/client/member-columns.tsrx +22 -9
- package/modules/users/src/client/state.ts +172 -7
- package/modules/users/src/domain/lists.ts +9 -0
- package/modules/users/src/index.ts +11 -0
- package/modules/users/src/platform.ts +50 -1
- package/modules/users/src/services/member-export.ts +51 -0
- package/modules/users/src/services/member-import.ts +249 -0
- package/modules/users/src/services/member-search.ts +145 -0
- package/modules/users/src/services/users-service.ts +117 -4
- package/modules/users/translations/en.json +57 -8
- package/modules/users/translations/pl.json +57 -8
- package/modules/workflows/migrations/0004_payload_rotation_inventory.up.sql +17 -0
- package/modules/workflows/migrations/0005_workflows_human_approval.up.sql +28 -0
- package/modules/workflows/migrations/0006_workflows_retention_indexes.up.sql +11 -0
- package/modules/workflows/migrations/0007_workflows_erasure_subject_indexes.up.sql +17 -0
- package/modules/workflows/migrations/0008_workflows_subject_account.up.sql +24 -0
- package/modules/workflows/migrations/0009_workflows_definition_list_indexes.up.sql +9 -0
- package/modules/workflows/module.json +34 -6
- package/modules/workflows/package.json +2 -1
- package/modules/workflows/spec/module.yaml +26 -9
- package/modules/workflows/src/api/endpoints.ts +131 -25
- package/modules/workflows/src/cli/commands.json +17 -0
- package/modules/workflows/src/cli/index.ts +114 -0
- package/modules/workflows/src/client/WorkflowExecutionHistory.tsrx +36 -27
- package/modules/workflows/src/client/WorkflowInspector.tsrx +116 -0
- package/modules/workflows/src/client/WorkflowNodePalette.tsrx +1 -0
- package/modules/workflows/src/client/WorkflowsView.tsrx +236 -99
- package/modules/workflows/src/client/api.ts +54 -34
- package/modules/workflows/src/client/canvas-model.ts +12 -0
- package/modules/workflows/src/client/state.ts +95 -0
- package/modules/workflows/src/domain/events.ts +25 -11
- package/modules/workflows/src/domain/graph.ts +135 -1
- package/modules/workflows/src/domain/types.ts +91 -4
- package/modules/workflows/src/platform.ts +12 -0
- package/modules/workflows/src/server/runtime.ts +78 -5
- package/modules/workflows/src/services/approvals.ts +62 -0
- package/modules/workflows/src/services/cursors.ts +37 -0
- package/modules/workflows/src/services/data-classes.ts +232 -0
- package/modules/workflows/src/services/database-repository.ts +660 -171
- package/modules/workflows/src/services/migration.ts +240 -0
- package/modules/workflows/src/services/notifications.ts +98 -0
- package/modules/workflows/src/services/payload-codec.ts +51 -28
- package/modules/workflows/src/services/payload-rotation.ts +171 -0
- package/modules/workflows/src/services/repository.ts +123 -8
- package/modules/workflows/src/services/simulation.ts +7 -0
- package/modules/workflows/src/services/worker.ts +446 -25
- package/modules/workflows/src/services/workflows-service.ts +264 -103
- package/modules/workflows/translations/en.json +12 -4
- package/modules/workflows/translations/pl.json +12 -4
- package/modules.json +48 -4
- package/package.json +64 -6
- package/packages/client/package.json +1 -0
- package/packages/client/src/ApplicationShell.tsrx +43 -0
- package/packages/client/src/contributions.ts +55 -0
- package/packages/client/src/i18n/locales/en.json +5 -0
- package/packages/client/src/i18n/locales/pl.json +5 -0
- package/packages/client/src/index.ts +13 -1
- package/packages/client/src/routing.ts +22 -0
- package/packages/client/src/shell/CommandPalette.tsrx +223 -11
- package/packages/client/src/shell/command-search.ts +126 -0
- package/packages/client/src/shell/command.ts +30 -0
- package/packages/client/src/shell/navigation.ts +12 -0
- package/packages/client/src/shell/palette-keys.ts +27 -0
- package/packages/client/src/shell/shell.css +7 -0
- package/packages/contracts/schemas/module-spec.schema.json +287 -1
- package/packages/contracts/schemas/module.schema.json +25 -0
- package/packages/contracts/src/index.ts +144 -1
- package/packages/database/src/backup.ts +180 -0
- package/packages/database/src/decoders.ts +25 -0
- package/packages/database/src/index.ts +17 -0
- package/packages/database/src/migrations.ts +7 -0
- package/packages/database/src/record-history.ts +5 -14
- package/packages/harness/src/index.ts +2 -0
- package/packages/harness/src/runtime.ts +378 -2
- package/packages/harness/src/tool-adapters.ts +20 -9
- package/packages/kernel/src/acl.ts +179 -0
- package/packages/kernel/src/approval-grant.ts +310 -0
- package/packages/kernel/src/capability-registry.ts +69 -2
- package/packages/kernel/src/data-class-registry.ts +354 -0
- package/packages/kernel/src/index.ts +85 -4
- package/packages/kernel/src/keyring.ts +208 -0
- package/packages/kernel/src/module-compatibility.ts +35 -4
- package/packages/kernel/src/module-registry.ts +49 -2
- package/packages/kernel/src/module-settings.ts +189 -27
- package/packages/server/package.json +2 -1
- package/packages/server/src/composition.ts +38 -0
- package/packages/server/src/endpoint.ts +104 -47
- package/packages/server/src/export/csv.ts +34 -0
- package/packages/server/src/export/definition.ts +346 -0
- package/packages/server/src/export/index.ts +61 -0
- package/packages/server/src/export/run.ts +116 -0
- package/packages/server/src/index.ts +155 -0
- package/packages/server/src/jobs/index.ts +93 -0
- package/packages/server/src/jobs/runner.ts +486 -0
- package/packages/server/src/log.ts +291 -0
- package/packages/server/src/mail/config.ts +184 -0
- package/packages/server/src/mail/contracts.ts +251 -0
- package/packages/server/src/mail/index.ts +31 -0
- package/packages/server/src/mail/port.ts +101 -0
- package/packages/server/src/mail/smtp.ts +191 -0
- package/packages/server/src/mail/template.ts +105 -0
- package/packages/server/src/metrics.ts +447 -0
- package/packages/server/src/pagination.ts +246 -0
- package/packages/server/src/trace/context.ts +103 -0
- package/packages/server/src/trace/egress.ts +66 -0
- package/packages/server/src/trace/error-sink.ts +296 -0
- package/packages/server/src/trace/exporter.ts +295 -0
- package/packages/server/src/trace/index.ts +60 -0
- package/packages/server/src/trace/job-sink.ts +172 -0
- package/packages/server/src/trace/tracer.ts +338 -0
- package/packages/server/src/web.ts +5 -1
- package/packages/storage/package.json +40 -0
- package/packages/storage/src/config.ts +189 -0
- package/packages/storage/src/content-type.ts +124 -0
- package/packages/storage/src/contracts.ts +112 -0
- package/packages/storage/src/envelope.ts +238 -0
- package/packages/storage/src/index.ts +49 -0
- package/packages/storage/src/local.ts +75 -0
- package/packages/storage/src/port.ts +262 -0
- package/packages/storage/src/read-token.ts +130 -0
- package/packages/storage/src/reseal.ts +128 -0
- package/packages/storage/src/s3.ts +144 -0
- package/packages/storage/src/scanner.ts +22 -0
- package/packages/storage/src/sigv4.ts +104 -0
- package/packages/storage/src/store.ts +13 -0
- package/packages/ui/package.json +1 -0
- package/packages/ui/src/components/Button.tsrx +3 -0
- package/packages/ui/src/components/ConfirmDialog.tsrx +13 -2
- package/packages/ui/src/components/DateField.tsrx +92 -0
- package/packages/ui/src/components/DatePicker.tsrx +525 -0
- package/packages/ui/src/components/DateRangeField.tsrx +93 -0
- package/packages/ui/src/components/Drawer.tsrx +10 -1
- package/packages/ui/src/components/FileUpload.tsrx +158 -0
- package/packages/ui/src/components/Pagination.tsrx +130 -0
- package/packages/ui/src/components/Select.tsrx +78 -0
- package/packages/ui/src/components/Table.tsrx +498 -81
- package/packages/ui/src/components/Tabs.tsrx +53 -0
- package/packages/ui/src/components/ToastHost.tsrx +48 -0
- package/packages/ui/src/components/calendar.ts +243 -0
- package/packages/ui/src/components/date-field.ts +62 -0
- package/packages/ui/src/components/file-upload.ts +49 -0
- package/packages/ui/src/components/focus-trap.ts +62 -0
- package/packages/ui/src/components/pagination.ts +68 -0
- package/packages/ui/src/components/table-sorting.ts +49 -0
- package/packages/ui/src/components/tabs.ts +61 -0
- package/packages/ui/src/components/toast-store.ts +116 -0
- package/packages/ui/src/icons/Icon.tsrx +5 -0
- package/packages/ui/src/index.ts +45 -0
- package/packages/ui/src/styles/components.css +598 -0
- package/modules/automations-workflows-integration/module.json +0 -27
- package/modules/automations-workflows-integration/spec/module.yaml +0 -93
- package/modules/automations-workflows-integration/src/index.ts +0 -14
- package/modules/automations-workflows-integration/src/platform.ts +0 -24
- package/modules/automations-workflows-integration/src/server/index.ts +0 -11
- package/modules/automations-workflows-integration/translations/en.json +0 -21
- package/modules/automations-workflows-integration/translations/pl.json +0 -21
- package/modules/workflows/src/services/cursor-codec.ts +0 -62
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
integer,
|
|
2
3
|
runDatabaseMigrations,
|
|
3
4
|
type DatabaseHandle,
|
|
4
5
|
type DatabaseParameter,
|
|
@@ -9,23 +10,30 @@ import type {
|
|
|
9
10
|
ActorKind,
|
|
10
11
|
ModuleSettingRecord,
|
|
11
12
|
ModuleSettingValue,
|
|
13
|
+
UserActor,
|
|
12
14
|
} from '@flowdular/sdk/kernel';
|
|
15
|
+
import { keysetWhere } from '@flowdular/sdk/server';
|
|
13
16
|
import { BUILTIN_ROLES } from '../acl/scopes.ts';
|
|
14
17
|
import type {
|
|
15
18
|
ApiTokenRecord,
|
|
19
|
+
AuditEvent,
|
|
16
20
|
AuditQuery,
|
|
17
21
|
AuthPrincipal,
|
|
18
22
|
AuthSession,
|
|
19
23
|
AuthTenantAccess,
|
|
24
|
+
IdentityProviderStatus,
|
|
25
|
+
MembershipStatus,
|
|
20
26
|
SessionSummary,
|
|
21
27
|
TenantRole,
|
|
22
28
|
} from '../domain/types.ts';
|
|
23
29
|
import { databaseMigrations } from './migration.ts';
|
|
24
30
|
import {
|
|
25
31
|
DuplicateAccountError,
|
|
32
|
+
DuplicateProviderKeyError,
|
|
26
33
|
DuplicateRoleKeyError,
|
|
27
34
|
DuplicateTenantSlugError,
|
|
28
35
|
type AccountCredential,
|
|
36
|
+
type AccountIdentity,
|
|
29
37
|
type AuditActorEvent,
|
|
30
38
|
type AuditRecord,
|
|
31
39
|
type AuthRepository,
|
|
@@ -35,13 +43,22 @@ import {
|
|
|
35
43
|
type CreateRoleRecord,
|
|
36
44
|
type CreateSessionRecord,
|
|
37
45
|
type CreateTenantMembershipRecord,
|
|
46
|
+
type ExternalIdentityBinding,
|
|
47
|
+
type ExternalIdentityRecord,
|
|
48
|
+
type IdentityProviderPatch,
|
|
49
|
+
type IdentityProviderRecord,
|
|
38
50
|
type MfaChallengeRecord,
|
|
39
51
|
type PasswordResetTokenRecord,
|
|
52
|
+
type SessionExportRecord,
|
|
40
53
|
type SignInFailureRecord,
|
|
41
54
|
type TenantInvitationRecord,
|
|
42
55
|
type TenantMember,
|
|
56
|
+
type TenantMemberSort,
|
|
57
|
+
type TenantMemberSortedPage,
|
|
58
|
+
type TenantMemberSortedRead,
|
|
43
59
|
type TenantSummary,
|
|
44
60
|
} from './repository.ts';
|
|
61
|
+
import type { SealedMfaSecret } from './totp.ts';
|
|
45
62
|
|
|
46
63
|
/**
|
|
47
64
|
* The tenant context an identity table is read and written under. Accounts,
|
|
@@ -52,6 +69,14 @@ import {
|
|
|
52
69
|
*/
|
|
53
70
|
export const IDENTITY_TENANT_CONTEXT = 'auth.core:identity';
|
|
54
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Rows one expired-session delete removes. The sweep walks a backlog of
|
|
74
|
+
* unknown size, and a single delete over it would hold the table for as long as
|
|
75
|
+
* it takes; a bounded batch keeps every statement short and leaves the rest to
|
|
76
|
+
* the next batch.
|
|
77
|
+
*/
|
|
78
|
+
export const EXPIRED_SESSION_SWEEP_BATCH = 1_000;
|
|
79
|
+
|
|
55
80
|
/**
|
|
56
81
|
* Storage tenant of a platform-scoped setting. The kernel addresses it as the
|
|
57
82
|
* empty string, which is not a tenant id a policy can be bound to, so the row
|
|
@@ -84,6 +109,7 @@ interface AccountRow {
|
|
|
84
109
|
role: string;
|
|
85
110
|
role_id: string | null;
|
|
86
111
|
status: 'active' | 'disabled';
|
|
112
|
+
membership_status: MembershipStatus;
|
|
87
113
|
password_change_required: number | bigint | string;
|
|
88
114
|
}
|
|
89
115
|
|
|
@@ -105,11 +131,38 @@ interface TenantMemberRow {
|
|
|
105
131
|
role: string;
|
|
106
132
|
role_id: string | null;
|
|
107
133
|
status: 'active' | 'disabled';
|
|
134
|
+
membership_status: MembershipStatus;
|
|
108
135
|
password_change_required: number | bigint | string;
|
|
109
136
|
scopes: string | null;
|
|
110
137
|
created_at: number | bigint | string;
|
|
111
138
|
}
|
|
112
139
|
|
|
140
|
+
interface SortedTenantMemberRow extends TenantMemberRow {
|
|
141
|
+
sort_value: string;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
interface IdentityProviderRow {
|
|
145
|
+
id: string;
|
|
146
|
+
tenant_id: string;
|
|
147
|
+
key: string;
|
|
148
|
+
label: string;
|
|
149
|
+
issuer: string;
|
|
150
|
+
authorization_endpoint: string;
|
|
151
|
+
token_endpoint: string;
|
|
152
|
+
user_info_endpoint: string;
|
|
153
|
+
client_id: string;
|
|
154
|
+
client_secret_ciphertext: string;
|
|
155
|
+
client_secret_key_id: string;
|
|
156
|
+
client_secret_fingerprint: string;
|
|
157
|
+
scopes_json: string;
|
|
158
|
+
jit_enabled: number | bigint | string;
|
|
159
|
+
allowed_domains_json: string;
|
|
160
|
+
jit_role: string;
|
|
161
|
+
status: IdentityProviderStatus;
|
|
162
|
+
created_at: number | bigint | string;
|
|
163
|
+
updated_at: number | bigint | string;
|
|
164
|
+
}
|
|
165
|
+
|
|
113
166
|
interface SessionRow extends AccountRow {
|
|
114
167
|
session_id: string;
|
|
115
168
|
csrf_token: string;
|
|
@@ -138,6 +191,15 @@ interface RoleRow {
|
|
|
138
191
|
updated_at: number | bigint | string;
|
|
139
192
|
}
|
|
140
193
|
|
|
194
|
+
interface SessionExportRow {
|
|
195
|
+
id: string;
|
|
196
|
+
tenant_id: string;
|
|
197
|
+
account_id: string;
|
|
198
|
+
created_at: number | bigint | string;
|
|
199
|
+
expires_at: number | bigint | string;
|
|
200
|
+
last_seen_at: number | bigint | string;
|
|
201
|
+
}
|
|
202
|
+
|
|
141
203
|
interface AuditRow {
|
|
142
204
|
id: number | bigint | string;
|
|
143
205
|
tenant_id: string;
|
|
@@ -145,6 +207,7 @@ interface AuditRow {
|
|
|
145
207
|
actor_label: string;
|
|
146
208
|
actor_kind: ActorKind;
|
|
147
209
|
actor_run_id: string | null;
|
|
210
|
+
configured_by_json: string | null;
|
|
148
211
|
action: string;
|
|
149
212
|
subject_type: string;
|
|
150
213
|
subject_id: string;
|
|
@@ -162,26 +225,128 @@ interface CountRow {
|
|
|
162
225
|
total: number | bigint | string;
|
|
163
226
|
}
|
|
164
227
|
|
|
165
|
-
/* PostgreSQL returns BIGINT as a string, and every timestamp, expiry and
|
|
166
|
-
counter here is one. An unnormalized read would compare and serialize as
|
|
167
|
-
text: a session would never look expired and a role would never look
|
|
168
|
-
built in. */
|
|
169
|
-
function integer(value: number | bigint | string): number {
|
|
170
|
-
const normalized = Number(value);
|
|
171
|
-
if (!Number.isSafeInteger(normalized)) {
|
|
172
|
-
throw new Error('The auth database returned an invalid integer.');
|
|
173
|
-
}
|
|
174
|
-
return normalized;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
228
|
function optionalInteger(
|
|
178
229
|
value: number | bigint | string | null,
|
|
230
|
+
field: string,
|
|
179
231
|
): number | null {
|
|
180
|
-
return value === null ? null : integer(value);
|
|
232
|
+
return value === null ? null : integer(value, field);
|
|
181
233
|
}
|
|
182
234
|
|
|
183
235
|
const ACCOUNT_COLUMNS = `a.id AS account_id, m.tenant_id, a.email, a.display_name,
|
|
184
|
-
a.password_hash, m.role, m.role_id, a.status,
|
|
236
|
+
a.password_hash, m.role, m.role_id, a.status,
|
|
237
|
+
m.status AS membership_status, a.password_change_required`;
|
|
238
|
+
|
|
239
|
+
/* One projection for the whole roll and for a single member, so the two reads
|
|
240
|
+
can never disagree about what a member is. */
|
|
241
|
+
const TENANT_MEMBER_COLUMNS = `a.id AS account_id, a.email, a.display_name,
|
|
242
|
+
m.role, m.role_id, a.status, m.status AS membership_status,
|
|
243
|
+
a.password_change_required, m.created_at,
|
|
244
|
+
(SELECT string_agg(s.scope, ' ' ORDER BY s.scope)
|
|
245
|
+
FROM auth_membership_scopes s
|
|
246
|
+
WHERE s.account_id = m.account_id AND s.tenant_id = m.tenant_id) AS scopes`;
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* The member search statement, exported so a plan assertion explains the
|
|
250
|
+
* statement the repository runs rather than a copy of it that can drift away
|
|
251
|
+
* from the indexes it was written for.
|
|
252
|
+
*/
|
|
253
|
+
export const TENANT_MEMBER_SEARCH_SQL = `SELECT ${TENANT_MEMBER_COLUMNS}
|
|
254
|
+
FROM auth_memberships m
|
|
255
|
+
JOIN auth_accounts a ON a.id = m.account_id
|
|
256
|
+
WHERE m.tenant_id = $1
|
|
257
|
+
AND (lower(a.display_name) LIKE $2 ESCAPE '\\'
|
|
258
|
+
OR a.email_normalized LIKE $2 ESCAPE '\\')
|
|
259
|
+
ORDER BY lower(a.display_name), a.id
|
|
260
|
+
LIMIT $3`;
|
|
261
|
+
|
|
262
|
+
const TENANT_MEMBER_SORT_COLUMNS: Readonly<Record<TenantMemberSort, string>> = {
|
|
263
|
+
displayName: 'lower(a.display_name)',
|
|
264
|
+
email: 'a.email_normalized',
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* The sorted member page statement, exported for the same plan assertion as
|
|
269
|
+
* the search. The sort column is projected once, so the keyset predicate names
|
|
270
|
+
* a plain identifier and the ORDER BY the same expression the index carries.
|
|
271
|
+
*/
|
|
272
|
+
export function tenantMemberSortedStatement(
|
|
273
|
+
tenantId: string,
|
|
274
|
+
read: TenantMemberSortedRead,
|
|
275
|
+
): DatabaseStatement {
|
|
276
|
+
const parameters: DatabaseParameter[] = [tenantId];
|
|
277
|
+
const filters = ['m.tenant_id = $1'];
|
|
278
|
+
if (read.membershipStatus !== null) {
|
|
279
|
+
parameters.push(read.membershipStatus);
|
|
280
|
+
filters.push(`m.status = $${parameters.length}`);
|
|
281
|
+
}
|
|
282
|
+
if (read.term !== null) {
|
|
283
|
+
parameters.push(`${read.term}%`);
|
|
284
|
+
filters.push(
|
|
285
|
+
`(lower(a.display_name) LIKE $${parameters.length} ESCAPE '\\'
|
|
286
|
+
OR a.email_normalized LIKE $${parameters.length} ESCAPE '\\')`,
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
let keyset = '';
|
|
290
|
+
if (read.after !== null) {
|
|
291
|
+
const predicate = keysetWhere(
|
|
292
|
+
['sort_value', 'account_id'],
|
|
293
|
+
[read.after.sortValue, read.after.accountId],
|
|
294
|
+
{ direction: read.direction, parameterOffset: parameters.length },
|
|
295
|
+
);
|
|
296
|
+
parameters.push(...predicate.parameters);
|
|
297
|
+
keyset = `WHERE ${predicate.text}`;
|
|
298
|
+
}
|
|
299
|
+
parameters.push(read.limit);
|
|
300
|
+
const order = read.direction === 'asc' ? 'ASC' : 'DESC';
|
|
301
|
+
return {
|
|
302
|
+
text: `SELECT * FROM (
|
|
303
|
+
SELECT ${TENANT_MEMBER_COLUMNS},
|
|
304
|
+
${TENANT_MEMBER_SORT_COLUMNS[read.sort]} AS sort_value
|
|
305
|
+
FROM auth_memberships m
|
|
306
|
+
JOIN auth_accounts a ON a.id = m.account_id
|
|
307
|
+
WHERE ${filters.join(' AND ')}
|
|
308
|
+
) members
|
|
309
|
+
${keyset}
|
|
310
|
+
ORDER BY sort_value ${order}, account_id ${order}
|
|
311
|
+
LIMIT $${parameters.length}`,
|
|
312
|
+
parameters,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* One PostgreSQL `text[]` literal. A bound parameter carries no array type, so
|
|
318
|
+
* a list travels as this literal and the statement casts it back. Every element
|
|
319
|
+
* is quoted with its backslashes and quotes escaped, so no element can close
|
|
320
|
+
* the literal early or add one of its own.
|
|
321
|
+
*/
|
|
322
|
+
export function textArrayLiteral(values: readonly string[]): string {
|
|
323
|
+
return `{${values
|
|
324
|
+
.map((value) => `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`)
|
|
325
|
+
.join(',')}}`;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
interface ExternalIdentityBindingRow {
|
|
329
|
+
account_id: string;
|
|
330
|
+
provider: string;
|
|
331
|
+
subject: string;
|
|
332
|
+
created_at: number | bigint | string;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function tenantMemberFrom(row: TenantMemberRow): TenantMember {
|
|
336
|
+
return {
|
|
337
|
+
accountId: row.account_id,
|
|
338
|
+
email: row.email,
|
|
339
|
+
displayName: row.display_name,
|
|
340
|
+
role: row.role,
|
|
341
|
+
roleId: row.role_id,
|
|
342
|
+
status: row.status,
|
|
343
|
+
membershipStatus: row.membership_status,
|
|
344
|
+
scopes: row.scopes ? row.scopes.split(' ') : [],
|
|
345
|
+
passwordChangeRequired:
|
|
346
|
+
integer(row.password_change_required, 'password_change_required') === 1,
|
|
347
|
+
createdAt: integer(row.created_at, 'created_at'),
|
|
348
|
+
};
|
|
349
|
+
}
|
|
185
350
|
|
|
186
351
|
export function builtinRoleId(tenantId: string, key: string): string {
|
|
187
352
|
return `${tenantId}:${key}`;
|
|
@@ -196,10 +361,10 @@ function fromApiTokenRow(row: ApiTokenRow): ApiTokenRecord {
|
|
|
196
361
|
prefix: row.prefix,
|
|
197
362
|
scopes: JSON.parse(row.scopes_json) as readonly string[],
|
|
198
363
|
createdBy: row.created_by,
|
|
199
|
-
createdAt: integer(row.created_at),
|
|
200
|
-
expiresAt: optionalInteger(row.expires_at),
|
|
201
|
-
lastUsedAt: optionalInteger(row.last_used_at),
|
|
202
|
-
revokedAt: optionalInteger(row.revoked_at),
|
|
364
|
+
createdAt: integer(row.created_at, 'created_at'),
|
|
365
|
+
expiresAt: optionalInteger(row.expires_at, 'expires_at'),
|
|
366
|
+
lastUsedAt: optionalInteger(row.last_used_at, 'last_used_at'),
|
|
367
|
+
revokedAt: optionalInteger(row.revoked_at, 'revoked_at'),
|
|
203
368
|
revokedBy: row.revoked_by,
|
|
204
369
|
};
|
|
205
370
|
}
|
|
@@ -212,9 +377,35 @@ function fromRoleRow(row: RoleRow): TenantRole {
|
|
|
212
377
|
name: row.name,
|
|
213
378
|
description: row.description,
|
|
214
379
|
scopes: JSON.parse(row.scopes_json) as readonly string[],
|
|
215
|
-
builtin: integer(row.builtin) === 1,
|
|
216
|
-
createdAt: integer(row.created_at),
|
|
217
|
-
updatedAt: integer(row.updated_at),
|
|
380
|
+
builtin: integer(row.builtin, 'builtin') === 1,
|
|
381
|
+
createdAt: integer(row.created_at, 'created_at'),
|
|
382
|
+
updatedAt: integer(row.updated_at, 'updated_at'),
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function fromIdentityProviderRow(
|
|
387
|
+
row: IdentityProviderRow,
|
|
388
|
+
): IdentityProviderRecord {
|
|
389
|
+
return {
|
|
390
|
+
id: row.id,
|
|
391
|
+
tenantId: row.tenant_id,
|
|
392
|
+
key: row.key,
|
|
393
|
+
label: row.label,
|
|
394
|
+
issuer: row.issuer,
|
|
395
|
+
authorizationEndpoint: row.authorization_endpoint,
|
|
396
|
+
tokenEndpoint: row.token_endpoint,
|
|
397
|
+
userInfoEndpoint: row.user_info_endpoint,
|
|
398
|
+
clientId: row.client_id,
|
|
399
|
+
secretCiphertext: row.client_secret_ciphertext,
|
|
400
|
+
secretKeyId: row.client_secret_key_id,
|
|
401
|
+
secretFingerprint: row.client_secret_fingerprint,
|
|
402
|
+
scopes: JSON.parse(row.scopes_json) as readonly string[],
|
|
403
|
+
jitEnabled: integer(row.jit_enabled, 'jit_enabled') === 1,
|
|
404
|
+
allowedDomains: JSON.parse(row.allowed_domains_json) as readonly string[],
|
|
405
|
+
jitRole: row.jit_role,
|
|
406
|
+
status: row.status,
|
|
407
|
+
createdAt: integer(row.created_at, 'created_at'),
|
|
408
|
+
updatedAt: integer(row.updated_at, 'updated_at'),
|
|
218
409
|
};
|
|
219
410
|
}
|
|
220
411
|
|
|
@@ -225,11 +416,16 @@ function tenantSummary(row: TenantRow): TenantSummary {
|
|
|
225
416
|
/* A unique violation arrives as the driver's own error. The constraint name is
|
|
226
417
|
the only stable part of it, so the duplicate a caller can act on is
|
|
227
418
|
recognized by name and everything else is rethrown untouched. */
|
|
228
|
-
function duplicate(
|
|
419
|
+
function duplicate(
|
|
420
|
+
error: unknown,
|
|
421
|
+
): 'email' | 'role-key' | 'slug' | 'provider-key' | null {
|
|
229
422
|
const text = String(error);
|
|
230
423
|
if (text.includes('auth_accounts_email_normalized_key')) return 'email';
|
|
231
424
|
if (text.includes('auth_tenants_slug_idx')) return 'slug';
|
|
232
425
|
if (text.includes('auth_roles_tenant_id_key_key')) return 'role-key';
|
|
426
|
+
if (text.includes('auth_identity_providers_tenant_id_key_key')) {
|
|
427
|
+
return 'provider-key';
|
|
428
|
+
}
|
|
233
429
|
return null;
|
|
234
430
|
}
|
|
235
431
|
|
|
@@ -318,9 +514,12 @@ export class DatabaseAuthRepository implements AuthRepository {
|
|
|
318
514
|
return result.rows;
|
|
319
515
|
}
|
|
320
516
|
|
|
517
|
+
/* A disabled membership answers for nothing: it must not be the workspace a
|
|
518
|
+
session cookie, a bearer token or a sign-in resolves to. */
|
|
321
519
|
async #tenantOfAccount(accountId: string): Promise<string | null> {
|
|
322
520
|
const rows = await this.#route<RoutedTenantRow>({
|
|
323
|
-
text: `SELECT tenant_id FROM auth_memberships
|
|
521
|
+
text: `SELECT tenant_id FROM auth_memberships
|
|
522
|
+
WHERE account_id = $1 AND status = 'active'
|
|
324
523
|
ORDER BY created_at, tenant_id LIMIT 1`,
|
|
325
524
|
parameters: [accountId],
|
|
326
525
|
});
|
|
@@ -353,7 +552,9 @@ export class DatabaseAuthRepository implements AuthRepository {
|
|
|
353
552
|
role: row.role,
|
|
354
553
|
roleId: row.role_id,
|
|
355
554
|
status: row.status,
|
|
356
|
-
|
|
555
|
+
membershipStatus: row.membership_status,
|
|
556
|
+
passwordChangeRequired:
|
|
557
|
+
integer(row.password_change_required, 'password_change_required') === 1,
|
|
357
558
|
scopes: await this.#scopes(transaction, row.account_id, row.tenant_id),
|
|
358
559
|
};
|
|
359
560
|
}
|
|
@@ -501,6 +702,30 @@ export class DatabaseAuthRepository implements AuthRepository {
|
|
|
501
702
|
return accountId ? this.findAccountCredentialById(accountId) : null;
|
|
502
703
|
}
|
|
503
704
|
|
|
705
|
+
async findAccountIdentity(
|
|
706
|
+
normalizedEmail: string,
|
|
707
|
+
): Promise<AccountIdentity | null> {
|
|
708
|
+
const rows = await this.#query<{
|
|
709
|
+
id: string;
|
|
710
|
+
email: string;
|
|
711
|
+
display_name: string;
|
|
712
|
+
status: 'active' | 'disabled';
|
|
713
|
+
}>(IDENTITY_TENANT_CONTEXT, {
|
|
714
|
+
text: `SELECT id, email, display_name, status FROM auth_accounts
|
|
715
|
+
WHERE email_normalized = $1`,
|
|
716
|
+
parameters: [normalizedEmail],
|
|
717
|
+
});
|
|
718
|
+
const row = rows[0];
|
|
719
|
+
return row
|
|
720
|
+
? {
|
|
721
|
+
accountId: row.id,
|
|
722
|
+
email: row.email,
|
|
723
|
+
displayName: row.display_name,
|
|
724
|
+
status: row.status,
|
|
725
|
+
}
|
|
726
|
+
: null;
|
|
727
|
+
}
|
|
728
|
+
|
|
504
729
|
async findAccountCredentialById(
|
|
505
730
|
accountId: string,
|
|
506
731
|
): Promise<AccountCredential | null> {
|
|
@@ -611,7 +836,7 @@ export class DatabaseAuthRepository implements AuthRepository {
|
|
|
611
836
|
text: 'SELECT count(*) AS total FROM auth_memberships WHERE account_id = $1',
|
|
612
837
|
parameters: [accountId],
|
|
613
838
|
});
|
|
614
|
-
return rows[0] ? integer(rows[0].total) : 0;
|
|
839
|
+
return rows[0] ? integer(rows[0].total, 'total') : 0;
|
|
615
840
|
}
|
|
616
841
|
|
|
617
842
|
/* The account row is the identity. Its memberships, sessions and tokens go
|
|
@@ -628,10 +853,11 @@ export class DatabaseAuthRepository implements AuthRepository {
|
|
|
628
853
|
const rows = await this.#query<CountRow>(tenantId, {
|
|
629
854
|
text: `SELECT count(*) AS total FROM auth_memberships m
|
|
630
855
|
JOIN auth_accounts a ON a.id = m.account_id
|
|
631
|
-
WHERE m.tenant_id = $1 AND m.role = 'owner'
|
|
856
|
+
WHERE m.tenant_id = $1 AND m.role = 'owner'
|
|
857
|
+
AND a.status = 'active' AND m.status = 'active'`,
|
|
632
858
|
parameters: [tenantId],
|
|
633
859
|
});
|
|
634
|
-
return rows[0] ? integer(rows[0].total) : 0;
|
|
860
|
+
return rows[0] ? integer(rows[0].total, 'total') : 0;
|
|
635
861
|
}
|
|
636
862
|
|
|
637
863
|
/* Which workspaces an account may enter is the one question that spans
|
|
@@ -643,7 +869,7 @@ export class DatabaseAuthRepository implements AuthRepository {
|
|
|
643
869
|
text: `SELECT t.id AS tenant_id, t.name, t.slug, m.role
|
|
644
870
|
FROM auth_memberships m
|
|
645
871
|
JOIN auth_tenants t ON t.id = m.tenant_id
|
|
646
|
-
WHERE m.account_id = $1
|
|
872
|
+
WHERE m.account_id = $1 AND m.status = 'active'
|
|
647
873
|
ORDER BY m.created_at, t.id`,
|
|
648
874
|
parameters: [accountId],
|
|
649
875
|
});
|
|
@@ -657,28 +883,103 @@ export class DatabaseAuthRepository implements AuthRepository {
|
|
|
657
883
|
|
|
658
884
|
async listTenantMembers(tenantId: string): Promise<readonly TenantMember[]> {
|
|
659
885
|
const rows = await this.#query<TenantMemberRow>(tenantId, {
|
|
660
|
-
text: `SELECT
|
|
661
|
-
a.status, a.password_change_required, m.created_at,
|
|
662
|
-
(SELECT string_agg(s.scope, ' ' ORDER BY s.scope)
|
|
663
|
-
FROM auth_membership_scopes s
|
|
664
|
-
WHERE s.account_id = m.account_id AND s.tenant_id = m.tenant_id) AS scopes
|
|
886
|
+
text: `SELECT ${TENANT_MEMBER_COLUMNS}
|
|
665
887
|
FROM auth_memberships m
|
|
666
888
|
JOIN auth_accounts a ON a.id = m.account_id
|
|
667
889
|
WHERE m.tenant_id = $1
|
|
668
890
|
ORDER BY lower(a.display_name), a.id`,
|
|
669
891
|
parameters: [tenantId],
|
|
670
892
|
});
|
|
671
|
-
return rows.map(
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
893
|
+
return rows.map(tenantMemberFrom);
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
/* Keyset by account id, which migration 0030 indexes behind the workspace,
|
|
897
|
+
so a page is an index range scan of exactly the rows it returns rather
|
|
898
|
+
than a walk over every workspace's memberships after the cursor. */
|
|
899
|
+
async listTenantMembersPage(
|
|
900
|
+
tenantId: string,
|
|
901
|
+
afterAccountId: string,
|
|
902
|
+
limit: number,
|
|
903
|
+
): Promise<readonly TenantMember[]> {
|
|
904
|
+
const rows = await this.#query<TenantMemberRow>(tenantId, {
|
|
905
|
+
text: `SELECT ${TENANT_MEMBER_COLUMNS}
|
|
906
|
+
FROM auth_memberships m
|
|
907
|
+
JOIN auth_accounts a ON a.id = m.account_id
|
|
908
|
+
WHERE m.tenant_id = $1 AND m.account_id > $2
|
|
909
|
+
ORDER BY m.account_id
|
|
910
|
+
LIMIT $3`,
|
|
911
|
+
parameters: [tenantId, afterAccountId, limit],
|
|
912
|
+
});
|
|
913
|
+
return rows.map(tenantMemberFrom);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
async listTenantMembersSorted(
|
|
917
|
+
tenantId: string,
|
|
918
|
+
read: TenantMemberSortedRead,
|
|
919
|
+
): Promise<TenantMemberSortedPage> {
|
|
920
|
+
const rows = await this.#query<SortedTenantMemberRow>(
|
|
921
|
+
tenantId,
|
|
922
|
+
tenantMemberSortedStatement(tenantId, read),
|
|
923
|
+
);
|
|
924
|
+
const last = rows[rows.length - 1];
|
|
925
|
+
return {
|
|
926
|
+
members: rows.map(tenantMemberFrom),
|
|
927
|
+
next:
|
|
928
|
+
last && rows.length === read.limit
|
|
929
|
+
? { sortValue: last.sort_value, accountId: last.account_id }
|
|
930
|
+
: null,
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
async countTenantMembers(tenantId: string): Promise<number> {
|
|
935
|
+
const rows = await this.#query<CountRow>(tenantId, {
|
|
936
|
+
text: 'SELECT count(*) AS total FROM auth_memberships WHERE tenant_id = $1',
|
|
937
|
+
parameters: [tenantId],
|
|
938
|
+
});
|
|
939
|
+
return rows[0] ? integer(rows[0].total, 'total') : 0;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
async findTenantMember(
|
|
943
|
+
tenantId: string,
|
|
944
|
+
accountId: string,
|
|
945
|
+
): Promise<TenantMember | null> {
|
|
946
|
+
const rows = await this.#query<TenantMemberRow>(tenantId, {
|
|
947
|
+
text: `SELECT ${TENANT_MEMBER_COLUMNS}
|
|
948
|
+
FROM auth_memberships m
|
|
949
|
+
JOIN auth_accounts a ON a.id = m.account_id
|
|
950
|
+
WHERE m.tenant_id = $1 AND m.account_id = $2`,
|
|
951
|
+
parameters: [tenantId, accountId],
|
|
952
|
+
});
|
|
953
|
+
const row = rows[0];
|
|
954
|
+
return row ? tenantMemberFrom(row) : null;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
async findTenantMembersByEmail(
|
|
958
|
+
tenantId: string,
|
|
959
|
+
normalizedEmails: readonly string[],
|
|
960
|
+
): Promise<readonly TenantMember[]> {
|
|
961
|
+
if (normalizedEmails.length === 0) return [];
|
|
962
|
+
const rows = await this.#query<TenantMemberRow>(tenantId, {
|
|
963
|
+
text: `SELECT ${TENANT_MEMBER_COLUMNS}
|
|
964
|
+
FROM auth_memberships m
|
|
965
|
+
JOIN auth_accounts a ON a.id = m.account_id
|
|
966
|
+
WHERE m.tenant_id = $1 AND a.email_normalized = ANY($2::text[])
|
|
967
|
+
ORDER BY lower(a.display_name), a.id`,
|
|
968
|
+
parameters: [tenantId, textArrayLiteral(normalizedEmails)],
|
|
969
|
+
});
|
|
970
|
+
return rows.map(tenantMemberFrom);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
async searchTenantMembers(
|
|
974
|
+
tenantId: string,
|
|
975
|
+
term: string,
|
|
976
|
+
limit: number,
|
|
977
|
+
): Promise<readonly TenantMember[]> {
|
|
978
|
+
const rows = await this.#query<TenantMemberRow>(tenantId, {
|
|
979
|
+
text: TENANT_MEMBER_SEARCH_SQL,
|
|
980
|
+
parameters: [tenantId, `${term}%`, limit],
|
|
981
|
+
});
|
|
982
|
+
return rows.map(tenantMemberFrom);
|
|
682
983
|
}
|
|
683
984
|
|
|
684
985
|
async listTenantScopes(tenantId: string): Promise<readonly string[]> {
|
|
@@ -1139,12 +1440,13 @@ export class DatabaseAuthRepository implements AuthRepository {
|
|
|
1139
1440
|
JOIN auth_accounts a ON a.id = s.account_id
|
|
1140
1441
|
JOIN auth_memberships m
|
|
1141
1442
|
ON m.account_id = s.account_id AND m.tenant_id = s.tenant_id
|
|
1142
|
-
WHERE s.token_hash = $1 AND s.expires_at > $2 AND a.status = 'active'
|
|
1443
|
+
WHERE s.token_hash = $1 AND s.expires_at > $2 AND a.status = 'active'
|
|
1444
|
+
AND m.status = 'active'`,
|
|
1143
1445
|
parameters: [tokenHash, now],
|
|
1144
1446
|
});
|
|
1145
1447
|
const row = result.rows[0];
|
|
1146
1448
|
if (!row) return null;
|
|
1147
|
-
const lastSeenAt = integer(row.last_seen_at);
|
|
1449
|
+
const lastSeenAt = integer(row.last_seen_at, 'last_seen_at');
|
|
1148
1450
|
if (now - lastSeenAt > idleMs) {
|
|
1149
1451
|
await transaction.execute({
|
|
1150
1452
|
text: 'DELETE FROM auth_sessions WHERE token_hash = $1',
|
|
@@ -1176,10 +1478,13 @@ export class DatabaseAuthRepository implements AuthRepository {
|
|
|
1176
1478
|
return {
|
|
1177
1479
|
principal,
|
|
1178
1480
|
csrfToken: session.row.csrf_token,
|
|
1179
|
-
expiresAt: integer(session.row.expires_at),
|
|
1481
|
+
expiresAt: integer(session.row.expires_at, 'expires_at'),
|
|
1180
1482
|
sessionId: session.row.session_id,
|
|
1181
1483
|
passwordChangeRequired:
|
|
1182
|
-
integer(
|
|
1484
|
+
integer(
|
|
1485
|
+
session.row.password_change_required,
|
|
1486
|
+
'password_change_required',
|
|
1487
|
+
) === 1,
|
|
1183
1488
|
};
|
|
1184
1489
|
}
|
|
1185
1490
|
|
|
@@ -1209,9 +1514,9 @@ export class DatabaseAuthRepository implements AuthRepository {
|
|
|
1209
1514
|
id: row.id,
|
|
1210
1515
|
tenantId: row.tenant_id,
|
|
1211
1516
|
tenantName: row.tenant_name,
|
|
1212
|
-
createdAt: integer(row.created_at),
|
|
1213
|
-
lastSeenAt: integer(row.last_seen_at),
|
|
1214
|
-
expiresAt: integer(row.expires_at),
|
|
1517
|
+
createdAt: integer(row.created_at, 'created_at'),
|
|
1518
|
+
lastSeenAt: integer(row.last_seen_at, 'last_seen_at'),
|
|
1519
|
+
expiresAt: integer(row.expires_at, 'expires_at'),
|
|
1215
1520
|
});
|
|
1216
1521
|
}
|
|
1217
1522
|
}
|
|
@@ -1249,17 +1554,38 @@ export class DatabaseAuthRepository implements AuthRepository {
|
|
|
1249
1554
|
return affected > 0;
|
|
1250
1555
|
}
|
|
1251
1556
|
|
|
1252
|
-
|
|
1557
|
+
/**
|
|
1558
|
+
* Removes sessions that expired before `now` in batches of
|
|
1559
|
+
* EXPIRED_SESSION_SWEEP_BATCH, at most `maxBatches` of them, and answers how
|
|
1560
|
+
* many rows went. A backlog of unknown size must not become one delete that
|
|
1561
|
+
* holds the table for the length of it, and what the bound leaves behind is
|
|
1562
|
+
* the next pass's work. Row security scopes a delete to one workspace, so
|
|
1563
|
+
* the rows are routed first and removed per tenant.
|
|
1564
|
+
*/
|
|
1565
|
+
async deleteExpiredSessions(
|
|
1566
|
+
now: number,
|
|
1567
|
+
maxBatches: number = Number.POSITIVE_INFINITY,
|
|
1568
|
+
): Promise<number> {
|
|
1253
1569
|
const tenants = await this.#route<RoutedTenantRow>({
|
|
1254
1570
|
text: 'SELECT DISTINCT tenant_id FROM auth_sessions WHERE expires_at <= $1',
|
|
1255
1571
|
parameters: [now],
|
|
1256
1572
|
});
|
|
1257
1573
|
let removed = 0;
|
|
1574
|
+
let batches = 0;
|
|
1258
1575
|
for (const tenant of tenants) {
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1576
|
+
while (batches < maxBatches) {
|
|
1577
|
+
batches += 1;
|
|
1578
|
+
const gone = await this.#exec(tenant.tenant_id, {
|
|
1579
|
+
text: `DELETE FROM auth_sessions
|
|
1580
|
+
WHERE id IN (SELECT id FROM auth_sessions
|
|
1581
|
+
WHERE expires_at <= $1
|
|
1582
|
+
LIMIT $2)`,
|
|
1583
|
+
parameters: [now, EXPIRED_SESSION_SWEEP_BATCH],
|
|
1584
|
+
});
|
|
1585
|
+
removed += gone;
|
|
1586
|
+
/* A short batch is the proof this workspace is drained. */
|
|
1587
|
+
if (gone < EXPIRED_SESSION_SWEEP_BATCH) break;
|
|
1588
|
+
}
|
|
1263
1589
|
}
|
|
1264
1590
|
return removed;
|
|
1265
1591
|
}
|
|
@@ -1287,6 +1613,24 @@ export class DatabaseAuthRepository implements AuthRepository {
|
|
|
1287
1613
|
});
|
|
1288
1614
|
}
|
|
1289
1615
|
|
|
1616
|
+
/* Reads the account a live link names so the password policy can run on the
|
|
1617
|
+
full account before anything spends the token. Claiming it stays the job
|
|
1618
|
+
of consumePasswordResetToken below. */
|
|
1619
|
+
async findPasswordResetTokenAccount(
|
|
1620
|
+
tokenHash: string,
|
|
1621
|
+
now: number,
|
|
1622
|
+
): Promise<string | null> {
|
|
1623
|
+
const rows = await this.#query<{ account_id: string }>(
|
|
1624
|
+
IDENTITY_TENANT_CONTEXT,
|
|
1625
|
+
{
|
|
1626
|
+
text: `SELECT account_id FROM auth_password_reset_tokens
|
|
1627
|
+
WHERE token_hash = $1 AND used_at IS NULL AND expires_at > $2`,
|
|
1628
|
+
parameters: [tokenHash, now],
|
|
1629
|
+
},
|
|
1630
|
+
);
|
|
1631
|
+
return rows[0]?.account_id ?? null;
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1290
1634
|
/* One statement claims the token: a second caller with the same link finds
|
|
1291
1635
|
used_at already set and gets nothing. */
|
|
1292
1636
|
async consumePasswordResetToken(
|
|
@@ -1378,30 +1722,33 @@ export class DatabaseAuthRepository implements AuthRepository {
|
|
|
1378
1722
|
|
|
1379
1723
|
async upsertMfaTotp(
|
|
1380
1724
|
accountId: string,
|
|
1381
|
-
|
|
1725
|
+
secret: SealedMfaSecret,
|
|
1382
1726
|
createdAt: number,
|
|
1383
1727
|
): Promise<void> {
|
|
1384
1728
|
await this.#exec(IDENTITY_TENANT_CONTEXT, {
|
|
1385
1729
|
text: `INSERT INTO auth_mfa_totp
|
|
1386
|
-
(account_id, secret_ciphertext, confirmed_at, created_at)
|
|
1387
|
-
VALUES ($1, $2, NULL, $
|
|
1730
|
+
(account_id, secret_ciphertext, key_id, confirmed_at, created_at)
|
|
1731
|
+
VALUES ($1, $2, $3, NULL, $4)
|
|
1388
1732
|
ON CONFLICT (account_id) DO UPDATE SET
|
|
1389
1733
|
secret_ciphertext = excluded.secret_ciphertext,
|
|
1734
|
+
key_id = excluded.key_id,
|
|
1390
1735
|
confirmed_at = NULL,
|
|
1391
1736
|
created_at = excluded.created_at`,
|
|
1392
|
-
parameters: [accountId,
|
|
1737
|
+
parameters: [accountId, secret.ciphertext, secret.keyId, createdAt],
|
|
1393
1738
|
});
|
|
1394
1739
|
}
|
|
1395
1740
|
|
|
1396
1741
|
async findMfaTotp(accountId: string): Promise<{
|
|
1397
1742
|
readonly secretCiphertext: string;
|
|
1743
|
+
readonly keyId: string | null;
|
|
1398
1744
|
readonly confirmedAt: number | null;
|
|
1399
1745
|
} | null> {
|
|
1400
1746
|
const rows = await this.#query<{
|
|
1401
1747
|
secret_ciphertext: string;
|
|
1748
|
+
key_id: string | null;
|
|
1402
1749
|
confirmed_at: number | bigint | string | null;
|
|
1403
1750
|
}>(IDENTITY_TENANT_CONTEXT, {
|
|
1404
|
-
text: `SELECT secret_ciphertext, confirmed_at FROM auth_mfa_totp
|
|
1751
|
+
text: `SELECT secret_ciphertext, key_id, confirmed_at FROM auth_mfa_totp
|
|
1405
1752
|
WHERE account_id = $1`,
|
|
1406
1753
|
parameters: [accountId],
|
|
1407
1754
|
});
|
|
@@ -1409,11 +1756,24 @@ export class DatabaseAuthRepository implements AuthRepository {
|
|
|
1409
1756
|
return row
|
|
1410
1757
|
? {
|
|
1411
1758
|
secretCiphertext: row.secret_ciphertext,
|
|
1412
|
-
|
|
1759
|
+
keyId: row.key_id,
|
|
1760
|
+
confirmedAt: optionalInteger(row.confirmed_at, 'confirmed_at'),
|
|
1413
1761
|
}
|
|
1414
1762
|
: null;
|
|
1415
1763
|
}
|
|
1416
1764
|
|
|
1765
|
+
async hasConfirmedMfaTotp(accountId: string): Promise<boolean> {
|
|
1766
|
+
const rows = await this.#query<{ account_id: string }>(
|
|
1767
|
+
IDENTITY_TENANT_CONTEXT,
|
|
1768
|
+
{
|
|
1769
|
+
text: `SELECT account_id FROM auth_mfa_totp
|
|
1770
|
+
WHERE account_id = $1 AND confirmed_at IS NOT NULL`,
|
|
1771
|
+
parameters: [accountId],
|
|
1772
|
+
},
|
|
1773
|
+
);
|
|
1774
|
+
return rows.length > 0;
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1417
1777
|
async confirmMfaTotp(accountId: string, confirmedAt: number): Promise<void> {
|
|
1418
1778
|
await this.#exec(IDENTITY_TENANT_CONTEXT, {
|
|
1419
1779
|
text: 'UPDATE auth_mfa_totp SET confirmed_at = $1 WHERE account_id = $2',
|
|
@@ -1421,6 +1781,19 @@ export class DatabaseAuthRepository implements AuthRepository {
|
|
|
1421
1781
|
});
|
|
1422
1782
|
}
|
|
1423
1783
|
|
|
1784
|
+
async deleteMfaEnrolment(accountId: string): Promise<void> {
|
|
1785
|
+
await this.#tx(IDENTITY_TENANT_CONTEXT, 'write', async (transaction) => {
|
|
1786
|
+
await transaction.execute({
|
|
1787
|
+
text: 'DELETE FROM auth_mfa_totp WHERE account_id = $1',
|
|
1788
|
+
parameters: [accountId],
|
|
1789
|
+
});
|
|
1790
|
+
await transaction.execute({
|
|
1791
|
+
text: 'DELETE FROM auth_mfa_recovery_codes WHERE account_id = $1',
|
|
1792
|
+
parameters: [accountId],
|
|
1793
|
+
});
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1424
1797
|
async replaceMfaRecoveryCodes(
|
|
1425
1798
|
accountId: string,
|
|
1426
1799
|
codeHashes: readonly string[],
|
|
@@ -1504,6 +1877,267 @@ export class DatabaseAuthRepository implements AuthRepository {
|
|
|
1504
1877
|
return row ? { accountId: row.account_id, tenantId: row.tenant_id } : null;
|
|
1505
1878
|
}
|
|
1506
1879
|
|
|
1880
|
+
/* A binding lives in one space: the platform space, whose rows carry no
|
|
1881
|
+
workspace and are read under the identity context exactly as before, or
|
|
1882
|
+
one workspace's space, read and written under that workspace. The policy
|
|
1883
|
+
on the table enforces the same split. */
|
|
1884
|
+
async findExternalIdentity(
|
|
1885
|
+
provider: string,
|
|
1886
|
+
subject: string,
|
|
1887
|
+
tenantId: string | null = null,
|
|
1888
|
+
): Promise<string | null> {
|
|
1889
|
+
const rows = await this.#query<{ account_id: string }>(
|
|
1890
|
+
tenantId ?? IDENTITY_TENANT_CONTEXT,
|
|
1891
|
+
{
|
|
1892
|
+
text: `SELECT account_id FROM auth_external_identities
|
|
1893
|
+
WHERE provider = $1 AND subject = $2
|
|
1894
|
+
AND tenant_id IS NOT DISTINCT FROM $3`,
|
|
1895
|
+
parameters: [provider, subject, tenantId],
|
|
1896
|
+
},
|
|
1897
|
+
);
|
|
1898
|
+
return rows[0]?.account_id ?? null;
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
async findExternalIdentitySubject(
|
|
1902
|
+
provider: string,
|
|
1903
|
+
accountId: string,
|
|
1904
|
+
tenantId: string | null = null,
|
|
1905
|
+
): Promise<string | null> {
|
|
1906
|
+
const rows = await this.#query<{ subject: string }>(
|
|
1907
|
+
tenantId ?? IDENTITY_TENANT_CONTEXT,
|
|
1908
|
+
{
|
|
1909
|
+
text: `SELECT subject FROM auth_external_identities
|
|
1910
|
+
WHERE account_id = $1 AND provider = $2
|
|
1911
|
+
AND tenant_id IS NOT DISTINCT FROM $3`,
|
|
1912
|
+
parameters: [accountId, provider, tenantId],
|
|
1913
|
+
},
|
|
1914
|
+
);
|
|
1915
|
+
return rows[0]?.subject ?? null;
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
async linkExternalIdentity(record: ExternalIdentityRecord): Promise<void> {
|
|
1919
|
+
const tenantId = record.tenantId ?? null;
|
|
1920
|
+
await this.#exec(tenantId ?? IDENTITY_TENANT_CONTEXT, {
|
|
1921
|
+
text: tenantId
|
|
1922
|
+
? `INSERT INTO auth_external_identities
|
|
1923
|
+
(provider, subject, account_id, tenant_id, created_at, last_seen_at)
|
|
1924
|
+
VALUES ($1, $2, $3, $4, $5, $5)
|
|
1925
|
+
ON CONFLICT (tenant_id, provider, subject) WHERE tenant_id IS NOT NULL
|
|
1926
|
+
DO UPDATE SET last_seen_at = excluded.last_seen_at`
|
|
1927
|
+
: `INSERT INTO auth_external_identities
|
|
1928
|
+
(provider, subject, account_id, tenant_id, created_at, last_seen_at)
|
|
1929
|
+
VALUES ($1, $2, $3, $4, $5, $5)
|
|
1930
|
+
ON CONFLICT (provider, subject) WHERE tenant_id IS NULL
|
|
1931
|
+
DO UPDATE SET last_seen_at = excluded.last_seen_at`,
|
|
1932
|
+
parameters: [
|
|
1933
|
+
record.provider,
|
|
1934
|
+
record.subject,
|
|
1935
|
+
record.accountId,
|
|
1936
|
+
tenantId,
|
|
1937
|
+
record.now,
|
|
1938
|
+
],
|
|
1939
|
+
});
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
/* Keyset by (provider, subject), which the unique workspace index covers in
|
|
1943
|
+
that order, so a page is the rows it returns. The select list is the
|
|
1944
|
+
binding and nothing else: no token, ciphertext, fingerprint or reported
|
|
1945
|
+
address leaves this statement. A platform provider's binding carries no
|
|
1946
|
+
workspace, so the predicate never reaches one. */
|
|
1947
|
+
async listExternalIdentitiesPage(
|
|
1948
|
+
tenantId: string,
|
|
1949
|
+
after: { readonly provider: string; readonly subject: string },
|
|
1950
|
+
limit: number,
|
|
1951
|
+
): Promise<readonly ExternalIdentityBinding[]> {
|
|
1952
|
+
const rows = await this.#query<ExternalIdentityBindingRow>(tenantId, {
|
|
1953
|
+
text: `SELECT account_id, provider, subject, created_at
|
|
1954
|
+
FROM auth_external_identities
|
|
1955
|
+
WHERE tenant_id = $1 AND (provider, subject) > ($2, $3)
|
|
1956
|
+
ORDER BY provider, subject
|
|
1957
|
+
LIMIT $4`,
|
|
1958
|
+
parameters: [tenantId, after.provider, after.subject, limit],
|
|
1959
|
+
});
|
|
1960
|
+
return rows.map((row) => ({
|
|
1961
|
+
accountId: row.account_id,
|
|
1962
|
+
provider: row.provider,
|
|
1963
|
+
subject: row.subject,
|
|
1964
|
+
linkedAt: integer(row.created_at, 'created_at'),
|
|
1965
|
+
}));
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
/* A deleted provider takes its workspace's bindings with it; the accounts
|
|
1969
|
+
stay and keep every other way of signing in. */
|
|
1970
|
+
async deleteExternalIdentitiesOfProvider(
|
|
1971
|
+
tenantId: string,
|
|
1972
|
+
provider: string,
|
|
1973
|
+
): Promise<void> {
|
|
1974
|
+
await this.#exec(tenantId, {
|
|
1975
|
+
text: `DELETE FROM auth_external_identities
|
|
1976
|
+
WHERE tenant_id = $1 AND provider = $2`,
|
|
1977
|
+
parameters: [tenantId, provider],
|
|
1978
|
+
});
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
async listIdentityProviders(
|
|
1982
|
+
tenantId: string,
|
|
1983
|
+
): Promise<readonly IdentityProviderRecord[]> {
|
|
1984
|
+
const rows = await this.#query<IdentityProviderRow>(tenantId, {
|
|
1985
|
+
text: `SELECT * FROM auth_identity_providers
|
|
1986
|
+
WHERE tenant_id = $1 ORDER BY key, id`,
|
|
1987
|
+
parameters: [tenantId],
|
|
1988
|
+
});
|
|
1989
|
+
return rows.map(fromIdentityProviderRow);
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
async findIdentityProvider(
|
|
1993
|
+
tenantId: string,
|
|
1994
|
+
id: string,
|
|
1995
|
+
): Promise<IdentityProviderRecord | null> {
|
|
1996
|
+
const rows = await this.#query<IdentityProviderRow>(tenantId, {
|
|
1997
|
+
text: 'SELECT * FROM auth_identity_providers WHERE tenant_id = $1 AND id = $2',
|
|
1998
|
+
parameters: [tenantId, id],
|
|
1999
|
+
});
|
|
2000
|
+
return rows[0] ? fromIdentityProviderRow(rows[0]) : null;
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
async findIdentityProviderByKey(
|
|
2004
|
+
tenantId: string,
|
|
2005
|
+
key: string,
|
|
2006
|
+
): Promise<IdentityProviderRecord | null> {
|
|
2007
|
+
const rows = await this.#query<IdentityProviderRow>(tenantId, {
|
|
2008
|
+
text: 'SELECT * FROM auth_identity_providers WHERE tenant_id = $1 AND key = $2',
|
|
2009
|
+
parameters: [tenantId, key],
|
|
2010
|
+
});
|
|
2011
|
+
return rows[0] ? fromIdentityProviderRow(rows[0]) : null;
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
async createIdentityProvider(
|
|
2015
|
+
record: IdentityProviderRecord,
|
|
2016
|
+
): Promise<IdentityProviderRecord> {
|
|
2017
|
+
try {
|
|
2018
|
+
const rows = await this.#tx(
|
|
2019
|
+
record.tenantId,
|
|
2020
|
+
'write',
|
|
2021
|
+
async (transaction) => {
|
|
2022
|
+
const result = await transaction.query<IdentityProviderRow>({
|
|
2023
|
+
text: `INSERT INTO auth_identity_providers
|
|
2024
|
+
(id, tenant_id, key, label, issuer, authorization_endpoint,
|
|
2025
|
+
token_endpoint, user_info_endpoint, client_id,
|
|
2026
|
+
client_secret_ciphertext, client_secret_key_id,
|
|
2027
|
+
client_secret_fingerprint, scopes_json, jit_enabled,
|
|
2028
|
+
allowed_domains_json, jit_role, status, created_at, updated_at)
|
|
2029
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13,
|
|
2030
|
+
$14, $15, $16, $17, $18, $18)
|
|
2031
|
+
RETURNING *`,
|
|
2032
|
+
parameters: [
|
|
2033
|
+
record.id,
|
|
2034
|
+
record.tenantId,
|
|
2035
|
+
record.key,
|
|
2036
|
+
record.label,
|
|
2037
|
+
record.issuer,
|
|
2038
|
+
record.authorizationEndpoint,
|
|
2039
|
+
record.tokenEndpoint,
|
|
2040
|
+
record.userInfoEndpoint,
|
|
2041
|
+
record.clientId,
|
|
2042
|
+
record.secretCiphertext,
|
|
2043
|
+
record.secretKeyId,
|
|
2044
|
+
record.secretFingerprint,
|
|
2045
|
+
JSON.stringify(record.scopes),
|
|
2046
|
+
record.jitEnabled ? 1 : 0,
|
|
2047
|
+
JSON.stringify(record.allowedDomains),
|
|
2048
|
+
record.jitRole,
|
|
2049
|
+
record.status,
|
|
2050
|
+
record.createdAt,
|
|
2051
|
+
],
|
|
2052
|
+
});
|
|
2053
|
+
return result.rows;
|
|
2054
|
+
},
|
|
2055
|
+
);
|
|
2056
|
+
return fromIdentityProviderRow(rows[0]!);
|
|
2057
|
+
} catch (error) {
|
|
2058
|
+
if (duplicate(error) === 'provider-key') {
|
|
2059
|
+
throw new DuplicateProviderKeyError();
|
|
2060
|
+
}
|
|
2061
|
+
throw error;
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
async updateIdentityProvider(
|
|
2066
|
+
tenantId: string,
|
|
2067
|
+
id: string,
|
|
2068
|
+
patch: IdentityProviderPatch,
|
|
2069
|
+
updatedAt: number,
|
|
2070
|
+
): Promise<IdentityProviderRecord | null> {
|
|
2071
|
+
const rows = await this.#tx(tenantId, 'write', async (transaction) => {
|
|
2072
|
+
const result = await transaction.query<IdentityProviderRow>({
|
|
2073
|
+
text: `UPDATE auth_identity_providers
|
|
2074
|
+
SET label = $3, issuer = $4, authorization_endpoint = $5,
|
|
2075
|
+
token_endpoint = $6, user_info_endpoint = $7, client_id = $8,
|
|
2076
|
+
client_secret_ciphertext = $9, client_secret_key_id = $10,
|
|
2077
|
+
client_secret_fingerprint = $11, scopes_json = $12,
|
|
2078
|
+
jit_enabled = $13, allowed_domains_json = $14, jit_role = $15,
|
|
2079
|
+
status = $16, updated_at = $17
|
|
2080
|
+
WHERE tenant_id = $1 AND id = $2
|
|
2081
|
+
RETURNING *`,
|
|
2082
|
+
parameters: [
|
|
2083
|
+
tenantId,
|
|
2084
|
+
id,
|
|
2085
|
+
patch.label,
|
|
2086
|
+
patch.issuer,
|
|
2087
|
+
patch.authorizationEndpoint,
|
|
2088
|
+
patch.tokenEndpoint,
|
|
2089
|
+
patch.userInfoEndpoint,
|
|
2090
|
+
patch.clientId,
|
|
2091
|
+
patch.secretCiphertext,
|
|
2092
|
+
patch.secretKeyId,
|
|
2093
|
+
patch.secretFingerprint,
|
|
2094
|
+
JSON.stringify(patch.scopes),
|
|
2095
|
+
patch.jitEnabled ? 1 : 0,
|
|
2096
|
+
JSON.stringify(patch.allowedDomains),
|
|
2097
|
+
patch.jitRole,
|
|
2098
|
+
patch.status,
|
|
2099
|
+
updatedAt,
|
|
2100
|
+
],
|
|
2101
|
+
});
|
|
2102
|
+
return result.rows;
|
|
2103
|
+
});
|
|
2104
|
+
return rows[0] ? fromIdentityProviderRow(rows[0]) : null;
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
async deleteIdentityProvider(tenantId: string, id: string): Promise<boolean> {
|
|
2108
|
+
const affected = await this.#exec(tenantId, {
|
|
2109
|
+
text: 'DELETE FROM auth_identity_providers WHERE tenant_id = $1 AND id = $2',
|
|
2110
|
+
parameters: [tenantId, id],
|
|
2111
|
+
});
|
|
2112
|
+
return affected > 0;
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
async revokeMembershipApiTokens(
|
|
2116
|
+
tenantId: string,
|
|
2117
|
+
accountId: string,
|
|
2118
|
+
revokedAt: number,
|
|
2119
|
+
revokedBy: string,
|
|
2120
|
+
): Promise<number> {
|
|
2121
|
+
return this.#exec(tenantId, {
|
|
2122
|
+
text: `UPDATE auth_api_tokens SET revoked_at = $1, revoked_by = $2
|
|
2123
|
+
WHERE tenant_id = $3 AND account_id = $4 AND revoked_at IS NULL`,
|
|
2124
|
+
parameters: [revokedAt, revokedBy, tenantId, accountId],
|
|
2125
|
+
});
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
async setMembershipStatus(
|
|
2129
|
+
accountId: string,
|
|
2130
|
+
tenantId: string,
|
|
2131
|
+
status: MembershipStatus,
|
|
2132
|
+
): Promise<boolean> {
|
|
2133
|
+
const affected = await this.#exec(tenantId, {
|
|
2134
|
+
text: `UPDATE auth_memberships SET status = $1
|
|
2135
|
+
WHERE account_id = $2 AND tenant_id = $3`,
|
|
2136
|
+
parameters: [status, accountId, tenantId],
|
|
2137
|
+
});
|
|
2138
|
+
return affected > 0;
|
|
2139
|
+
}
|
|
2140
|
+
|
|
1507
2141
|
async findSignInFailure(
|
|
1508
2142
|
normalizedEmail: string,
|
|
1509
2143
|
): Promise<SignInFailureRecord | null> {
|
|
@@ -1518,8 +2152,8 @@ export class DatabaseAuthRepository implements AuthRepository {
|
|
|
1518
2152
|
const row = rows[0];
|
|
1519
2153
|
return row
|
|
1520
2154
|
? {
|
|
1521
|
-
failures: integer(row.failures),
|
|
1522
|
-
lockedUntil: optionalInteger(row.locked_until),
|
|
2155
|
+
failures: integer(row.failures, 'failures'),
|
|
2156
|
+
lockedUntil: optionalInteger(row.locked_until, 'locked_until'),
|
|
1523
2157
|
}
|
|
1524
2158
|
: null;
|
|
1525
2159
|
}
|
|
@@ -1550,12 +2184,13 @@ export class DatabaseAuthRepository implements AuthRepository {
|
|
|
1550
2184
|
parameters: [normalizedEmail],
|
|
1551
2185
|
});
|
|
1552
2186
|
const previous = current.rows[0];
|
|
1553
|
-
const failures =
|
|
2187
|
+
const failures =
|
|
2188
|
+
(previous ? integer(previous.failures, 'failures') : 0) + 1;
|
|
1554
2189
|
const lockedUntil =
|
|
1555
2190
|
failures >= lockThreshold
|
|
1556
2191
|
? now + lockMs
|
|
1557
2192
|
: previous
|
|
1558
|
-
? optionalInteger(previous.locked_until)
|
|
2193
|
+
? optionalInteger(previous.locked_until, 'locked_until')
|
|
1559
2194
|
: null;
|
|
1560
2195
|
await transaction.execute({
|
|
1561
2196
|
text: `INSERT INTO auth_sign_in_failures
|
|
@@ -1693,21 +2328,23 @@ export class DatabaseAuthRepository implements AuthRepository {
|
|
|
1693
2328
|
WHERE tenant_id = $1 AND role_id = $2`,
|
|
1694
2329
|
parameters: [tenantId, roleId],
|
|
1695
2330
|
});
|
|
1696
|
-
return rows[0] ? integer(rows[0].total) : 0;
|
|
2331
|
+
return rows[0] ? integer(rows[0].total, 'total') : 0;
|
|
1697
2332
|
}
|
|
1698
2333
|
|
|
1699
2334
|
async appendAudit(record: AuditRecord): Promise<void> {
|
|
1700
2335
|
await this.#exec(record.tenantId, {
|
|
1701
2336
|
text: `INSERT INTO auth_audit
|
|
1702
2337
|
(tenant_id, actor_account_id, actor_label, actor_kind, actor_run_id,
|
|
1703
|
-
action, subject_type, subject_id, metadata_json,
|
|
1704
|
-
|
|
2338
|
+
configured_by_json, action, subject_type, subject_id, metadata_json,
|
|
2339
|
+
occurred_at)
|
|
2340
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`,
|
|
1705
2341
|
parameters: [
|
|
1706
2342
|
record.tenantId,
|
|
1707
2343
|
record.actorAccountId,
|
|
1708
2344
|
record.actorLabel,
|
|
1709
2345
|
record.actorKind,
|
|
1710
2346
|
record.actorRunId,
|
|
2347
|
+
record.configuredBy ? JSON.stringify(record.configuredBy) : null,
|
|
1711
2348
|
record.action,
|
|
1712
2349
|
record.subjectType,
|
|
1713
2350
|
record.subjectId,
|
|
@@ -1725,6 +2362,16 @@ export class DatabaseAuthRepository implements AuthRepository {
|
|
|
1725
2362
|
return `$${parameters.length}`;
|
|
1726
2363
|
};
|
|
1727
2364
|
if (query.action) conditions.push(`action = ${marker(query.action)}`);
|
|
2365
|
+
/* The window is inclusive and each end is independent, so a caller asks
|
|
2366
|
+
for a date range instead of seeding the cursor at its ceiling and
|
|
2367
|
+
watching every page for the floor. It composes with the filters and the
|
|
2368
|
+
keyset rather than replacing any of them. */
|
|
2369
|
+
if (query.from !== undefined && query.from !== null) {
|
|
2370
|
+
conditions.push(`occurred_at >= ${marker(query.from)}`);
|
|
2371
|
+
}
|
|
2372
|
+
if (query.to !== undefined && query.to !== null) {
|
|
2373
|
+
conditions.push(`occurred_at <= ${marker(query.to)}`);
|
|
2374
|
+
}
|
|
1728
2375
|
if (query.actor) {
|
|
1729
2376
|
conditions.push(
|
|
1730
2377
|
`(actor_account_id = ${marker(query.actor)} OR actor_label = ${marker(query.actor)})`,
|
|
@@ -1744,20 +2391,155 @@ export class DatabaseAuthRepository implements AuthRepository {
|
|
|
1744
2391
|
parameters,
|
|
1745
2392
|
});
|
|
1746
2393
|
return rows.map((row) => ({
|
|
1747
|
-
id: integer(row.id),
|
|
2394
|
+
id: integer(row.id, 'id'),
|
|
1748
2395
|
tenantId: row.tenant_id,
|
|
1749
2396
|
actorAccountId: row.actor_account_id,
|
|
1750
2397
|
actorLabel: row.actor_label,
|
|
1751
2398
|
actorKind: row.actor_kind,
|
|
1752
2399
|
actorRunId: row.actor_run_id,
|
|
2400
|
+
configuredBy: row.configured_by_json
|
|
2401
|
+
? (JSON.parse(row.configured_by_json) as UserActor)
|
|
2402
|
+
: null,
|
|
2403
|
+
action: row.action,
|
|
2404
|
+
subjectType: row.subject_type,
|
|
2405
|
+
subjectId: row.subject_id,
|
|
2406
|
+
metadata: JSON.parse(row.metadata_json) as Record<string, unknown>,
|
|
2407
|
+
occurredAt: integer(row.occurred_at, 'occurred_at'),
|
|
2408
|
+
}));
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2411
|
+
async exportSessionsPage(
|
|
2412
|
+
tenantId: string,
|
|
2413
|
+
afterId: string,
|
|
2414
|
+
limit: number,
|
|
2415
|
+
): Promise<readonly SessionExportRecord[]> {
|
|
2416
|
+
const rows = await this.#query<SessionExportRow>(tenantId, {
|
|
2417
|
+
text: `SELECT id, tenant_id, account_id, created_at, expires_at,
|
|
2418
|
+
last_seen_at
|
|
2419
|
+
FROM auth_sessions
|
|
2420
|
+
WHERE tenant_id = $1 AND id > $2
|
|
2421
|
+
ORDER BY id LIMIT $3`,
|
|
2422
|
+
parameters: [tenantId, afterId, limit],
|
|
2423
|
+
});
|
|
2424
|
+
return rows.map((row) => ({
|
|
2425
|
+
id: row.id,
|
|
2426
|
+
tenantId: row.tenant_id,
|
|
2427
|
+
accountId: row.account_id,
|
|
2428
|
+
createdAt: integer(row.created_at, 'created_at'),
|
|
2429
|
+
expiresAt: integer(row.expires_at, 'expires_at'),
|
|
2430
|
+
lastSeenAt: integer(row.last_seen_at, 'last_seen_at'),
|
|
2431
|
+
}));
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
async deleteSessionsExpiredBefore(
|
|
2435
|
+
tenantId: string,
|
|
2436
|
+
before: number,
|
|
2437
|
+
limit: number,
|
|
2438
|
+
): Promise<number> {
|
|
2439
|
+
return this.#exec(tenantId, {
|
|
2440
|
+
text: `DELETE FROM auth_sessions WHERE token_hash IN (
|
|
2441
|
+
SELECT token_hash FROM auth_sessions
|
|
2442
|
+
WHERE tenant_id = $1 AND expires_at < $2
|
|
2443
|
+
ORDER BY expires_at LIMIT $3)`,
|
|
2444
|
+
parameters: [tenantId, before, limit],
|
|
2445
|
+
});
|
|
2446
|
+
}
|
|
2447
|
+
|
|
2448
|
+
async exportApiTokensPage(
|
|
2449
|
+
tenantId: string,
|
|
2450
|
+
afterId: string,
|
|
2451
|
+
limit: number,
|
|
2452
|
+
): Promise<readonly ApiTokenRecord[]> {
|
|
2453
|
+
const rows = await this.#query<ApiTokenRow>(tenantId, {
|
|
2454
|
+
text: `SELECT * FROM auth_api_tokens
|
|
2455
|
+
WHERE tenant_id = $1 AND id > $2
|
|
2456
|
+
ORDER BY id LIMIT $3`,
|
|
2457
|
+
parameters: [tenantId, afterId, limit],
|
|
2458
|
+
});
|
|
2459
|
+
return rows.map(fromApiTokenRow);
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2462
|
+
async deleteApiTokensRetiredBefore(
|
|
2463
|
+
tenantId: string,
|
|
2464
|
+
before: number,
|
|
2465
|
+
limit: number,
|
|
2466
|
+
): Promise<number> {
|
|
2467
|
+
return this.#exec(tenantId, {
|
|
2468
|
+
text: `DELETE FROM auth_api_tokens WHERE id IN (
|
|
2469
|
+
SELECT id FROM auth_api_tokens
|
|
2470
|
+
WHERE tenant_id = $1
|
|
2471
|
+
AND (revoked_at < $2 OR expires_at < $2)
|
|
2472
|
+
ORDER BY id LIMIT $3)`,
|
|
2473
|
+
parameters: [tenantId, before, limit],
|
|
2474
|
+
});
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2477
|
+
async exportAuditEventsPage(
|
|
2478
|
+
tenantId: string,
|
|
2479
|
+
afterId: number,
|
|
2480
|
+
limit: number,
|
|
2481
|
+
): Promise<readonly AuditEvent[]> {
|
|
2482
|
+
const rows = await this.#query<AuditRow>(tenantId, {
|
|
2483
|
+
text: `SELECT * FROM auth_audit
|
|
2484
|
+
WHERE tenant_id = $1 AND id > $2
|
|
2485
|
+
ORDER BY id LIMIT $3`,
|
|
2486
|
+
parameters: [tenantId, afterId, limit],
|
|
2487
|
+
});
|
|
2488
|
+
return rows.map((row) => ({
|
|
2489
|
+
id: integer(row.id, 'id'),
|
|
2490
|
+
tenantId: row.tenant_id,
|
|
2491
|
+
actorAccountId: row.actor_account_id,
|
|
2492
|
+
actorLabel: row.actor_label,
|
|
1753
2493
|
action: row.action,
|
|
1754
2494
|
subjectType: row.subject_type,
|
|
1755
2495
|
subjectId: row.subject_id,
|
|
1756
2496
|
metadata: JSON.parse(row.metadata_json) as Record<string, unknown>,
|
|
1757
|
-
occurredAt: integer(row.occurred_at),
|
|
2497
|
+
occurredAt: integer(row.occurred_at, 'occurred_at'),
|
|
1758
2498
|
}));
|
|
1759
2499
|
}
|
|
1760
2500
|
|
|
2501
|
+
async deleteAuditEventsBefore(
|
|
2502
|
+
tenantId: string,
|
|
2503
|
+
before: number,
|
|
2504
|
+
limit: number,
|
|
2505
|
+
): Promise<number> {
|
|
2506
|
+
return this.#exec(tenantId, {
|
|
2507
|
+
text: `DELETE FROM auth_audit WHERE id IN (
|
|
2508
|
+
SELECT id FROM auth_audit
|
|
2509
|
+
WHERE tenant_id = $1 AND occurred_at < $2
|
|
2510
|
+
ORDER BY occurred_at LIMIT $3)`,
|
|
2511
|
+
parameters: [tenantId, before, limit],
|
|
2512
|
+
});
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2515
|
+
async deleteMembershipSessionsOf(
|
|
2516
|
+
tenantId: string,
|
|
2517
|
+
accountId: string,
|
|
2518
|
+
limit: number,
|
|
2519
|
+
): Promise<number> {
|
|
2520
|
+
return this.#exec(tenantId, {
|
|
2521
|
+
text: `DELETE FROM auth_sessions WHERE token_hash IN (
|
|
2522
|
+
SELECT token_hash FROM auth_sessions
|
|
2523
|
+
WHERE tenant_id = $1 AND account_id = $2
|
|
2524
|
+
ORDER BY token_hash LIMIT $3)`,
|
|
2525
|
+
parameters: [tenantId, accountId, limit],
|
|
2526
|
+
});
|
|
2527
|
+
}
|
|
2528
|
+
|
|
2529
|
+
async deleteMembershipApiTokensOf(
|
|
2530
|
+
tenantId: string,
|
|
2531
|
+
accountId: string,
|
|
2532
|
+
limit: number,
|
|
2533
|
+
): Promise<number> {
|
|
2534
|
+
return this.#exec(tenantId, {
|
|
2535
|
+
text: `DELETE FROM auth_api_tokens WHERE id IN (
|
|
2536
|
+
SELECT id FROM auth_api_tokens
|
|
2537
|
+
WHERE tenant_id = $1 AND account_id = $2
|
|
2538
|
+
ORDER BY id LIMIT $3)`,
|
|
2539
|
+
parameters: [tenantId, accountId, limit],
|
|
2540
|
+
});
|
|
2541
|
+
}
|
|
2542
|
+
|
|
1761
2543
|
async loadSettings(
|
|
1762
2544
|
tenantId: string,
|
|
1763
2545
|
moduleId: string,
|