2021-03-19 12:43:32 +00:00
|
|
|
import React, { useState } from "react";
|
2020-11-24 20:11:13 +00:00
|
|
|
import { useTranslation } from "react-i18next";
|
|
|
|
import {
|
2020-12-04 21:08:11 +00:00
|
|
|
AlertVariant,
|
2020-11-24 20:11:13 +00:00
|
|
|
Button,
|
|
|
|
Dropdown,
|
|
|
|
DropdownItem,
|
2020-12-07 13:42:08 +00:00
|
|
|
KebabToggle,
|
|
|
|
ToolbarItem,
|
2020-11-24 20:11:13 +00:00
|
|
|
} from "@patternfly/react-core";
|
2021-08-26 08:39:35 +00:00
|
|
|
import type ClientScopeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientScopeRepresentation";
|
2020-11-24 20:11:13 +00:00
|
|
|
|
2021-03-19 12:43:32 +00:00
|
|
|
import { useAdminClient } from "../../context/auth/AdminClient";
|
2020-11-24 20:11:13 +00:00
|
|
|
import { ListEmptyState } from "../../components/list-empty-state/ListEmptyState";
|
|
|
|
import { AddScopeDialog } from "./AddScopeDialog";
|
|
|
|
import {
|
|
|
|
ClientScope,
|
2021-04-06 07:29:11 +00:00
|
|
|
CellDropdown,
|
2021-09-14 15:48:48 +00:00
|
|
|
AllClientScopes,
|
|
|
|
AllClientScopeType,
|
|
|
|
changeClientScope,
|
|
|
|
addClientScope,
|
|
|
|
removeClientScope,
|
2021-04-06 07:29:11 +00:00
|
|
|
} from "../../components/client-scope/ClientScopeTypes";
|
2020-12-04 21:08:11 +00:00
|
|
|
import { useAlerts } from "../../components/alert/Alerts";
|
2021-03-19 12:43:32 +00:00
|
|
|
import { KeycloakDataTable } from "../../components/table-toolbar/KeycloakDataTable";
|
2021-09-14 15:48:48 +00:00
|
|
|
import {
|
|
|
|
nameFilter,
|
|
|
|
SearchDropdown,
|
|
|
|
SearchToolbar,
|
|
|
|
SearchType,
|
|
|
|
typeFilter,
|
|
|
|
} from "../../client-scopes/details/SearchFilter";
|
2020-11-24 20:11:13 +00:00
|
|
|
|
2021-04-07 05:42:30 +00:00
|
|
|
import "./client-scopes.css";
|
2021-09-14 15:48:48 +00:00
|
|
|
import { ChangeTypeDropdown } from "../../client-scopes/ChangeTypeDropdown";
|
2021-04-07 05:42:30 +00:00
|
|
|
|
2020-11-24 20:11:13 +00:00
|
|
|
export type ClientScopesProps = {
|
|
|
|
clientId: string;
|
|
|
|
protocol: string;
|
2021-11-23 14:59:04 +00:00
|
|
|
clientName: string;
|
2020-11-24 20:11:13 +00:00
|
|
|
};
|
|
|
|
|
2021-09-14 15:48:48 +00:00
|
|
|
export type Row = ClientScopeRepresentation & {
|
|
|
|
type: AllClientScopeType;
|
|
|
|
description?: string;
|
2020-11-24 20:11:13 +00:00
|
|
|
};
|
|
|
|
|
2021-11-23 14:59:04 +00:00
|
|
|
export const ClientScopes = ({
|
|
|
|
clientId,
|
|
|
|
protocol,
|
|
|
|
clientName,
|
|
|
|
}: ClientScopesProps) => {
|
2020-11-24 20:11:13 +00:00
|
|
|
const { t } = useTranslation("clients");
|
|
|
|
const adminClient = useAdminClient();
|
2021-07-28 12:01:42 +00:00
|
|
|
const { addAlert, addError } = useAlerts();
|
2020-12-04 21:08:11 +00:00
|
|
|
|
2021-09-14 15:48:48 +00:00
|
|
|
const [searchType, setSearchType] = useState<SearchType>("name");
|
|
|
|
|
|
|
|
const [searchTypeType, setSearchTypeType] = useState<AllClientScopes>(
|
|
|
|
AllClientScopes.none
|
|
|
|
);
|
|
|
|
|
2020-11-24 20:11:13 +00:00
|
|
|
const [addDialogOpen, setAddDialogOpen] = useState(false);
|
2020-12-07 13:42:08 +00:00
|
|
|
const [kebabOpen, setKebabOpen] = useState(false);
|
2020-11-24 20:11:13 +00:00
|
|
|
|
|
|
|
const [rest, setRest] = useState<ClientScopeRepresentation[]>();
|
2021-03-19 12:43:32 +00:00
|
|
|
const [selectedRows, setSelectedRows] = useState<Row[]>([]);
|
2020-11-24 20:11:13 +00:00
|
|
|
|
2021-01-05 13:39:27 +00:00
|
|
|
const [key, setKey] = useState(0);
|
|
|
|
const refresh = () => setKey(new Date().getTime());
|
2020-11-24 20:11:13 +00:00
|
|
|
|
2021-09-14 15:48:48 +00:00
|
|
|
const loader = async (first?: number, max?: number, search?: string) => {
|
2021-07-05 11:24:10 +00:00
|
|
|
const defaultClientScopes =
|
|
|
|
await adminClient.clients.listDefaultClientScopes({ id: clientId });
|
|
|
|
const optionalClientScopes =
|
|
|
|
await adminClient.clients.listOptionalClientScopes({ id: clientId });
|
2021-03-19 12:43:32 +00:00
|
|
|
const clientScopes = await adminClient.clientScopes.find();
|
2020-11-24 20:11:13 +00:00
|
|
|
|
2021-03-19 12:43:32 +00:00
|
|
|
const find = (id: string) =>
|
|
|
|
clientScopes.find((clientScope) => id === clientScope.id)!;
|
2020-11-24 20:11:13 +00:00
|
|
|
|
2021-03-19 12:43:32 +00:00
|
|
|
const optional = optionalClientScopes.map((c) => {
|
|
|
|
const scope = find(c.id!);
|
2021-09-14 15:48:48 +00:00
|
|
|
const row: Row = {
|
2021-03-19 12:43:32 +00:00
|
|
|
...c,
|
|
|
|
type: ClientScope.optional,
|
|
|
|
description: scope.description,
|
2021-09-14 15:48:48 +00:00
|
|
|
};
|
|
|
|
return row;
|
2021-03-19 12:43:32 +00:00
|
|
|
});
|
2020-11-24 20:11:13 +00:00
|
|
|
|
2021-03-19 12:43:32 +00:00
|
|
|
const defaultScopes = defaultClientScopes.map((c) => {
|
|
|
|
const scope = find(c.id!);
|
2021-09-14 15:48:48 +00:00
|
|
|
const row: Row = {
|
2021-03-19 12:43:32 +00:00
|
|
|
...c,
|
|
|
|
type: ClientScope.default,
|
|
|
|
description: scope.description,
|
2021-09-14 15:48:48 +00:00
|
|
|
};
|
|
|
|
return row;
|
2021-03-19 12:43:32 +00:00
|
|
|
});
|
2020-11-24 20:11:13 +00:00
|
|
|
|
2021-03-19 12:43:32 +00:00
|
|
|
const rows = [...optional, ...defaultScopes];
|
|
|
|
const names = rows.map((row) => row.name);
|
|
|
|
setRest(
|
|
|
|
clientScopes
|
|
|
|
.filter((scope) => !names.includes(scope.name))
|
|
|
|
.filter((scope) => scope.protocol === protocol)
|
2021-01-05 13:39:27 +00:00
|
|
|
);
|
2020-12-04 21:08:11 +00:00
|
|
|
|
2021-09-14 15:48:48 +00:00
|
|
|
const filter =
|
|
|
|
searchType === "name" ? nameFilter(search) : typeFilter(searchTypeType);
|
|
|
|
return rows.filter(filter).slice(first, max);
|
2021-03-19 12:43:32 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const TypeSelector = (scope: Row) => (
|
2021-08-26 12:15:28 +00:00
|
|
|
<CellDropdown
|
|
|
|
clientScope={scope}
|
|
|
|
type={scope.type}
|
|
|
|
onSelect={async (value) => {
|
|
|
|
try {
|
2021-09-14 15:48:48 +00:00
|
|
|
await changeClientScope(
|
2021-08-26 12:15:28 +00:00
|
|
|
adminClient,
|
|
|
|
clientId,
|
|
|
|
scope,
|
|
|
|
scope.type,
|
|
|
|
value as ClientScope
|
|
|
|
);
|
|
|
|
addAlert(t("clientScopeSuccess"), AlertVariant.success);
|
|
|
|
refresh();
|
|
|
|
} catch (error) {
|
|
|
|
addError("clients:clientScopeError", error);
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
/>
|
2021-03-19 12:43:32 +00:00
|
|
|
);
|
2020-11-24 20:11:13 +00:00
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
2020-12-04 21:08:11 +00:00
|
|
|
{rest && (
|
|
|
|
<AddScopeDialog
|
|
|
|
clientScopes={rest}
|
2021-11-23 14:59:04 +00:00
|
|
|
clientName={clientName!}
|
2020-12-04 21:08:11 +00:00
|
|
|
open={addDialogOpen}
|
|
|
|
toggleDialog={() => setAddDialogOpen(!addDialogOpen)}
|
|
|
|
onAdd={async (scopes) => {
|
|
|
|
try {
|
|
|
|
await Promise.all(
|
|
|
|
scopes.map(
|
|
|
|
async (scope) =>
|
2021-09-14 15:48:48 +00:00
|
|
|
await addClientScope(
|
2020-12-04 21:08:11 +00:00
|
|
|
adminClient,
|
|
|
|
clientId,
|
|
|
|
scope.scope,
|
2021-11-23 14:59:04 +00:00
|
|
|
scope.type!
|
2020-12-04 21:08:11 +00:00
|
|
|
)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
addAlert(t("clientScopeSuccess"), AlertVariant.success);
|
2021-01-05 13:39:27 +00:00
|
|
|
refresh();
|
2020-12-04 21:08:11 +00:00
|
|
|
} catch (error) {
|
2021-07-28 12:01:42 +00:00
|
|
|
addError("clients:clientScopeError", error);
|
2020-12-04 21:08:11 +00:00
|
|
|
}
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
|
2021-03-19 12:43:32 +00:00
|
|
|
<KeycloakDataTable
|
|
|
|
key={key}
|
|
|
|
loader={loader}
|
|
|
|
ariaLabelKey="clients:clientScopeList"
|
2021-09-14 15:48:48 +00:00
|
|
|
searchPlaceholderKey={
|
|
|
|
searchType === "name" ? "clients:searchByName" : undefined
|
|
|
|
}
|
2021-04-07 05:42:30 +00:00
|
|
|
canSelectAll
|
2021-09-14 15:48:48 +00:00
|
|
|
isPaginated
|
|
|
|
isSearching={searchType === "type"}
|
2021-03-19 12:43:32 +00:00
|
|
|
onSelect={(rows) => setSelectedRows([...rows])}
|
|
|
|
searchTypeComponent={
|
2021-09-14 15:48:48 +00:00
|
|
|
<SearchDropdown
|
|
|
|
searchType={searchType}
|
|
|
|
onSelect={(searchType) => setSearchType(searchType)}
|
2021-03-19 12:43:32 +00:00
|
|
|
/>
|
|
|
|
}
|
|
|
|
toolbarItem={
|
|
|
|
<>
|
2021-09-14 15:48:48 +00:00
|
|
|
<SearchToolbar
|
|
|
|
searchType={searchType}
|
|
|
|
type={searchTypeType}
|
|
|
|
onSelect={(searchType) => setSearchType(searchType)}
|
|
|
|
onType={(value) => {
|
|
|
|
setSearchTypeType(value);
|
|
|
|
refresh();
|
|
|
|
}}
|
|
|
|
/>
|
2021-03-19 12:43:32 +00:00
|
|
|
<ToolbarItem>
|
|
|
|
<Button onClick={() => setAddDialogOpen(true)}>
|
|
|
|
{t("addClientScope")}
|
|
|
|
</Button>
|
|
|
|
</ToolbarItem>
|
|
|
|
<ToolbarItem>
|
2021-09-14 15:48:48 +00:00
|
|
|
<ChangeTypeDropdown
|
|
|
|
clientId={clientId}
|
|
|
|
selectedRows={selectedRows}
|
|
|
|
refresh={refresh}
|
|
|
|
/>
|
2021-03-19 12:43:32 +00:00
|
|
|
</ToolbarItem>
|
|
|
|
<ToolbarItem>
|
|
|
|
<Dropdown
|
|
|
|
toggle={
|
|
|
|
<KebabToggle onToggle={() => setKebabOpen(!kebabOpen)} />
|
|
|
|
}
|
|
|
|
isOpen={kebabOpen}
|
|
|
|
isPlain
|
|
|
|
dropdownItems={[
|
|
|
|
<DropdownItem
|
|
|
|
key="deleteAll"
|
|
|
|
isDisabled={selectedRows.length === 0}
|
|
|
|
onClick={async () => {
|
|
|
|
try {
|
|
|
|
await Promise.all(
|
|
|
|
selectedRows.map(async (row) => {
|
2021-09-14 15:48:48 +00:00
|
|
|
await removeClientScope(
|
2021-03-19 12:43:32 +00:00
|
|
|
adminClient,
|
|
|
|
clientId,
|
|
|
|
{ ...row },
|
2021-09-14 15:48:48 +00:00
|
|
|
row.type as ClientScope
|
2021-03-19 12:43:32 +00:00
|
|
|
);
|
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
setKebabOpen(false);
|
|
|
|
addAlert(
|
|
|
|
t("clientScopeRemoveSuccess"),
|
|
|
|
AlertVariant.success
|
|
|
|
);
|
|
|
|
refresh();
|
|
|
|
} catch (error) {
|
2021-07-28 12:01:42 +00:00
|
|
|
addError("clients:clientScopeRemoveError", error);
|
2021-03-19 12:43:32 +00:00
|
|
|
}
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
{t("common:remove")}
|
|
|
|
</DropdownItem>,
|
|
|
|
]}
|
|
|
|
/>
|
|
|
|
</ToolbarItem>
|
|
|
|
</>
|
|
|
|
}
|
|
|
|
columns={[
|
|
|
|
{
|
|
|
|
name: "name",
|
2021-04-07 05:42:30 +00:00
|
|
|
displayKey: "clients:assignedClientScope",
|
2021-03-19 12:43:32 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "type",
|
|
|
|
displayKey: "clients:assignedType",
|
|
|
|
cellRenderer: TypeSelector,
|
|
|
|
},
|
|
|
|
{ name: "description" },
|
|
|
|
]}
|
2021-04-07 05:42:30 +00:00
|
|
|
actions={[
|
|
|
|
{
|
|
|
|
title: t("common:remove"),
|
|
|
|
onRowClick: async (row) => {
|
|
|
|
try {
|
2021-09-14 15:48:48 +00:00
|
|
|
await removeClientScope(
|
|
|
|
adminClient,
|
|
|
|
clientId,
|
|
|
|
row,
|
|
|
|
row.type as ClientScope
|
|
|
|
);
|
2021-04-07 05:42:30 +00:00
|
|
|
addAlert(t("clientScopeRemoveSuccess"), AlertVariant.success);
|
|
|
|
refresh();
|
|
|
|
} catch (error) {
|
2021-07-28 12:01:42 +00:00
|
|
|
addError("clients:clientScopeRemoveError", error);
|
2021-04-07 05:42:30 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
]}
|
2021-03-19 12:43:32 +00:00
|
|
|
emptyState={
|
|
|
|
<ListEmptyState
|
|
|
|
message={t("clients:emptyClientScopes")}
|
|
|
|
instructions={t("clients:emptyClientScopesInstructions")}
|
|
|
|
primaryActionText={t("clients:emptyClientScopesPrimaryAction")}
|
|
|
|
onPrimaryAction={() => setAddDialogOpen(true)}
|
|
|
|
/>
|
|
|
|
}
|
|
|
|
/>
|
2020-11-24 20:11:13 +00:00
|
|
|
</>
|
|
|
|
);
|
|
|
|
};
|