Expose credential envelope administration
This commit is contained in:
@@ -72,6 +72,8 @@ ACCESS_PERMISSIONS: tuple[PermissionDefinition, ...] = (
|
|||||||
_permission("access:system_role:assign", "Assign system roles", "Assign instance-wide roles to accounts while preserving a system owner.", "Access", "system"),
|
_permission("access:system_role:assign", "Assign system roles", "Assign instance-wide roles to accounts while preserving a system owner.", "Access", "system"),
|
||||||
_permission("access:system_setting:read", "View system settings", "Read instance defaults and tenant-governance defaults.", "Access", "system"),
|
_permission("access:system_setting:read", "View system settings", "Read instance defaults and tenant-governance defaults.", "Access", "system"),
|
||||||
_permission("access:system_setting:write", "Manage system settings", "Change instance defaults and tenant-governance defaults.", "Access", "system"),
|
_permission("access:system_setting:write", "Manage system settings", "Change instance defaults and tenant-governance defaults.", "Access", "system"),
|
||||||
|
_permission("access:system_credential:read", "View system credentials", "List instance-wide reusable credential envelopes without revealing secret values.", "Access", "system"),
|
||||||
|
_permission("access:system_credential:write", "Manage system credentials", "Create, update, and retire instance-wide reusable credential envelopes.", "Access", "system"),
|
||||||
_permission("access:maintenance:access", "Access during maintenance", "Use the system while maintenance mode is active.", "Access", "system"),
|
_permission("access:maintenance:access", "Access during maintenance", "Use the system while maintenance mode is active.", "Access", "system"),
|
||||||
_permission("access:audit:read", "View system audit", "Read audit records across tenants.", "Access", "system"),
|
_permission("access:audit:read", "View system audit", "Read audit records across tenants.", "Access", "system"),
|
||||||
_permission("access:membership:read", "View memberships", "List tenant memberships and effective access.", "Tenant access", "tenant"),
|
_permission("access:membership:read", "View memberships", "List tenant memberships and effective access.", "Tenant access", "tenant"),
|
||||||
@@ -92,6 +94,9 @@ ACCESS_PERMISSIONS: tuple[PermissionDefinition, ...] = (
|
|||||||
_permission("access:api_key:revoke", "Revoke API keys", "Revoke tenant API keys.", "Tenant access", "tenant"),
|
_permission("access:api_key:revoke", "Revoke API keys", "Revoke tenant API keys.", "Tenant access", "tenant"),
|
||||||
_permission("access:setting:read", "View settings", "Read access and governance settings.", "Tenant access", "tenant"),
|
_permission("access:setting:read", "View settings", "Read access and governance settings.", "Tenant access", "tenant"),
|
||||||
_permission("access:setting:write", "Manage settings", "Update access and governance settings.", "Tenant access", "tenant"),
|
_permission("access:setting:write", "Manage settings", "Update access and governance settings.", "Tenant access", "tenant"),
|
||||||
|
_permission("access:credential:read", "View credentials", "List reusable credential envelopes without revealing secret values.", "Tenant access", "tenant"),
|
||||||
|
_permission("access:credential:write", "Manage credentials", "Create, update, and retire reusable credential envelopes.", "Tenant access", "tenant"),
|
||||||
|
_permission("access:credential:manage_own", "Manage own credentials", "Manage reusable credentials owned by the current membership.", "Tenant access", "tenant"),
|
||||||
_permission("access:policy:read", "View tenant policies", "Read tenant policy and governance settings.", "Tenant access", "tenant"),
|
_permission("access:policy:read", "View tenant policies", "Read tenant policy and governance settings.", "Tenant access", "tenant"),
|
||||||
_permission("access:policy:write", "Manage tenant policies", "Change tenant policy and governance settings where system policy permits it.", "Tenant access", "tenant"),
|
_permission("access:policy:write", "Manage tenant policies", "Change tenant policy and governance settings where system policy permits it.", "Tenant access", "tenant"),
|
||||||
_permission("access:governance:read", "View governance", "Inspect managed role and group templates.", "Access", "system"),
|
_permission("access:governance:read", "View governance", "Inspect managed role and group templates.", "Access", "system"),
|
||||||
@@ -126,6 +131,8 @@ ACCESS_ROLE_TEMPLATES: tuple[RoleTemplate, ...] = (
|
|||||||
"access:system_role:assign",
|
"access:system_role:assign",
|
||||||
"access:system_setting:read",
|
"access:system_setting:read",
|
||||||
"access:system_setting:write",
|
"access:system_setting:write",
|
||||||
|
"access:system_credential:read",
|
||||||
|
"access:system_credential:write",
|
||||||
"access:governance:read",
|
"access:governance:read",
|
||||||
"access:governance:write",
|
"access:governance:write",
|
||||||
),
|
),
|
||||||
@@ -186,6 +193,8 @@ ACCESS_ROLE_TEMPLATES: tuple[RoleTemplate, ...] = (
|
|||||||
"access:api_key:revoke",
|
"access:api_key:revoke",
|
||||||
"access:setting:read",
|
"access:setting:read",
|
||||||
"access:setting:write",
|
"access:setting:write",
|
||||||
|
"access:credential:read",
|
||||||
|
"access:credential:write",
|
||||||
"access:policy:read",
|
"access:policy:read",
|
||||||
"access:policy:write",
|
"access:policy:write",
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import ExternalFunctionRoleMappingsPanel from "./ExternalFunctionRoleMappingsPan
|
|||||||
import ApiKeysPanel from "./ApiKeysPanel";
|
import ApiKeysPanel from "./ApiKeysPanel";
|
||||||
import FileConnectorsPanel from "./FileConnectorsPanel";
|
import FileConnectorsPanel from "./FileConnectorsPanel";
|
||||||
import MailProfilesPanel from "./MailProfilesPanel";
|
import MailProfilesPanel from "./MailProfilesPanel";
|
||||||
|
import CredentialEnvelopesPanel from "./CredentialEnvelopesPanel";
|
||||||
import { usePlatformUiCapabilities, usePlatformUiCapability } from "@govoplan/core-webui";
|
import { usePlatformUiCapabilities, usePlatformUiCapability } from "@govoplan/core-webui";
|
||||||
|
|
||||||
type AdminSection = string;
|
type AdminSection = string;
|
||||||
@@ -43,6 +44,7 @@ const handledAdminSectionIds = new Set<string>([
|
|||||||
"system-users",
|
"system-users",
|
||||||
"system-file-connectors",
|
"system-file-connectors",
|
||||||
"system-mail-servers",
|
"system-mail-servers",
|
||||||
|
"system-credentials",
|
||||||
"tenant-settings",
|
"tenant-settings",
|
||||||
"tenant-roles",
|
"tenant-roles",
|
||||||
"tenant-function-role-mappings",
|
"tenant-function-role-mappings",
|
||||||
@@ -50,11 +52,14 @@ const handledAdminSectionIds = new Set<string>([
|
|||||||
"tenant-users",
|
"tenant-users",
|
||||||
"tenant-file-connectors",
|
"tenant-file-connectors",
|
||||||
"tenant-mail-servers",
|
"tenant-mail-servers",
|
||||||
|
"tenant-credentials",
|
||||||
"tenant-api-keys",
|
"tenant-api-keys",
|
||||||
"tenant-group-file-connectors",
|
"tenant-group-file-connectors",
|
||||||
"tenant-group-mail-servers",
|
"tenant-group-mail-servers",
|
||||||
|
"tenant-group-credentials",
|
||||||
"tenant-user-file-connectors",
|
"tenant-user-file-connectors",
|
||||||
"tenant-user-mail-servers"
|
"tenant-user-mail-servers",
|
||||||
|
"tenant-user-credentials"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export default function AdminPage({
|
export default function AdminPage({
|
||||||
@@ -95,6 +100,9 @@ export default function AdminPage({
|
|||||||
if (hasScope(auth, "system:settings:read")) {
|
if (hasScope(auth, "system:settings:read")) {
|
||||||
if (mailProfilesAvailable) sections.add("system-mail-servers");
|
if (mailProfilesAvailable) sections.add("system-mail-servers");
|
||||||
}
|
}
|
||||||
|
if (hasAnyScope(auth, ["system:settings:read", "access:system_credential:read"])) {
|
||||||
|
sections.add("system-credentials");
|
||||||
|
}
|
||||||
if (hasScope(auth, "system:tenants:read")) sections.add("system-tenants");
|
if (hasScope(auth, "system:tenants:read")) sections.add("system-tenants");
|
||||||
if (hasAnyScope(auth, ["system:accounts:read", "system:access:read"])) sections.add("system-users");
|
if (hasAnyScope(auth, ["system:accounts:read", "system:access:read"])) sections.add("system-users");
|
||||||
if (hasAnyScope(auth, ["system:roles:read", "system:access:read"])) sections.add("system-roles");
|
if (hasAnyScope(auth, ["system:roles:read", "system:access:read"])) sections.add("system-roles");
|
||||||
@@ -113,6 +121,11 @@ export default function AdminPage({
|
|||||||
if (hasScope(auth, "admin:groups:read")) sections.add("tenant-group-file-connectors");
|
if (hasScope(auth, "admin:groups:read")) sections.add("tenant-group-file-connectors");
|
||||||
}
|
}
|
||||||
if (hasScope(auth, "admin:settings:read")) sections.add("tenant-settings");
|
if (hasScope(auth, "admin:settings:read")) sections.add("tenant-settings");
|
||||||
|
if (hasAnyScope(auth, ["admin:settings:read", "access:credential:read"])) {
|
||||||
|
sections.add("tenant-credentials");
|
||||||
|
if (hasScope(auth, "admin:users:read")) sections.add("tenant-user-credentials");
|
||||||
|
if (hasScope(auth, "admin:groups:read")) sections.add("tenant-group-credentials");
|
||||||
|
}
|
||||||
return sections;
|
return sections;
|
||||||
}, [auth, contributedSections, fileConnectorsAvailable, mailProfilesAvailable, organizationFunctionPicker]);
|
}, [auth, contributedSections, fileConnectorsAvailable, mailProfilesAvailable, organizationFunctionPicker]);
|
||||||
const [searchParams, setSearchParams] = useSearchParams();
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
@@ -170,6 +183,7 @@ export default function AdminPage({
|
|||||||
visibleNavItem(available, "system-users", "i18n:govoplan-access.users.57f2b181", 50),
|
visibleNavItem(available, "system-users", "i18n:govoplan-access.users.57f2b181", 50),
|
||||||
visibleNavItem(available, "system-file-connectors", "i18n:govoplan-access.file_connections.1e362326", 60),
|
visibleNavItem(available, "system-file-connectors", "i18n:govoplan-access.file_connections.1e362326", 60),
|
||||||
visibleNavItem(available, "system-mail-servers", "i18n:govoplan-access.mail_servers.d627326a", 70),
|
visibleNavItem(available, "system-mail-servers", "i18n:govoplan-access.mail_servers.d627326a", 70),
|
||||||
|
visibleNavItem(available, "system-credentials", "i18n:govoplan-core.credentials.dd097a22", 80),
|
||||||
...contributedNavItems(contributedSections, available, "GLOBAL", handledAdminSectionIds),
|
...contributedNavItems(contributedSections, available, "GLOBAL", handledAdminSectionIds),
|
||||||
...contributedNavItems(contributedSections, available, "SYSTEM", handledAdminSectionIds)
|
...contributedNavItems(contributedSections, available, "SYSTEM", handledAdminSectionIds)
|
||||||
])
|
])
|
||||||
@@ -185,7 +199,8 @@ export default function AdminPage({
|
|||||||
visibleNavItem(available, "tenant-users", "i18n:govoplan-access.users.57f2b181", 40),
|
visibleNavItem(available, "tenant-users", "i18n:govoplan-access.users.57f2b181", 40),
|
||||||
visibleNavItem(available, "tenant-file-connectors", "i18n:govoplan-access.file_connections.1e362326", 50),
|
visibleNavItem(available, "tenant-file-connectors", "i18n:govoplan-access.file_connections.1e362326", 50),
|
||||||
visibleNavItem(available, "tenant-mail-servers", "i18n:govoplan-access.mail_servers.d627326a", 60),
|
visibleNavItem(available, "tenant-mail-servers", "i18n:govoplan-access.mail_servers.d627326a", 60),
|
||||||
visibleNavItem(available, "tenant-api-keys", "i18n:govoplan-access.api_keys.94fcf3c2", 70),
|
visibleNavItem(available, "tenant-credentials", "i18n:govoplan-core.credentials.dd097a22", 70),
|
||||||
|
visibleNavItem(available, "tenant-api-keys", "i18n:govoplan-access.api_keys.94fcf3c2", 80),
|
||||||
visibleNavItem(available, "tenant-settings", "i18n:govoplan-access.general.9239ee2c", 90),
|
visibleNavItem(available, "tenant-settings", "i18n:govoplan-access.general.9239ee2c", 90),
|
||||||
...contributedNavItems(contributedSections, available, "TENANT", handledAdminSectionIds)
|
...contributedNavItems(contributedSections, available, "TENANT", handledAdminSectionIds)
|
||||||
])
|
])
|
||||||
@@ -197,6 +212,7 @@ export default function AdminPage({
|
|||||||
sortNavItems([
|
sortNavItems([
|
||||||
visibleNavItem(available, "tenant-group-file-connectors", "i18n:govoplan-access.file_connections.1e362326", 10),
|
visibleNavItem(available, "tenant-group-file-connectors", "i18n:govoplan-access.file_connections.1e362326", 10),
|
||||||
visibleNavItem(available, "tenant-group-mail-servers", "i18n:govoplan-access.mail_servers.d627326a", 20),
|
visibleNavItem(available, "tenant-group-mail-servers", "i18n:govoplan-access.mail_servers.d627326a", 20),
|
||||||
|
visibleNavItem(available, "tenant-group-credentials", "i18n:govoplan-core.credentials.dd097a22", 30),
|
||||||
...contributedNavItems(contributedSections, available, "GROUP", handledAdminSectionIds)
|
...contributedNavItems(contributedSections, available, "GROUP", handledAdminSectionIds)
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
@@ -207,6 +223,7 @@ export default function AdminPage({
|
|||||||
sortNavItems([
|
sortNavItems([
|
||||||
visibleNavItem(available, "tenant-user-file-connectors", "i18n:govoplan-access.file_connections.1e362326", 10),
|
visibleNavItem(available, "tenant-user-file-connectors", "i18n:govoplan-access.file_connections.1e362326", 10),
|
||||||
visibleNavItem(available, "tenant-user-mail-servers", "i18n:govoplan-access.mail_servers.d627326a", 20),
|
visibleNavItem(available, "tenant-user-mail-servers", "i18n:govoplan-access.mail_servers.d627326a", 20),
|
||||||
|
visibleNavItem(available, "tenant-user-credentials", "i18n:govoplan-core.credentials.dd097a22", 30),
|
||||||
...contributedNavItems(contributedSections, available, "USER", handledAdminSectionIds)
|
...contributedNavItems(contributedSections, available, "USER", handledAdminSectionIds)
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
@@ -224,6 +241,13 @@ export default function AdminPage({
|
|||||||
{!contributedSection && active === "system-mail-servers" && (
|
{!contributedSection && active === "system-mail-servers" && (
|
||||||
<MailProfilesPanel settings={settings} scopeType="system" canWriteProfiles={hasScope(auth, "system:settings:write")} canManageCredentials={hasScope(auth, "system:settings:write")} canWritePolicy={hasScope(auth, "system:settings:write")} />
|
<MailProfilesPanel settings={settings} scopeType="system" canWriteProfiles={hasScope(auth, "system:settings:write")} canManageCredentials={hasScope(auth, "system:settings:write")} canWritePolicy={hasScope(auth, "system:settings:write")} />
|
||||||
)}
|
)}
|
||||||
|
{!contributedSection && active === "system-credentials" && (
|
||||||
|
<CredentialEnvelopesPanel
|
||||||
|
settings={settings}
|
||||||
|
scopeType="system"
|
||||||
|
canWrite={hasAnyScope(auth, ["system:settings:write", "access:system_credential:write"])}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{!contributedSection && active === "system-tenants" && (
|
{!contributedSection && active === "system-tenants" && (
|
||||||
<TenantsPanel settings={settings} auth={auth} canCreate={hasScope(auth, "system:tenants:create")} canUpdate={hasScope(auth, "system:tenants:update")} canSuspend={hasScope(auth, "system:tenants:suspend")} onAuthRefresh={refreshAuth} />
|
<TenantsPanel settings={settings} auth={auth} canCreate={hasScope(auth, "system:tenants:create")} canUpdate={hasScope(auth, "system:tenants:update")} canSuspend={hasScope(auth, "system:tenants:suspend")} onAuthRefresh={refreshAuth} />
|
||||||
)}
|
)}
|
||||||
@@ -245,8 +269,11 @@ export default function AdminPage({
|
|||||||
{!contributedSection && active === "tenant-function-role-mappings" && organizationFunctionPicker && <ExternalFunctionRoleMappingsPanel settings={settings} auth={auth} functionPicker={organizationFunctionPicker} canWrite={hasAnyScope(auth, ["admin:roles:write", "access:function:write", "access:role:assign"])} onAuthRefresh={refreshAuth} />}
|
{!contributedSection && active === "tenant-function-role-mappings" && organizationFunctionPicker && <ExternalFunctionRoleMappingsPanel settings={settings} auth={auth} functionPicker={organizationFunctionPicker} canWrite={hasAnyScope(auth, ["admin:roles:write", "access:function:write", "access:role:assign"])} onAuthRefresh={refreshAuth} />}
|
||||||
{!contributedSection && active === "tenant-api-keys" && <ApiKeysPanel settings={settings} auth={auth} canCreate={hasScope(auth, "admin:api_keys:create")} canRevoke={hasScope(auth, "admin:api_keys:revoke")} />}
|
{!contributedSection && active === "tenant-api-keys" && <ApiKeysPanel settings={settings} auth={auth} canCreate={hasScope(auth, "admin:api_keys:create")} canRevoke={hasScope(auth, "admin:api_keys:revoke")} />}
|
||||||
{!contributedSection && active === "tenant-mail-servers" && <MailProfilesPanel settings={settings} scopeType="tenant" canWriteProfiles={hasScope(auth, "mail_servers:write")} canManageCredentials={hasScope(auth, "mail_servers:manage_credentials")} canWritePolicy={hasScope(auth, "admin:policies:write")} />}
|
{!contributedSection && active === "tenant-mail-servers" && <MailProfilesPanel settings={settings} scopeType="tenant" canWriteProfiles={hasScope(auth, "mail_servers:write")} canManageCredentials={hasScope(auth, "mail_servers:manage_credentials")} canWritePolicy={hasScope(auth, "admin:policies:write")} />}
|
||||||
|
{!contributedSection && active === "tenant-credentials" && <CredentialEnvelopesPanel settings={settings} scopeType="tenant" canWrite={hasAnyScope(auth, ["admin:settings:write", "access:credential:write"])} />}
|
||||||
{!contributedSection && active === "tenant-user-mail-servers" && <MailProfilesPanel settings={settings} scopeType="user" canWriteProfiles={hasScope(auth, "mail_servers:write")} canManageCredentials={hasScope(auth, "mail_servers:manage_credentials")} canWritePolicy={hasAnyScope(auth, ["admin:policies:write", "mail_servers:write"])} />}
|
{!contributedSection && active === "tenant-user-mail-servers" && <MailProfilesPanel settings={settings} scopeType="user" canWriteProfiles={hasScope(auth, "mail_servers:write")} canManageCredentials={hasScope(auth, "mail_servers:manage_credentials")} canWritePolicy={hasAnyScope(auth, ["admin:policies:write", "mail_servers:write"])} />}
|
||||||
{!contributedSection && active === "tenant-group-mail-servers" && <MailProfilesPanel settings={settings} scopeType="group" canWriteProfiles={hasScope(auth, "mail_servers:write")} canManageCredentials={hasScope(auth, "mail_servers:manage_credentials")} canWritePolicy={hasAnyScope(auth, ["admin:policies:write", "mail_servers:write"])} />}
|
{!contributedSection && active === "tenant-group-mail-servers" && <MailProfilesPanel settings={settings} scopeType="group" canWriteProfiles={hasScope(auth, "mail_servers:write")} canManageCredentials={hasScope(auth, "mail_servers:manage_credentials")} canWritePolicy={hasAnyScope(auth, ["admin:policies:write", "mail_servers:write"])} />}
|
||||||
|
{!contributedSection && active === "tenant-user-credentials" && <CredentialEnvelopesPanel settings={settings} scopeType="user" canWrite={hasAnyScope(auth, ["admin:settings:write", "access:credential:write"])} />}
|
||||||
|
{!contributedSection && active === "tenant-group-credentials" && <CredentialEnvelopesPanel settings={settings} scopeType="group" canWrite={hasAnyScope(auth, ["admin:settings:write", "access:credential:write"])} />}
|
||||||
{!contributedSection && active === "tenant-user-file-connectors" && <FileConnectorsPanel settings={settings} scopeType="user" canWrite={hasAnyScope(auth, ["files:file:admin", "admin:settings:write"])} />}
|
{!contributedSection && active === "tenant-user-file-connectors" && <FileConnectorsPanel settings={settings} scopeType="user" canWrite={hasAnyScope(auth, ["files:file:admin", "admin:settings:write"])} />}
|
||||||
{!contributedSection && active === "tenant-group-file-connectors" && <FileConnectorsPanel settings={settings} scopeType="group" canWrite={hasAnyScope(auth, ["files:file:admin", "admin:settings:write"])} />}
|
{!contributedSection && active === "tenant-group-file-connectors" && <FileConnectorsPanel settings={settings} scopeType="group" canWrite={hasAnyScope(auth, ["files:file:admin", "admin:settings:write"])} />}
|
||||||
{!contributedSection && active === "tenant-settings" && <TenantSettingsPanel settings={settings} canWrite={hasScope(auth, "admin:settings:write")} onAuthRefresh={refreshAuth} />}
|
{!contributedSection && active === "tenant-settings" && <TenantSettingsPanel settings={settings} canWrite={hasScope(auth, "admin:settings:write")} onAuthRefresh={refreshAuth} />}
|
||||||
|
|||||||
147
webui/src/features/admin/CredentialEnvelopesPanel.tsx
Normal file
147
webui/src/features/admin/CredentialEnvelopesPanel.tsx
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import {
|
||||||
|
AdminPageLayout,
|
||||||
|
CredentialEnvelopeManager,
|
||||||
|
adminErrorMessage,
|
||||||
|
useDeltaWatermarks,
|
||||||
|
type ApiSettings,
|
||||||
|
type CredentialEnvelopeTargetOption,
|
||||||
|
type MailProfileScope
|
||||||
|
} from "@govoplan/core-webui";
|
||||||
|
import {
|
||||||
|
fetchGroupsDelta,
|
||||||
|
fetchUsersDelta,
|
||||||
|
type GroupSummary,
|
||||||
|
type UserAdminItem
|
||||||
|
} from "../../api/admin";
|
||||||
|
import { loadDeltaRows } from "./utils/deltaRows";
|
||||||
|
|
||||||
|
type ScopeType = Extract<MailProfileScope, "system" | "tenant" | "user" | "group">;
|
||||||
|
|
||||||
|
export default function CredentialEnvelopesPanel({
|
||||||
|
settings,
|
||||||
|
scopeType,
|
||||||
|
canWrite
|
||||||
|
}: {
|
||||||
|
settings: ApiSettings;
|
||||||
|
scopeType: ScopeType;
|
||||||
|
canWrite: boolean;
|
||||||
|
}) {
|
||||||
|
const [targets, setTargets] = useState<CredentialEnvelopeTargetOption[]>([]);
|
||||||
|
const [loadingTargets, setLoadingTargets] = useState(
|
||||||
|
scopeType === "user" || scopeType === "group"
|
||||||
|
);
|
||||||
|
const [targetError, setTargetError] = useState("");
|
||||||
|
const usersRef = useRef<UserAdminItem[]>([]);
|
||||||
|
const groupsRef = useRef<GroupSummary[]>([]);
|
||||||
|
const { getDeltaWatermark, setDeltaWatermark, resetDeltaWatermark } =
|
||||||
|
useDeltaWatermarks();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
usersRef.current = [];
|
||||||
|
groupsRef.current = [];
|
||||||
|
resetDeltaWatermark();
|
||||||
|
void loadTargets();
|
||||||
|
}, [
|
||||||
|
resetDeltaWatermark,
|
||||||
|
scopeType,
|
||||||
|
settings.accessToken,
|
||||||
|
settings.apiBaseUrl,
|
||||||
|
settings.apiKey
|
||||||
|
]);
|
||||||
|
|
||||||
|
async function loadTargets() {
|
||||||
|
if (scopeType !== "user" && scopeType !== "group") {
|
||||||
|
setTargets([]);
|
||||||
|
setLoadingTargets(false);
|
||||||
|
setTargetError("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setLoadingTargets(true);
|
||||||
|
setTargetError("");
|
||||||
|
try {
|
||||||
|
if (scopeType === "user") {
|
||||||
|
const users = await loadDeltaRows(
|
||||||
|
usersRef.current,
|
||||||
|
"access:credential-users",
|
||||||
|
getDeltaWatermark,
|
||||||
|
setDeltaWatermark,
|
||||||
|
(since) => fetchUsersDelta(settings, { since }),
|
||||||
|
(response) => response.users,
|
||||||
|
(user) => user.id,
|
||||||
|
"access_user",
|
||||||
|
(left, right) => left.email.localeCompare(right.email)
|
||||||
|
);
|
||||||
|
usersRef.current = users;
|
||||||
|
setTargets(
|
||||||
|
users.map((user) => ({
|
||||||
|
id: user.id,
|
||||||
|
label: user.display_name || user.email,
|
||||||
|
secondary: user.display_name ? user.email : null
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const groups = await loadDeltaRows(
|
||||||
|
groupsRef.current,
|
||||||
|
"access:credential-groups",
|
||||||
|
getDeltaWatermark,
|
||||||
|
setDeltaWatermark,
|
||||||
|
(since) => fetchGroupsDelta(settings, { since }),
|
||||||
|
(response) => response.groups,
|
||||||
|
(group) => group.id,
|
||||||
|
"access_group",
|
||||||
|
(left, right) =>
|
||||||
|
left.name.localeCompare(right.name) || left.slug.localeCompare(right.slug)
|
||||||
|
);
|
||||||
|
groupsRef.current = groups;
|
||||||
|
setTargets(
|
||||||
|
groups.map((group) => ({
|
||||||
|
id: group.id,
|
||||||
|
label: group.name,
|
||||||
|
secondary: group.slug
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
setTargets([]);
|
||||||
|
setTargetError(adminErrorMessage(err));
|
||||||
|
} finally {
|
||||||
|
setLoadingTargets(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AdminPageLayout
|
||||||
|
title={scopeTitle(scopeType)}
|
||||||
|
description={scopeDescription(scopeType)}
|
||||||
|
loading={loadingTargets}
|
||||||
|
error={targetError}
|
||||||
|
>
|
||||||
|
<CredentialEnvelopeManager
|
||||||
|
settings={settings}
|
||||||
|
scopeType={scopeType}
|
||||||
|
targetOptions={targets}
|
||||||
|
targetLabel={scopeType === "group" ? "Group" : "User"}
|
||||||
|
title="Reusable credentials"
|
||||||
|
canWrite={canWrite}
|
||||||
|
/>
|
||||||
|
</AdminPageLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function scopeTitle(scopeType: ScopeType): string {
|
||||||
|
if (scopeType === "system") return "System credentials";
|
||||||
|
if (scopeType === "tenant") return "Tenant credentials";
|
||||||
|
if (scopeType === "group") return "Group credentials";
|
||||||
|
return "User credentials";
|
||||||
|
}
|
||||||
|
|
||||||
|
function scopeDescription(scopeType: ScopeType): string {
|
||||||
|
if (scopeType === "system") {
|
||||||
|
return "Instance credentials that can be inherited by tenants and limited to selected modules or servers.";
|
||||||
|
}
|
||||||
|
if (scopeType === "tenant") {
|
||||||
|
return "Tenant credentials shared by Mail, Files, Calendar, Addresses, and other permitted modules.";
|
||||||
|
}
|
||||||
|
return `Reusable credentials owned by the selected ${scopeType}.`;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user