Client policies(policies): Create, add, delete and list client profiles in existing policy (#1393)
* add create client policy form; WIP add client policy tests checkout realm settings test from master RealmSettingsPage.ts master remove comment and add missing translation fix tests PR feedback from Jon and Erik rebase editClientPolicy edit client policy add client policy conditions form fix bug in create form remove comment update help text fixes breadcrumbs add support for adding multiple conditions, deleting conditions, and list conditions in data table clean up names add delete functionality to conditions form PR feedback from Jon useMemo for conditions remove comments and logs remove unused hook add profiles modal wip addprofiles wip profiles wip help text wip add help text remove comments remove duplicate message update data test id PR feedback from Jon 1 Apply suggestions from code review Co-authored-by: Jon Koops <jonkoops@gmail.com> remove fragment create policy detail attribute type * PR feedback from Jon 1 * PR feedback from Jon 2 * add spinner to prevent loader from being called * remove duplicate identifier * fix and rename route * rename route
This commit is contained in:
parent
5e63bac12e
commit
9e5f711bea
9 changed files with 379 additions and 35 deletions
137
src/realm-settings/AddClientProfileModal.tsx
Normal file
137
src/realm-settings/AddClientProfileModal.tsx
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Label,
|
||||||
|
Modal,
|
||||||
|
ModalVariant,
|
||||||
|
Spinner,
|
||||||
|
} from "@patternfly/react-core";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useFetch, useAdminClient } from "../context/auth/AdminClient";
|
||||||
|
import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation";
|
||||||
|
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
|
||||||
|
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
|
||||||
|
import type ClientProfileRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientProfileRepresentation";
|
||||||
|
|
||||||
|
type ClientProfile = ClientProfileRepresentation & {
|
||||||
|
global: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AddClientProfileModalProps = {
|
||||||
|
open: boolean;
|
||||||
|
toggleDialog: () => void;
|
||||||
|
onConfirm: (newReps: RoleRepresentation[]) => void;
|
||||||
|
allProfiles: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AddClientProfileModal = (props: AddClientProfileModalProps) => {
|
||||||
|
const { t } = useTranslation("roles");
|
||||||
|
const adminClient = useAdminClient();
|
||||||
|
const [selectedRows, setSelectedRows] = useState<RoleRepresentation[]>([]);
|
||||||
|
|
||||||
|
const [tableProfiles, setTableProfiles] = useState<ClientProfile[]>();
|
||||||
|
|
||||||
|
useFetch(
|
||||||
|
() =>
|
||||||
|
adminClient.clientPolicies.listProfiles({
|
||||||
|
includeGlobalProfiles: true,
|
||||||
|
}),
|
||||||
|
(allProfiles) => {
|
||||||
|
const globalProfiles = allProfiles.globalProfiles?.map(
|
||||||
|
(globalProfiles) => ({
|
||||||
|
...globalProfiles,
|
||||||
|
global: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const profiles = allProfiles.profiles?.map((profiles) => ({
|
||||||
|
...profiles,
|
||||||
|
global: false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
setTableProfiles([...(globalProfiles ?? []), ...(profiles ?? [])]);
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const loader = async () => tableProfiles ?? [];
|
||||||
|
|
||||||
|
if (!tableProfiles) {
|
||||||
|
return (
|
||||||
|
<div className="pf-u-text-align-center">
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const AliasRenderer = ({ name }: ClientProfile) => (
|
||||||
|
<>
|
||||||
|
{name && <Label color="blue">{name}</Label>} {name}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
data-testid="addClientProfile"
|
||||||
|
title={t("realm-settings:addClientProfile")}
|
||||||
|
isOpen={props.open}
|
||||||
|
onClose={props.toggleDialog}
|
||||||
|
variant={ModalVariant.large}
|
||||||
|
actions={[
|
||||||
|
<Button
|
||||||
|
key="add"
|
||||||
|
data-testid="add-client-profile-button"
|
||||||
|
variant="primary"
|
||||||
|
isDisabled={!selectedRows.length}
|
||||||
|
onClick={() => {
|
||||||
|
props.toggleDialog();
|
||||||
|
props.onConfirm(selectedRows);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("common:add")}
|
||||||
|
</Button>,
|
||||||
|
<Button
|
||||||
|
key="cancel"
|
||||||
|
variant="link"
|
||||||
|
onClick={() => {
|
||||||
|
props.toggleDialog();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("common:cancel")}
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<KeycloakDataTable
|
||||||
|
loader={loader}
|
||||||
|
isRowDisabled={(value) =>
|
||||||
|
props.allProfiles.includes(value.name!) || false
|
||||||
|
}
|
||||||
|
ariaLabelKey="realm-settings:profilesList"
|
||||||
|
searchPlaceholderKey="realm-settings:searchProfile"
|
||||||
|
canSelectAll
|
||||||
|
onSelect={(rows) => {
|
||||||
|
setSelectedRows([...rows]);
|
||||||
|
}}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
name: "name",
|
||||||
|
displayKey: "realm-settings:clientProfileName",
|
||||||
|
cellRenderer: AliasRenderer,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "description",
|
||||||
|
displayKey: "common:description",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
emptyState={
|
||||||
|
<ListEmptyState
|
||||||
|
hasIcon
|
||||||
|
message={t("noRoles")}
|
||||||
|
instructions={t("noRolesInstructions")}
|
||||||
|
primaryActionText={t("createRole")}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
|
@ -38,6 +38,8 @@ import { toClientPolicies } from "./routes/ClientPolicies";
|
||||||
import { toNewClientPolicyCondition } from "./routes/AddCondition";
|
import { toNewClientPolicyCondition } from "./routes/AddCondition";
|
||||||
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
||||||
import type { EditClientPolicyParams } from "./routes/EditClientPolicy";
|
import type { EditClientPolicyParams } from "./routes/EditClientPolicy";
|
||||||
|
import { AddClientProfileModal } from "./AddClientProfileModal";
|
||||||
|
import type ClientProfileRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientProfileRepresentation";
|
||||||
|
|
||||||
type NewClientPolicyForm = Required<ClientPolicyRepresentation>;
|
type NewClientPolicyForm = Required<ClientPolicyRepresentation>;
|
||||||
|
|
||||||
|
@ -49,6 +51,11 @@ const defaultValues: NewClientPolicyForm = {
|
||||||
profiles: [],
|
profiles: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type PolicyDetailAttributes = {
|
||||||
|
idx: number;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
export const NewClientPolicyForm = () => {
|
export const NewClientPolicyForm = () => {
|
||||||
const { t } = useTranslation("realm-settings");
|
const { t } = useTranslation("realm-settings");
|
||||||
const { errors, reset: resetForm } = useForm<NewClientPolicyForm>({
|
const { errors, reset: resetForm } = useForm<NewClientPolicyForm>({
|
||||||
|
@ -58,6 +65,10 @@ export const NewClientPolicyForm = () => {
|
||||||
const { addAlert, addError } = useAlerts();
|
const { addAlert, addError } = useAlerts();
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const [policies, setPolicies] = useState<ClientPolicyRepresentation[]>([]);
|
const [policies, setPolicies] = useState<ClientPolicyRepresentation[]>([]);
|
||||||
|
const [clientProfiles, setClientProfiles] = useState<
|
||||||
|
ClientProfileRepresentation[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
const [currentPolicy, setCurrentPolicy] =
|
const [currentPolicy, setCurrentPolicy] =
|
||||||
useState<ClientPolicyRepresentation>();
|
useState<ClientPolicyRepresentation>();
|
||||||
const [
|
const [
|
||||||
|
@ -66,7 +77,12 @@ export const NewClientPolicyForm = () => {
|
||||||
] = useState(false);
|
] = useState(false);
|
||||||
|
|
||||||
const [conditionToDelete, setConditionToDelete] =
|
const [conditionToDelete, setConditionToDelete] =
|
||||||
useState<{ idx: number; name: string }>();
|
useState<PolicyDetailAttributes>();
|
||||||
|
|
||||||
|
const [profilesModalOpen, setProfilesModalOpen] = useState(false);
|
||||||
|
|
||||||
|
const [profileToDelete, setProfileToDelete] =
|
||||||
|
useState<PolicyDetailAttributes>();
|
||||||
|
|
||||||
const { policyName } = useParams<EditClientPolicyParams>();
|
const { policyName } = useParams<EditClientPolicyParams>();
|
||||||
|
|
||||||
|
@ -78,14 +94,30 @@ export const NewClientPolicyForm = () => {
|
||||||
const refresh = () => setKey(new Date().getTime());
|
const refresh = () => setKey(new Date().getTime());
|
||||||
|
|
||||||
useFetch(
|
useFetch(
|
||||||
() => adminClient.clientPolicies.listPolicies(),
|
async () => {
|
||||||
(policies) => {
|
const [policies, profiles] = await Promise.all([
|
||||||
|
adminClient.clientPolicies.listPolicies(),
|
||||||
|
adminClient.clientPolicies.listProfiles({
|
||||||
|
includeGlobalProfiles: true,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return { policies, profiles };
|
||||||
|
},
|
||||||
|
({ policies, profiles }) => {
|
||||||
const currentPolicy = policies.policies?.find(
|
const currentPolicy = policies.policies?.find(
|
||||||
(item) => item.name === policyName
|
(item) => item.name === policyName
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const allClientProfiles = [
|
||||||
|
...(profiles.globalProfiles ?? []),
|
||||||
|
...(profiles.profiles ?? []),
|
||||||
|
];
|
||||||
|
|
||||||
setPolicies(policies.policies ?? []);
|
setPolicies(policies.policies ?? []);
|
||||||
if (currentPolicy) {
|
if (currentPolicy) {
|
||||||
setupForm(currentPolicy);
|
setupForm(currentPolicy);
|
||||||
|
setClientProfiles(allClientProfiles);
|
||||||
setCurrentPolicy(currentPolicy);
|
setCurrentPolicy(currentPolicy);
|
||||||
setShowAddConditionsAndProfilesForm(true);
|
setShowAddConditionsAndProfilesForm(true);
|
||||||
}
|
}
|
||||||
|
@ -102,6 +134,7 @@ export const NewClientPolicyForm = () => {
|
||||||
|
|
||||||
const policy = policies.filter((policy) => policy.name === policyName);
|
const policy = policies.filter((policy) => policy.name === policyName);
|
||||||
const policyConditions = policy[0]?.conditions || [];
|
const policyConditions = policy[0]?.conditions || [];
|
||||||
|
const policyProfiles = policy[0]?.profiles || [];
|
||||||
|
|
||||||
const serverInfo = useServerInfo();
|
const serverInfo = useServerInfo();
|
||||||
|
|
||||||
|
@ -110,8 +143,10 @@ export const NewClientPolicyForm = () => {
|
||||||
"org.keycloak.services.clientpolicy.condition.ClientPolicyConditionProvider"
|
"org.keycloak.services.clientpolicy.condition.ClientPolicyConditionProvider"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const formValues = form.getValues();
|
||||||
|
|
||||||
const save = async () => {
|
const save = async () => {
|
||||||
const createdForm = form.getValues();
|
const createdForm = formValues;
|
||||||
const createdPolicy = {
|
const createdPolicy = {
|
||||||
...createdForm,
|
...createdForm,
|
||||||
profiles: [],
|
profiles: [],
|
||||||
|
@ -137,9 +172,7 @@ export const NewClientPolicyForm = () => {
|
||||||
AlertVariant.success
|
AlertVariant.success
|
||||||
);
|
);
|
||||||
history.push(
|
history.push(
|
||||||
`/${realm}/realm-settings/clientPolicies/${
|
`/${realm}/realm-settings/clientPolicies/${formValues.name}/edit-policy`
|
||||||
form.getValues().name
|
|
||||||
}/edit-policy`
|
|
||||||
);
|
);
|
||||||
setShowAddConditionsAndProfilesForm(true);
|
setShowAddConditionsAndProfilesForm(true);
|
||||||
refresh();
|
refresh();
|
||||||
|
@ -189,10 +222,9 @@ export const NewClientPolicyForm = () => {
|
||||||
});
|
});
|
||||||
addAlert(t("deleteConditionSuccess"), AlertVariant.success);
|
addAlert(t("deleteConditionSuccess"), AlertVariant.success);
|
||||||
history.push(
|
history.push(
|
||||||
`/${realm}/realm-settings/clientPolicies/${
|
`/${realm}/realm-settings/clientPolicies/${formValues.name}/edit-policy`
|
||||||
form.getValues().name
|
|
||||||
}/edit-policy`
|
|
||||||
);
|
);
|
||||||
|
refresh();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
addError(t("deleteConditionError"), error);
|
addError(t("deleteConditionError"), error);
|
||||||
}
|
}
|
||||||
|
@ -214,15 +246,109 @@ export const NewClientPolicyForm = () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [toggleDeleteProfileDialog, DeleteProfileConfirm] = useConfirmDialog({
|
||||||
|
titleKey: t("deleteClientPolicyProfileConfirmTitle"),
|
||||||
|
messageKey: t("deleteClientPolicyProfileConfirm", {
|
||||||
|
profileName: profileToDelete?.name,
|
||||||
|
policyName,
|
||||||
|
}),
|
||||||
|
continueButtonLabel: t("delete"),
|
||||||
|
continueButtonVariant: ButtonVariant.danger,
|
||||||
|
onConfirm: async () => {
|
||||||
|
if (profileToDelete?.name) {
|
||||||
|
currentPolicy?.profiles?.splice(profileToDelete.idx!, 1);
|
||||||
|
try {
|
||||||
|
await adminClient.clientPolicies.updatePolicy({
|
||||||
|
policies: policies,
|
||||||
|
});
|
||||||
|
addAlert(t("deleteClientPolicyProfileSuccess"), AlertVariant.success);
|
||||||
|
history.push(
|
||||||
|
`/${realm}/realm-settings/clientPolicies/${formValues.name}/edit-policy`
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
addError(t("deleteClientPolicyProfileError"), error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const updatedPolicies = policies.filter(
|
||||||
|
(policy) => policy.name !== policyName
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await adminClient.clientPolicies.updatePolicy({
|
||||||
|
policies: updatedPolicies,
|
||||||
|
});
|
||||||
|
addAlert(t("deleteClientSuccess"), AlertVariant.success);
|
||||||
|
history.push(toClientPolicies({ realm }));
|
||||||
|
} catch (error) {
|
||||||
|
addError(t("deleteClientError"), error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const reset = () => {
|
const reset = () => {
|
||||||
form.setValue("name", currentPolicy?.name);
|
form.setValue("name", currentPolicy?.name);
|
||||||
form.setValue("description", currentPolicy?.description);
|
form.setValue("description", currentPolicy?.description);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toggleModal = () => {
|
||||||
|
setProfilesModalOpen(!profilesModalOpen);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addProfiles = async (profiles: string[]) => {
|
||||||
|
const createdPolicy = {
|
||||||
|
...currentPolicy,
|
||||||
|
profiles: (currentPolicy?.profiles ?? []).concat(profiles),
|
||||||
|
conditions: currentPolicy?.conditions,
|
||||||
|
};
|
||||||
|
|
||||||
|
const index = policies.findIndex(
|
||||||
|
(policy) => createdPolicy.name === policy.name
|
||||||
|
);
|
||||||
|
|
||||||
|
if (index === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newPolicies = [
|
||||||
|
...policies.slice(0, index),
|
||||||
|
createdPolicy,
|
||||||
|
...policies.slice(index + 1),
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
await adminClient.clientPolicies.updatePolicy({
|
||||||
|
policies: newPolicies,
|
||||||
|
});
|
||||||
|
setPolicies(newPolicies);
|
||||||
|
history.push(
|
||||||
|
`/${realm}/realm-settings/clientPolicies/${formValues.name}/edit-policy`
|
||||||
|
);
|
||||||
|
addAlert(
|
||||||
|
t("realm-settings:addClientProfileSuccess"),
|
||||||
|
AlertVariant.success
|
||||||
|
);
|
||||||
|
refresh();
|
||||||
|
} catch (error) {
|
||||||
|
addError("realm-settings:addClientProfileError", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("NY", policyConditions);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DeleteConfirm />
|
<DeleteConfirm />
|
||||||
<DeleteConditionConfirm />
|
<DeleteConditionConfirm />
|
||||||
|
<DeleteProfileConfirm />
|
||||||
|
<AddClientProfileModal
|
||||||
|
onConfirm={(profiles: ClientProfileRepresentation[]) => {
|
||||||
|
addProfiles(profiles.map((item) => item.name!));
|
||||||
|
}}
|
||||||
|
allProfiles={policyProfiles}
|
||||||
|
open={profilesModalOpen}
|
||||||
|
toggleDialog={toggleModal}
|
||||||
|
/>
|
||||||
<ViewHeader
|
<ViewHeader
|
||||||
titleKey={
|
titleKey={
|
||||||
showAddConditionsAndProfilesForm || policyName
|
showAddConditionsAndProfilesForm || policyName
|
||||||
|
@ -286,6 +412,7 @@ export const NewClientPolicyForm = () => {
|
||||||
variant="primary"
|
variant="primary"
|
||||||
type="submit"
|
type="submit"
|
||||||
data-testid="saveCreatePolicy"
|
data-testid="saveCreatePolicy"
|
||||||
|
isDisabled={!formValues.name}
|
||||||
>
|
>
|
||||||
{t("common:save")}
|
{t("common:save")}
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -325,7 +452,7 @@ export const NewClientPolicyForm = () => {
|
||||||
{...props}
|
{...props}
|
||||||
to={toNewClientPolicyCondition({
|
to={toNewClientPolicyCondition({
|
||||||
realm,
|
realm,
|
||||||
policyName: form.getValues().name!,
|
policyName: formValues.name!,
|
||||||
})}
|
})}
|
||||||
></Link>
|
></Link>
|
||||||
)}
|
)}
|
||||||
|
@ -342,9 +469,10 @@ export const NewClientPolicyForm = () => {
|
||||||
<DataList aria-label={t("conditions")} isCompact>
|
<DataList aria-label={t("conditions")} isCompact>
|
||||||
{policyConditions.map((condition, idx) => (
|
{policyConditions.map((condition, idx) => (
|
||||||
<DataListItem
|
<DataListItem
|
||||||
aria-labelledby={"conditions-list-item"}
|
aria-labelledby="conditions-list-item"
|
||||||
key={`list-item-${idx}`}
|
key={`list-item-${idx}`}
|
||||||
id={condition.condition}
|
id={condition.condition}
|
||||||
|
data-testid="conditions-list-item"
|
||||||
>
|
>
|
||||||
<DataListItemRow data-testid="conditions-list-row">
|
<DataListItemRow data-testid="conditions-list-row">
|
||||||
<DataListItemCells
|
<DataListItemCells
|
||||||
|
@ -435,16 +563,83 @@ export const NewClientPolicyForm = () => {
|
||||||
</FlexItem>
|
</FlexItem>
|
||||||
<FlexItem align={{ default: "alignRight" }}>
|
<FlexItem align={{ default: "alignRight" }}>
|
||||||
<Button
|
<Button
|
||||||
id="addExecutor"
|
id="addClientProfile"
|
||||||
variant="link"
|
variant="link"
|
||||||
className="kc-addClientProfile"
|
className="kc-addClientProfile"
|
||||||
data-testid="cancelCreateProfile"
|
data-testid="cancelCreateProfile"
|
||||||
icon={<PlusCircleIcon />}
|
icon={<PlusCircleIcon />}
|
||||||
|
onClick={toggleModal}
|
||||||
>
|
>
|
||||||
{t("realm-settings:addClientProfile")}
|
{t("realm-settings:addClientProfile")}
|
||||||
</Button>
|
</Button>
|
||||||
</FlexItem>
|
</FlexItem>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
{policyProfiles.length > 0 ? (
|
||||||
|
<DataList aria-label={t("profiles")} isCompact>
|
||||||
|
{policyProfiles.map((profile, idx) => (
|
||||||
|
<DataListItem
|
||||||
|
aria-labelledby={`${profile}-profile-list-item`}
|
||||||
|
key={profile}
|
||||||
|
id={`${profile}-profile-list-item`}
|
||||||
|
data-testid={"profile-list-item"}
|
||||||
|
>
|
||||||
|
<DataListItemRow data-testid="profile-list-row">
|
||||||
|
<DataListItemCells
|
||||||
|
dataListCells={[
|
||||||
|
<DataListCell key="name" data-testid="profile-name">
|
||||||
|
{profile && (
|
||||||
|
<Link
|
||||||
|
key={profile}
|
||||||
|
data-testid="profile-name-link"
|
||||||
|
to={""}
|
||||||
|
className="kc-profile-link"
|
||||||
|
>
|
||||||
|
{profile}
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
{policyProfiles
|
||||||
|
.filter((type) => type === profile)
|
||||||
|
.map((type) => (
|
||||||
|
<>
|
||||||
|
<HelpItem
|
||||||
|
helpText={
|
||||||
|
clientProfiles.find(
|
||||||
|
(profile) => type === profile.name
|
||||||
|
)?.description
|
||||||
|
}
|
||||||
|
forLabel={profile}
|
||||||
|
forID={t(`common:helpLabel`, {
|
||||||
|
label: profile,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
isInline
|
||||||
|
icon={
|
||||||
|
<TrashIcon
|
||||||
|
className="kc-conditionType-trash-icon"
|
||||||
|
data-testid="deleteClientProfileDropdown"
|
||||||
|
onClick={() => {
|
||||||
|
toggleDeleteProfileDialog();
|
||||||
|
setProfileToDelete({
|
||||||
|
idx: idx,
|
||||||
|
name: type!,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
></Button>
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
</DataListCell>,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</DataListItemRow>
|
||||||
|
</DataListItem>
|
||||||
|
))}
|
||||||
|
</DataList>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Text
|
<Text
|
||||||
className="kc-emptyClientProfiles"
|
className="kc-emptyClientProfiles"
|
||||||
|
@ -454,6 +649,8 @@ export const NewClientPolicyForm = () => {
|
||||||
</Text>
|
</Text>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</FormAccess>
|
</FormAccess>
|
||||||
</PageSection>
|
</PageSection>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -26,7 +26,7 @@ import { useAlerts } from "../components/alert/Alerts";
|
||||||
|
|
||||||
import "./RealmSettingsSection.css";
|
import "./RealmSettingsSection.css";
|
||||||
import { useRealm } from "../context/realm-context/RealmContext";
|
import { useRealm } from "../context/realm-context/RealmContext";
|
||||||
import { toNewClientPolicy } from "./routes/NewClientPolicy";
|
import { toAddClientPolicy } from "./routes/AddClientPolicy";
|
||||||
import { toEditClientPolicy } from "./routes/EditClientPolicy";
|
import { toEditClientPolicy } from "./routes/EditClientPolicy";
|
||||||
export const PoliciesTab = () => {
|
export const PoliciesTab = () => {
|
||||||
const { t } = useTranslation("realm-settings");
|
const { t } = useTranslation("realm-settings");
|
||||||
|
@ -161,7 +161,7 @@ export const PoliciesTab = () => {
|
||||||
message={t("realm-settings:noClientPolicies")}
|
message={t("realm-settings:noClientPolicies")}
|
||||||
instructions={t("realm-settings:noClientPoliciesInstructions")}
|
instructions={t("realm-settings:noClientPoliciesInstructions")}
|
||||||
primaryActionText={t("realm-settings:createClientPolicy")}
|
primaryActionText={t("realm-settings:createClientPolicy")}
|
||||||
onPrimaryAction={() => history.push(toNewClientPolicy({ realm }))}
|
onPrimaryAction={() => history.push(toAddClientPolicy({ realm }))}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
ariaLabelKey="realm-settings:clientPolicies"
|
ariaLabelKey="realm-settings:clientPolicies"
|
||||||
|
@ -172,7 +172,7 @@ export const PoliciesTab = () => {
|
||||||
<Button
|
<Button
|
||||||
id="createPolicy"
|
id="createPolicy"
|
||||||
component={(props) => (
|
component={(props) => (
|
||||||
<Link {...props} to={toNewClientPolicy({ realm })} />
|
<Link {...props} to={toAddClientPolicy({ realm })} />
|
||||||
)}
|
)}
|
||||||
data-testid="createPolicy"
|
data-testid="createPolicy"
|
||||||
>
|
>
|
||||||
|
|
|
@ -226,7 +226,7 @@ export const ProfilesTab = () => {
|
||||||
columns={[
|
columns={[
|
||||||
{
|
{
|
||||||
name: "name",
|
name: "name",
|
||||||
displayKey: t("clientProfileName"),
|
displayKey: t("common:name"),
|
||||||
cellRenderer: cellFormatter,
|
cellRenderer: cellFormatter,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -231,7 +231,8 @@ export default {
|
||||||
jsonEditor: "JSON editor",
|
jsonEditor: "JSON editor",
|
||||||
},
|
},
|
||||||
clientProfileSearch: "Search",
|
clientProfileSearch: "Search",
|
||||||
clientProfileName: "Name",
|
searchProfile: "Search profile",
|
||||||
|
clientProfileName: "Client profile name",
|
||||||
clientProfileDescription: "Description",
|
clientProfileDescription: "Description",
|
||||||
emptyClientProfiles: "No profiles",
|
emptyClientProfiles: "No profiles",
|
||||||
emptyClientProfilesInstructions:
|
emptyClientProfilesInstructions:
|
||||||
|
@ -241,12 +242,20 @@ export default {
|
||||||
"This action will permanently delete the profile {{profileName}}. This cannot be undone.",
|
"This action will permanently delete the profile {{profileName}}. This cannot be undone.",
|
||||||
deleteClientSuccess: "Client profile deleted",
|
deleteClientSuccess: "Client profile deleted",
|
||||||
deleteClientError: "Could not delete profile: {{error}}",
|
deleteClientError: "Could not delete profile: {{error}}",
|
||||||
|
deleteClientPolicyProfileConfirmTitle: "Delete profile?",
|
||||||
|
deleteClientPolicyProfileConfirm:
|
||||||
|
"This action will permanently delete {{profileName}} from the policy {{policyName}}. This cannot be undone.",
|
||||||
|
deleteClientPolicyProfileSuccess:
|
||||||
|
"Profile successfully removed from the policy.",
|
||||||
|
deleteClientPolicyProfileError:
|
||||||
|
"Could not delete profile from the policy: {{error}}",
|
||||||
createClientProfile: "Create client profile",
|
createClientProfile: "Create client profile",
|
||||||
deleteClientProfile: "Delete this client profile",
|
deleteClientProfile: "Delete this client profile",
|
||||||
createClientProfileSuccess: "New client profile created",
|
createClientProfileSuccess: "New client profile created",
|
||||||
updateClientProfileSuccess: "Client profile updated successfully",
|
updateClientProfileSuccess: "Client profile updated successfully",
|
||||||
createClientProfileError: "Could not create client profile: '{{error}}'",
|
createClientProfileError: "Could not create client profile: '{{error}}'",
|
||||||
updateClientProfileError: "Could not update client profile: '{{error}}'",
|
addClientProfileSuccess: "New client profile added",
|
||||||
|
addClientProfileError: "Could not create client profile: '{{error}}'",
|
||||||
createClientProfileNameHelperText:
|
createClientProfileNameHelperText:
|
||||||
"The name must be unique within the realm",
|
"The name must be unique within the realm",
|
||||||
allClientPolicies: "Client policies",
|
allClientPolicies: "Client policies",
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { ClientPoliciesRoute } from "./routes/ClientPolicies";
|
||||||
import { AddClientProfileRoute } from "./routes/AddClientProfile";
|
import { AddClientProfileRoute } from "./routes/AddClientProfile";
|
||||||
import { ClientProfileRoute } from "./routes/ClientProfile";
|
import { ClientProfileRoute } from "./routes/ClientProfile";
|
||||||
import { AddExecutorRoute } from "./routes/AddExecutor";
|
import { AddExecutorRoute } from "./routes/AddExecutor";
|
||||||
import { NewClientPolicyRoute } from "./routes/NewClientPolicy";
|
import { AddClientPolicyRoute } from "./routes/AddClientPolicy";
|
||||||
import { EditClientPolicyRoute } from "./routes/EditClientPolicy";
|
import { EditClientPolicyRoute } from "./routes/EditClientPolicy";
|
||||||
import { NewClientPolicyConditionRoute } from "./routes/AddCondition";
|
import { NewClientPolicyConditionRoute } from "./routes/AddCondition";
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ const routes: RouteDef[] = [
|
||||||
AddClientProfileRoute,
|
AddClientProfileRoute,
|
||||||
AddExecutorRoute,
|
AddExecutorRoute,
|
||||||
ClientProfileRoute,
|
ClientProfileRoute,
|
||||||
NewClientPolicyRoute,
|
AddClientPolicyRoute,
|
||||||
EditClientPolicyRoute,
|
EditClientPolicyRoute,
|
||||||
NewClientPolicyConditionRoute,
|
NewClientPolicyConditionRoute,
|
||||||
];
|
];
|
||||||
|
|
|
@ -4,17 +4,17 @@ import type { RouteDef } from "../../route-config";
|
||||||
import { NewClientPolicyForm } from "../NewClientPolicyForm";
|
import { NewClientPolicyForm } from "../NewClientPolicyForm";
|
||||||
import { NewPolicyCrumb } from "../RealmSettingsSection";
|
import { NewPolicyCrumb } from "../RealmSettingsSection";
|
||||||
|
|
||||||
export type NewClientPolicyParams = { realm: string };
|
export type AddClientPolicyParams = { realm: string };
|
||||||
|
|
||||||
export const NewClientPolicyRoute: RouteDef = {
|
export const AddClientPolicyRoute: RouteDef = {
|
||||||
path: "/:realm/realm-settings/clientPolicies/new-client-policy",
|
path: "/:realm/realm-settings/clientPolicies/add-client-policy",
|
||||||
component: NewClientPolicyForm,
|
component: NewClientPolicyForm,
|
||||||
breadcrumb: () => NewPolicyCrumb,
|
breadcrumb: () => NewPolicyCrumb,
|
||||||
access: "manage-clients",
|
access: "manage-clients",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const toNewClientPolicy = (
|
export const toAddClientPolicy = (
|
||||||
params: NewClientPolicyParams
|
params: AddClientPolicyParams
|
||||||
): LocationDescriptorObject => ({
|
): LocationDescriptorObject => ({
|
||||||
pathname: generatePath(NewClientPolicyRoute.path, params),
|
pathname: generatePath(AddClientPolicyRoute.path, params),
|
||||||
});
|
});
|
|
@ -9,7 +9,7 @@ export type ClientProfileParams = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ClientProfileRoute: RouteDef = {
|
export const ClientProfileRoute: RouteDef = {
|
||||||
path: "/:realm/realm-settings/clientPolicies/:profileName",
|
path: "/:realm/realm-settings/clientPolicies/:profileName/edit-profile",
|
||||||
component: ClientProfileForm,
|
component: ClientProfileForm,
|
||||||
breadcrumb: (t) => t("realm-settings:clientProfile"),
|
breadcrumb: (t) => t("realm-settings:clientProfile"),
|
||||||
access: ["view-realm", "view-users"],
|
access: ["view-realm", "view-users"],
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import type { LocationDescriptorObject } from "history";
|
import type { LocationDescriptorObject } from "history";
|
||||||
import { lazy } from "react";
|
import { lazy } from "react";
|
||||||
import { generatePath } from "react-router-dom";
|
import { generatePath } from "react-router-dom";
|
||||||
|
|
||||||
import type { RouteDef } from "../../route-config";
|
import type { RouteDef } from "../../route-config";
|
||||||
|
|
||||||
export type AddUserParams = { realm: string };
|
export type AddUserParams = { realm: string };
|
||||||
|
|
Loading…
Reference in a new issue