Allow creation of policy on permission screen (#21909)

Closes #21908
This commit is contained in:
Andreas Blättlinger 2023-08-01 11:30:06 +02:00 committed by GitHub
parent 748c53df7f
commit c62f04e7c9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -5,6 +5,7 @@ import type {
PolicyQuery, PolicyQuery,
} from "@keycloak/keycloak-admin-client/lib/resources/clients"; } from "@keycloak/keycloak-admin-client/lib/resources/clients";
import { import {
Button,
ButtonVariant, ButtonVariant,
Chip, Chip,
ChipGroup, ChipGroup,
@ -26,6 +27,10 @@ import { useRealm } from "../../context/realm-context/RealmContext";
import { useFetch } from "../../utils/useFetch"; import { useFetch } from "../../utils/useFetch";
import { toPolicyDetails } from "../routes/PolicyDetails"; import { toPolicyDetails } from "../routes/PolicyDetails";
import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog";
import { toCreatePolicy } from "../routes/NewPolicy";
import { NewPolicyDialog } from "./NewPolicyDialog";
import useToggle from "../../utils/useToggle";
import PolicyProviderRepresentation from "@keycloak/keycloak-admin-client/lib/defs/policyProviderRepresentation";
type Type = "resources" | "policies"; type Type = "resources" | "policies";
@ -84,7 +89,11 @@ export const ResourcesPolicySelect = ({
const [items, setItems] = useState<Policies[]>([]); const [items, setItems] = useState<Policies[]>([]);
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [clickedPolicy, setClickedPolicy] = useState<Policies>(); const [createPolicyDialog, toggleCreatePolicyDialog] = useToggle();
const [policyProviders, setPolicyProviders] =
useState<PolicyProviderRepresentation[]>();
const [onUnsavedChangesConfirm, setOnUnsavedChangesConfirm] =
useState<() => void>();
const functions = typeMapping[name]; const functions = typeMapping[name];
@ -102,8 +111,8 @@ export const ResourcesPolicySelect = ({
{ id: clientId, first: 0, max: 10, permission: "false" }, { id: clientId, first: 0, max: 10, permission: "false" },
search === "" ? null : { name: search }, search === "" ? null : { name: search },
); );
return ( return await Promise.all([
await Promise.all([ adminClient.clients.listPolicyProviders({ id: clientId }),
adminClient.clients[functions.searchFunction](params), adminClient.clients[functions.searchFunction](params),
permissionId permissionId
? adminClient.clients[functions.fetchFunction]({ ? adminClient.clients[functions.fetchFunction]({
@ -111,8 +120,14 @@ export const ResourcesPolicySelect = ({
permissionId, permissionId,
}) })
: Promise.resolve([]), : Promise.resolve([]),
]) ]);
) },
([providers, ...policies]) => {
setPolicyProviders(
providers.filter((p) => p.type !== "resource" && p.type !== "scope"),
);
setItems(
policies
.flat() .flat()
.filter( .filter(
(r): r is PolicyRepresentation | ResourceRepresentation => (r): r is PolicyRepresentation | ResourceRepresentation =>
@ -122,9 +137,9 @@ export const ResourcesPolicySelect = ({
.filter( .filter(
({ id }, index, self) => ({ id }, index, self) =>
index === self.findIndex(({ id: otherId }) => id === otherId), index === self.findIndex(({ id: otherId }) => id === otherId),
),
); );
}, },
setItems,
[search], [search],
); );
@ -133,7 +148,7 @@ export const ResourcesPolicySelect = ({
messageKey: t("unsavedChangesConfirm"), messageKey: t("unsavedChangesConfirm"),
continueButtonLabel: t("common:continue"), continueButtonLabel: t("common:continue"),
continueButtonVariant: ButtonVariant.danger, continueButtonVariant: ButtonVariant.danger,
onConfirm: () => navigate(to(clickedPolicy!)), onConfirm: onUnsavedChangesConfirm!,
}); });
const to = (policy: Policies) => const to = (policy: Policies) =>
@ -165,6 +180,8 @@ export const ResourcesPolicySelect = ({
return null; return null;
} }
const location = to(policy);
return ( return (
<Chip <Chip
key={policy.id} key={policy.id}
@ -175,13 +192,13 @@ export const ResourcesPolicySelect = ({
> >
{policy.type ? ( {policy.type ? (
<Link <Link
to={to(policy)} to={location}
onClick={(event) => { onClick={(event) => {
if (isDirty) { if (isDirty) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
setOpen(false); setOpen(false);
setClickedPolicy(policy); setOnUnsavedChangesConfirm(() => navigate(location));
toggleUnsavedChangesDialog(); toggleUnsavedChangesDialog();
} }
}} }}
@ -201,6 +218,17 @@ export const ResourcesPolicySelect = ({
return ( return (
<> <>
<UnsavedChangesConfirm /> <UnsavedChangesConfirm />
{createPolicyDialog && (
<NewPolicyDialog
policyProviders={policyProviders}
onSelect={(p) => {
navigate(
toCreatePolicy({ id: clientId, realm, policyType: p.type! }),
);
}}
toggleDialog={toggleCreatePolicyDialog}
/>
)}
<Controller <Controller
name={name} name={name}
defaultValue={preSelected ? [preSelected] : []} defaultValue={preSelected ? [preSelected] : []}
@ -241,6 +269,23 @@ export const ResourcesPolicySelect = ({
validated={errors[name] ? "error" : "default"} validated={errors[name] ? "error" : "default"}
typeAheadAriaLabel={t(name)} typeAheadAriaLabel={t(name)}
chipGroupComponent={toChipGroupItems(field)} chipGroupComponent={toChipGroupItems(field)}
footer={
<Button
variant="link"
isInline
onClick={() => {
if (isDirty) {
setOpen(false);
setOnUnsavedChangesConfirm(() => toggleCreatePolicyDialog);
toggleUnsavedChangesDialog();
} else {
toggleCreatePolicyDialog();
}
}}
>
{t("createPolicy")}
</Button>
}
> >
{toSelectOptions()} {toSelectOptions()}
</Select> </Select>