@k2b/cloud 0.6.0-rc.0
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/README.md +98 -0
- package/package.json +128 -0
- package/public/logo.svg +1 -0
- package/scripts/app-favicon.test.ts +49 -0
- package/scripts/app-favicon.ts +51 -0
- package/scripts/build.ts +205 -0
- package/scripts/preload.ts +164 -0
- package/scripts/sync-recovery-smoke.ts +200 -0
- package/src/_internal/app-favicon.ts +2 -0
- package/src/_internal/app-presentation.ts +61 -0
- package/src/_internal/app-workspace-state.ts +24 -0
- package/src/_internal/bounded-json.ts +47 -0
- package/src/_internal/build-metadata.ts +9 -0
- package/src/_internal/capabilities.ts +1118 -0
- package/src/_internal/define-app.ts +801 -0
- package/src/_internal/heartbeat.ts +167 -0
- package/src/_internal/help-catalog.ts +241 -0
- package/src/_internal/help.ts +95 -0
- package/src/_internal/nats-connection.ts +34 -0
- package/src/_internal/page-responses.ts +40 -0
- package/src/_internal/process-sync.ts +117 -0
- package/src/_internal/readiness.ts +3 -0
- package/src/_internal/registry-validation.ts +136 -0
- package/src/_internal/registry-watch.ts +52 -0
- package/src/_internal/registry.ts +338 -0
- package/src/_internal/runtime-compatibility.ts +64 -0
- package/src/_internal/runtime-context.ts +84 -0
- package/src/_internal/runtime-watcher.ts +78 -0
- package/src/_internal/static-assets.ts +86 -0
- package/src/_internal/status-preserving-ssr.ts +49 -0
- package/src/access/PermissionEditor.tsx +486 -0
- package/src/access/ResourceApiKeys.tsx +271 -0
- package/src/access/messages.ts +121 -0
- package/src/access/ui.ts +16 -0
- package/src/account/Avatar.tsx +28 -0
- package/src/account/EntitySearch.tsx +363 -0
- package/src/account/avatar-upload-dialog.tsx +197 -0
- package/src/account/avatar-upload.ts +149 -0
- package/src/account/ui.ts +3 -0
- package/src/ai/admin.ts +47 -0
- package/src/ai/approval-routes.ts +130 -0
- package/src/ai/approvals.ts +139 -0
- package/src/ai/assistant-models.ts +61 -0
- package/src/ai/attachments.ts +40 -0
- package/src/ai/browser.ts +92 -0
- package/src/ai/capabilities.ts +714 -0
- package/src/ai/capability-execution.ts +151 -0
- package/src/ai/chat/blocks.render.test.tsx +1108 -0
- package/src/ai/chat/blocks.tsx +730 -0
- package/src/ai/chat/builtin-tools.tsx +428 -0
- package/src/ai/chat/capability-table.tsx +87 -0
- package/src/ai/chat/composer-adapter.ts +169 -0
- package/src/ai/chat/file-tools.tsx +81 -0
- package/src/ai/chat/message-actions.tsx +370 -0
- package/src/ai/chat/message-utils.ts +479 -0
- package/src/ai/chat/messages.ts +43 -0
- package/src/ai/chat/presentation.tsx +395 -0
- package/src/ai/chat/primitives.tsx +3 -0
- package/src/ai/chat/tool-disclosure.tsx +41 -0
- package/src/ai/chat/turn-navigator-utils.ts +28 -0
- package/src/ai/chat/turn-navigator.tsx +164 -0
- package/src/ai/chat/user-message.tsx +149 -0
- package/src/ai/chat/visual-tools.tsx +435 -0
- package/src/ai/chat/web-tools.tsx +202 -0
- package/src/ai/chat-task-contracts.ts +113 -0
- package/src/ai/chat-tasks.ts +952 -0
- package/src/ai/client/controller.ts +1211 -0
- package/src/ai/client/file-source.ts +95 -0
- package/src/ai/client/live-connection.ts +140 -0
- package/src/ai/client/projection.ts +178 -0
- package/src/ai/client/transport.ts +111 -0
- package/src/ai/compaction.ts +182 -0
- package/src/ai/credentials.ts +124 -0
- package/src/ai/default-tool-contracts.ts +108 -0
- package/src/ai/default-tools.ts +127 -0
- package/src/ai/enrich.ts +280 -0
- package/src/ai/executor.ts +1624 -0
- package/src/ai/fetch-file-tool.ts +313 -0
- package/src/ai/file-context.ts +92 -0
- package/src/ai/file-media-type.ts +28 -0
- package/src/ai/file-mount.ts +16 -0
- package/src/ai/file-tools.ts +566 -0
- package/src/ai/files-store.ts +390 -0
- package/src/ai/firecrawl-tools.ts +305 -0
- package/src/ai/grids-skill.ts +126 -0
- package/src/ai/http.ts +265 -0
- package/src/ai/index.ts +337 -0
- package/src/ai/kit-skill.ts +22 -0
- package/src/ai/limits.ts +5 -0
- package/src/ai/live-events.ts +289 -0
- package/src/ai/live-messages.ts +45 -0
- package/src/ai/live-outbox.ts +102 -0
- package/src/ai/live-routes.ts +412 -0
- package/src/ai/live.ts +2 -0
- package/src/ai/maintenance.ts +302 -0
- package/src/ai/markdown-pdf-tool.ts +95 -0
- package/src/ai/memories.ts +605 -0
- package/src/ai/memory-learning-runs.ts +222 -0
- package/src/ai/memory-learning.ts +619 -0
- package/src/ai/memory-tool.ts +120 -0
- package/src/ai/memory-workflow-evidence.ts +158 -0
- package/src/ai/migrate.ts +2082 -0
- package/src/ai/model-access-migrate.ts +30 -0
- package/src/ai/model-access.ts +191 -0
- package/src/ai/personal-agent.ts +6 -0
- package/src/ai/prefs.ts +72 -0
- package/src/ai/project-tool.ts +94 -0
- package/src/ai/projects-routes.ts +277 -0
- package/src/ai/projects.ts +902 -0
- package/src/ai/protocol.ts +279 -0
- package/src/ai/provider.ts +73 -0
- package/src/ai/public-projection.ts +25 -0
- package/src/ai/resource-markers.ts +31 -0
- package/src/ai/resource-refs.ts +70 -0
- package/src/ai/routes.ts +1131 -0
- package/src/ai/runtime-tools.ts +39 -0
- package/src/ai/runtime.ts +544 -0
- package/src/ai/settings.ts +529 -0
- package/src/ai/short-id.ts +45 -0
- package/src/ai/skill-catalog.ts +73 -0
- package/src/ai/skill-format.ts +145 -0
- package/src/ai/skill-seeds.ts +430 -0
- package/src/ai/skill-tool.ts +81 -0
- package/src/ai/skills-routes.ts +143 -0
- package/src/ai/skills.ts +934 -0
- package/src/ai/solid.ts +27 -0
- package/src/ai/store.ts +3113 -0
- package/src/ai/stream.ts +308 -0
- package/src/ai/structured-runs.ts +66 -0
- package/src/ai/structured.ts +186 -0
- package/src/ai/system-prompt.ts +170 -0
- package/src/ai/task-prompt.ts +22 -0
- package/src/ai/timeline.ts +148 -0
- package/src/ai/tool-audit.ts +177 -0
- package/src/ai/tool-result-budget.ts +16 -0
- package/src/ai/tools.ts +155 -0
- package/src/ai/types.ts +967 -0
- package/src/ai/ui.tsx +39 -0
- package/src/ai/usage-migrate.ts +81 -0
- package/src/ai/usage.ts +315 -0
- package/src/ai/validate.ts +54 -0
- package/src/ai/vision-tool.ts +79 -0
- package/src/api/accounts-entities.ts +174 -0
- package/src/api/admin-ai-projects.ts +74 -0
- package/src/api/admin-ai-skills.ts +76 -0
- package/src/api/admin-ai-usage.ts +42 -0
- package/src/api/admin-core-settings.ts +508 -0
- package/src/api/admin-identity.ts +148 -0
- package/src/api/admin-lifecycle.ts +206 -0
- package/src/api/admin-linux-identities.ts +66 -0
- package/src/api/announcements.ts +131 -0
- package/src/api/app-approval.ts +129 -0
- package/src/api/apps.ts +51 -0
- package/src/api/auth/schemas.ts +54 -0
- package/src/api/auth.ts +370 -0
- package/src/api/capabilities.ts +658 -0
- package/src/api/help-messages.ts +19 -0
- package/src/api/help.ts +78 -0
- package/src/api/index.ts +108 -0
- package/src/api/mcp.ts +744 -0
- package/src/api/me-rail.ts +45 -0
- package/src/api/me.ts +753 -0
- package/src/api/search/schemas.ts +79 -0
- package/src/api/search.bench.ts +185 -0
- package/src/api/search.ts +407 -0
- package/src/api/sync-ops.ts +79 -0
- package/src/api/widgets.ts +143 -0
- package/src/browser/CloudResourceSearch.behavior.test.tsx +74 -0
- package/src/browser/CloudResourceSearch.tsx +791 -0
- package/src/browser/app-approval-vault.ts +305 -0
- package/src/browser/app-approval.ts +234 -0
- package/src/browser/live-websocket.ts +268 -0
- package/src/browser/locale-preference.ts +25 -0
- package/src/browser/notification-target.ts +27 -0
- package/src/browser/notification-timeout.ts +14 -0
- package/src/browser/notifications.ts +181 -0
- package/src/browser/resource-clipboard.ts +70 -0
- package/src/browser/resource-picker.tsx +39 -0
- package/src/browser/resource-search-messages.ts +55 -0
- package/src/browser/resource-search.ts +27 -0
- package/src/capabilities/client.ts +149 -0
- package/src/capabilities/response.ts +39 -0
- package/src/capabilities/server.ts +215 -0
- package/src/capabilities/signals.ts +4 -0
- package/src/capabilities/testing.ts +12 -0
- package/src/capabilities/types.ts +32 -0
- package/src/cli/access.ts +499 -0
- package/src/cli/account.ts +382 -0
- package/src/cli/admin/account-administration.ts +45 -0
- package/src/cli/admin/account-categories.ts +49 -0
- package/src/cli/admin/ai-usage.ts +118 -0
- package/src/cli/admin/app-sign-in.ts +49 -0
- package/src/cli/admin/data.ts +343 -0
- package/src/cli/admin/documentation.ts +34 -0
- package/src/cli/admin/gateway.ts +155 -0
- package/src/cli/admin/index.ts +61 -0
- package/src/cli/admin/instance.ts +250 -0
- package/src/cli/admin/jobs.ts +223 -0
- package/src/cli/admin/legal.ts +122 -0
- package/src/cli/admin/linux.ts +53 -0
- package/src/cli/admin/logs.ts +324 -0
- package/src/cli/admin/metrics.ts +142 -0
- package/src/cli/admin/nats.ts +167 -0
- package/src/cli/admin/notifications.ts +516 -0
- package/src/cli/admin/shared.ts +113 -0
- package/src/cli/admin/sync.ts +188 -0
- package/src/cli/admin/telemetry.ts +314 -0
- package/src/cli/admin/webhooks.ts +222 -0
- package/src/cli/admin/workflows.ts +367 -0
- package/src/cli/apps.ts +40 -0
- package/src/cli/capabilities.ts +239 -0
- package/src/cli/commands.ts +604 -0
- package/src/cli/index.ts +61 -0
- package/src/cli/locale.ts +31 -0
- package/src/cli/output.ts +58 -0
- package/src/clients/core.ts +20 -0
- package/src/config/env.ts +54 -0
- package/src/config/index.ts +6 -0
- package/src/contracts/account-categories.ts +62 -0
- package/src/contracts/announcements.ts +121 -0
- package/src/contracts/app-approval.ts +197 -0
- package/src/contracts/app.ts +162 -0
- package/src/contracts/capabilities.ts +484 -0
- package/src/contracts/index.ts +14 -0
- package/src/contracts/notification-live.ts +66 -0
- package/src/contracts/notification-types.ts +235 -0
- package/src/contracts/posix.ts +69 -0
- package/src/contracts/profile.ts +85 -0
- package/src/contracts/rail-preferences.ts +53 -0
- package/src/contracts/registry.ts +110 -0
- package/src/contracts/resource-clipboard.ts +33 -0
- package/src/contracts/settings-types.ts +98 -0
- package/src/contracts/shared.ts +408 -0
- package/src/contracts/user-notifications.ts +101 -0
- package/src/contracts/widgets.ts +183 -0
- package/src/desktop/index.ts +704 -0
- package/src/desktop/solid.tsx +938 -0
- package/src/index.ts +58 -0
- package/src/server/actor.ts +41 -0
- package/src/server/api/index.ts +2 -0
- package/src/server/api/respond.ts +96 -0
- package/src/server/api-client.ts +54 -0
- package/src/server/app-context.ts +39 -0
- package/src/server/help.ts +187 -0
- package/src/server/index.ts +89 -0
- package/src/server/locale.ts +55 -0
- package/src/server/middleware/auth.ts +386 -0
- package/src/server/middleware/index.ts +28 -0
- package/src/server/middleware/invocation.ts +55 -0
- package/src/server/middleware/middleware.ts +65 -0
- package/src/server/middleware/openapi.ts +126 -0
- package/src/server/middleware/rate-limit.ts +125 -0
- package/src/server/middleware/request-logger.ts +41 -0
- package/src/server/middleware/route-template.ts +46 -0
- package/src/server/middleware/runtime.ts +22 -0
- package/src/server/middleware/settings.ts +55 -0
- package/src/server/middleware/validator.ts +49 -0
- package/src/server/middleware/workload.ts +22 -0
- package/src/server/ratelimit.ts +151 -0
- package/src/server/request-cookies.ts +18 -0
- package/src/server/services/access.ts +644 -0
- package/src/server/services/freeipa/client.ts +137 -0
- package/src/server/services/freeipa/index.ts +9 -0
- package/src/server/services/freeipa/session.ts +123 -0
- package/src/server/services/freeipa/test-certificates.ts +44 -0
- package/src/server/services/freeipa/tls.ts +44 -0
- package/src/server/services/freeipa/transport.ts +116 -0
- package/src/server/services/freeipa/util.ts +59 -0
- package/src/server/services/geo.ts +198 -0
- package/src/server/services/index.ts +48 -0
- package/src/server/services/pagination.ts +22 -0
- package/src/server/services/services.ts +13 -0
- package/src/server/time.ts +33 -0
- package/src/services/account-category-policy.ts +51 -0
- package/src/services/account-lifecycle/audit.ts +41 -0
- package/src/services/account-lifecycle/index.ts +940 -0
- package/src/services/account-lifecycle/ipa-backfill.ts +152 -0
- package/src/services/account-lifecycle/notification-sender.ts +13 -0
- package/src/services/account-lifecycle/scheduler.ts +506 -0
- package/src/services/account-model.ts +22 -0
- package/src/services/accounts/app.ts +1671 -0
- package/src/services/accounts/authz.ts +44 -0
- package/src/services/accounts/avatar.ts +120 -0
- package/src/services/accounts/base-group.ts +11 -0
- package/src/services/accounts/base-user.ts +46 -0
- package/src/services/accounts/duplicate-emails.ts +82 -0
- package/src/services/accounts/entities.ts +622 -0
- package/src/services/accounts/group-sql.ts +106 -0
- package/src/services/accounts/groups.ts +272 -0
- package/src/services/accounts/index.ts +14 -0
- package/src/services/accounts/ipa-data.ts +66 -0
- package/src/services/accounts/lifecycle.ts +2 -0
- package/src/services/accounts/local-groups.ts +499 -0
- package/src/services/accounts/model.ts +129 -0
- package/src/services/accounts/notification-sender.ts +31 -0
- package/src/services/accounts/posix.ts +386 -0
- package/src/services/accounts/request-policy.ts +11 -0
- package/src/services/accounts/switching.ts +126 -0
- package/src/services/accounts/users.ts +745 -0
- package/src/services/announcements/index.ts +224 -0
- package/src/services/app-approval.ts +498 -0
- package/src/services/audit/index.ts +438 -0
- package/src/services/auth-flows/index.ts +15 -0
- package/src/services/auth-flows/ipa.ts +197 -0
- package/src/services/auth-flows/magic-link.ts +185 -0
- package/src/services/auth-flows/notification-sender.ts +10 -0
- package/src/services/auth-flows/password-reset.ts +268 -0
- package/src/services/auth-flows/proxy-return.ts +49 -0
- package/src/services/document-extraction.ts +148 -0
- package/src/services/freeipa-config.ts +179 -0
- package/src/services/gateway.ts +186 -0
- package/src/services/identity/constants.ts +18 -0
- package/src/services/identity/index.ts +52 -0
- package/src/services/identity/invocation-actor.ts +120 -0
- package/src/services/identity/invocation-authority.ts +53 -0
- package/src/services/identity/invocation-operations.ts +15 -0
- package/src/services/identity/invocation-token.ts +276 -0
- package/src/services/identity/key-config.ts +41 -0
- package/src/services/identity/key-ring.ts +530 -0
- package/src/services/identity/metrics.ts +29 -0
- package/src/services/identity/routes.ts +18 -0
- package/src/services/identity/runtime-config.ts +88 -0
- package/src/services/identity/session-token.ts +152 -0
- package/src/services/identity/signer-refresh.ts +44 -0
- package/src/services/identity/workload-auth.ts +64 -0
- package/src/services/index.ts +187 -0
- package/src/services/ipa/auth.ts +216 -0
- package/src/services/ipa/connection.ts +57 -0
- package/src/services/ipa/effective-groups.ts +70 -0
- package/src/services/ipa/groups.ts +722 -0
- package/src/services/ipa/guard.ts +17 -0
- package/src/services/ipa/index.ts +17 -0
- package/src/services/ipa/posix.ts +19 -0
- package/src/services/ipa/profile.ts +132 -0
- package/src/services/ipa/runtime.ts +28 -0
- package/src/services/ipa/search.ts +154 -0
- package/src/services/ipa/service-account.ts +15 -0
- package/src/services/ipa/sync-planning.ts +100 -0
- package/src/services/ipa/sync.ts +862 -0
- package/src/services/ipa/users.ts +806 -0
- package/src/services/legal-consent.ts +76 -0
- package/src/services/logging/index.ts +477 -0
- package/src/services/logging/redaction.ts +20 -0
- package/src/services/logging/trace.ts +1231 -0
- package/src/services/mandates/index.ts +855 -0
- package/src/services/mandates/policy.ts +85 -0
- package/src/services/network-security.ts +31 -0
- package/src/services/notifications/batches.ts +722 -0
- package/src/services/notifications/browser.ts +252 -0
- package/src/services/notifications/catalog.ts +113 -0
- package/src/services/notifications/channels.ts +104 -0
- package/src/services/notifications/dispatcher.ts +139 -0
- package/src/services/notifications/email.ts +118 -0
- package/src/services/notifications/index.ts +633 -0
- package/src/services/notifications/live.ts +50 -0
- package/src/services/notifications/observability.ts +407 -0
- package/src/services/notifications/platform.ts +421 -0
- package/src/services/notifications/runtime.ts +120 -0
- package/src/services/notifications/user.ts +248 -0
- package/src/services/notifications/web-push-transport.ts +135 -0
- package/src/services/oauth-tokens.ts +209 -0
- package/src/services/outbox.ts +187 -0
- package/src/services/pdf/gotenberg.ts +290 -0
- package/src/services/pdf/index.ts +43 -0
- package/src/services/pdf/markdown.ts +209 -0
- package/src/services/pdf/template-preview.ts +122 -0
- package/src/services/postgres.ts +76 -0
- package/src/services/providers/index.ts +27 -0
- package/src/services/providers/local/auth.ts +51 -0
- package/src/services/providers/local/index.ts +4 -0
- package/src/services/providers/local/users.ts +263 -0
- package/src/services/rail-preferences.ts +27 -0
- package/src/services/runtime-lifecycle.ts +191 -0
- package/src/services/secrets.ts +10 -0
- package/src/services/service-account-credentials.ts +891 -0
- package/src/services/service-accounts.ts +184 -0
- package/src/services/session/index.ts +236 -0
- package/src/services/session/test-fixture.ts +18 -0
- package/src/services/session/user.ts +144 -0
- package/src/services/settings/api.ts +61 -0
- package/src/services/settings/app.ts +86 -0
- package/src/services/settings/core-settings.ts +796 -0
- package/src/services/settings/crypto.ts +69 -0
- package/src/services/settings/defaults.ts +374 -0
- package/src/services/settings/index.ts +215 -0
- package/src/services/settings/namespace.ts +9 -0
- package/src/services/settings/snapshot.ts +49 -0
- package/src/services/settings/store.ts +241 -0
- package/src/services/settings/templates.ts +4 -0
- package/src/services/sync-ops.ts +403 -0
- package/src/services/topic-cursor.ts +22 -0
- package/src/services/weather/forecast.ts +323 -0
- package/src/services/weather/geo.ts +119 -0
- package/src/services/weather/index.ts +67 -0
- package/src/services/weather/location.ts +24 -0
- package/src/services/weather/locations.ts +133 -0
- package/src/services/weather/migrate.ts +45 -0
- package/src/services/weather/settings.ts +66 -0
- package/src/services/weather/short-id.ts +57 -0
- package/src/services/weather/types.ts +61 -0
- package/src/services/weather/ui.ts +50 -0
- package/src/services/webauthn.ts +385 -0
- package/src/shared/account-action-notice.ts +70 -0
- package/src/shared/account-display.ts +15 -0
- package/src/shared/account-session.ts +15 -0
- package/src/shared/ai-platform-prompt.ts +134 -0
- package/src/shared/ai-usage.ts +49 -0
- package/src/shared/app-presentation.ts +48 -0
- package/src/shared/app-url.ts +12 -0
- package/src/shared/branding.ts +14 -0
- package/src/shared/capability-messages.ts +70 -0
- package/src/shared/email-html.ts +54 -0
- package/src/shared/format.ts +91 -0
- package/src/shared/help.ts +68 -0
- package/src/shared/icons.ts +400 -0
- package/src/shared/index.ts +54 -0
- package/src/shared/locale.ts +48 -0
- package/src/shared/login-method.ts +44 -0
- package/src/shared/markdown/client.ts +130 -0
- package/src/shared/markdown/extensions/code.ts +105 -0
- package/src/shared/markdown/extensions/guided-help.ts +100 -0
- package/src/shared/markdown/extensions/images.ts +79 -0
- package/src/shared/markdown/extensions/info-blocks.ts +97 -0
- package/src/shared/markdown/extensions/katex.ts +120 -0
- package/src/shared/markdown/extensions/links.ts +43 -0
- package/src/shared/markdown/extensions/mark.ts +48 -0
- package/src/shared/markdown/extensions/sub-sup.ts +60 -0
- package/src/shared/markdown/extensions/tables.ts +109 -0
- package/src/shared/markdown/extensions/task-list.ts +53 -0
- package/src/shared/markdown/formula.ts +1187 -0
- package/src/shared/markdown/index.ts +193 -0
- package/src/shared/markdown/shared.ts +36 -0
- package/src/shared/mock-cover.ts +130 -0
- package/src/shared/network-address.ts +106 -0
- package/src/shared/redirect.ts +56 -0
- package/src/shared/template-rendering.ts +140 -0
- package/src/shared/theme.ts +68 -0
- package/src/shared/time.ts +13 -0
- package/src/ssr/AdminLayout.tsx +53 -0
- package/src/ssr/AdminSidebar.tsx +50 -0
- package/src/ssr/AppLaunchpad.island.tsx +236 -0
- package/src/ssr/AppWorkspaceController.island.tsx +21 -0
- package/src/ssr/BrowserNotifications.island.tsx +135 -0
- package/src/ssr/GlobalAnnouncements.island.tsx +142 -0
- package/src/ssr/GlobalSearchDialog.behavior.test.tsx +96 -0
- package/src/ssr/GlobalSearchDialog.tsx +35 -0
- package/src/ssr/GlobalSearchHelpDialog.tsx +104 -0
- package/src/ssr/GlobalSearchTrigger.island.tsx +55 -0
- package/src/ssr/HotkeysHelpRail.island.tsx +55 -0
- package/src/ssr/Layout.render.test.tsx +148 -0
- package/src/ssr/Layout.tsx +292 -0
- package/src/ssr/LayoutBreadcrumbs.island.tsx +44 -0
- package/src/ssr/LayoutFooter.tsx +22 -0
- package/src/ssr/LayoutHeader.tsx +81 -0
- package/src/ssr/LayoutHelp.tsx +869 -0
- package/src/ssr/LayoutPreferences.island.tsx +36 -0
- package/src/ssr/LayoutRail.tsx +58 -0
- package/src/ssr/MinimalLayout.tsx +46 -0
- package/src/ssr/PageError.tsx +29 -0
- package/src/ssr/ProfilePreferences.island.tsx +92 -0
- package/src/ssr/RailApps.island.tsx +61 -0
- package/src/ssr/RailEditor.tsx +293 -0
- package/src/ssr/RegisteredHelpDocuments.island.tsx +3 -0
- package/src/ssr/TimezoneCookie.island.tsx +23 -0
- package/src/ssr/admin-active-link.ts +47 -0
- package/src/ssr/admin-navigation.ts +78 -0
- package/src/ssr/app-appearance.ts +23 -0
- package/src/ssr/app-navigation.ts +41 -0
- package/src/ssr/help-messages.ts +104 -0
- package/src/ssr/index.ts +14 -0
- package/src/ssr/islands/SearchBar.island.tsx +80 -0
- package/src/ssr/islands/index.ts +16 -0
- package/src/ssr/layout-context.ts +17 -0
- package/src/ssr/layout-help-markdown.ts +9 -0
- package/src/ssr/layout-help-navigation.ts +24 -0
- package/src/ssr/layout-help-url.ts +5 -0
- package/src/ssr/layout-runtime.ts +22 -0
- package/src/ssr/page-error-messages.ts +25 -0
- package/src/ssr/platform-messages.ts +135 -0
- package/src/ssr/preference-controller.ts +44 -0
- package/src/ssr/profile-preferences-messages.ts +29 -0
- package/src/ssr/rail-context.ts +21 -0
- package/src/ssr/rail-messages.ts +66 -0
- package/src/ssr/rail-navigation.ts +60 -0
- package/src/ssr/runtime.ts +31 -0
- package/src/ssr/url-filter.ts +127 -0
- package/src/styles/base-popover.css +36 -0
- package/src/styles/effects.css +851 -0
- package/src/styles/global.css +268 -0
- package/src/styles/input.css +58 -0
- package/src/styles/tokens.css +230 -0
- package/src/styles/utilities-buttons.css +405 -0
- package/src/styles/utilities-data.css +33 -0
- package/src/styles/utilities-feedback.css +232 -0
- package/src/styles/utilities-layout.css +603 -0
- package/src/styles/utilities-markdown-editor.css +525 -0
- package/src/styles/utilities-markdown-table.css +234 -0
- package/src/styles/utilities-navigation.css +1054 -0
- package/src/types/ambient.d.ts +24 -0
- package/src/workflows/ai/index.ts +17 -0
- package/src/workflows/ai/runtime.ts +286 -0
- package/src/workflows/ai/store.ts +314 -0
- package/src/workflows/ai/types.ts +111 -0
- package/src/workflows/ai-actions.ts +269 -0
- package/src/workflows/authoring.ts +88 -0
- package/src/workflows/builtins.ts +193 -0
- package/src/workflows/contracts.ts +280 -0
- package/src/workflows/definition.ts +334 -0
- package/src/workflows/editor.ts +113 -0
- package/src/workflows/index.ts +71 -0
- package/src/workflows/language/binder.ts +45 -0
- package/src/workflows/language/canonical.ts +29 -0
- package/src/workflows/language/compiler.ts +396 -0
- package/src/workflows/language/expressions.ts +34 -0
- package/src/workflows/language/index.ts +20 -0
- package/src/workflows/language/references.ts +55 -0
- package/src/workflows/language/schema.ts +134 -0
- package/src/workflows/language/strict-yaml.ts +131 -0
- package/src/workflows/manifest.ts +24 -0
- package/src/workflows/module.ts +12 -0
- package/src/workflows/runtime/coordinator.ts +115 -0
- package/src/workflows/runtime/executor.ts +1003 -0
- package/src/workflows/runtime/index.ts +60 -0
- package/src/workflows/runtime/instant.ts +8 -0
- package/src/workflows/runtime/ports.ts +213 -0
- package/src/workflows/runtime/schedule.ts +203 -0
- package/src/workflows/runtime/trigger.ts +37 -0
- package/src/workflows/runtime/wake.ts +13 -0
- package/src/workflows/store/actions.ts +438 -0
- package/src/workflows/store/budget.ts +145 -0
- package/src/workflows/store/definitions.ts +345 -0
- package/src/workflows/store/events.ts +383 -0
- package/src/workflows/store/index.ts +107 -0
- package/src/workflows/store/observability.ts +594 -0
- package/src/workflows/store/runs.ts +975 -0
- package/src/workflows/store/transaction.ts +23 -0
- package/src/workflows/store/worker.ts +439 -0
- package/src/workflows/testing/conformance.ts +72 -0
- package/src/workflows/testing/index.ts +11 -0
- package/src/workflows/testing/process-fixtures.ts +275 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { MutationResult } from "../../contracts/shared";
|
|
2
|
+
import { getFreeIpaConfig } from "../freeipa-config";
|
|
3
|
+
|
|
4
|
+
type MutationError = Extract<MutationResult<unknown>, { ok: false }>;
|
|
5
|
+
|
|
6
|
+
export const getIpaUrl = async (): Promise<string> => (await getFreeIpaConfig()).url;
|
|
7
|
+
|
|
8
|
+
export const ensureFreeIpaMutationAvailable = async (): Promise<MutationError | null> => {
|
|
9
|
+
const config = await getFreeIpaConfig();
|
|
10
|
+
if (!config.enabled) {
|
|
11
|
+
return { ok: false, error: "FreeIPA is disabled.", status: 400 };
|
|
12
|
+
}
|
|
13
|
+
if (!config.configured) {
|
|
14
|
+
return { ok: false, error: `FreeIPA is enabled but not fully configured. Missing: ${config.missingSettings.join(", ")}.`, status: 500 };
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { providers } from "../providers";
|
|
2
|
+
import { search } from "./search";
|
|
3
|
+
|
|
4
|
+
export const ipa = {
|
|
5
|
+
auth: providers.ipa.auth,
|
|
6
|
+
users: {
|
|
7
|
+
...providers.ipa.users,
|
|
8
|
+
addIpa: providers.ipa.users.create,
|
|
9
|
+
delete: providers.ipa.users.remove,
|
|
10
|
+
},
|
|
11
|
+
groups: {
|
|
12
|
+
...providers.ipa.groups,
|
|
13
|
+
delete: providers.ipa.groups.remove,
|
|
14
|
+
},
|
|
15
|
+
search,
|
|
16
|
+
sync: providers.ipa.sync,
|
|
17
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { sql } from "bun";
|
|
2
|
+
|
|
3
|
+
/** Mirror directory-owned attributes, including missing values; never apply local defaults. */
|
|
4
|
+
export const mirrorIpaPosix = async (
|
|
5
|
+
db: typeof sql,
|
|
6
|
+
user: {
|
|
7
|
+
userId: string;
|
|
8
|
+
uidNumber: number | null;
|
|
9
|
+
primaryGidNumber: number | null;
|
|
10
|
+
homeDirectory: string | null;
|
|
11
|
+
loginShell: string | null;
|
|
12
|
+
},
|
|
13
|
+
): Promise<void> => {
|
|
14
|
+
await db`INSERT INTO auth.user_posix(user_id, managed_by, uid_number, primary_gid_number, home_directory, login_shell)
|
|
15
|
+
VALUES (${user.userId}::uuid, 'ipa', ${user.uidNumber}, ${user.primaryGidNumber}, ${user.homeDirectory}, ${user.loginShell})
|
|
16
|
+
ON CONFLICT(user_id) DO UPDATE SET uid_number = EXCLUDED.uid_number,
|
|
17
|
+
primary_gid_number = EXCLUDED.primary_gid_number, home_directory = EXCLUDED.home_directory, login_shell = EXCLUDED.login_shell
|
|
18
|
+
WHERE auth.user_posix.managed_by = 'ipa'`;
|
|
19
|
+
};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized profile calculation for IPA-backed users.
|
|
3
|
+
*
|
|
4
|
+
* Full sync writes `auth.ipa_user_effective_groups` from FreeIPA group_find.
|
|
5
|
+
* Local group mutations keep that projection as source of truth; the local
|
|
6
|
+
* display mirror is only a bootstrap fallback before the first full sync.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { sql } from "bun";
|
|
10
|
+
import { calculateIpaProfileFromGroupNames } from "../account-model";
|
|
11
|
+
import { getFreeIpaConfig } from "../freeipa-config";
|
|
12
|
+
|
|
13
|
+
type DbRow = Record<string, unknown>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get all IPA group names a user belongs to (direct + inherited via parent groups).
|
|
17
|
+
*/
|
|
18
|
+
export const getAllUserGroups = async (userId: string): Promise<string[]> => {
|
|
19
|
+
const rows: DbRow[] = await sql`
|
|
20
|
+
WITH RECURSIVE all_groups AS (
|
|
21
|
+
SELECT ug.group_id
|
|
22
|
+
FROM auth.user_groups_v2 ug
|
|
23
|
+
JOIN auth.groups g ON g.id = ug.group_id
|
|
24
|
+
WHERE ug.user_id = ${userId} AND g.provider = 'ipa'
|
|
25
|
+
UNION
|
|
26
|
+
SELECT gg.parent_group_id
|
|
27
|
+
FROM auth.group_groups_v2 gg
|
|
28
|
+
JOIN auth.groups g ON g.id = gg.parent_group_id
|
|
29
|
+
JOIN all_groups ag ON gg.child_group_id = ag.group_id
|
|
30
|
+
WHERE g.provider = 'ipa'
|
|
31
|
+
)
|
|
32
|
+
SELECT DISTINCT g.name
|
|
33
|
+
FROM all_groups ag
|
|
34
|
+
JOIN auth.groups g ON g.id = ag.group_id
|
|
35
|
+
ORDER BY g.name
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
return rows.map((row) => row.name as string);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const getEffectiveUserGroups = async (userId: string): Promise<string[]> => {
|
|
42
|
+
const rows: DbRow[] = await sql`
|
|
43
|
+
SELECT group_name
|
|
44
|
+
FROM auth.ipa_user_effective_groups
|
|
45
|
+
WHERE user_id = ${userId}::uuid
|
|
46
|
+
ORDER BY group_name
|
|
47
|
+
`;
|
|
48
|
+
|
|
49
|
+
return rows.map((row) => row.group_name as string);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Calculate canonical IPA profile from effective group names. Reads
|
|
54
|
+
* `freeipa.groups.base_ipa_realm` from settings (cache-aside).
|
|
55
|
+
*/
|
|
56
|
+
export const calculateIpaProfile = async (memberOfGroups: string[]): Promise<"user" | "guest"> => {
|
|
57
|
+
const config = await getFreeIpaConfig();
|
|
58
|
+
return calculateIpaProfileFromGroupNames(memberOfGroups, config.groupsBaseIpaRealm);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Calculate canonical IPA profile for a user from the local DB mirror.
|
|
63
|
+
*/
|
|
64
|
+
export const calculateIpaProfileFromLocalDb = async (userId: string): Promise<"user" | "guest"> => {
|
|
65
|
+
const groups = await getAllUserGroups(userId);
|
|
66
|
+
return calculateIpaProfile(groups);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export const calculateIpaProfileFromEffectiveProjection = async (userId: string): Promise<"user" | "guest"> => {
|
|
70
|
+
const groups = await getEffectiveUserGroups(userId);
|
|
71
|
+
return calculateIpaProfile(groups);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const rebuildEffectiveProjectionFromLocalMirror = async (userId: string): Promise<string[]> => {
|
|
75
|
+
const groups = await getAllUserGroups(userId);
|
|
76
|
+
await sql`
|
|
77
|
+
DELETE FROM auth.ipa_user_effective_groups
|
|
78
|
+
WHERE user_id = ${userId}::uuid
|
|
79
|
+
`;
|
|
80
|
+
|
|
81
|
+
for (const group of groups) {
|
|
82
|
+
await sql`
|
|
83
|
+
INSERT INTO auth.ipa_user_effective_groups (user_id, group_name)
|
|
84
|
+
VALUES (${userId}::uuid, ${group})
|
|
85
|
+
ON CONFLICT DO NOTHING
|
|
86
|
+
`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return groups;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const getEffectiveUserGroupsWithMirrorFallback = async (userId: string): Promise<string[]> => {
|
|
93
|
+
const projectedGroups = await getEffectiveUserGroups(userId);
|
|
94
|
+
if (projectedGroups.length > 0) return projectedGroups;
|
|
95
|
+
return rebuildEffectiveProjectionFromLocalMirror(userId);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Update one IPA-backed user's canonical profile projection.
|
|
100
|
+
*/
|
|
101
|
+
export const updateUserIpaProfile = async (userId: string): Promise<void> => {
|
|
102
|
+
const groups = await getEffectiveUserGroupsWithMirrorFallback(userId);
|
|
103
|
+
const profile = await calculateIpaProfile(groups);
|
|
104
|
+
await sql`
|
|
105
|
+
UPDATE auth.users
|
|
106
|
+
SET provider = 'ipa',
|
|
107
|
+
profile = ${profile}
|
|
108
|
+
WHERE id = ${userId} AND provider = 'ipa'
|
|
109
|
+
`;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Update all IPA-backed users affected by a group hierarchy change.
|
|
114
|
+
*/
|
|
115
|
+
export const updateProfileForAffectedUsers = async (groupId: string): Promise<void> => {
|
|
116
|
+
const affectedUsers: DbRow[] = await sql`
|
|
117
|
+
WITH RECURSIVE affected_groups AS (
|
|
118
|
+
SELECT ${groupId}::uuid AS group_id
|
|
119
|
+
UNION
|
|
120
|
+
SELECT gg.child_group_id
|
|
121
|
+
FROM auth.group_groups_v2 gg
|
|
122
|
+
JOIN affected_groups ag ON gg.parent_group_id = ag.group_id
|
|
123
|
+
)
|
|
124
|
+
SELECT DISTINCT ug.user_id
|
|
125
|
+
FROM auth.user_groups_v2 ug
|
|
126
|
+
JOIN affected_groups ag ON ug.group_id = ag.group_id
|
|
127
|
+
`;
|
|
128
|
+
|
|
129
|
+
for (const row of affectedUsers) {
|
|
130
|
+
await updateUserIpaProfile(row.user_id as string);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type IpaSyncRuntime = {
|
|
2
|
+
signal?: AbortSignal;
|
|
3
|
+
heartbeat?: () => Promise<void>;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export const createIpaRuntimeCheckpoint = (
|
|
7
|
+
runtime: IpaSyncRuntime,
|
|
8
|
+
options: {
|
|
9
|
+
intervalMs?: number;
|
|
10
|
+
now?: () => number;
|
|
11
|
+
onHeartbeat?: () => void;
|
|
12
|
+
} = {},
|
|
13
|
+
): ((force?: boolean) => Promise<void>) => {
|
|
14
|
+
const intervalMs = options.intervalMs ?? 30_000;
|
|
15
|
+
const now = options.now ?? Date.now;
|
|
16
|
+
let lastHeartbeatAt: number | null = null;
|
|
17
|
+
|
|
18
|
+
return async (force = false) => {
|
|
19
|
+
if (runtime.signal?.aborted) throw new DOMException("FreeIPA operation was cancelled", "AbortError");
|
|
20
|
+
const currentTime = now();
|
|
21
|
+
if (runtime.heartbeat && (force || lastHeartbeatAt === null || currentTime - lastHeartbeatAt >= intervalMs)) {
|
|
22
|
+
await runtime.heartbeat();
|
|
23
|
+
lastHeartbeatAt = now();
|
|
24
|
+
options.onHeartbeat?.();
|
|
25
|
+
}
|
|
26
|
+
if (runtime.signal?.aborted) throw new DOMException("FreeIPA operation was cancelled", "AbortError");
|
|
27
|
+
};
|
|
28
|
+
};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { sql } from "bun";
|
|
2
|
+
import type { BaseGroup, BaseUser, UserProfile, UserProvider } from "../../contracts/shared";
|
|
3
|
+
import { freeipa } from "../../server/services";
|
|
4
|
+
import { buildRoles } from "../account-model";
|
|
5
|
+
import { getFreeIpaConfig } from "../freeipa-config";
|
|
6
|
+
import { toPgTextArray, toPgUuidArray } from "../postgres";
|
|
7
|
+
|
|
8
|
+
// ==========================
|
|
9
|
+
// Search Options
|
|
10
|
+
// ==========================
|
|
11
|
+
|
|
12
|
+
export type SearchOptions = {
|
|
13
|
+
/** Search users */
|
|
14
|
+
users?: boolean;
|
|
15
|
+
/** Search groups */
|
|
16
|
+
groups?: boolean;
|
|
17
|
+
/** User UUIDs to exclude from results */
|
|
18
|
+
excludeUserIds?: string[];
|
|
19
|
+
/** Group IDs to exclude from results */
|
|
20
|
+
excludeGroups?: string[];
|
|
21
|
+
/** Only return groups the user is a member of */
|
|
22
|
+
onlyUserGroups?: string[];
|
|
23
|
+
/** Only return POSIX groups (have gid_number) */
|
|
24
|
+
onlyPosixGroups?: boolean;
|
|
25
|
+
/** Only return users that are members of these groups */
|
|
26
|
+
usersInGroups?: string[];
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// ==========================
|
|
30
|
+
// Search (autocomplete for member/manager add dialogs)
|
|
31
|
+
// Returns BaseUser/BaseGroup (no relations needed for autocomplete)
|
|
32
|
+
// ==========================
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Executes a filtered lookup query and returns normalized matches.
|
|
36
|
+
*/
|
|
37
|
+
export const search = async (query: string, options: SearchOptions): Promise<{ users: BaseUser[]; groups: BaseGroup[] }> => {
|
|
38
|
+
const q = `%${freeipa.util.escapeLike(query.toLowerCase())}%`;
|
|
39
|
+
let users: BaseUser[] = [];
|
|
40
|
+
let groups: BaseGroup[] = [];
|
|
41
|
+
|
|
42
|
+
// ========== Search Users ==========
|
|
43
|
+
if (options.users) {
|
|
44
|
+
const excludeIds = options.excludeUserIds ?? [];
|
|
45
|
+
const inGroups = options.usersInGroups ?? [];
|
|
46
|
+
const groupsAdmin = (await getFreeIpaConfig()).groupsAdmin;
|
|
47
|
+
|
|
48
|
+
// Build optional WHERE fragments
|
|
49
|
+
const excludeFilter = excludeIds.length > 0 ? sql`AND u.id <> ALL(${toPgUuidArray(excludeIds)}::uuid[])` : sql``;
|
|
50
|
+
const groupFilter =
|
|
51
|
+
inGroups.length > 0
|
|
52
|
+
? sql`AND EXISTS (
|
|
53
|
+
SELECT 1
|
|
54
|
+
FROM auth.user_groups_v2 ug
|
|
55
|
+
JOIN auth.groups g ON g.id = ug.group_id
|
|
56
|
+
WHERE ug.user_id = u.id
|
|
57
|
+
AND g.provider = 'ipa'
|
|
58
|
+
AND ug.group_id = ANY(${toPgUuidArray(inGroups)}::uuid[])
|
|
59
|
+
)`
|
|
60
|
+
: sql``;
|
|
61
|
+
|
|
62
|
+
const rows = await sql`
|
|
63
|
+
SELECT u.id, u.uid, u.provider, u.profile, u.given_name, u.sn, u.display_name, u.mail, u.avatar_hash,
|
|
64
|
+
EXISTS(
|
|
65
|
+
SELECT 1
|
|
66
|
+
FROM auth.ipa_user_effective_groups eg
|
|
67
|
+
WHERE eg.user_id = u.id
|
|
68
|
+
AND eg.group_name = ANY(${toPgTextArray(groupsAdmin)}::text[])
|
|
69
|
+
) AS effective_admin
|
|
70
|
+
FROM auth.users u
|
|
71
|
+
WHERE u.provider = 'ipa'
|
|
72
|
+
AND (
|
|
73
|
+
LOWER(u.uid) LIKE ${q} ESCAPE '\\' OR LOWER(u.display_name) LIKE ${q} ESCAPE '\\' OR
|
|
74
|
+
LOWER(u.given_name) LIKE ${q} ESCAPE '\\' OR LOWER(u.sn) LIKE ${q} ESCAPE '\\' OR LOWER(u.mail) LIKE ${q} ESCAPE '\\'
|
|
75
|
+
)
|
|
76
|
+
${excludeFilter}
|
|
77
|
+
${groupFilter}
|
|
78
|
+
ORDER BY u.uid
|
|
79
|
+
LIMIT 10
|
|
80
|
+
`;
|
|
81
|
+
|
|
82
|
+
type UserRow = {
|
|
83
|
+
id: string;
|
|
84
|
+
uid: string;
|
|
85
|
+
provider: UserProvider;
|
|
86
|
+
profile: UserProfile;
|
|
87
|
+
effective_admin: boolean | null;
|
|
88
|
+
given_name: string | null;
|
|
89
|
+
sn: string | null;
|
|
90
|
+
display_name: string | null;
|
|
91
|
+
mail: string | null;
|
|
92
|
+
avatar_hash: string | null;
|
|
93
|
+
};
|
|
94
|
+
users = (rows as UserRow[]).map((row) => ({
|
|
95
|
+
id: row.id,
|
|
96
|
+
uid: row.uid,
|
|
97
|
+
roles: buildRoles({
|
|
98
|
+
provider: row.provider,
|
|
99
|
+
profile: row.profile,
|
|
100
|
+
memberofGroup: [],
|
|
101
|
+
manages: [],
|
|
102
|
+
admin: Boolean(row.effective_admin),
|
|
103
|
+
}),
|
|
104
|
+
provider: row.provider,
|
|
105
|
+
profile: row.profile,
|
|
106
|
+
givenname: row.given_name ?? "",
|
|
107
|
+
sn: row.sn ?? "",
|
|
108
|
+
displayName: row.display_name ?? "",
|
|
109
|
+
mail: row.mail ?? null,
|
|
110
|
+
avatarHash: row.avatar_hash,
|
|
111
|
+
}));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ========== Search Groups ==========
|
|
115
|
+
if (options.groups) {
|
|
116
|
+
const excludeIds = options.excludeGroups ?? [];
|
|
117
|
+
const onlyUserGroups = options.onlyUserGroups ?? [];
|
|
118
|
+
const onlyPosix = options.onlyPosixGroups ?? false;
|
|
119
|
+
|
|
120
|
+
// Build optional WHERE fragments
|
|
121
|
+
const excludeFilter = excludeIds.length > 0 ? sql`AND id <> ALL(${toPgUuidArray(excludeIds)}::uuid[])` : sql``;
|
|
122
|
+
const userGroupsFilter = onlyUserGroups.length > 0 ? sql`AND id = ANY(${toPgUuidArray(onlyUserGroups)}::uuid[])` : sql``;
|
|
123
|
+
const posixFilter = onlyPosix ? sql`AND gid_number IS NOT NULL` : sql``;
|
|
124
|
+
|
|
125
|
+
const rows = await sql`
|
|
126
|
+
SELECT id, provider, name, description, gid_number
|
|
127
|
+
FROM auth.groups
|
|
128
|
+
WHERE provider = 'ipa'
|
|
129
|
+
AND (LOWER(name) LIKE ${q} ESCAPE '\\' OR LOWER(description) LIKE ${q} ESCAPE '\\')
|
|
130
|
+
${excludeFilter}
|
|
131
|
+
${userGroupsFilter}
|
|
132
|
+
${posixFilter}
|
|
133
|
+
ORDER BY name
|
|
134
|
+
LIMIT 10
|
|
135
|
+
`;
|
|
136
|
+
|
|
137
|
+
type GroupRow = {
|
|
138
|
+
id: string;
|
|
139
|
+
provider: UserProvider;
|
|
140
|
+
name: string;
|
|
141
|
+
description: string | null;
|
|
142
|
+
gid_number: number | null;
|
|
143
|
+
};
|
|
144
|
+
groups = (rows as GroupRow[]).map((row) => ({
|
|
145
|
+
id: row.id,
|
|
146
|
+
provider: row.provider,
|
|
147
|
+
name: row.name,
|
|
148
|
+
description: row.description ?? null,
|
|
149
|
+
gidnumber: row.gid_number ?? null,
|
|
150
|
+
}));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return { users, groups };
|
|
154
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { MutationResult } from "../../contracts/shared";
|
|
2
|
+
import { getFreeIpaConfig } from "../freeipa-config";
|
|
3
|
+
import { providers } from "../providers";
|
|
4
|
+
|
|
5
|
+
export const getServiceIpaSession = async (): Promise<MutationResult<string>> => {
|
|
6
|
+
if (!(await getFreeIpaConfig()).enabled) {
|
|
7
|
+
return { ok: false, error: "FreeIPA is disabled.", status: 400 };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
return { ok: true, data: await providers.ipa.auth.getServiceSession() };
|
|
12
|
+
} catch {
|
|
13
|
+
return { ok: false, error: "Internal FreeIPA service session unavailable.", status: 500 };
|
|
14
|
+
}
|
|
15
|
+
};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
export type IpaIdentity = {
|
|
2
|
+
uid: string;
|
|
3
|
+
mail: string | null;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export const readCompleteIpaList = (params: {
|
|
7
|
+
response: {
|
|
8
|
+
result: { result?: unknown; truncated?: unknown } | null;
|
|
9
|
+
error: { message: string } | null;
|
|
10
|
+
};
|
|
11
|
+
entity: string;
|
|
12
|
+
}): Record<string, unknown>[] => {
|
|
13
|
+
if (params.response.error) {
|
|
14
|
+
throw new Error(`IPA ${params.entity} fetch failed: ${params.response.error.message}`);
|
|
15
|
+
}
|
|
16
|
+
const records = params.response.result?.result;
|
|
17
|
+
if (!Array.isArray(records)) {
|
|
18
|
+
throw new Error(`IPA ${params.entity} fetch returned invalid list payload`);
|
|
19
|
+
}
|
|
20
|
+
if (params.response.result?.truncated === true) {
|
|
21
|
+
throw new Error(`IPA ${params.entity} fetch returned a truncated snapshot`);
|
|
22
|
+
}
|
|
23
|
+
if (params.response.result?.truncated !== false) {
|
|
24
|
+
throw new Error(`IPA ${params.entity} fetch returned invalid completeness metadata`);
|
|
25
|
+
}
|
|
26
|
+
return records as Record<string, unknown>[];
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type IpaDestructionGuardLimits = {
|
|
30
|
+
maxUserChanges: number;
|
|
31
|
+
maxUserChangePercent: number;
|
|
32
|
+
maxGroupDeletions: number;
|
|
33
|
+
maxGroupDeletionPercent: number;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type IpaDestructionGuardReport = {
|
|
37
|
+
userChanges: number;
|
|
38
|
+
userChangePercent: number;
|
|
39
|
+
localUsers: number;
|
|
40
|
+
groupDeletions: number;
|
|
41
|
+
groupDeletionPercent: number;
|
|
42
|
+
localGroups: number;
|
|
43
|
+
limits: IpaDestructionGuardLimits;
|
|
44
|
+
violations: Array<"user_count" | "user_percent" | "group_count" | "group_percent">;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const selectStaleLocalIpaRows = <T extends IpaIdentity>(params: { localRows: T[]; activeRemoteUsers: IpaIdentity[] }): T[] => {
|
|
48
|
+
const activeUids = new Set(params.activeRemoteUsers.map((user) => user.uid));
|
|
49
|
+
const activeMails = new Set(params.activeRemoteUsers.map((user) => user.mail).filter((mail): mail is string => Boolean(mail)));
|
|
50
|
+
|
|
51
|
+
return params.localRows.filter((row) => {
|
|
52
|
+
if (activeUids.has(row.uid)) return false;
|
|
53
|
+
if (row.mail && activeMails.has(row.mail)) return false;
|
|
54
|
+
return true;
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const percentage = (affected: number, total: number): number => {
|
|
59
|
+
if (affected === 0) return 0;
|
|
60
|
+
if (total <= 0) return 100;
|
|
61
|
+
return (affected / total) * 100;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const assessIpaDestructionGuard = (params: {
|
|
65
|
+
affectedUserIds: Iterable<string>;
|
|
66
|
+
localUsers: number;
|
|
67
|
+
deletedGroupNames: Iterable<string>;
|
|
68
|
+
localGroups: number;
|
|
69
|
+
limits: IpaDestructionGuardLimits;
|
|
70
|
+
}): IpaDestructionGuardReport => {
|
|
71
|
+
const userChanges = new Set(params.affectedUserIds).size;
|
|
72
|
+
const groupDeletions = new Set(params.deletedGroupNames).size;
|
|
73
|
+
const userChangePercent = percentage(userChanges, params.localUsers);
|
|
74
|
+
const groupDeletionPercent = percentage(groupDeletions, params.localGroups);
|
|
75
|
+
const violations: IpaDestructionGuardReport["violations"] = [];
|
|
76
|
+
|
|
77
|
+
if (userChanges > params.limits.maxUserChanges) violations.push("user_count");
|
|
78
|
+
if (userChangePercent > params.limits.maxUserChangePercent) violations.push("user_percent");
|
|
79
|
+
if (groupDeletions > params.limits.maxGroupDeletions) violations.push("group_count");
|
|
80
|
+
if (groupDeletionPercent > params.limits.maxGroupDeletionPercent) violations.push("group_percent");
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
userChanges,
|
|
84
|
+
userChangePercent,
|
|
85
|
+
localUsers: params.localUsers,
|
|
86
|
+
groupDeletions,
|
|
87
|
+
groupDeletionPercent,
|
|
88
|
+
localGroups: params.localGroups,
|
|
89
|
+
limits: params.limits,
|
|
90
|
+
violations,
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const formatIpaDestructionGuardFailure = (report: IpaDestructionGuardReport): string =>
|
|
95
|
+
`Refusing IPA sync: destructive plan exceeds configured guard limits ` +
|
|
96
|
+
`(users ${report.userChanges}/${report.localUsers} = ${report.userChangePercent.toFixed(2)}%, ` +
|
|
97
|
+
`limits ${report.limits.maxUserChanges} and ${report.limits.maxUserChangePercent}%; ` +
|
|
98
|
+
`groups ${report.groupDeletions}/${report.localGroups} = ${report.groupDeletionPercent.toFixed(2)}%, ` +
|
|
99
|
+
`limits ${report.limits.maxGroupDeletions} and ${report.limits.maxGroupDeletionPercent}%; ` +
|
|
100
|
+
`violations: ${report.violations.join(", ")})`;
|