2022-08-03 12:12:07 +00:00
|
|
|
import { useState } from "react";
|
2022-01-21 14:10:36 +00:00
|
|
|
import { useTranslation } from "react-i18next";
|
|
|
|
import { useFormContext, Controller } from "react-hook-form";
|
|
|
|
import { FormGroup, Button, Checkbox } from "@patternfly/react-core";
|
|
|
|
import { MinusCircleIcon } from "@patternfly/react-icons";
|
|
|
|
import {
|
|
|
|
TableComposable,
|
|
|
|
Tbody,
|
|
|
|
Td,
|
|
|
|
Th,
|
|
|
|
Thead,
|
|
|
|
Tr,
|
|
|
|
} from "@patternfly/react-table";
|
|
|
|
|
|
|
|
import type ClientScopeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientScopeRepresentation";
|
|
|
|
import { useAdminClient, useFetch } from "../../../context/auth/AdminClient";
|
|
|
|
import { HelpItem } from "../../../components/help-enabler/HelpItem";
|
|
|
|
import { AddScopeDialog } from "../../scopes/AddScopeDialog";
|
2022-05-09 10:44:07 +00:00
|
|
|
import useLocaleSort, { mapByKey } from "../../../utils/useLocaleSort";
|
2022-01-21 14:10:36 +00:00
|
|
|
|
|
|
|
export type RequiredIdValue = {
|
|
|
|
id: string;
|
|
|
|
required: boolean;
|
|
|
|
};
|
|
|
|
|
|
|
|
export const ClientScope = () => {
|
|
|
|
const { t } = useTranslation("clients");
|
2022-04-08 12:37:31 +00:00
|
|
|
const {
|
|
|
|
control,
|
|
|
|
getValues,
|
|
|
|
setValue,
|
|
|
|
formState: { errors },
|
|
|
|
} = useFormContext<{
|
2022-03-28 10:55:21 +00:00
|
|
|
clientScopes: RequiredIdValue[];
|
|
|
|
}>();
|
2022-01-21 14:10:36 +00:00
|
|
|
|
|
|
|
const [open, setOpen] = useState(false);
|
|
|
|
const [scopes, setScopes] = useState<ClientScopeRepresentation[]>([]);
|
|
|
|
const [selectedScopes, setSelectedScopes] = useState<
|
|
|
|
ClientScopeRepresentation[]
|
|
|
|
>([]);
|
|
|
|
|
2022-07-14 13:02:28 +00:00
|
|
|
const { adminClient } = useAdminClient();
|
2022-05-09 10:44:07 +00:00
|
|
|
const localeSort = useLocaleSort();
|
2022-01-21 14:10:36 +00:00
|
|
|
|
|
|
|
useFetch(
|
|
|
|
() => adminClient.clientScopes.find(),
|
|
|
|
(scopes) => {
|
|
|
|
setSelectedScopes(
|
|
|
|
getValues("clientScopes").map((s) => scopes.find((c) => c.id === s.id)!)
|
|
|
|
);
|
2022-05-09 10:44:07 +00:00
|
|
|
setScopes(localeSort(scopes, mapByKey("name")));
|
2022-01-21 14:10:36 +00:00
|
|
|
},
|
|
|
|
[]
|
|
|
|
);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<FormGroup
|
|
|
|
label={t("clientScopes")}
|
|
|
|
labelIcon={
|
|
|
|
<HelpItem
|
|
|
|
helpText="clients-help:clientScopes"
|
|
|
|
fieldLabelId="clients:clientScopes"
|
|
|
|
/>
|
|
|
|
}
|
|
|
|
fieldId="clientScopes"
|
|
|
|
helperTextInvalid={t("requiredClientScope")}
|
|
|
|
validated={errors.clientScopes ? "error" : "default"}
|
|
|
|
isRequired
|
|
|
|
>
|
|
|
|
<Controller
|
|
|
|
name="clientScopes"
|
|
|
|
control={control}
|
|
|
|
defaultValue={[]}
|
|
|
|
rules={{
|
|
|
|
validate: (value: RequiredIdValue[]) =>
|
|
|
|
value.filter((c) => c.id).length > 0,
|
|
|
|
}}
|
|
|
|
render={({ onChange, value }) => (
|
|
|
|
<>
|
|
|
|
{open && (
|
|
|
|
<AddScopeDialog
|
|
|
|
clientScopes={scopes.filter(
|
|
|
|
(scope) =>
|
|
|
|
!value.map((c: RequiredIdValue) => c.id).includes(scope.id!)
|
|
|
|
)}
|
|
|
|
isClientScopesConditionType
|
|
|
|
open={open}
|
|
|
|
toggleDialog={() => setOpen(!open)}
|
|
|
|
onAdd={(scopes) => {
|
|
|
|
setSelectedScopes([
|
|
|
|
...selectedScopes,
|
|
|
|
...scopes.map((s) => s.scope),
|
|
|
|
]);
|
|
|
|
onChange([
|
|
|
|
...value,
|
|
|
|
...scopes
|
|
|
|
.map((scope) => scope.scope)
|
|
|
|
.map((item) => ({ id: item.id!, required: false })),
|
|
|
|
]);
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
<Button
|
|
|
|
data-testid="select-scope-button"
|
|
|
|
variant="secondary"
|
|
|
|
onClick={() => {
|
|
|
|
setOpen(true);
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
{t("addClientScopes")}
|
|
|
|
</Button>
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
/>
|
|
|
|
{selectedScopes.length > 0 && (
|
2022-07-25 11:09:47 +00:00
|
|
|
<TableComposable variant="compact">
|
2022-01-21 14:10:36 +00:00
|
|
|
<Thead>
|
|
|
|
<Tr>
|
|
|
|
<Th>{t("clientScope")}</Th>
|
|
|
|
<Th>{t("required")}</Th>
|
|
|
|
<Th />
|
|
|
|
</Tr>
|
|
|
|
</Thead>
|
|
|
|
<Tbody>
|
|
|
|
{selectedScopes.map((scope, index) => (
|
|
|
|
<Tr key={scope.id}>
|
|
|
|
<Td>{scope.name}</Td>
|
|
|
|
<Td>
|
|
|
|
<Controller
|
|
|
|
name={`clientScopes[${index}].required`}
|
|
|
|
defaultValue={false}
|
|
|
|
control={control}
|
|
|
|
render={({ onChange, value }) => (
|
|
|
|
<Checkbox
|
|
|
|
id="required"
|
|
|
|
data-testid="standard"
|
|
|
|
name="required"
|
|
|
|
isChecked={value}
|
|
|
|
onChange={onChange}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
/>
|
|
|
|
</Td>
|
|
|
|
<Td>
|
|
|
|
<Button
|
|
|
|
variant="link"
|
|
|
|
className="keycloak__client-authorization__policy-row-remove"
|
|
|
|
icon={<MinusCircleIcon />}
|
|
|
|
onClick={() => {
|
|
|
|
setValue("clientScopes", [
|
|
|
|
...getValues("clientScopes").filter(
|
|
|
|
(s) => s.id !== scope.id
|
|
|
|
),
|
|
|
|
]);
|
|
|
|
setSelectedScopes([
|
|
|
|
...selectedScopes.filter((s) => s.id !== scope.id),
|
|
|
|
]);
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</Td>
|
|
|
|
</Tr>
|
|
|
|
))}
|
|
|
|
</Tbody>
|
|
|
|
</TableComposable>
|
|
|
|
)}
|
|
|
|
</FormGroup>
|
|
|
|
);
|
|
|
|
};
|