@k2b/cloud 0.42.0 → 0.44.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/package.json +13 -5
- package/scripts/app-favicon.ts +7 -6
- package/scripts/build.ts +3 -0
- package/scripts/installable-assets.ts +61 -0
- package/scripts/preload.ts +3 -0
- package/src/_internal/app-access-path.ts +12 -0
- package/src/_internal/app-access-request.ts +13 -0
- package/src/_internal/app-access-resources.ts +141 -0
- package/src/_internal/app-event-catalog.ts +40 -0
- package/src/_internal/app-event-schemas.ts +127 -0
- package/src/_internal/application-package.ts +40 -0
- package/src/_internal/built-in-app-ids.json +27 -0
- package/src/_internal/capabilities.ts +185 -12
- package/src/_internal/capability-origin.ts +4 -0
- package/src/_internal/capability-streams.ts +10 -5
- package/src/_internal/define-app.ts +159 -45
- package/src/_internal/installable-assets.ts +90 -0
- package/src/_internal/installable-head.ts +22 -0
- package/src/_internal/object-store-area.ts +9 -0
- package/src/_internal/object-store-config.ts +101 -0
- package/src/_internal/object-store-key.ts +14 -0
- package/src/_internal/page-responses.ts +34 -3
- package/src/_internal/process-identity.ts +7 -2
- package/src/_internal/registry-validation.ts +15 -1
- package/src/_internal/registry.ts +44 -1
- package/src/_internal/runtime-context.ts +9 -1
- package/src/_internal/runtime-watcher.ts +8 -0
- package/src/_internal/status-preserving-ssr.ts +12 -1
- package/src/access/AppAccessEditor.browser-fixture.ts +429 -0
- package/src/access/AppAccessEditor.browser-frame.tsx +68 -0
- package/src/access/AppAccessEditor.browser-harness.tsx +65 -0
- package/src/access/AppAccessEditor.tsx +380 -0
- package/src/access/PermissionEditor.browser-harness.tsx +22 -2
- package/src/access/PermissionEditor.tsx +386 -77
- package/src/access/PrincipalAvatar.tsx +63 -0
- package/src/access/PrincipalPicker.tsx +29 -8
- package/src/access/ResourceApiKeys.tsx +3 -4
- package/src/access/app-access-adapter.ts +51 -0
- package/src/access/app-access-messages.ts +172 -0
- package/src/access/messages.ts +14 -0
- package/src/access/ui.ts +4 -1
- package/src/account/Avatar.tsx +21 -3
- package/src/account/ProfilePictureDialog.tsx +699 -0
- package/src/account/profile-picture-messages.ts +269 -0
- package/src/account/ui.ts +8 -1
- package/src/ai/admin.ts +3 -0
- package/src/ai/app-frame.ts +12 -0
- package/src/ai/application-tasks.ts +101 -0
- package/src/ai/assistant-models.ts +126 -64
- package/src/ai/capabilities.ts +19 -4
- package/src/ai/capability-execution.ts +5 -2
- package/src/ai/chart-block.ts +63 -30
- package/src/ai/chat/chart-block.tsx +12 -3
- package/src/ai/chat/composer-adapter.ts +8 -7
- package/src/ai/chat/message-actions.tsx +1 -1
- package/src/ai/chat/message-utils.ts +2 -2
- package/src/ai/chat/presentation.tsx +22 -1
- package/src/ai/chat-quotas.ts +5 -4
- package/src/ai/chat-tasks.ts +2 -2
- package/src/ai/client/controller.ts +11 -10
- package/src/ai/client/projection.ts +3 -3
- package/src/ai/code-capability-routes.ts +1 -0
- package/src/ai/compaction.ts +3 -3
- package/src/ai/credentials.ts +13 -114
- package/src/ai/default-tool-contracts.ts +4 -3
- package/src/ai/dictation-runtime.ts +4 -4
- package/src/ai/enrich.ts +2 -2
- package/src/ai/executor.ts +38 -27
- package/src/ai/http.ts +17 -29
- package/src/ai/index.ts +11 -5
- package/src/ai/inference-calls.ts +6 -4
- package/src/ai/maintenance.ts +1 -1
- package/src/ai/memory-learning-runs.ts +5 -5
- package/src/ai/memory-learning.ts +3 -3
- package/src/ai/message-queue.ts +1 -2
- package/src/ai/migrate.ts +75 -42
- package/src/ai/model-access-migrate.ts +3 -17
- package/src/ai/model-access.ts +44 -85
- package/src/ai/model-card.tsx +134 -0
- package/src/ai/model-catalog.ts +359 -0
- package/src/ai/model-chooser-messages.ts +88 -0
- package/src/ai/model-chooser.tsx +577 -0
- package/src/ai/model-delete.ts +234 -0
- package/src/ai/model-fixture.ts +39 -0
- package/src/ai/model-icon.tsx +36 -0
- package/src/ai/model-overview.ts +53 -0
- package/src/ai/model-usage.ts +45 -0
- package/src/ai/openai-api-messages.ts +0 -4
- package/src/ai/openai-api-slots.ts +10 -2
- package/src/ai/openai-api.ts +83 -16
- package/src/ai/personal-agent.ts +1 -1
- package/src/ai/prefs.ts +12 -10
- package/src/ai/projects-routes.ts +2 -1
- package/src/ai/projects.ts +13 -12
- package/src/ai/protocol.ts +2 -2
- package/src/ai/provider-connections.ts +161 -0
- package/src/ai/provider-errors.ts +138 -0
- package/src/ai/provider-fetch.ts +4 -0
- package/src/ai/provider-retry.ts +1 -1
- package/src/ai/provider.ts +2 -1
- package/src/ai/quota-limits.ts +36 -0
- package/src/ai/quota-provider.ts +11 -2
- package/src/ai/quota-report.ts +7 -4
- package/src/ai/quotas-migrate.ts +1 -0
- package/src/ai/quotas.ts +19 -8
- package/src/ai/recent-resources.ts +6 -0
- package/src/ai/request-headers.ts +14 -87
- package/src/ai/resource-refs.ts +6 -2
- package/src/ai/routes.ts +17 -18
- package/src/ai/runtime.ts +50 -22
- package/src/ai/settings.ts +93 -580
- package/src/ai/store.ts +45 -23
- package/src/ai/stream.ts +1 -1
- package/src/ai/structured-runs.ts +2 -2
- package/src/ai/structured.ts +29 -46
- package/src/ai/task-contracts.ts +25 -5
- package/src/ai/task-execution.ts +15 -6
- package/src/ai/task-model.ts +9 -0
- package/src/ai/transcription.ts +37 -21
- package/src/ai/turn-failure.ts +24 -2
- package/src/ai/types.ts +50 -62
- package/src/ai/ui.tsx +13 -0
- package/src/ai/usage.ts +8 -8
- package/src/ai/validate.ts +8 -7
- package/src/ai/view-block.ts +2 -2
- package/src/api/accounts-entities.ts +40 -16
- package/src/api/admin-ai-quotas.ts +34 -56
- package/src/api/admin-ai-usage.ts +1 -1
- package/src/api/admin-ai.ts +173 -0
- package/src/api/admin-core-settings.ts +68 -244
- package/src/api/admin-directory-shares.ts +55 -0
- package/src/api/admin-identity.ts +81 -2
- package/src/api/app-access.ts +85 -0
- package/src/api/app-approval.ts +3 -1
- package/src/api/app-event-delivery.ts +51 -0
- package/src/api/app-events.ts +123 -0
- package/src/api/app-resource-access.ts +48 -0
- package/src/api/auth/account-mismatch.ts +24 -0
- package/src/api/auth.ts +26 -3
- package/src/api/capabilities.ts +127 -15
- package/src/api/capability-streams.ts +2 -5
- package/src/api/identity-child-invocation.ts +145 -0
- package/src/api/index.ts +11 -2
- package/src/api/mcp.ts +180 -5
- package/src/api/me.ts +38 -17
- package/src/api/pwa-phone.ts +87 -48
- package/src/api/pwa.ts +34 -8
- package/src/api/resource-links.ts +83 -6
- package/src/api/resource-resolve.ts +50 -0
- package/src/api/search/schemas.ts +3 -1
- package/src/api/search.ts +9 -7
- package/src/api/widgets.ts +1 -0
- package/src/app-pwa/authority.ts +75 -0
- package/src/app-pwa/contracts.ts +177 -0
- package/src/app-pwa/core-query-error.ts +20 -0
- package/src/app-pwa/index.ts +23 -0
- package/src/app-pwa/protocol.ts +27 -0
- package/src/app-pwa/schema.ts +59 -0
- package/src/app-pwa/service.ts +861 -0
- package/src/app-pwa/transaction.ts +338 -0
- package/src/app-pwa/transport-input.ts +57 -0
- package/src/app-pwa/transport.ts +235 -0
- package/src/approvals/messages.ts +2 -2
- package/src/browser/app-access-recovery.ts +44 -0
- package/src/browser/app-session.ts +14 -3
- package/src/browser/cloud-id.ts +86 -0
- package/src/browser/live-websocket.ts +5 -0
- package/src/browser/pwa-drafts.ts +57 -0
- package/src/browser/resource-clipboard.ts +4 -0
- package/src/browser/resource-links.ts +52 -0
- package/src/browser/resource-picker.tsx +3 -2
- package/src/capabilities/calendar-sources.ts +302 -0
- package/src/capabilities/executions.ts +10 -3
- package/src/capabilities/migrate.ts +4 -0
- package/src/capabilities/nested.ts +43 -0
- package/src/capabilities/server-invoke.ts +202 -0
- package/src/capabilities/server.ts +26 -187
- package/src/capabilities/testing.ts +5 -2
- package/src/cli/access.ts +6 -5
- package/src/cli/account.ts +57 -0
- package/src/cli/admin/agents.ts +66 -0
- package/src/cli/admin/ai-models.ts +199 -0
- package/src/cli/admin/ai-quotas.ts +6 -102
- package/src/cli/admin/ai-usage.ts +4 -4
- package/src/cli/admin/app-credentials.ts +5 -3
- package/src/cli/admin/index.ts +7 -3
- package/src/cli/app-access.ts +60 -0
- package/src/cli/apps.ts +89 -26
- package/src/cli/avatar.ts +64 -0
- package/src/cli/capabilities.ts +22 -1
- package/src/cli/events.ts +320 -0
- package/src/cli/index.ts +1 -1
- package/src/config/env.ts +40 -0
- package/src/contracts/app-access.ts +52 -5
- package/src/contracts/app-approval.ts +8 -1
- package/src/contracts/app-details.ts +89 -0
- package/src/contracts/app-events.ts +51 -0
- package/src/contracts/app.ts +24 -5
- package/src/contracts/calendar-source.ts +274 -0
- package/src/contracts/calendar-view.ts +35 -0
- package/src/contracts/capabilities.ts +164 -3
- package/src/contracts/geo-admin.ts +2 -0
- package/src/contracts/geo-route.ts +0 -11
- package/src/contracts/geo.ts +14 -2
- package/src/contracts/index.ts +6 -0
- package/src/contracts/installable.ts +40 -0
- package/src/contracts/notification-types.ts +36 -9
- package/src/contracts/profile.ts +32 -0
- package/src/contracts/pwa-builds.ts +14 -0
- package/src/contracts/pwa-location.ts +38 -0
- package/src/contracts/pwa.ts +24 -3
- package/src/contracts/registry.ts +8 -0
- package/src/contracts/resource-clipboard.ts +35 -0
- package/src/contracts/resource-pages.ts +140 -0
- package/src/contracts/resource-resolution.ts +13 -0
- package/src/contracts/shared.ts +4 -0
- package/src/contracts/user-notifications.ts +2 -0
- package/src/events/app-event-delivery.ts +100 -0
- package/src/events/app-events.testing.ts +65 -0
- package/src/events/app-events.ts +134 -0
- package/src/events/core-user-created.ts +24 -0
- package/src/events/index.ts +9 -0
- package/src/events/testing.ts +2 -0
- package/src/index.ts +2 -1
- package/src/server/api/respond.ts +8 -3
- package/src/server/index.ts +1 -0
- package/src/server/middleware/app-access.ts +7 -6
- package/src/server/middleware/auth.ts +168 -66
- package/src/server/middleware/index.ts +1 -0
- package/src/server/middleware/invocation.ts +4 -2
- package/src/server/middleware/workload.ts +3 -3
- package/src/server/oauth-scope.ts +10 -0
- package/src/server/public-storage.ts +106 -0
- package/src/server/services/access.ts +8 -0
- package/src/services/accounts/app.ts +35 -3
- package/src/services/accounts/avatar.ts +342 -35
- package/src/services/accounts/base-group.ts +4 -5
- package/src/services/accounts/directory-shares.ts +157 -0
- package/src/services/accounts/entities.ts +50 -6
- package/src/services/accounts/groups.ts +3 -1
- package/src/services/accounts/identities.ts +64 -12
- package/src/services/accounts/local-groups.ts +1 -1
- package/src/services/accounts/users.ts +23 -15
- package/src/services/app-access-management.ts +519 -0
- package/src/services/app-access.ts +25 -7
- package/src/services/app-approval.ts +15 -4
- package/src/services/app-details.ts +224 -0
- package/src/services/app-discovery.ts +2 -1
- package/src/services/app-event-shapes.ts +139 -0
- package/src/services/app-events/accept.ts +116 -0
- package/src/services/app-events/admin.ts +250 -0
- package/src/services/app-events/declarations.ts +187 -0
- package/src/services/app-events/deliver.ts +218 -0
- package/src/services/app-events.ts +92 -0
- package/src/services/config-conflict.ts +11 -0
- package/src/services/core-user-events.ts +1 -0
- package/src/services/freeipa-config.ts +9 -0
- package/src/services/geo/admin.ts +4 -1
- package/src/services/geo/ledger.ts +47 -14
- package/src/services/geo/route-chain.ts +40 -18
- package/src/services/geo/route-estimate.ts +19 -0
- package/src/services/geo/route-plan.ts +0 -2
- package/src/services/geo/route-service.ts +57 -39
- package/src/services/geo/service.ts +51 -25
- package/src/services/identity/core-origin.ts +12 -0
- package/src/services/identity/index.ts +2 -1
- package/src/services/identity/invocation-actor.ts +50 -2
- package/src/services/identity/invocation-authority.ts +38 -1
- package/src/services/identity/invocation-token.ts +255 -50
- package/src/services/identity/issue-invocation.ts +9 -1
- package/src/services/identity/models-only-auth.ts +13 -4
- package/src/services/identity/nested-invocation.ts +128 -0
- package/src/services/image-metadata.ts +65 -1
- package/src/services/index.ts +24 -1
- package/src/services/ipa/sync.ts +6 -2
- package/src/services/ipa/users.ts +4 -1
- package/src/services/mandates/policy.ts +19 -12
- package/src/services/notifications/browser.ts +4 -0
- package/src/services/notifications/catalog.ts +5 -2
- package/src/services/notifications/platform.ts +7 -2
- package/src/services/notifications/user.ts +18 -7
- package/src/services/oauth-tokens.ts +28 -2
- package/src/services/object-store.ts +217 -0
- package/src/services/providers/local/users.ts +5 -0
- package/src/services/pwa-devices.ts +147 -37
- package/src/services/service-account-credentials.ts +5 -2
- package/src/services/session/index.ts +14 -0
- package/src/services/settings/core-settings.ts +91 -47
- package/src/services/settings/index.ts +8 -0
- package/src/services/settings/versions.ts +26 -0
- package/src/shared/ai-models.ts +274 -0
- package/src/shared/ai-quotas.ts +2 -2
- package/src/shared/ai-usage.ts +2 -2
- package/src/shared/index.ts +2 -0
- package/src/shared-text/documents.ts +25 -8
- package/src/shared-text/log.ts +1 -1
- package/src/shared-text/text-diff.ts +75 -0
- package/src/ssr/AdminArea.tsx +131 -0
- package/src/ssr/AdminLayout.tsx +11 -3
- package/src/ssr/AdminSidebar.tsx +7 -7
- package/src/ssr/AppAccessDenied.tsx +114 -0
- package/src/ssr/AppBadge.tsx +14 -0
- package/src/ssr/AppLaunchpadPanel.tsx +1 -1
- package/src/ssr/AppPwaRuntime.island.tsx +82 -0
- package/src/ssr/CloudShell.island.tsx +25 -4
- package/src/ssr/Layout.tsx +15 -7
- package/src/ssr/PwaLayout.tsx +73 -8
- package/src/ssr/PwaPartCount.island.tsx +23 -0
- package/src/ssr/PwaRuntime.island.tsx +85 -6
- package/src/ssr/SettingsSaveOutcome.tsx +58 -0
- package/src/ssr/SettingsSaveStatus.island.tsx +16 -0
- package/src/ssr/ShellRuntime.island.tsx +5 -0
- package/src/ssr/__snapshots__/Layout.unchanged.render.test.tsx.snap +3 -3
- package/src/ssr/__snapshots__/LayoutHelp.ssr.render.test.tsx.snap +1 -1
- package/src/ssr/admin-active-link.ts +16 -0
- package/src/ssr/admin-navigation.ts +26 -20
- package/src/ssr/app-badges.ts +31 -7
- package/src/ssr/app-pwa-document.tsx +16 -0
- package/src/ssr/app-pwa-messages.ts +17 -0
- package/src/ssr/index.ts +8 -0
- package/src/ssr/layout-context.ts +1 -1
- package/src/ssr/page-trail-runtime.ts +3 -1
- package/src/ssr/platform-messages.ts +9 -1
- package/src/ssr/pwa-messages.ts +10 -2
- package/src/ssr/rail-navigation.ts +5 -1
- package/src/ssr/settings-draft.ts +153 -0
- package/src/ssr/shell-data.tsx +2 -0
- package/src/styles/global.css +1 -0
- package/src/styles/profile-picture.css +270 -0
- package/src/styles/utilities-navigation.css +6 -0
- package/src/workflows/ai/policy.ts +49 -0
- package/src/workflows/ai/runtime.ts +131 -21
- package/src/workflows/ai/store.ts +283 -35
- package/src/workflows/ai/types.ts +8 -2
- package/src/workflows/ai-actions.ts +148 -120
- package/src/workflows/authoring.ts +31 -0
- package/src/workflows/contracts.ts +1 -0
- package/src/workflows/definition.ts +2 -0
- package/src/workflows/editor.ts +15 -0
- package/src/workflows/index.ts +2 -1
- package/src/workflows/language/compiler.ts +34 -3
- package/src/workflows/runtime/executor.ts +132 -33
- package/src/workflows/runtime/index.ts +2 -0
- package/src/workflows/runtime/ports.ts +5 -1
- package/src/workflows/store/actions.ts +1 -1
- package/src/workflows/store/definitions.ts +5 -0
- package/src/workflows/store/runs.ts +5 -3
- package/src/workflows/store/worker.ts +7 -2
- package/src/ai/model-pricing.ts +0 -43
- package/src/ai/model-request-settings.ts +0 -98
- package/src/ai/settings-messages.ts +0 -129
- package/src/shared/ai-model-request-settings.ts +0 -21
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Show } from "solid-js";
|
|
2
|
+
import { CloudAvatar } from "../account/Avatar";
|
|
3
|
+
import type { AccessEntry } from "../contracts/shared";
|
|
4
|
+
|
|
5
|
+
// One hue per principal kind from the avatar palette, so a group, a service account and all signed-in people
|
|
6
|
+
// read apart at a glance. People use a neutral background.
|
|
7
|
+
const hues = { group: "#22c55e", service_account: "#8b5cf6", authenticated: "#3b82f6" } as const;
|
|
8
|
+
|
|
9
|
+
/** The 32px avatar of an access entry in the expanded editor and on the denial page. */
|
|
10
|
+
export default function PrincipalAvatar(props: {
|
|
11
|
+
entry: Pick<AccessEntry, "principal" | "avatarHash" | "serviceAccountKind">;
|
|
12
|
+
label: string;
|
|
13
|
+
}) {
|
|
14
|
+
const icon = () => {
|
|
15
|
+
switch (props.entry.principal.type) {
|
|
16
|
+
case "group":
|
|
17
|
+
return "ti-users-group";
|
|
18
|
+
case "service_account":
|
|
19
|
+
return "ti-robot";
|
|
20
|
+
case "authenticated":
|
|
21
|
+
return "ti-world";
|
|
22
|
+
default:
|
|
23
|
+
return "ti-world";
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const hue = () => {
|
|
27
|
+
const type = props.entry.principal.type;
|
|
28
|
+
return type === "group" || type === "service_account" || type === "authenticated" ? hues[type] : undefined;
|
|
29
|
+
};
|
|
30
|
+
return (
|
|
31
|
+
<Show
|
|
32
|
+
when={props.entry.principal.type === "user" ? props.entry.principal : undefined}
|
|
33
|
+
fallback={
|
|
34
|
+
<span
|
|
35
|
+
class="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[var(--ui-surface-muted)] text-secondary"
|
|
36
|
+
// The avatar formula: a light share of the hue on the surface, a dark share on the text, in both themes.
|
|
37
|
+
style={
|
|
38
|
+
hue()
|
|
39
|
+
? {
|
|
40
|
+
background: `color-mix(in srgb, ${hue()} 18%, var(--k2b-surface))`,
|
|
41
|
+
color: `color-mix(in srgb, ${hue()} 55%, var(--k2b-text))`,
|
|
42
|
+
}
|
|
43
|
+
: undefined
|
|
44
|
+
}
|
|
45
|
+
aria-hidden="true"
|
|
46
|
+
>
|
|
47
|
+
<i class={`ti ${icon()} text-xs`} />
|
|
48
|
+
</span>
|
|
49
|
+
}
|
|
50
|
+
>
|
|
51
|
+
{(user) => (
|
|
52
|
+
<CloudAvatar
|
|
53
|
+
username={props.label}
|
|
54
|
+
userId={user().userId}
|
|
55
|
+
avatarHash={props.entry.avatarHash}
|
|
56
|
+
size="sm"
|
|
57
|
+
class="h-8 w-8"
|
|
58
|
+
style={{ background: "var(--k2b-surface-muted)", color: "var(--k2b-text-secondary)" }}
|
|
59
|
+
/>
|
|
60
|
+
)}
|
|
61
|
+
</Show>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Combobox, type ComboboxOption, useLocale } from "@k2b/ui";
|
|
2
|
-
import type { Principal, ServiceAccountKind } from "../contracts/shared";
|
|
2
|
+
import type { Principal, ServiceAccountKind, ServiceAccountStatus } from "../contracts/shared";
|
|
3
3
|
import { accessMessages } from "./messages";
|
|
4
4
|
import { principalLabel } from "./principal-label";
|
|
5
5
|
import { serviceAccountKindDisplay } from "./service-account-kind";
|
|
@@ -24,16 +24,29 @@ type Entity =
|
|
|
24
24
|
appId: string | null;
|
|
25
25
|
resourceType: string | null;
|
|
26
26
|
resourceId: string | null;
|
|
27
|
+
status: "active" | "disabled";
|
|
27
28
|
};
|
|
28
29
|
};
|
|
30
|
+
/** The service-account facts a consumer can check before offering an account. */
|
|
31
|
+
export type PrincipalPickerServiceAccount = Extract<Entity, { kind: "service_account" }>["serviceAccount"];
|
|
29
32
|
/** Shared identity selector; the consumer owns the meaning of the assignment. */
|
|
30
33
|
export default function PrincipalPicker(props: {
|
|
31
34
|
existing?: Principal[];
|
|
32
35
|
allowPublic?: boolean;
|
|
33
36
|
allowAuthenticated?: boolean;
|
|
37
|
+
/** Defaults to true; disable only for a documented user-backed workflow. */
|
|
34
38
|
allowServiceAccounts?: boolean;
|
|
39
|
+
/** Optional directory filters for the resource's accepted credential kinds. */
|
|
40
|
+
serviceAccountKinds?: ServiceAccountKind[];
|
|
41
|
+
serviceAccountStatus?: ServiceAccountStatus;
|
|
42
|
+
/** Offers only the service accounts this returns `true` for, such as active accounts the resource can accept. */
|
|
43
|
+
serviceAccountFilter?: (account: PrincipalPickerServiceAccount) => boolean;
|
|
35
44
|
disabled?: boolean;
|
|
36
45
|
readOnly?: boolean;
|
|
46
|
+
/** Replaces the default placeholder, which lists the principal kinds on offer. */
|
|
47
|
+
placeholder?: string;
|
|
48
|
+
/** The consumer's name and description for every signed-in person. */
|
|
49
|
+
signedIn?: { label: string; description: string };
|
|
37
50
|
/** `serviceAccountKind` is set for service accounts so a new row can show the same kind as the result. */
|
|
38
51
|
onSelect: (
|
|
39
52
|
principal: Principal,
|
|
@@ -56,8 +69,9 @@ export default function PrincipalPicker(props: {
|
|
|
56
69
|
if (props.allowAuthenticated !== false)
|
|
57
70
|
entries.push({
|
|
58
71
|
principal: { type: "authenticated" },
|
|
59
|
-
label: principalLabel({ principal: { type: "authenticated" } }, locale()),
|
|
72
|
+
label: props.signedIn?.label ?? principalLabel({ principal: { type: "authenticated" } }, locale()),
|
|
60
73
|
icon: "ti ti-lock-open-2",
|
|
74
|
+
description: props.signedIn?.description,
|
|
61
75
|
});
|
|
62
76
|
if (props.allowPublic)
|
|
63
77
|
entries.push({
|
|
@@ -68,7 +82,13 @@ export default function PrincipalPicker(props: {
|
|
|
68
82
|
if (search.length >= 2) {
|
|
69
83
|
const url = new URL("/api/accounts/entities", window.location.origin);
|
|
70
84
|
url.searchParams.set("search", search);
|
|
71
|
-
url.searchParams.set("kinds", props.allowServiceAccounts ? "user,group,service_account" : "user,group");
|
|
85
|
+
url.searchParams.set("kinds", props.allowServiceAccounts !== false ? "user,group,service_account" : "user,group");
|
|
86
|
+
if (props.allowServiceAccounts !== false && props.serviceAccountKinds?.length) {
|
|
87
|
+
url.searchParams.set("service_account_kinds", props.serviceAccountKinds.join(","));
|
|
88
|
+
}
|
|
89
|
+
if (props.allowServiceAccounts !== false && props.serviceAccountStatus) {
|
|
90
|
+
url.searchParams.set("service_account_status", props.serviceAccountStatus);
|
|
91
|
+
}
|
|
72
92
|
url.searchParams.set("per_page", "10");
|
|
73
93
|
const users = props.existing?.flatMap((p) => (p.type === "user" ? [p.userId] : [])) ?? [];
|
|
74
94
|
const groups = props.existing?.flatMap((p) => (p.type === "group" ? [p.groupId] : [])) ?? [];
|
|
@@ -98,7 +118,7 @@ export default function PrincipalPicker(props: {
|
|
|
98
118
|
icon: "ti ti-users-group",
|
|
99
119
|
description: e.group.description ?? undefined,
|
|
100
120
|
});
|
|
101
|
-
else {
|
|
121
|
+
else if (!props.serviceAccountFilter || props.serviceAccountFilter(e.serviceAccount)) {
|
|
102
122
|
const account = e.serviceAccount;
|
|
103
123
|
const kind = serviceAccountKindDisplay(account.kind, t());
|
|
104
124
|
entries.push({
|
|
@@ -133,13 +153,14 @@ export default function PrincipalPicker(props: {
|
|
|
133
153
|
disabled={props.disabled}
|
|
134
154
|
readOnly={props.readOnly}
|
|
135
155
|
placeholder={
|
|
136
|
-
props.
|
|
137
|
-
|
|
156
|
+
props.placeholder ??
|
|
157
|
+
(props.allowAuthenticated !== false || props.allowPublic
|
|
158
|
+
? props.allowServiceAccounts !== false
|
|
138
159
|
? t().addAll
|
|
139
160
|
: t().addAudience
|
|
140
|
-
: props.allowServiceAccounts
|
|
161
|
+
: props.allowServiceAccounts !== false
|
|
141
162
|
? t().addService
|
|
142
|
-
: t().addBasic
|
|
163
|
+
: t().addBasic)
|
|
143
164
|
}
|
|
144
165
|
fetchData={load}
|
|
145
166
|
onSelect={(option) => {
|
|
@@ -217,12 +217,11 @@ export default function ResourceApiKeys(props: ResourceApiKeysProps) {
|
|
|
217
217
|
if (confirmed) await revokeMutation.mutate({ id: key.id, name: key.name });
|
|
218
218
|
};
|
|
219
219
|
|
|
220
|
-
// The heading is a detail panel section heading: in a
|
|
221
|
-
// and below a settings group title they read as one level down.
|
|
220
|
+
// The heading is a detail panel section heading without an icon: in a details panel the keys line up with the
|
|
221
|
+
// sections around them, and below a settings group title they read as one level down. Keys are flat rows.
|
|
222
222
|
return (
|
|
223
223
|
<DetailPanel.Section
|
|
224
224
|
title={props.title ?? t().apiKeys}
|
|
225
|
-
icon="ti ti-key"
|
|
226
225
|
description={props.description ?? t().apiKeysDescription}
|
|
227
226
|
actions={
|
|
228
227
|
<Button type="button" variant="secondary" size="sm" onClick={openCreate} disabled={createMutation.loading()}>
|
|
@@ -235,7 +234,7 @@ export default function ResourceApiKeys(props: ResourceApiKeysProps) {
|
|
|
235
234
|
<div class="flex flex-col gap-2">
|
|
236
235
|
<For each={keys()}>
|
|
237
236
|
{(key) => (
|
|
238
|
-
<div class="group/api-key flex items-center gap-3
|
|
237
|
+
<div class="group/api-key flex items-center gap-3 py-1.5">
|
|
239
238
|
<div class="min-w-0 flex-1">
|
|
240
239
|
{/* The name and its tags wrap instead of squeezing the name to a few letters in a narrow section. */}
|
|
241
240
|
<div class="flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1">
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { hc } from "hono/client";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import type { AppAccessApi } from "../api/app-access";
|
|
4
|
+
import { AppAccessSnapshotSchema } from "../contracts/app-access";
|
|
5
|
+
import { AccessEntrySchema } from "../contracts/shared";
|
|
6
|
+
import type { AppAccessChange, AppAccessSnapshot } from "../services/app-access-management";
|
|
7
|
+
|
|
8
|
+
export class AppAccessClientError extends Error {
|
|
9
|
+
constructor(
|
|
10
|
+
readonly code: string,
|
|
11
|
+
message: string,
|
|
12
|
+
readonly status: number,
|
|
13
|
+
) {
|
|
14
|
+
super(message);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Same audited route for an app's own settings and explicit administration repair. */
|
|
19
|
+
export const appAccessAdapter = (appId: string, options: { repair?: boolean } = {}) => {
|
|
20
|
+
const client = hc<AppAccessApi>("/_cloud/app-access");
|
|
21
|
+
const param = { appId };
|
|
22
|
+
const query = options.repair ? { repair: "1" as const } : {};
|
|
23
|
+
return {
|
|
24
|
+
async read(signal?: AbortSignal): Promise<AppAccessSnapshot> {
|
|
25
|
+
const response = await client[":appId"].$get({ param, query }, { init: { credentials: "same-origin", signal } });
|
|
26
|
+
const data = await response.json();
|
|
27
|
+
if (response.ok) return AppAccessSnapshotSchema.parse(data);
|
|
28
|
+
throw new AppAccessClientError(
|
|
29
|
+
typeof data === "object" && data !== null && "code" in data && typeof data.code === "string" ? data.code : "APP_ACCESS_READ_FAILED",
|
|
30
|
+
typeof data === "object" && data !== null && "message" in data && typeof data.message === "string"
|
|
31
|
+
? data.message
|
|
32
|
+
: "The access list could not be loaded.",
|
|
33
|
+
response.status,
|
|
34
|
+
);
|
|
35
|
+
},
|
|
36
|
+
async change(change: AppAccessChange, signal?: AbortSignal) {
|
|
37
|
+
const response = await client[":appId"].$post({ param, query, json: change }, { init: { credentials: "same-origin", signal } });
|
|
38
|
+
const data = await response.json();
|
|
39
|
+
if (response.ok) return z.object({ entry: AccessEntrySchema.optional(), canUse: z.boolean() }).parse(data);
|
|
40
|
+
throw new AppAccessClientError(
|
|
41
|
+
typeof data === "object" && data !== null && "code" in data && typeof data.code === "string"
|
|
42
|
+
? data.code
|
|
43
|
+
: "APP_ACCESS_CHANGE_FAILED",
|
|
44
|
+
typeof data === "object" && data !== null && "message" in data && typeof data.message === "string"
|
|
45
|
+
? data.message
|
|
46
|
+
: "The access change could not be confirmed.",
|
|
47
|
+
response.status,
|
|
48
|
+
);
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
};
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { i18n } from "@k2b/stdlib";
|
|
2
|
+
|
|
3
|
+
export const appAccessMessages = i18n.define({
|
|
4
|
+
baseLocale: "en",
|
|
5
|
+
messages: {
|
|
6
|
+
en: {
|
|
7
|
+
access: "Access",
|
|
8
|
+
apps: "Apps",
|
|
9
|
+
app: "App",
|
|
10
|
+
use: "Use",
|
|
11
|
+
manage: "Manage access",
|
|
12
|
+
retry: "Try again",
|
|
13
|
+
reload: "Reload",
|
|
14
|
+
accessChanged: "Your app access changed. Reload to check your access.",
|
|
15
|
+
whoUses: ({ name }: { name: string }) => `Who uses ${name}`,
|
|
16
|
+
description: ({ name }: { name: string }) =>
|
|
17
|
+
`Only these people, groups and service accounts see ${name} in the launcher, search and Assistant.`,
|
|
18
|
+
useDescription: "Open the app and work with it. Permissions within the app control what each person sees.",
|
|
19
|
+
manageDescription: "Use the app and change this list.",
|
|
20
|
+
repairNotice: ({ name }: { name: string }) =>
|
|
21
|
+
`You are editing access as a Cloud administrator. This does not give you use of ${name}. Every change is recorded in the audit log.`,
|
|
22
|
+
noManagerLead: "No one manages access.",
|
|
23
|
+
noManager: "Assign at least one person Manage access.",
|
|
24
|
+
inherited: ({ name }: { name: string }) =>
|
|
25
|
+
`Until now, all signed-in people could use ${name}. That stays the same until you change it.`,
|
|
26
|
+
loadFailedLead: "The list could not be loaded.",
|
|
27
|
+
loadFailed: "No changes were made.",
|
|
28
|
+
saveFailed: "Not saved.",
|
|
29
|
+
listChanged: "This grant has changed. Reload the list before trying again.",
|
|
30
|
+
alreadyGranted: "This entry already has access. Reload the list to see the current access.",
|
|
31
|
+
uncertain: "The change could not be confirmed. Reload the list before trying again.",
|
|
32
|
+
savedRefreshFailed: "Saved. The list could not be refreshed. Reload before making another change.",
|
|
33
|
+
lastManager: "At least one person must manage access. Add another manager first.",
|
|
34
|
+
forbidden: "You are not allowed to manage this list.",
|
|
35
|
+
removeSelfTitle: "Remove your own access?",
|
|
36
|
+
changeSelfTitle: "Change your own access?",
|
|
37
|
+
removeAudienceTitle: "Remove access for all signed-in people?",
|
|
38
|
+
removeAudience: ({ name }: { name: string }) => `People without another grant can no longer use ${name}.`,
|
|
39
|
+
changeSelfEntry: ({ name }: { name: string }) => `This grant for ${name} changes to Use. Your other grants still apply.`,
|
|
40
|
+
removeSelf: ({ name }: { name: string }) => `Afterwards, you cannot open ${name} or change this list.`,
|
|
41
|
+
removeSelfManagement: ({ name }: { name: string }) => `You can still use ${name}, but can no longer change this list.`,
|
|
42
|
+
removeSelfEntry: ({ name }: { name: string }) => `This removes this grant for ${name}. Your other grants still apply.`,
|
|
43
|
+
remainingManagers: ({ managers, count }: { managers: string; count: number }) =>
|
|
44
|
+
count === 0 ? "" : count === 1 ? `${managers} still manages access.` : `${managers} still manage access.`,
|
|
45
|
+
closeNotice: "Close notice",
|
|
46
|
+
cancel: "Cancel",
|
|
47
|
+
remove: "Remove",
|
|
48
|
+
change: "Change",
|
|
49
|
+
add: "Add a person, group or service account",
|
|
50
|
+
signedIn: "All signed-in people",
|
|
51
|
+
signedInDescription: "Everyone with an account in this Cloud",
|
|
52
|
+
group: ({ count }: { count: number }) =>
|
|
53
|
+
`Group · ${count === 0 ? "no people" : count === 1 ? "1 person" : `${count.toLocaleString("en")} people`}`,
|
|
54
|
+
fixedSignedIn: "All signed-in people. The app sets this rule; there is nothing to configure here.",
|
|
55
|
+
fixedNoGuests: "Signed-in people except guests. The app sets this rule; there is nothing to configure here.",
|
|
56
|
+
fixedAdmins: "Cloud administrators only. The app sets this rule; there is nothing to configure here.",
|
|
57
|
+
fixedRoles: "People with the Cloud roles required by the app. The app sets this rule.",
|
|
58
|
+
resources: "Accounts with an effective resource permission in this app. Manage permissions within the app.",
|
|
59
|
+
resourcesLabel: "Resource permissions",
|
|
60
|
+
legacy: "This app uses its existing access checks.",
|
|
61
|
+
deniedTitle: ({ name }: { name: string }) => `${name} is not available to you`,
|
|
62
|
+
revokedTitle: ({ name }: { name: string }) => `Your access to ${name} was removed`,
|
|
63
|
+
revokedDescription: "You had access until now. If this was a mistake, ask:",
|
|
64
|
+
deniedGrants: "Only people with access may use this app. Access is managed by:",
|
|
65
|
+
deniedFixed: "If you need this app, contact your Cloud administration.",
|
|
66
|
+
deniedResources: "Ask someone who manages resource permissions in this app for access.",
|
|
67
|
+
deniedAdmins: ({ name }: { name: string }) => `Only Cloud administrators use ${name}.`,
|
|
68
|
+
deniedNoGuests: ({ name }: { name: string }) => `Guests cannot use ${name}.`,
|
|
69
|
+
deniedRoles: ({ name }: { name: string }) => `Only people with specific Cloud roles use ${name}.`,
|
|
70
|
+
askAdministration: "Cloud administration",
|
|
71
|
+
home: "Go to home",
|
|
72
|
+
adminHintBefore: "As a Cloud administrator, you can repair access under ",
|
|
73
|
+
adminHintLink: "Administration → Apps",
|
|
74
|
+
adminHintAfter: ". You need your own grant to use the app.",
|
|
75
|
+
repair: "Repair access",
|
|
76
|
+
repairShort: "Repair",
|
|
77
|
+
backToApps: "Back to Apps",
|
|
78
|
+
configurable: "Configured in the app",
|
|
79
|
+
rule: "Who uses the app",
|
|
80
|
+
overview: "Each app decides who may use it. Review its rule here and repair configurable access when no one manages it.",
|
|
81
|
+
fixedSignedInLabel: "Fixed: all signed-in people",
|
|
82
|
+
fixedNoGuestsLabel: "Fixed: no guests",
|
|
83
|
+
fixedAdminsLabel: "Fixed: Cloud administrators only",
|
|
84
|
+
fixedRolesLabel: "Fixed: Cloud roles",
|
|
85
|
+
legacyLabel: "Existing access checks",
|
|
86
|
+
count: ({ count }: { count: number }) => (count === 1 ? "1 grant" : `${count} grants`),
|
|
87
|
+
},
|
|
88
|
+
de: {
|
|
89
|
+
access: "Zugriff",
|
|
90
|
+
apps: "Apps",
|
|
91
|
+
app: "App",
|
|
92
|
+
use: "Nutzen",
|
|
93
|
+
manage: "Zugriff verwalten",
|
|
94
|
+
retry: "Erneut versuchen",
|
|
95
|
+
reload: "Erneut laden",
|
|
96
|
+
accessChanged: "Dein App-Zugriff hat sich geändert. Lade die Seite neu, um deinen Zugriff zu prüfen.",
|
|
97
|
+
whoUses: ({ name }: { name: string }) => `Wer ${name} nutzt`,
|
|
98
|
+
description: ({ name }: { name: string }) =>
|
|
99
|
+
`Nur diese Personen, Gruppen und Dienstkonten sehen ${name} im Start-Menü, in der Suche und beim Assistenten.`,
|
|
100
|
+
useDescription: "App öffnen und mit ihr arbeiten. Was jemand darin sieht, regeln die Rechte der App.",
|
|
101
|
+
manageDescription: "Nutzen und diese Liste ändern.",
|
|
102
|
+
repairNotice: ({ name }: { name: string }) =>
|
|
103
|
+
`Du bearbeitest den Zugriff als Cloud-Admin. Nutzen kannst du ${name} damit nicht. Jede Änderung steht im Protokoll.`,
|
|
104
|
+
noManagerLead: "Niemand verwaltet den Zugriff.",
|
|
105
|
+
noManager: "Lege mindestens eine Person mit „Zugriff verwalten“ fest.",
|
|
106
|
+
inherited: ({ name }: { name: string }) => `Bisher nutzten alle angemeldeten Personen ${name}. Das bleibt so, bis du es änderst.`,
|
|
107
|
+
loadFailedLead: "Die Liste lädt gerade nicht.",
|
|
108
|
+
loadFailed: "Es hat sich nichts geändert.",
|
|
109
|
+
saveFailed: "Nicht gespeichert.",
|
|
110
|
+
listChanged: "Diese Freigabe wurde inzwischen geändert. Lade die Liste vor einem neuen Versuch erneut.",
|
|
111
|
+
alreadyGranted: "Dieser Eintrag hat schon Zugriff. Lade die Liste erneut, um den aktuellen Zugriff zu sehen.",
|
|
112
|
+
uncertain: "Die Änderung konnte nicht bestätigt werden. Lade die Liste vor einem neuen Versuch erneut.",
|
|
113
|
+
savedRefreshFailed: "Gespeichert. Die Liste konnte nicht aktualisiert werden. Lade sie vor einer weiteren Änderung erneut.",
|
|
114
|
+
lastManager: "Mindestens eine Person muss den Zugriff verwalten. Füge zuerst eine weitere hinzu.",
|
|
115
|
+
forbidden: "Du darfst diese Liste nicht verwalten.",
|
|
116
|
+
removeSelfTitle: "Eigenen Zugriff entfernen?",
|
|
117
|
+
changeSelfTitle: "Eigenen Zugriff ändern?",
|
|
118
|
+
removeAudienceTitle: "Zugriff für alle angemeldeten Personen entfernen?",
|
|
119
|
+
removeAudience: ({ name }: { name: string }) => `Personen ohne weitere Freigabe können ${name} danach nicht mehr nutzen.`,
|
|
120
|
+
changeSelfEntry: ({ name }: { name: string }) =>
|
|
121
|
+
`Diese Freigabe für ${name} wird auf Nutzen geändert. Deine anderen Freigaben gelten weiter.`,
|
|
122
|
+
removeSelf: ({ name }: { name: string }) => `Danach kannst du ${name} nicht mehr öffnen und diese Liste nicht mehr ändern.`,
|
|
123
|
+
removeSelfManagement: ({ name }: { name: string }) => `Du kannst ${name} weiter nutzen, aber diese Liste nicht mehr ändern.`,
|
|
124
|
+
removeSelfEntry: ({ name }: { name: string }) => `Diese Freigabe für ${name} wird entfernt. Deine anderen Freigaben gelten weiter.`,
|
|
125
|
+
remainingManagers: ({ managers, count }: { managers: string; count: number }) =>
|
|
126
|
+
count === 0 ? "" : count === 1 ? `${managers} verwaltet den Zugriff weiter.` : `${managers} verwalten den Zugriff weiter.`,
|
|
127
|
+
closeNotice: "Hinweis schließen",
|
|
128
|
+
cancel: "Abbrechen",
|
|
129
|
+
remove: "Entfernen",
|
|
130
|
+
change: "Ändern",
|
|
131
|
+
add: "Person, Gruppe oder Dienstkonto hinzufügen",
|
|
132
|
+
signedIn: "Alle angemeldeten Personen",
|
|
133
|
+
signedInDescription: "Jede Person mit Konto in dieser Cloud",
|
|
134
|
+
group: ({ count }: { count: number }) =>
|
|
135
|
+
`Gruppe · ${count === 0 ? "keine Personen" : count === 1 ? "1 Person" : `${count.toLocaleString("de")} Personen`}`,
|
|
136
|
+
fixedSignedIn: "Alle angemeldeten Personen. Das legt die App fest; hier gibt es dazu nichts einzustellen.",
|
|
137
|
+
fixedNoGuests: "Angemeldete Personen außer Gästen. Das legt die App fest; hier gibt es dazu nichts einzustellen.",
|
|
138
|
+
fixedAdmins: "Nur Cloud-Admins. Das legt die App fest; hier gibt es dazu nichts einzustellen.",
|
|
139
|
+
fixedRoles: "Personen mit den von der App verlangten Cloud-Rollen. Das legt die App fest.",
|
|
140
|
+
resources: "Konten mit einer wirksamen Ressourcenberechtigung in dieser App. Berechtigungen werden innerhalb der App verwaltet.",
|
|
141
|
+
resourcesLabel: "Ressourcenberechtigungen",
|
|
142
|
+
legacy: "Diese App verwendet ihre bisherigen Zugriffsprüfungen.",
|
|
143
|
+
deniedTitle: ({ name }: { name: string }) => `${name} ist für dich nicht freigegeben`,
|
|
144
|
+
revokedTitle: ({ name }: { name: string }) => `Dein Zugriff auf ${name} wurde entfernt`,
|
|
145
|
+
revokedDescription: "Du hattest bis eben Zugriff. Wenn das ein Versehen ist, frag:",
|
|
146
|
+
deniedGrants: "Diese App nutzen nur Personen mit Freigabe. Den Zugriff verwalten:",
|
|
147
|
+
deniedFixed: "Wenn du die App brauchst, wende dich an die Cloud-Verwaltung.",
|
|
148
|
+
deniedResources: "Bitte jemanden, der Ressourcenberechtigungen in dieser App verwaltet, um Zugriff.",
|
|
149
|
+
deniedAdmins: ({ name }: { name: string }) => `${name} nutzen nur Cloud-Admins.`,
|
|
150
|
+
deniedNoGuests: ({ name }: { name: string }) => `${name} nutzen nur angemeldete Personen außer Gästen.`,
|
|
151
|
+
deniedRoles: ({ name }: { name: string }) => `${name} nutzen nur Personen mit bestimmten Cloud-Rollen.`,
|
|
152
|
+
askAdministration: "Cloud-Verwaltung",
|
|
153
|
+
home: "Zur Startseite",
|
|
154
|
+
adminHintBefore: "Als Cloud-Admin kannst du den Zugriff unter ",
|
|
155
|
+
adminHintLink: "Verwaltung → Apps",
|
|
156
|
+
adminHintAfter: " reparieren. Nutzen kannst du die App erst mit einer Freigabe.",
|
|
157
|
+
repair: "Zugriff reparieren",
|
|
158
|
+
repairShort: "Reparieren",
|
|
159
|
+
backToApps: "Zurück zu Apps",
|
|
160
|
+
configurable: "Einstellbar in der App",
|
|
161
|
+
rule: "Wer nutzt die App",
|
|
162
|
+
overview:
|
|
163
|
+
"Jede App legt selbst fest, wer sie nutzt. Hier siehst du die Regeln und reparierst einstellbaren Zugriff, wenn niemand ihn mehr verwaltet.",
|
|
164
|
+
fixedSignedInLabel: "Fest: alle angemeldeten Personen",
|
|
165
|
+
fixedNoGuestsLabel: "Fest: keine Gäste",
|
|
166
|
+
fixedAdminsLabel: "Fest: nur Cloud-Admins",
|
|
167
|
+
fixedRolesLabel: "Fest: Cloud-Rollen",
|
|
168
|
+
legacyLabel: "Bisherige Zugriffsprüfungen",
|
|
169
|
+
count: ({ count }: { count: number }) => (count === 1 ? "1 Freigabe" : `${count} Freigaben`),
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
});
|
package/src/access/messages.ts
CHANGED
|
@@ -4,6 +4,13 @@ export const accessMessages = i18n.define({
|
|
|
4
4
|
baseLocale: "en",
|
|
5
5
|
messages: {
|
|
6
6
|
en: {
|
|
7
|
+
newPermission: "Permission for new entry",
|
|
8
|
+
saved: "Saved",
|
|
9
|
+
saving: "Saving",
|
|
10
|
+
you: "you",
|
|
11
|
+
clearSelection: ({ name }: { name: string }) => `Remove selection: ${name}`,
|
|
12
|
+
person: "Person",
|
|
13
|
+
group: "Group",
|
|
7
14
|
view: "View",
|
|
8
15
|
edit: "Edit",
|
|
9
16
|
manage: "Manage",
|
|
@@ -87,6 +94,13 @@ export const accessMessages = i18n.define({
|
|
|
87
94
|
neverUsed: "Never used",
|
|
88
95
|
},
|
|
89
96
|
de: {
|
|
97
|
+
newPermission: "Recht für neuen Eintrag",
|
|
98
|
+
saved: "Gespeichert",
|
|
99
|
+
saving: "Wird gespeichert",
|
|
100
|
+
you: "du",
|
|
101
|
+
clearSelection: ({ name }) => `Auswahl entfernen: ${name}`,
|
|
102
|
+
person: "Person",
|
|
103
|
+
group: "Gruppe",
|
|
90
104
|
view: "Ansehen",
|
|
91
105
|
edit: "Bearbeiten",
|
|
92
106
|
manage: "Verwalten",
|
package/src/access/ui.ts
CHANGED
|
@@ -6,10 +6,13 @@
|
|
|
6
6
|
* from depending on Cloud's broad legacy UI entrypoint.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
export { type AppAccessEditorProps, default as AppAccessEditor } from "./AppAccessEditor";
|
|
10
|
+
export { appAccessAdapter } from "./app-access-adapter";
|
|
11
|
+
export { appAccessMessages } from "./app-access-messages";
|
|
9
12
|
export { isManagerEntry, type ManagerCandidate } from "./managers";
|
|
10
13
|
export type { AllowedLevel, GrantableLevel } from "./PermissionEditor";
|
|
11
14
|
export { default as PermissionEditor } from "./PermissionEditor";
|
|
12
|
-
export { default as PrincipalPicker, principalKey } from "./PrincipalPicker";
|
|
15
|
+
export { default as PrincipalPicker, type PrincipalPickerServiceAccount, principalKey } from "./PrincipalPicker";
|
|
13
16
|
export { principalLabel } from "./principal-label";
|
|
14
17
|
export type {
|
|
15
18
|
ResourceApiKey,
|
package/src/account/Avatar.tsx
CHANGED
|
@@ -16,22 +16,40 @@ const avatarSource = (userId?: string | null, avatarHash?: string | null): strin
|
|
|
16
16
|
return `/api/accounts/users/${encodeURIComponent(userId)}/avatar?rev=${encodeURIComponent(avatarHash)}`;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
/** The URL of a person's
|
|
19
|
+
/** The URL of a person's picture, or undefined while the person shows initials. */
|
|
20
20
|
export const cloudAvatarSource = avatarSource;
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Where an avatar's image comes from. A person has one only with a hash; a service account always has one, its photo
|
|
24
|
+
* or its robot, so it is linked even without a hash and revalidates. A system actor has none.
|
|
25
|
+
*/
|
|
26
|
+
export const cloudAvatarImage = (params: {
|
|
27
|
+
kind: CloudAvatarKind;
|
|
28
|
+
userId?: string | null;
|
|
29
|
+
avatarHash?: string | null;
|
|
30
|
+
}): string | undefined => {
|
|
31
|
+
if (params.kind === "user") return avatarSource(params.userId, params.avatarHash);
|
|
32
|
+
if (params.kind === "system" || !params.userId) return undefined;
|
|
33
|
+
const rev = params.avatarHash ? `?rev=${encodeURIComponent(params.avatarHash)}` : "";
|
|
34
|
+
return `/api/accounts/users/${encodeURIComponent(params.userId)}/avatar${rev}`;
|
|
35
|
+
};
|
|
36
|
+
|
|
22
37
|
const KIND_ICON: Record<CloudAvatarKind, string | undefined> = {
|
|
23
38
|
user: undefined,
|
|
24
39
|
service_account: "ti ti-robot",
|
|
25
40
|
system: "ti ti-settings-automation",
|
|
26
41
|
};
|
|
27
42
|
|
|
28
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* One avatar for every Cloud surface. A person shows a photo, a built avatar, or initials; a service account shows its
|
|
45
|
+
* photo or robot in a square, with its icon only while the image cannot load.
|
|
46
|
+
*/
|
|
29
47
|
export function CloudAvatar(props: CloudAvatarProps): JSX.Element {
|
|
30
48
|
const kind = () => props.kind ?? "user";
|
|
31
49
|
return (
|
|
32
50
|
<Avatar
|
|
33
51
|
name={props.username}
|
|
34
|
-
src={kind()
|
|
52
|
+
src={cloudAvatarImage({ kind: kind(), userId: props.userId, avatarHash: props.avatarHash })}
|
|
35
53
|
icon={KIND_ICON[kind()]}
|
|
36
54
|
shape={kind() === "user" ? "circle" : "square"}
|
|
37
55
|
alt={props.alt}
|