Expose credential envelope administration
This commit is contained in:
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