Create custom user federation providers page (#1745)
* Create custom user federation providers page fixes: #1722 * added breadcrumb
This commit is contained in:
parent
33a1769c39
commit
f1c7e5ecb3
17 changed files with 798 additions and 616 deletions
|
@ -150,7 +150,7 @@ describe("User Fed Kerberos tests", () => {
|
|||
});
|
||||
|
||||
it("Delete a Kerberos provider using the Settings view's Action menu", () => {
|
||||
providersPage.deleteCardFromMenu(provider, firstKerberosName);
|
||||
providersPage.deleteCardFromMenu(firstKerberosName);
|
||||
|
||||
modalUtils.checkModalTitle(deleteModalTitle).confirmModal();
|
||||
masthead.checkNotificationMessage(deletedSuccessMessage);
|
||||
|
|
|
@ -207,7 +207,7 @@ describe("User Fed LDAP mapper tests", () => {
|
|||
|
||||
// *** test cleanup ***
|
||||
it("Cleanup - delete LDAP provider", () => {
|
||||
providersPage.deleteCardFromMenu(provider, ldapName);
|
||||
providersPage.deleteCardFromMenu(ldapName);
|
||||
modalUtils.checkModalTitle(providerDeleteTitle).confirmModal();
|
||||
masthead.checkNotificationMessage(providerDeleteSuccess);
|
||||
});
|
||||
|
|
|
@ -227,7 +227,7 @@ describe("User Fed LDAP mapper tests", () => {
|
|||
|
||||
// *** test cleanup ***
|
||||
it("Cleanup - delete LDAP provider", () => {
|
||||
providersPage.deleteCardFromMenu(provider, ldapName);
|
||||
providersPage.deleteCardFromMenu(ldapName);
|
||||
modalUtils.checkModalTitle(providerDeleteTitle).confirmModal();
|
||||
masthead.checkNotificationMessage(providerDeleteSuccess);
|
||||
});
|
||||
|
|
|
@ -181,7 +181,7 @@ describe("User Fed LDAP tests", () => {
|
|||
});
|
||||
|
||||
it("Delete an LDAP provider using the Settings view's Action menu", () => {
|
||||
providersPage.deleteCardFromMenu(provider, firstLdapName);
|
||||
providersPage.deleteCardFromMenu(firstLdapName);
|
||||
modalUtils.checkModalTitle(deleteModalTitle).confirmModal();
|
||||
masthead.checkNotificationMessage(deletedSuccessMessage);
|
||||
});
|
||||
|
|
|
@ -105,10 +105,10 @@ export default class ProviderPage {
|
|||
return this;
|
||||
}
|
||||
|
||||
deleteCardFromMenu(providerType: string, card: string) {
|
||||
deleteCardFromMenu(card: string) {
|
||||
this.clickExistingCard(card);
|
||||
cy.get('[data-testid="action-dropdown"]').click();
|
||||
cy.get(`[data-testid="delete-${providerType}-cmd"]`).click();
|
||||
cy.get(`[data-testid="delete-cmd"]`).click();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@ import {
|
|||
ActionGroup,
|
||||
AlertVariant,
|
||||
Button,
|
||||
ButtonVariant,
|
||||
DropdownItem,
|
||||
Form,
|
||||
PageSection,
|
||||
} from "@patternfly/react-core";
|
||||
|
@ -14,69 +12,13 @@ import { SettingsCache } from "./shared/SettingsCache";
|
|||
import { useRealm } from "../context/realm-context/RealmContext";
|
||||
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
|
||||
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
||||
import { FormProvider, useForm } from "react-hook-form";
|
||||
import { useAdminClient, useFetch } from "../context/auth/AdminClient";
|
||||
import { useAlerts } from "../components/alert/Alerts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||
import { useHistory, useParams } from "react-router-dom";
|
||||
|
||||
type KerberosSettingsHeaderProps = {
|
||||
onChange: (value: string) => void;
|
||||
value: string;
|
||||
save: () => void;
|
||||
toggleDeleteDialog: () => void;
|
||||
};
|
||||
|
||||
const KerberosSettingsHeader = ({
|
||||
onChange,
|
||||
value,
|
||||
save,
|
||||
toggleDeleteDialog,
|
||||
}: KerberosSettingsHeaderProps) => {
|
||||
const { t } = useTranslation("user-federation");
|
||||
const { id } = useParams<{ id?: string }>();
|
||||
const [toggleDisableDialog, DisableConfirm] = useConfirmDialog({
|
||||
titleKey: "user-federation:userFedDisableConfirmTitle",
|
||||
messageKey: "user-federation:userFedDisableConfirm",
|
||||
continueButtonLabel: "common:disable",
|
||||
onConfirm: () => {
|
||||
onChange("false");
|
||||
save();
|
||||
},
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<DisableConfirm />
|
||||
{!id ? (
|
||||
<ViewHeader titleKey="Kerberos" />
|
||||
) : (
|
||||
<ViewHeader
|
||||
titleKey="Kerberos"
|
||||
dropdownItems={[
|
||||
<DropdownItem
|
||||
key="delete"
|
||||
onClick={() => toggleDeleteDialog()}
|
||||
data-testid="delete-kerberos-cmd"
|
||||
>
|
||||
{t("deleteProvider")}
|
||||
</DropdownItem>,
|
||||
]}
|
||||
isEnabled={value === "true"}
|
||||
onToggle={(value) => {
|
||||
if (!value) {
|
||||
toggleDisableDialog();
|
||||
} else {
|
||||
onChange(value.toString());
|
||||
save();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
import { Header } from "./shared/Header";
|
||||
import { toUserFederation } from "./routes/UserFederation";
|
||||
|
||||
export default function UserFederationKerberosSettings() {
|
||||
const { t } = useTranslation("user-federation");
|
||||
|
@ -124,38 +66,11 @@ export default function UserFederationKerberosSettings() {
|
|||
}
|
||||
};
|
||||
|
||||
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
||||
titleKey: "user-federation:userFedDeleteConfirmTitle",
|
||||
messageKey: "user-federation:userFedDeleteConfirm",
|
||||
continueButtonLabel: "common:delete",
|
||||
continueButtonVariant: ButtonVariant.danger,
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
await adminClient.components.del({ id: id! });
|
||||
addAlert(t("userFedDeletedSuccess"), AlertVariant.success);
|
||||
history.replace(`/${realm}/user-federation`);
|
||||
} catch (error: any) {
|
||||
addAlert("user-federation:userFedDeleteError", error);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<DeleteConfirm />
|
||||
<Controller
|
||||
name="config.enabled[0]"
|
||||
defaultValue={["true"][0]}
|
||||
control={form.control}
|
||||
render={({ onChange, value }) => (
|
||||
<KerberosSettingsHeader
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
save={() => save(form.getValues())}
|
||||
toggleDeleteDialog={toggleDeleteDialog}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<FormProvider {...form}>
|
||||
<Header provider="Kerberos" save={() => form.handleSubmit(save)()} />
|
||||
</FormProvider>
|
||||
<PageSection variant="light">
|
||||
<KerberosSettingsRequired form={form} showSectionHeading />
|
||||
</PageSection>
|
||||
|
@ -173,7 +88,7 @@ export default function UserFederationKerberosSettings() {
|
|||
</Button>
|
||||
<Button
|
||||
variant="link"
|
||||
onClick={() => history.push(`/${realm}/user-federation`)}
|
||||
onClick={() => history.push(toUserFederation({ realm }))}
|
||||
data-testid="kerberos-cancel"
|
||||
>
|
||||
{t("common:cancel")}
|
||||
|
|
|
@ -3,9 +3,6 @@ import {
|
|||
ActionGroup,
|
||||
AlertVariant,
|
||||
Button,
|
||||
ButtonVariant,
|
||||
DropdownItem,
|
||||
DropdownSeparator,
|
||||
Form,
|
||||
PageSection,
|
||||
Tab,
|
||||
|
@ -23,17 +20,17 @@ import { LdapSettingsSearching } from "./ldap/LdapSettingsSearching";
|
|||
import { useRealm } from "../context/realm-context/RealmContext";
|
||||
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
|
||||
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
||||
import { FormProvider, useForm, useFormContext } from "react-hook-form";
|
||||
import { useAdminClient, useFetch } from "../context/auth/AdminClient";
|
||||
import { useAlerts } from "../components/alert/Alerts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||
import { useHistory, useParams } from "react-router-dom";
|
||||
import { ScrollForm } from "../components/scroll-form/ScrollForm";
|
||||
|
||||
import { KeycloakTabs } from "../components/keycloak-tabs/KeycloakTabs";
|
||||
import { LdapMapperList } from "./ldap/mappers/LdapMapperList";
|
||||
import { toUserFederation } from "./routes/UserFederation";
|
||||
import { ExtendedHeader } from "./shared/ExtendedHeader";
|
||||
|
||||
type ldapComponentRepresentation = ComponentRepresentation & {
|
||||
config?: {
|
||||
|
@ -42,145 +39,58 @@ type ldapComponentRepresentation = ComponentRepresentation & {
|
|||
};
|
||||
};
|
||||
|
||||
type LdapSettingsHeaderProps = {
|
||||
onChange: (value: string) => void;
|
||||
value: string;
|
||||
editMode?: string | string[];
|
||||
save: () => void;
|
||||
toggleDeleteDialog: () => void;
|
||||
toggleRemoveUsersDialog: () => void;
|
||||
};
|
||||
|
||||
const LdapSettingsHeader = ({
|
||||
onChange,
|
||||
value,
|
||||
editMode,
|
||||
const AddLdapFormContent = ({
|
||||
save,
|
||||
toggleDeleteDialog,
|
||||
toggleRemoveUsersDialog,
|
||||
}: LdapSettingsHeaderProps) => {
|
||||
}: {
|
||||
save: (component: ldapComponentRepresentation) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation("user-federation");
|
||||
const form = useFormContext();
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const adminClient = useAdminClient();
|
||||
const { addAlert, addError } = useAlerts();
|
||||
const [toggleDisableDialog, DisableConfirm] = useConfirmDialog({
|
||||
titleKey: "user-federation:userFedDisableConfirmTitle",
|
||||
messageKey: "user-federation:userFedDisableConfirm",
|
||||
continueButtonLabel: "common:disable",
|
||||
onConfirm: () => {
|
||||
onChange("false");
|
||||
save();
|
||||
},
|
||||
});
|
||||
const history = useHistory();
|
||||
|
||||
const [toggleUnlinkUsersDialog, UnlinkUsersDialog] = useConfirmDialog({
|
||||
titleKey: "user-federation:userFedUnlinkUsersConfirmTitle",
|
||||
messageKey: "user-federation:userFedUnlinkUsersConfirm",
|
||||
continueButtonLabel: "user-federation:unlinkUsers",
|
||||
onConfirm: () => unlinkUsers(),
|
||||
});
|
||||
|
||||
const syncChangedUsers = async () => {
|
||||
try {
|
||||
if (id) {
|
||||
const response = await adminClient.userStorageProvider.sync({
|
||||
id: id,
|
||||
action: "triggerChangedUsersSync",
|
||||
});
|
||||
if (response.ignored) {
|
||||
addAlert(`${response.status}.`, AlertVariant.warning);
|
||||
} else {
|
||||
addAlert(
|
||||
t("syncUsersSuccess") +
|
||||
`${response.added} users added, ${response.updated} users updated, ${response.removed} users removed, ${response.failed} users failed.`,
|
||||
AlertVariant.success
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
addError("user-federation:syncUsersError", error);
|
||||
}
|
||||
};
|
||||
|
||||
const syncAllUsers = async () => {
|
||||
try {
|
||||
if (id) {
|
||||
const response = await adminClient.userStorageProvider.sync({
|
||||
id: id,
|
||||
action: "triggerFullSync",
|
||||
});
|
||||
if (response.ignored) {
|
||||
addAlert(`${response.status}.`, AlertVariant.warning);
|
||||
} else {
|
||||
addAlert(
|
||||
t("syncUsersSuccess") +
|
||||
`${response.added} users added, ${response.updated} users updated, ${response.removed} users removed, ${response.failed} users failed.`,
|
||||
AlertVariant.success
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
addError("user-federation:syncUsersError", error);
|
||||
}
|
||||
};
|
||||
|
||||
const unlinkUsers = async () => {
|
||||
try {
|
||||
if (id) {
|
||||
await adminClient.userStorageProvider.unlinkUsers({ id });
|
||||
}
|
||||
addAlert(t("unlinkUsersSuccess"), AlertVariant.success);
|
||||
} catch (error) {
|
||||
addError("user-federation:unlinkUsersError", error);
|
||||
}
|
||||
};
|
||||
const { realm } = useRealm();
|
||||
|
||||
return (
|
||||
<>
|
||||
<DisableConfirm />
|
||||
<UnlinkUsersDialog />
|
||||
{!id ? (
|
||||
<ViewHeader titleKey={t("addOneLdap")} />
|
||||
) : (
|
||||
<ViewHeader
|
||||
titleKey="LDAP"
|
||||
dropdownItems={[
|
||||
<DropdownItem key="sync" onClick={syncChangedUsers}>
|
||||
{t("syncChangedUsers")}
|
||||
</DropdownItem>,
|
||||
<DropdownItem key="syncall" onClick={syncAllUsers}>
|
||||
{t("syncAllUsers")}
|
||||
</DropdownItem>,
|
||||
<DropdownItem
|
||||
key="unlink"
|
||||
isDisabled={editMode ? !editMode.includes("UNSYNCED") : false}
|
||||
onClick={toggleUnlinkUsersDialog}
|
||||
>
|
||||
{t("unlinkUsers")}
|
||||
</DropdownItem>,
|
||||
<DropdownItem key="remove" onClick={toggleRemoveUsersDialog}>
|
||||
{t("removeImported")}
|
||||
</DropdownItem>,
|
||||
<DropdownSeparator key="separator" />,
|
||||
<DropdownItem
|
||||
key="delete"
|
||||
onClick={toggleDeleteDialog}
|
||||
data-testid="delete-ldap-cmd"
|
||||
>
|
||||
{t("deleteProvider")}
|
||||
</DropdownItem>,
|
||||
]}
|
||||
isEnabled={value === "true"}
|
||||
onToggle={(value) => {
|
||||
if (!value) {
|
||||
toggleDisableDialog();
|
||||
} else {
|
||||
onChange("" + value);
|
||||
save();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<ScrollForm
|
||||
sections={[
|
||||
t("generalOptions"),
|
||||
t("connectionAndAuthenticationSettings"),
|
||||
t("ldapSearchingAndUpdatingSettings"),
|
||||
t("synchronizationSettings"),
|
||||
t("kerberosIntegration"),
|
||||
t("cacheSettings"),
|
||||
t("advancedSettings"),
|
||||
]}
|
||||
>
|
||||
<LdapSettingsGeneral form={form} vendorEdit={!!id} />
|
||||
<LdapSettingsConnection form={form} edit={!!id} />
|
||||
<LdapSettingsSearching form={form} />
|
||||
<LdapSettingsSynchronization form={form} />
|
||||
<LdapSettingsKerberosIntegration form={form} />
|
||||
<SettingsCache form={form} />
|
||||
<LdapSettingsAdvanced form={form} />
|
||||
</ScrollForm>
|
||||
<Form onSubmit={form.handleSubmit(save)}>
|
||||
<ActionGroup className="keycloak__form_actions">
|
||||
<Button
|
||||
isDisabled={!form.formState.isDirty}
|
||||
variant="primary"
|
||||
type="submit"
|
||||
data-testid="ldap-save"
|
||||
>
|
||||
{t("common:save")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="link"
|
||||
onClick={() => history.push(toUserFederation({ realm }))}
|
||||
data-testid="ldap-cancel"
|
||||
>
|
||||
{t("common:cancel")}
|
||||
</Button>
|
||||
</ActionGroup>
|
||||
</Form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -231,34 +141,23 @@ export default function UserFederationLdapSettings() {
|
|||
);
|
||||
};
|
||||
|
||||
const removeImportedUsers = async () => {
|
||||
try {
|
||||
if (id) {
|
||||
await adminClient.userStorageProvider.removeImportedUsers({ id });
|
||||
}
|
||||
addAlert(t("removeImportedUsersSuccess"), AlertVariant.success);
|
||||
} catch (error) {
|
||||
addError("user-federation:removeImportedUsersError", error);
|
||||
}
|
||||
};
|
||||
|
||||
const save = async (component: ldapComponentRepresentation) => {
|
||||
if (component.config?.periodicChangedUsersSync !== null) {
|
||||
if (component.config?.periodicChangedUsersSync === false) {
|
||||
if (component.config?.periodicChangedUsersSync !== undefined) {
|
||||
if (component.config.periodicChangedUsersSync === false) {
|
||||
component.config.changedSyncPeriod = ["-1"];
|
||||
}
|
||||
delete component.config?.periodicChangedUsersSync;
|
||||
delete component.config.periodicChangedUsersSync;
|
||||
}
|
||||
if (component.config?.periodicFullSync !== null) {
|
||||
if (component.config?.periodicFullSync === false) {
|
||||
if (component.config?.periodicFullSync !== undefined) {
|
||||
if (component.config.periodicFullSync === false) {
|
||||
component.config.fullSyncPeriod = ["-1"];
|
||||
}
|
||||
delete component.config?.periodicFullSync;
|
||||
delete component.config.periodicFullSync;
|
||||
}
|
||||
try {
|
||||
if (!id) {
|
||||
await adminClient.components.create(component);
|
||||
history.push(`/${realm}/user-federation`);
|
||||
history.push(toUserFederation({ realm }));
|
||||
} else {
|
||||
await adminClient.components.update({ id }, component);
|
||||
}
|
||||
|
@ -269,101 +168,15 @@ export default function UserFederationLdapSettings() {
|
|||
}
|
||||
};
|
||||
|
||||
const [toggleRemoveUsersDialog, RemoveUsersConfirm] = useConfirmDialog({
|
||||
titleKey: t("removeImportedUsers"),
|
||||
messageKey: t("removeImportedUsersMessage"),
|
||||
continueButtonLabel: "common:remove",
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
removeImportedUsers();
|
||||
addAlert(t("removeImportedUsersSuccess"), AlertVariant.success);
|
||||
} catch (error) {
|
||||
addError("user-federation:removeImportedUsersError", error);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
||||
titleKey: "user-federation:userFedDeleteConfirmTitle",
|
||||
messageKey: "user-federation:userFedDeleteConfirm",
|
||||
continueButtonLabel: "common:delete",
|
||||
continueButtonVariant: ButtonVariant.danger,
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
await adminClient.components.del({ id });
|
||||
addAlert(t("userFedDeletedSuccess"), AlertVariant.success);
|
||||
history.replace(`/${realm}/user-federation`);
|
||||
} catch (error) {
|
||||
addError("user-federation:userFedDeleteError", error);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const addLdapFormContent = () => {
|
||||
return (
|
||||
<>
|
||||
<ScrollForm
|
||||
sections={[
|
||||
t("generalOptions"),
|
||||
t("connectionAndAuthenticationSettings"),
|
||||
t("ldapSearchingAndUpdatingSettings"),
|
||||
t("synchronizationSettings"),
|
||||
t("kerberosIntegration"),
|
||||
t("cacheSettings"),
|
||||
t("advancedSettings"),
|
||||
]}
|
||||
>
|
||||
<LdapSettingsGeneral form={form} vendorEdit={!!id} />
|
||||
<LdapSettingsConnection form={form} edit={!!id} />
|
||||
<LdapSettingsSearching form={form} />
|
||||
<LdapSettingsSynchronization form={form} />
|
||||
<LdapSettingsKerberosIntegration form={form} />
|
||||
<SettingsCache form={form} />
|
||||
<LdapSettingsAdvanced form={form} />
|
||||
</ScrollForm>
|
||||
<Form onSubmit={form.handleSubmit(save)}>
|
||||
<ActionGroup className="keycloak__form_actions">
|
||||
<Button
|
||||
isDisabled={!form.formState.isDirty}
|
||||
variant="primary"
|
||||
type="submit"
|
||||
data-testid="ldap-save"
|
||||
>
|
||||
{t("common:save")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="link"
|
||||
onClick={() => history.push(`/${realm}/user-federation`)}
|
||||
data-testid="ldap-cancel"
|
||||
>
|
||||
{t("common:cancel")}
|
||||
</Button>
|
||||
</ActionGroup>
|
||||
</Form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<DeleteConfirm />
|
||||
<RemoveUsersConfirm />
|
||||
<Controller
|
||||
name="config.enabled[0]"
|
||||
defaultValue={["true"][0]}
|
||||
control={form.control}
|
||||
render={({ onChange, value }) => (
|
||||
<LdapSettingsHeader
|
||||
editMode={editMode}
|
||||
value={value}
|
||||
save={() => save(form.getValues())}
|
||||
onChange={onChange}
|
||||
toggleDeleteDialog={toggleDeleteDialog}
|
||||
toggleRemoveUsersDialog={toggleRemoveUsersDialog}
|
||||
/>
|
||||
)}
|
||||
<FormProvider {...form}>
|
||||
<ExtendedHeader
|
||||
provider="LDAP"
|
||||
noDivider
|
||||
editMode={editMode}
|
||||
save={() => form.handleSubmit(save)()}
|
||||
/>
|
||||
<PageSection variant="light" isFilled>
|
||||
<PageSection variant="light" className="pf-u-p-0">
|
||||
{id ? (
|
||||
<KeycloakTabs isBox>
|
||||
<Tab
|
||||
|
@ -371,7 +184,9 @@ export default function UserFederationLdapSettings() {
|
|||
eventKey="settings"
|
||||
title={<TabTitleText>{t("common:settings")}</TabTitleText>}
|
||||
>
|
||||
{addLdapFormContent()}
|
||||
<PageSection variant="light">
|
||||
<AddLdapFormContent save={save} />
|
||||
</PageSection>
|
||||
</Tab>
|
||||
<Tab
|
||||
id="mappers"
|
||||
|
@ -379,13 +194,17 @@ export default function UserFederationLdapSettings() {
|
|||
title={<TabTitleText>{t("common:mappers")}</TabTitleText>}
|
||||
data-testid="ldap-mappers-tab"
|
||||
>
|
||||
<LdapMapperList />
|
||||
<PageSection>
|
||||
<LdapMapperList />
|
||||
</PageSection>
|
||||
</Tab>
|
||||
</KeycloakTabs>
|
||||
) : (
|
||||
addLdapFormContent()
|
||||
<PageSection variant="light">
|
||||
<AddLdapFormContent save={save} />
|
||||
</PageSection>
|
||||
)}
|
||||
</PageSection>
|
||||
</>
|
||||
</FormProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,15 +15,19 @@ import {
|
|||
} from "@patternfly/react-core";
|
||||
import { DatabaseIcon } from "@patternfly/react-icons";
|
||||
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
|
||||
import React, { useState } from "react";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useHistory, useRouteMatch } from "react-router-dom";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { useAlerts } from "../components/alert/Alerts";
|
||||
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
||||
import { KeycloakCard } from "../components/keycloak-card/KeycloakCard";
|
||||
import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||
import { useAdminClient, useFetch } from "../context/auth/AdminClient";
|
||||
import { useRealm } from "../context/realm-context/RealmContext";
|
||||
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
||||
import { toUpperCase } from "../util";
|
||||
import { toProvider } from "./routes/NewProvider";
|
||||
|
||||
import "./user-federation.css";
|
||||
import helpUrls from "../help-urls";
|
||||
|
||||
|
@ -37,9 +41,13 @@ export default function UserFederationSection() {
|
|||
const [key, setKey] = useState(0);
|
||||
const refresh = () => setKey(new Date().getTime());
|
||||
|
||||
const { url } = useRouteMatch();
|
||||
const history = useHistory();
|
||||
|
||||
const providers =
|
||||
useServerInfo().componentTypes?.[
|
||||
"org.keycloak.storage.UserStorageProvider"
|
||||
] || [];
|
||||
|
||||
useFetch(
|
||||
async () => {
|
||||
const realmModel = await adminClient.realms.findOne({ realm });
|
||||
|
@ -55,20 +63,20 @@ export default function UserFederationSection() {
|
|||
[key]
|
||||
);
|
||||
|
||||
const ufAddProviderDropdownItems = [
|
||||
<DropdownItem
|
||||
key="itemLDAP"
|
||||
onClick={() => history.push(`${url}/ldap/new`)}
|
||||
>
|
||||
LDAP
|
||||
</DropdownItem>,
|
||||
<DropdownItem
|
||||
key="itemKerberos"
|
||||
onClick={() => history.push(`${url}/kerberos/new`)}
|
||||
>
|
||||
Kerberos
|
||||
</DropdownItem>,
|
||||
];
|
||||
const ufAddProviderDropdownItems = useMemo(
|
||||
() =>
|
||||
providers.map((p) => (
|
||||
<DropdownItem
|
||||
key={p.id}
|
||||
onClick={() =>
|
||||
history.push(toProvider({ realm, providerId: p.id!, id: "new" }))
|
||||
}
|
||||
>
|
||||
{toUpperCase(p.id)}
|
||||
</DropdownItem>
|
||||
)),
|
||||
[]
|
||||
);
|
||||
|
||||
// const learnMoreLinkProps = {
|
||||
// title: t("common:learnMore"),
|
||||
|
@ -122,9 +130,7 @@ export default function UserFederationSection() {
|
|||
dropdownItems={ufCardDropdownItems}
|
||||
providerId={userFederation.providerId!}
|
||||
title={userFederation.name!}
|
||||
footerText={
|
||||
userFederation.providerId === "ldap" ? "LDAP" : "Kerberos"
|
||||
}
|
||||
footerText={toUpperCase(userFederation.providerId!)}
|
||||
labelText={
|
||||
userFederation.config!["enabled"][0] !== "false"
|
||||
? `${t("common:enabled")}`
|
||||
|
@ -170,36 +176,31 @@ export default function UserFederationSection() {
|
|||
</TextContent>
|
||||
<hr className="pf-u-mb-lg" />
|
||||
<Gallery hasGutter>
|
||||
<Card
|
||||
className="keycloak-empty-state-card"
|
||||
isHoverable
|
||||
onClick={() => history.push(`${url}/kerberos/new`)}
|
||||
data-testid="kerberos-card"
|
||||
>
|
||||
<CardTitle>
|
||||
<Split hasGutter>
|
||||
<SplitItem>
|
||||
<DatabaseIcon size="lg" />
|
||||
</SplitItem>
|
||||
<SplitItem isFilled>{t("addKerberos")}</SplitItem>
|
||||
</Split>
|
||||
</CardTitle>
|
||||
</Card>
|
||||
<Card
|
||||
className="keycloak-empty-state-card"
|
||||
isHoverable
|
||||
onClick={() => history.push(`${url}/ldap/new`)}
|
||||
data-testid="ldap-card"
|
||||
>
|
||||
<CardTitle>
|
||||
<Split hasGutter>
|
||||
<SplitItem>
|
||||
<DatabaseIcon size="lg" />
|
||||
</SplitItem>
|
||||
<SplitItem isFilled>{t("addLdap")}</SplitItem>
|
||||
</Split>
|
||||
</CardTitle>
|
||||
</Card>
|
||||
{providers.map((p) => (
|
||||
<Card
|
||||
key={p.id}
|
||||
className="keycloak-empty-state-card"
|
||||
isHoverable
|
||||
onClick={() =>
|
||||
history.push(toProvider({ realm, providerId: p.id! }))
|
||||
}
|
||||
data-testid={`${p.id}-card`}
|
||||
>
|
||||
<CardTitle>
|
||||
<Split hasGutter>
|
||||
<SplitItem>
|
||||
<DatabaseIcon size="lg" />
|
||||
</SplitItem>
|
||||
<SplitItem isFilled>
|
||||
{t("addProvider", {
|
||||
provider: toUpperCase(p.id!),
|
||||
count: 4,
|
||||
})}
|
||||
</SplitItem>
|
||||
</Split>
|
||||
</CardTitle>
|
||||
</Card>
|
||||
))}
|
||||
</Gallery>
|
||||
</>
|
||||
)}
|
||||
|
|
141
src/user-federation/custom/CustomProviderSettings.tsx
Normal file
141
src/user-federation/custom/CustomProviderSettings.tsx
Normal file
|
@ -0,0 +1,141 @@
|
|||
import React from "react";
|
||||
import { useHistory, useParams } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FormProvider, useForm } from "react-hook-form";
|
||||
import {
|
||||
ActionGroup,
|
||||
AlertVariant,
|
||||
Button,
|
||||
FormGroup,
|
||||
PageSection,
|
||||
TextInput,
|
||||
} from "@patternfly/react-core";
|
||||
|
||||
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
|
||||
import type { ProviderRouteParams } from "../routes/NewProvider";
|
||||
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
||||
import { FormAccess } from "../../components/form-access/FormAccess";
|
||||
import { toUserFederation } from "../routes/UserFederation";
|
||||
import { useAdminClient, useFetch } from "../../context/auth/AdminClient";
|
||||
import { useRealm } from "../../context/realm-context/RealmContext";
|
||||
import { useAlerts } from "../../components/alert/Alerts";
|
||||
import { SettingsCache } from "../shared/SettingsCache";
|
||||
import { ExtendedHeader } from "../shared/ExtendedHeader";
|
||||
|
||||
import "./custom-provider-settings.css";
|
||||
|
||||
export default function CustomProviderSettings() {
|
||||
const { t } = useTranslation("user-federation");
|
||||
const { id, providerId } = useParams<ProviderRouteParams>();
|
||||
const history = useHistory();
|
||||
const form = useForm<ComponentRepresentation>({
|
||||
mode: "onChange",
|
||||
});
|
||||
const {
|
||||
register,
|
||||
errors,
|
||||
reset,
|
||||
handleSubmit,
|
||||
formState: { isDirty },
|
||||
} = form;
|
||||
|
||||
const adminClient = useAdminClient();
|
||||
const { addAlert, addError } = useAlerts();
|
||||
const { realm } = useRealm();
|
||||
|
||||
useFetch(
|
||||
async () => {
|
||||
if (id) {
|
||||
return await adminClient.components.findOne({ id });
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
(fetchedComponent) => {
|
||||
if (fetchedComponent) {
|
||||
reset({ ...fetchedComponent });
|
||||
} else if (id) {
|
||||
throw new Error(t("common:notFound"));
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const save = async (component: ComponentRepresentation) => {
|
||||
const saveComponent = {
|
||||
...component,
|
||||
providerId,
|
||||
providerType: "org.keycloak.storage.UserStorageProvider",
|
||||
parentId: realm,
|
||||
};
|
||||
try {
|
||||
if (!id) {
|
||||
await adminClient.components.create(saveComponent);
|
||||
history.push(toUserFederation({ realm }));
|
||||
} else {
|
||||
await adminClient.components.update({ id }, saveComponent);
|
||||
}
|
||||
reset({ ...component });
|
||||
addAlert(t(!id ? "createSuccess" : "saveSuccess"), AlertVariant.success);
|
||||
} catch (error) {
|
||||
addError(`user-federation:${!id ? "createError" : "saveError"}`, error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
<ExtendedHeader provider={providerId} save={() => handleSubmit(save)()} />
|
||||
<PageSection variant="light">
|
||||
<FormAccess
|
||||
role="manage-realm"
|
||||
isHorizontal
|
||||
className="keycloak__user-federation__custom-form"
|
||||
onSubmit={handleSubmit(save)}
|
||||
>
|
||||
<FormGroup
|
||||
label={t("consoleDisplayName")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="users-federation-help:consoleDisplayNameHelp"
|
||||
fieldLabelId="users-federation:consoleDisplayName"
|
||||
/>
|
||||
}
|
||||
helperTextInvalid={t("validateName")}
|
||||
validated={errors.name ? "error" : "default"}
|
||||
fieldId="kc-console-display-name"
|
||||
isRequired
|
||||
>
|
||||
<TextInput
|
||||
isRequired
|
||||
type="text"
|
||||
id="kc-console-display-name"
|
||||
name="name"
|
||||
ref={register({
|
||||
required: true,
|
||||
})}
|
||||
data-testid="console-name"
|
||||
validated={errors.name ? "error" : "default"}
|
||||
/>
|
||||
</FormGroup>
|
||||
<SettingsCache form={form} unWrap />
|
||||
<ActionGroup>
|
||||
<Button
|
||||
isDisabled={!isDirty}
|
||||
variant="primary"
|
||||
type="submit"
|
||||
data-testid="custom-save"
|
||||
>
|
||||
{t("common:save")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="link"
|
||||
onClick={() => history.push(toUserFederation({ realm }))}
|
||||
data-testid="custom-cancel"
|
||||
>
|
||||
{t("common:cancel")}
|
||||
</Button>
|
||||
</ActionGroup>
|
||||
</FormAccess>
|
||||
</PageSection>
|
||||
</FormProvider>
|
||||
);
|
||||
}
|
3
src/user-federation/custom/custom-provider-settings.css
Normal file
3
src/user-federation/custom/custom-provider-settings.css
Normal file
|
@ -0,0 +1,3 @@
|
|||
.keycloak__user-federation__custom-form {
|
||||
--pf-c-form--m-horizontal__group-label--md--GridColumnWidth: 12rem;
|
||||
}
|
|
@ -7,9 +7,8 @@ export default {
|
|||
"Keycloak can federate external user databases. Out of the box we have support for LDAP and Active Directory.",
|
||||
getStarted: "To get started, select a provider from the list below.",
|
||||
providers: "Add providers",
|
||||
addKerberos: "Add Kerberos providers",
|
||||
addLdap: "Add LDAP providers",
|
||||
addOneLdap: "Add LDAP provider",
|
||||
addProvider_one: "Add {{provider}} provider",
|
||||
addProvider_other: "Add {{provider}} providers",
|
||||
addKerberosWizardTitle: "Add Kerberos user federation provider",
|
||||
addLdapWizardTitle: "Add LDAP user federation provider",
|
||||
|
||||
|
@ -97,6 +96,8 @@ export default {
|
|||
|
||||
learnMore: "Learn more",
|
||||
addNewProvider: "Add new provider",
|
||||
addCustomProvider: "Add custom provider",
|
||||
providerDetails: "Provider details",
|
||||
userFedDeletedSuccess: "The user federation provider has been deleted.",
|
||||
userFedDeleteError:
|
||||
"Could not delete user federation provider: '{{error}}'",
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import type { RouteDef } from "../route-config";
|
||||
import { NewKerberosUserFederationRoute } from "./routes/NewKerberosUserFederation";
|
||||
import { NewLdapUserFederationRoute } from "./routes/NewLdapUserFederation";
|
||||
import {
|
||||
CustomEditProviderRoute,
|
||||
CustomProviderRoute,
|
||||
} from "./routes/NewProvider";
|
||||
import { UserFederationRoute } from "./routes/UserFederation";
|
||||
import { UserFederationKerberosRoute } from "./routes/UserFederationKerberos";
|
||||
import { UserFederationLdapRoute } from "./routes/UserFederationLdap";
|
||||
|
@ -17,6 +21,8 @@ const routes: RouteDef[] = [
|
|||
NewLdapUserFederationRoute,
|
||||
UserFederationLdapRoute,
|
||||
UserFederationLdapMapperRoute,
|
||||
CustomProviderRoute,
|
||||
CustomEditProviderRoute,
|
||||
];
|
||||
|
||||
export default routes;
|
||||
|
|
|
@ -8,7 +8,8 @@ export type NewLdapUserFederationParams = { realm: string };
|
|||
export const NewLdapUserFederationRoute: RouteDef = {
|
||||
path: "/:realm/user-federation/ldap/new",
|
||||
component: lazy(() => import("../UserFederationLdapSettings")),
|
||||
breadcrumb: (t) => t("user-federation:addOneLdap"),
|
||||
breadcrumb: (t) =>
|
||||
t("user-federation:addProvider", { provider: "LDAP", count: 1 }),
|
||||
access: "view-realm",
|
||||
};
|
||||
|
||||
|
|
31
src/user-federation/routes/NewProvider.ts
Normal file
31
src/user-federation/routes/NewProvider.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import { lazy } from "react";
|
||||
import { generatePath } from "react-router-dom";
|
||||
|
||||
import type { LocationDescriptorObject } from "history";
|
||||
import type { RouteDef } from "../../route-config";
|
||||
|
||||
export type ProviderRouteParams = {
|
||||
realm: string;
|
||||
providerId: string;
|
||||
id?: string;
|
||||
};
|
||||
|
||||
export const CustomProviderRoute: RouteDef = {
|
||||
path: "/:realm/user-federation/:providerId/new",
|
||||
component: lazy(() => import("../custom/CustomProviderSettings")),
|
||||
breadcrumb: (t) => t("user-federation:addCustomProvider"),
|
||||
access: "view-realm",
|
||||
};
|
||||
|
||||
export const CustomEditProviderRoute: RouteDef = {
|
||||
path: "/:realm/user-federation/:providerId/:id",
|
||||
component: lazy(() => import("../custom/CustomProviderSettings")),
|
||||
breadcrumb: (t) => t("user-federation:providerDetails"),
|
||||
access: "view-realm",
|
||||
};
|
||||
|
||||
export const toProvider = (
|
||||
params: ProviderRouteParams
|
||||
): LocationDescriptorObject => ({
|
||||
pathname: generatePath(CustomProviderRoute.path, params),
|
||||
});
|
150
src/user-federation/shared/ExtendedHeader.tsx
Normal file
150
src/user-federation/shared/ExtendedHeader.tsx
Normal file
|
@ -0,0 +1,150 @@
|
|||
import React from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
AlertVariant,
|
||||
DropdownItem,
|
||||
DropdownSeparator,
|
||||
} from "@patternfly/react-core";
|
||||
|
||||
import { useAlerts } from "../../components/alert/Alerts";
|
||||
import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog";
|
||||
import { useAdminClient } from "../../context/auth/AdminClient";
|
||||
import { Header } from "./Header";
|
||||
|
||||
type ExtendedHeaderProps = {
|
||||
provider: string;
|
||||
editMode?: string | string[];
|
||||
save: () => void;
|
||||
noDivider?: boolean;
|
||||
};
|
||||
|
||||
export const ExtendedHeader = ({
|
||||
provider,
|
||||
editMode,
|
||||
save,
|
||||
noDivider = false,
|
||||
}: ExtendedHeaderProps) => {
|
||||
const { t } = useTranslation("user-federation");
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const adminClient = useAdminClient();
|
||||
const { addAlert, addError } = useAlerts();
|
||||
|
||||
const [toggleUnlinkUsersDialog, UnlinkUsersDialog] = useConfirmDialog({
|
||||
titleKey: "user-federation:userFedUnlinkUsersConfirmTitle",
|
||||
messageKey: "user-federation:userFedUnlinkUsersConfirm",
|
||||
continueButtonLabel: "user-federation:unlinkUsers",
|
||||
onConfirm: () => unlinkUsers(),
|
||||
});
|
||||
|
||||
const [toggleRemoveUsersDialog, RemoveUsersConfirm] = useConfirmDialog({
|
||||
titleKey: t("removeImportedUsers"),
|
||||
messageKey: t("removeImportedUsersMessage"),
|
||||
continueButtonLabel: "common:remove",
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
removeImportedUsers();
|
||||
addAlert(t("removeImportedUsersSuccess"), AlertVariant.success);
|
||||
} catch (error) {
|
||||
addError("user-federation:removeImportedUsersError", error);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const removeImportedUsers = async () => {
|
||||
try {
|
||||
if (id) {
|
||||
await adminClient.userStorageProvider.removeImportedUsers({ id });
|
||||
}
|
||||
addAlert(t("removeImportedUsersSuccess"), AlertVariant.success);
|
||||
} catch (error) {
|
||||
addError("user-federation:removeImportedUsersError", error);
|
||||
}
|
||||
};
|
||||
|
||||
const syncChangedUsers = async () => {
|
||||
try {
|
||||
if (id) {
|
||||
const response = await adminClient.userStorageProvider.sync({
|
||||
id: id,
|
||||
action: "triggerChangedUsersSync",
|
||||
});
|
||||
if (response.ignored) {
|
||||
addAlert(`${response.status}.`, AlertVariant.warning);
|
||||
} else {
|
||||
addAlert(
|
||||
t("syncUsersSuccess") +
|
||||
`${response.added} users added, ${response.updated} users updated, ${response.removed} users removed, ${response.failed} users failed.`,
|
||||
AlertVariant.success
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
addError("user-federation:syncUsersError", error);
|
||||
}
|
||||
};
|
||||
|
||||
const syncAllUsers = async () => {
|
||||
try {
|
||||
if (id) {
|
||||
const response = await adminClient.userStorageProvider.sync({
|
||||
id: id,
|
||||
action: "triggerFullSync",
|
||||
});
|
||||
if (response.ignored) {
|
||||
addAlert(`${response.status}.`, AlertVariant.warning);
|
||||
} else {
|
||||
addAlert(
|
||||
t("syncUsersSuccess") +
|
||||
`${response.added} users added, ${response.updated} users updated, ${response.removed} users removed, ${response.failed} users failed.`,
|
||||
AlertVariant.success
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
addError("user-federation:syncUsersError", error);
|
||||
}
|
||||
};
|
||||
|
||||
const unlinkUsers = async () => {
|
||||
try {
|
||||
if (id) {
|
||||
await adminClient.userStorageProvider.unlinkUsers({ id });
|
||||
}
|
||||
addAlert(t("unlinkUsersSuccess"), AlertVariant.success);
|
||||
} catch (error) {
|
||||
addError("user-federation:unlinkUsersError", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<UnlinkUsersDialog />
|
||||
<RemoveUsersConfirm />
|
||||
<Header
|
||||
provider={provider}
|
||||
noDivider={noDivider}
|
||||
save={save}
|
||||
dropdownItems={[
|
||||
<DropdownItem key="sync" onClick={syncChangedUsers}>
|
||||
{t("syncChangedUsers")}
|
||||
</DropdownItem>,
|
||||
<DropdownItem key="syncall" onClick={syncAllUsers}>
|
||||
{t("syncAllUsers")}
|
||||
</DropdownItem>,
|
||||
<DropdownItem
|
||||
key="unlink"
|
||||
isDisabled={editMode ? !editMode.includes("UNSYNCED") : false}
|
||||
onClick={toggleUnlinkUsersDialog}
|
||||
>
|
||||
{t("unlinkUsers")}
|
||||
</DropdownItem>,
|
||||
<DropdownItem key="remove" onClick={toggleRemoveUsersDialog}>
|
||||
{t("removeImported")}
|
||||
</DropdownItem>,
|
||||
<DropdownSeparator key="separator" />,
|
||||
]}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
113
src/user-federation/shared/Header.tsx
Normal file
113
src/user-federation/shared/Header.tsx
Normal file
|
@ -0,0 +1,113 @@
|
|||
import React, { ReactElement } from "react";
|
||||
import { useHistory, useParams } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
AlertVariant,
|
||||
ButtonVariant,
|
||||
DropdownItem,
|
||||
} from "@patternfly/react-core";
|
||||
|
||||
import type { ProviderRouteParams } from "../routes/NewProvider";
|
||||
import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog";
|
||||
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
||||
import { useAdminClient } from "../../context/auth/AdminClient";
|
||||
import { useAlerts } from "../../components/alert/Alerts";
|
||||
import { useRealm } from "../../context/realm-context/RealmContext";
|
||||
import { toUserFederation } from "../routes/UserFederation";
|
||||
import { Controller, useFormContext } from "react-hook-form";
|
||||
|
||||
type HeaderProps = {
|
||||
provider: string;
|
||||
save: () => void;
|
||||
dropdownItems?: ReactElement[];
|
||||
noDivider?: boolean;
|
||||
};
|
||||
|
||||
export const Header = ({
|
||||
provider,
|
||||
save,
|
||||
noDivider = false,
|
||||
dropdownItems = [],
|
||||
}: HeaderProps) => {
|
||||
const { t } = useTranslation("user-federation");
|
||||
const { id } = useParams<ProviderRouteParams>();
|
||||
const history = useHistory();
|
||||
|
||||
const adminClient = useAdminClient();
|
||||
const { addAlert, addError } = useAlerts();
|
||||
const { realm } = useRealm();
|
||||
|
||||
const { control, setValue } = useFormContext();
|
||||
|
||||
const [toggleDisableDialog, DisableConfirm] = useConfirmDialog({
|
||||
titleKey: "user-federation:userFedDisableConfirmTitle",
|
||||
messageKey: "user-federation:userFedDisableConfirm",
|
||||
continueButtonLabel: "common:disable",
|
||||
onConfirm: () => {
|
||||
setValue("config.enabled[0]", "false");
|
||||
save();
|
||||
},
|
||||
});
|
||||
|
||||
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
||||
titleKey: "user-federation:userFedDeleteConfirmTitle",
|
||||
messageKey: "user-federation:userFedDeleteConfirm",
|
||||
continueButtonLabel: "common:delete",
|
||||
continueButtonVariant: ButtonVariant.danger,
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
await adminClient.components.del({ id: id! });
|
||||
addAlert(t("userFedDeletedSuccess"), AlertVariant.success);
|
||||
history.replace(toUserFederation({ realm }));
|
||||
} catch (error) {
|
||||
addError("user-federation:userFedDeleteError", error);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<DisableConfirm />
|
||||
<DeleteConfirm />
|
||||
<Controller
|
||||
name="config.enabled[0]"
|
||||
defaultValue={["true"][0]}
|
||||
control={control}
|
||||
render={({ onChange, value }) =>
|
||||
!id ? (
|
||||
<ViewHeader
|
||||
titleKey={t("addProvider", {
|
||||
provider: provider,
|
||||
count: 1,
|
||||
})}
|
||||
/>
|
||||
) : (
|
||||
<ViewHeader
|
||||
divider={!noDivider}
|
||||
titleKey={provider}
|
||||
dropdownItems={[
|
||||
...dropdownItems,
|
||||
<DropdownItem
|
||||
key="delete"
|
||||
onClick={() => toggleDeleteDialog()}
|
||||
data-testid="delete-cmd"
|
||||
>
|
||||
{t("deleteProvider")}
|
||||
</DropdownItem>,
|
||||
]}
|
||||
isEnabled={value === "true"}
|
||||
onToggle={(value) => {
|
||||
if (!value) {
|
||||
toggleDisableDialog();
|
||||
} else {
|
||||
onChange(value.toString());
|
||||
save();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -6,44 +6,35 @@ import {
|
|||
TextInput,
|
||||
} from "@patternfly/react-core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import React, { useState } from "react";
|
||||
import React from "react";
|
||||
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
||||
import { UseFormMethods, useWatch, Controller } from "react-hook-form";
|
||||
import { FormAccess } from "../../components/form-access/FormAccess";
|
||||
import _ from "lodash";
|
||||
import { WizardSectionHeader } from "../../components/wizard-section-header/WizardSectionHeader";
|
||||
import useToggle from "../../utils/useToggle";
|
||||
|
||||
export type SettingsCacheProps = {
|
||||
form: UseFormMethods;
|
||||
showSectionHeading?: boolean;
|
||||
showSectionDescription?: boolean;
|
||||
unWrap?: boolean;
|
||||
};
|
||||
|
||||
export const SettingsCache = ({
|
||||
form,
|
||||
showSectionHeading = false,
|
||||
showSectionDescription = false,
|
||||
}: SettingsCacheProps) => {
|
||||
const CacheFields = ({ form }: { form: UseFormMethods }) => {
|
||||
const { t } = useTranslation("user-federation");
|
||||
const { t: helpText } = useTranslation("user-federation-help");
|
||||
|
||||
const [isCachePolicyDropdownOpen, setIsCachePolicyDropdownOpen] =
|
||||
useState(false);
|
||||
const [isCachePolicyOpen, toggleCachePolicy] = useToggle();
|
||||
const [isEvictionHourOpen, toggleEvictionHour] = useToggle();
|
||||
const [isEvictionMinuteOpen, toggleEvictionMinute] = useToggle();
|
||||
|
||||
const [isEvictionHourDropdownOpen, setIsEvictionHourDropdownOpen] =
|
||||
useState(false);
|
||||
const [isEvictionDayOpen, toggleEvictionDay] = useToggle();
|
||||
|
||||
const cachePolicyType = useWatch({
|
||||
control: form.control,
|
||||
name: "config.cachePolicy",
|
||||
});
|
||||
|
||||
const [isEvictionMinuteDropdownOpen, setIsEvictionMinuteDropdownOpen] =
|
||||
useState(false);
|
||||
|
||||
const [isEvictionDayDropdownOpen, setIsEvictionDayDropdownOpen] =
|
||||
useState(false);
|
||||
|
||||
const hourOptions = [
|
||||
<SelectOption key={0} value={[`${0}`]} isPlaceholder>
|
||||
{[`0${0}`]}
|
||||
|
@ -82,6 +73,203 @@ export const SettingsCache = ({
|
|||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormGroup
|
||||
label={t("cachePolicy")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="user-federation-help:cachePolicyHelp"
|
||||
fieldLabelId="user-federation:cachePolicy"
|
||||
/>
|
||||
}
|
||||
fieldId="kc-cache-policy"
|
||||
>
|
||||
<Controller
|
||||
name="config.cachePolicy"
|
||||
defaultValue={["DEFAULT"]}
|
||||
control={form.control}
|
||||
render={({ onChange, value }) => (
|
||||
<Select
|
||||
toggleId="kc-cache-policy"
|
||||
required
|
||||
onToggle={toggleCachePolicy}
|
||||
isOpen={isCachePolicyOpen}
|
||||
onSelect={(_, value) => {
|
||||
onChange(value as string);
|
||||
toggleCachePolicy();
|
||||
}}
|
||||
selections={value}
|
||||
variant={SelectVariant.single}
|
||||
data-testid="kerberos-cache-policy"
|
||||
>
|
||||
<SelectOption key={0} value={["DEFAULT"]} isPlaceholder />
|
||||
<SelectOption key={1} value={["EVICT_DAILY"]} />
|
||||
<SelectOption key={2} value={["EVICT_WEEKLY"]} />
|
||||
<SelectOption key={3} value={["MAX_LIFESPAN"]} />
|
||||
<SelectOption key={4} value={["NO_CACHE"]} />
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
{_.isEqual(cachePolicyType, ["EVICT_WEEKLY"]) ? (
|
||||
<FormGroup
|
||||
label={t("evictionDay")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="user-federation-help:evictionDayHelp"
|
||||
fieldLabelId="user-federation:evictionDay"
|
||||
/>
|
||||
}
|
||||
isRequired
|
||||
fieldId="kc-eviction-day"
|
||||
>
|
||||
<Controller
|
||||
name="config.evictionDay[0]"
|
||||
defaultValue={"1"}
|
||||
control={form.control}
|
||||
render={({ onChange, value }) => (
|
||||
<Select
|
||||
data-testid="cache-day"
|
||||
toggleId="kc-eviction-day"
|
||||
required
|
||||
onToggle={toggleEvictionDay}
|
||||
isOpen={isEvictionDayOpen}
|
||||
onSelect={(_, value) => {
|
||||
onChange(value as string);
|
||||
toggleEvictionDay();
|
||||
}}
|
||||
selections={value}
|
||||
variant={SelectVariant.single}
|
||||
>
|
||||
<SelectOption key={0} value="1" isPlaceholder>
|
||||
{t("common:Sunday")}
|
||||
</SelectOption>
|
||||
<SelectOption key={1} value="2">
|
||||
{t("common:Monday")}
|
||||
</SelectOption>
|
||||
<SelectOption key={2} value="3">
|
||||
{t("common:Tuesday")}
|
||||
</SelectOption>
|
||||
<SelectOption key={3} value="4">
|
||||
{t("common:Wednesday")}
|
||||
</SelectOption>
|
||||
<SelectOption key={4} value="5">
|
||||
{t("common:Thursday")}
|
||||
</SelectOption>
|
||||
<SelectOption key={5} value="6">
|
||||
{t("common:Friday")}
|
||||
</SelectOption>
|
||||
<SelectOption key={6} value="7">
|
||||
{t("common:Saturday")}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
) : null}
|
||||
{_.isEqual(cachePolicyType, ["EVICT_DAILY"]) ||
|
||||
_.isEqual(cachePolicyType, ["EVICT_WEEKLY"]) ? (
|
||||
<>
|
||||
<FormGroup
|
||||
label={t("evictionHour")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="user-federation-help:evictionHourHelp"
|
||||
fieldLabelId="user-federation:evictionHour"
|
||||
/>
|
||||
}
|
||||
isRequired
|
||||
fieldId="kc-eviction-hour"
|
||||
>
|
||||
<Controller
|
||||
name="config.evictionHour"
|
||||
defaultValue={["0"]}
|
||||
control={form.control}
|
||||
render={({ onChange, value }) => (
|
||||
<Select
|
||||
toggleId="kc-eviction-hour"
|
||||
onToggle={toggleEvictionHour}
|
||||
isOpen={isEvictionHourOpen}
|
||||
onSelect={(_, value) => {
|
||||
onChange(value as string);
|
||||
toggleEvictionHour();
|
||||
}}
|
||||
selections={value}
|
||||
variant={SelectVariant.single}
|
||||
>
|
||||
{hourOptions}
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={t("evictionMinute")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="user-federation-help:evictionMinuteHelp"
|
||||
fieldLabelId="user-federation:evictionMinute"
|
||||
/>
|
||||
}
|
||||
isRequired
|
||||
fieldId="kc-eviction-minute"
|
||||
>
|
||||
<Controller
|
||||
name="config.evictionMinute"
|
||||
defaultValue={["0"]}
|
||||
control={form.control}
|
||||
render={({ onChange, value }) => (
|
||||
<Select
|
||||
toggleId="kc-eviction-minute"
|
||||
onToggle={toggleEvictionMinute}
|
||||
isOpen={isEvictionMinuteOpen}
|
||||
onSelect={(_, value) => {
|
||||
onChange(value as string);
|
||||
toggleEvictionMinute();
|
||||
}}
|
||||
selections={value}
|
||||
variant={SelectVariant.single}
|
||||
>
|
||||
{minuteOptions}
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
</>
|
||||
) : null}
|
||||
{_.isEqual(cachePolicyType, ["MAX_LIFESPAN"]) ? (
|
||||
<FormGroup
|
||||
label={t("maxLifespan")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="user-federation-help:maxLifespanHelp"
|
||||
fieldLabelId="user-federation:maxLifespan"
|
||||
/>
|
||||
}
|
||||
fieldId="kc-max-lifespan"
|
||||
>
|
||||
<TextInput
|
||||
type="text"
|
||||
id="kc-max-lifespan"
|
||||
name="config.maxLifespan[0]"
|
||||
ref={form.register}
|
||||
data-testid="kerberos-cache-lifespan"
|
||||
/>
|
||||
</FormGroup>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const SettingsCache = ({
|
||||
form,
|
||||
showSectionHeading = false,
|
||||
showSectionDescription = false,
|
||||
unWrap = false,
|
||||
}: SettingsCacheProps) => {
|
||||
const { t } = useTranslation("user-federation");
|
||||
const { t: helpText } = useTranslation("user-federation-help");
|
||||
|
||||
return (
|
||||
<>
|
||||
{showSectionHeading && (
|
||||
|
@ -91,200 +279,13 @@ export const SettingsCache = ({
|
|||
showDescription={showSectionDescription}
|
||||
/>
|
||||
)}
|
||||
<FormAccess role="manage-realm" isHorizontal>
|
||||
<FormGroup
|
||||
label={t("cachePolicy")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="user-federation-help:cachePolicyHelp"
|
||||
fieldLabelId="user-federation:cachePolicy"
|
||||
/>
|
||||
}
|
||||
fieldId="kc-cache-policy"
|
||||
>
|
||||
<Controller
|
||||
name="config.cachePolicy"
|
||||
defaultValue={["DEFAULT"]}
|
||||
control={form.control}
|
||||
render={({ onChange, value }) => (
|
||||
<Select
|
||||
toggleId="kc-cache-policy"
|
||||
required
|
||||
onToggle={() =>
|
||||
setIsCachePolicyDropdownOpen(!isCachePolicyDropdownOpen)
|
||||
}
|
||||
isOpen={isCachePolicyDropdownOpen}
|
||||
onSelect={(_, value) => {
|
||||
onChange(value as string);
|
||||
setIsCachePolicyDropdownOpen(false);
|
||||
}}
|
||||
selections={value}
|
||||
variant={SelectVariant.single}
|
||||
data-testid="kerberos-cache-policy"
|
||||
>
|
||||
<SelectOption key={0} value={["DEFAULT"]} isPlaceholder />
|
||||
<SelectOption key={1} value={["EVICT_DAILY"]} />
|
||||
<SelectOption key={2} value={["EVICT_WEEKLY"]} />
|
||||
<SelectOption key={3} value={["MAX_LIFESPAN"]} />
|
||||
<SelectOption key={4} value={["NO_CACHE"]} />
|
||||
</Select>
|
||||
)}
|
||||
></Controller>
|
||||
</FormGroup>
|
||||
{_.isEqual(cachePolicyType, ["EVICT_WEEKLY"]) ? (
|
||||
<FormGroup
|
||||
label={t("evictionDay")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="user-federation-help:evictionDayHelp"
|
||||
fieldLabelId="user-federation:evictionDay"
|
||||
/>
|
||||
}
|
||||
isRequired
|
||||
fieldId="kc-eviction-day"
|
||||
>
|
||||
<Controller
|
||||
name="config.evictionDay[0]"
|
||||
defaultValue={"1"}
|
||||
control={form.control}
|
||||
render={({ onChange, value }) => (
|
||||
<Select
|
||||
data-testid="cache-day"
|
||||
toggleId="kc-eviction-day"
|
||||
required
|
||||
onToggle={() =>
|
||||
setIsEvictionDayDropdownOpen(!isEvictionDayDropdownOpen)
|
||||
}
|
||||
isOpen={isEvictionDayDropdownOpen}
|
||||
onSelect={(_, value) => {
|
||||
onChange(value as string);
|
||||
setIsEvictionDayDropdownOpen(false);
|
||||
}}
|
||||
selections={value}
|
||||
variant={SelectVariant.single}
|
||||
>
|
||||
<SelectOption key={0} value="1" isPlaceholder>
|
||||
{t("common:Sunday")}
|
||||
</SelectOption>
|
||||
<SelectOption key={1} value="2">
|
||||
{t("common:Monday")}
|
||||
</SelectOption>
|
||||
<SelectOption key={2} value="3">
|
||||
{t("common:Tuesday")}
|
||||
</SelectOption>
|
||||
<SelectOption key={3} value="4">
|
||||
{t("common:Wednesday")}
|
||||
</SelectOption>
|
||||
<SelectOption key={4} value="5">
|
||||
{t("common:Thursday")}
|
||||
</SelectOption>
|
||||
<SelectOption key={5} value="6">
|
||||
{t("common:Friday")}
|
||||
</SelectOption>
|
||||
<SelectOption key={6} value="7">
|
||||
{t("common:Saturday")}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
)}
|
||||
></Controller>
|
||||
</FormGroup>
|
||||
) : null}
|
||||
{_.isEqual(cachePolicyType, ["EVICT_DAILY"]) ||
|
||||
_.isEqual(cachePolicyType, ["EVICT_WEEKLY"]) ? (
|
||||
<>
|
||||
<FormGroup
|
||||
label={t("evictionHour")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="user-federation-help:evictionHourHelp"
|
||||
fieldLabelId="user-federation:evictionHour"
|
||||
/>
|
||||
}
|
||||
isRequired
|
||||
fieldId="kc-eviction-hour"
|
||||
>
|
||||
<Controller
|
||||
name="config.evictionHour"
|
||||
defaultValue={["0"]}
|
||||
control={form.control}
|
||||
render={({ onChange, value }) => (
|
||||
<Select
|
||||
toggleId="kc-eviction-hour"
|
||||
onToggle={() =>
|
||||
setIsEvictionHourDropdownOpen(!isEvictionHourDropdownOpen)
|
||||
}
|
||||
isOpen={isEvictionHourDropdownOpen}
|
||||
onSelect={(_, value) => {
|
||||
onChange(value as string);
|
||||
setIsEvictionHourDropdownOpen(false);
|
||||
}}
|
||||
selections={value}
|
||||
variant={SelectVariant.single}
|
||||
>
|
||||
{hourOptions}
|
||||
</Select>
|
||||
)}
|
||||
></Controller>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={t("evictionMinute")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="user-federation-help:evictionMinuteHelp"
|
||||
fieldLabelId="user-federation:evictionMinute"
|
||||
/>
|
||||
}
|
||||
isRequired
|
||||
fieldId="kc-eviction-minute"
|
||||
>
|
||||
<Controller
|
||||
name="config.evictionMinute"
|
||||
defaultValue={["0"]}
|
||||
control={form.control}
|
||||
render={({ onChange, value }) => (
|
||||
<Select
|
||||
toggleId="kc-eviction-minute"
|
||||
onToggle={() =>
|
||||
setIsEvictionMinuteDropdownOpen(
|
||||
!isEvictionMinuteDropdownOpen
|
||||
)
|
||||
}
|
||||
isOpen={isEvictionMinuteDropdownOpen}
|
||||
onSelect={(_, value) => {
|
||||
onChange(value as string);
|
||||
setIsEvictionMinuteDropdownOpen(false);
|
||||
}}
|
||||
selections={value}
|
||||
variant={SelectVariant.single}
|
||||
>
|
||||
{minuteOptions}
|
||||
</Select>
|
||||
)}
|
||||
></Controller>
|
||||
</FormGroup>
|
||||
</>
|
||||
) : null}
|
||||
{_.isEqual(cachePolicyType, ["MAX_LIFESPAN"]) ? (
|
||||
<FormGroup
|
||||
label={t("maxLifespan")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="user-federation-help:maxLifespanHelp"
|
||||
fieldLabelId="user-federation:maxLifespan"
|
||||
/>
|
||||
}
|
||||
fieldId="kc-max-lifespan"
|
||||
>
|
||||
<TextInput
|
||||
type="text"
|
||||
id="kc-max-lifespan"
|
||||
name="config.maxLifespan[0]"
|
||||
ref={form.register}
|
||||
data-testid="kerberos-cache-lifespan"
|
||||
/>
|
||||
</FormGroup>
|
||||
) : null}
|
||||
</FormAccess>
|
||||
{unWrap ? (
|
||||
<CacheFields form={form} />
|
||||
) : (
|
||||
<FormAccess role="manage-realm" isHorizontal>
|
||||
<CacheFields form={form} />
|
||||
</FormAccess>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue