keycloak-scim/src/realm-settings/keys/KeysListTab.tsx

246 lines
7.3 KiB
TypeScript
Raw Normal View History

import React, { useState } from "react";
import { useHistory } from "react-router-dom";
import { useTranslation } from "react-i18next";
import {
Button,
ButtonVariant,
PageSection,
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core";
import { cellWidth } from "@patternfly/react-table";
import { FilterIcon } from "@patternfly/react-icons";
import type { KeyMetadataRepresentation } from "@keycloak/keycloak-admin-client/lib/defs/keyMetadataRepresentation";
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
import { ListEmptyState } from "../../components/list-empty-state/ListEmptyState";
import { KeycloakDataTable } from "../../components/table-toolbar/KeycloakDataTable";
import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog";
import { emptyFormatter } from "../../util";
2022-03-28 20:57:32 +00:00
import { useAdminClient, useFetch } from "../../context/auth/AdminClient";
import { toKeysTab } from "../routes/KeysTab";
2022-03-28 20:57:32 +00:00
import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner";
import { useRealm } from "../../context/realm-context/RealmContext";
import useToggle from "../../utils/useToggle";
import "../realm-settings-section.css";
const FILTER_OPTIONS = ["ACTIVE", "PASSIVE", "DISABLED"] as const;
2022-03-28 20:57:32 +00:00
type FilterType = typeof FILTER_OPTIONS[number];
type KeyData = KeyMetadataRepresentation & {
provider?: string;
};
type KeysListTabProps = {
realmComponents: ComponentRepresentation[];
};
2022-03-28 20:57:32 +00:00
type SelectFilterProps = {
onFilter: (filter: FilterType) => void;
};
const SelectFilter = ({ onFilter }: SelectFilterProps) => {
const { t } = useTranslation("realm-settings");
const [filterType, setFilterType] = useState<FilterType>(FILTER_OPTIONS[0]);
const [filterDropdownOpen, toggleFilter] = useToggle();
return (
<Select
width={300}
data-testid="filter-type-select"
isOpen={filterDropdownOpen}
className="kc-filter-type-select"
variant={SelectVariant.single}
onToggle={toggleFilter}
toggleIcon={<FilterIcon />}
onSelect={(_, value) => {
const filter =
FILTER_OPTIONS.find((o) => o === value.toString()) ||
FILTER_OPTIONS[0];
setFilterType(filter);
onFilter(filter);
toggleFilter();
}}
selections={filterType}
>
{FILTER_OPTIONS.map((option) => (
<SelectOption
key={option}
data-testid={`${option}-option`}
value={option}
>
{t(`keysFilter.${option}`)}
</SelectOption>
))}
</Select>
);
};
export const KeysListTab = ({ realmComponents }: KeysListTabProps) => {
const { t } = useTranslation("realm-settings");
const history = useHistory();
const [publicKey, setPublicKey] = useState("");
const [certificate, setCertificate] = useState("");
const adminClient = useAdminClient();
2022-03-28 20:57:32 +00:00
const { realm } = useRealm();
const [keyData, setKeyData] = useState<KeyData[]>();
const [filteredKeyData, setFilteredKeyData] = useState<KeyData[]>();
useFetch(
async () => {
const keysMetaData = await adminClient.realms.getKeys({ realm });
return keysMetaData.keys?.map((key) => {
const provider = realmComponents.find(
(component: ComponentRepresentation) =>
component.id === key.providerId
);
return { ...key, provider: provider?.name } as KeyData;
})!;
},
setKeyData,
[]
);
const [togglePublicKeyDialog, PublicKeyDialog] = useConfirmDialog({
titleKey: t("publicKeys").slice(0, -1),
messageKey: publicKey,
continueButtonLabel: "common:close",
continueButtonVariant: ButtonVariant.primary,
onConfirm: () => Promise.resolve(),
});
const [toggleCertificateDialog, CertificateDialog] = useConfirmDialog({
titleKey: t("certificate"),
messageKey: certificate,
continueButtonLabel: "common:close",
continueButtonVariant: ButtonVariant.primary,
onConfirm: () => Promise.resolve(),
});
2021-08-26 12:15:28 +00:00
const ProviderRenderer = ({ provider }: KeyData) => provider;
const ButtonRenderer = ({ type, publicKey, certificate }: KeyData) => {
if (type === "EC") {
2021-04-30 18:48:57 +00:00
return (
2021-08-26 12:15:28 +00:00
<Button
onClick={() => {
togglePublicKeyDialog();
setPublicKey(publicKey!);
}}
variant="secondary"
id="kc-public-key"
>
{t("publicKeys").slice(0, -1)}
2021-08-26 12:15:28 +00:00
</Button>
);
} else if (type === "RSA") {
return (
<div className="button-wrapper">
2021-04-30 18:48:57 +00:00
<Button
onClick={() => {
togglePublicKeyDialog();
setPublicKey(publicKey!);
}}
variant="secondary"
id={publicKey}
2021-04-30 18:48:57 +00:00
>
{t("publicKeys").slice(0, -1)}
2021-04-30 18:48:57 +00:00
</Button>
2021-08-26 12:15:28 +00:00
<Button
onClick={() => {
toggleCertificateDialog();
setCertificate(certificate!);
}}
variant="secondary"
id={certificate}
className="kc-certificate"
2021-08-26 12:15:28 +00:00
>
{t("certificate")}
2021-08-26 12:15:28 +00:00
</Button>
</div>
);
}
};
2022-03-28 20:57:32 +00:00
if (!keyData) {
return <KeycloakSpinner />;
}
return (
2021-08-26 12:15:28 +00:00
<PageSection variant="light" padding={{ default: "noPadding" }}>
<PublicKeyDialog />
<CertificateDialog />
<KeycloakDataTable
2022-03-28 20:57:32 +00:00
isNotCompact
className="kc-keys-list"
2022-03-28 20:57:32 +00:00
loader={filteredKeyData || keyData}
ariaLabelKey="realm-settings:keysList"
2021-08-26 12:15:28 +00:00
searchPlaceholderKey="realm-settings:searchKey"
searchTypeComponent={
2022-03-28 20:57:32 +00:00
<SelectFilter
onFilter={(filterType) =>
setFilteredKeyData(
filterType !== FILTER_OPTIONS[0]
? keyData!.filter(({ status }) => status === filterType)
: undefined
)
}
/>
2021-08-26 12:15:28 +00:00
}
canSelectAll
columns={[
{
name: "algorithm",
2022-03-28 20:57:32 +00:00
displayKey: "realm-settings:algorithm",
2021-08-26 12:15:28 +00:00
cellFormatters: [emptyFormatter()],
transforms: [cellWidth(15)],
},
{
name: "type",
displayKey: "type",
2021-08-26 12:15:28 +00:00
cellFormatters: [emptyFormatter()],
transforms: [cellWidth(10)],
},
{
name: "kid",
2022-03-28 20:57:32 +00:00
displayKey: "realm-settings:kid",
2021-08-26 12:15:28 +00:00
cellFormatters: [emptyFormatter()],
transforms: [cellWidth(10)],
},
{
name: "provider",
2022-03-28 20:57:32 +00:00
displayKey: "realm-settings:provider",
2021-08-26 12:15:28 +00:00
cellRenderer: ProviderRenderer,
cellFormatters: [emptyFormatter()],
transforms: [cellWidth(10)],
},
{
name: "publicKeys",
2022-03-28 20:57:32 +00:00
displayKey: "realm-settings:publicKeys",
2021-08-26 12:15:28 +00:00
cellRenderer: ButtonRenderer,
cellFormatters: [],
transforms: [cellWidth(20)],
},
]}
2022-03-28 20:57:32 +00:00
isSearching={!!filteredKeyData}
2021-08-26 12:15:28 +00:00
emptyState={
<ListEmptyState
hasIcon
message={t("noKeys")}
instructions={t("noKeysDescription")}
primaryActionText={t("addProvider")}
onPrimaryAction={() =>
2022-03-28 20:57:32 +00:00
history.push(toKeysTab({ realm, tab: "providers" }))
2021-08-26 12:15:28 +00:00
}
/>
}
/>
</PageSection>
);
};