Add priorities to User Federation (#2005)
This commit is contained in:
parent
d4b1230260
commit
f9d3f9dfa7
9 changed files with 264 additions and 26 deletions
|
@ -4,6 +4,7 @@ import ProviderPage from "../support/pages/admin_console/manage/providers/Provid
|
|||
import Masthead from "../support/pages/admin_console/Masthead";
|
||||
import ModalUtils from "../support/util/ModalUtils";
|
||||
import { keycloakBefore } from "../support/util/keycloak_hooks";
|
||||
import PriorityDialog from "../support/pages/admin_console/manage/providers/PriorityDialog";
|
||||
|
||||
const loginPage = new LoginPage();
|
||||
const masthead = new Masthead();
|
||||
|
@ -14,15 +15,20 @@ const modalUtils = new ModalUtils();
|
|||
const provider = "kerberos";
|
||||
const initCapProvider = provider.charAt(0).toUpperCase() + provider.slice(1);
|
||||
|
||||
const firstKerberosName = "my-kerberos";
|
||||
const firstKerberosRealm = "my-realm";
|
||||
const firstKerberosPrincipal = "my-principal";
|
||||
const firstKerberosKeytab = "my-keytab";
|
||||
const kerberosName = "my-kerberos";
|
||||
const kerberosRealm = "my-realm";
|
||||
const kerberosPrincipal = "my-principal";
|
||||
const kerberosKeytab = "my-keytab";
|
||||
|
||||
const secondKerberosName = `${firstKerberosName}-2`;
|
||||
const secondKerberosRealm = `${firstKerberosRealm}-2`;
|
||||
const secondKerberosPrincipal = `${firstKerberosPrincipal}-2`;
|
||||
const secondKerberosKeytab = `${firstKerberosKeytab}-2`;
|
||||
const firstKerberosName = `${kerberosName}-1`;
|
||||
const firstKerberosRealm = `${kerberosRealm}-1`;
|
||||
const firstKerberosPrincipal = `${kerberosPrincipal}-1`;
|
||||
const firstKerberosKeytab = `${kerberosKeytab}-1`;
|
||||
|
||||
const secondKerberosName = `${kerberosName}-2`;
|
||||
const secondKerberosRealm = `${kerberosRealm}-2`;
|
||||
const secondKerberosPrincipal = `${kerberosPrincipal}-2`;
|
||||
const secondKerberosKeytab = `${kerberosKeytab}-2`;
|
||||
|
||||
const defaultPolicy = "DEFAULT";
|
||||
const newPolicy = "EVICT_WEEKLY";
|
||||
|
@ -39,6 +45,8 @@ const savedSuccessMessage = "User federation provider successfully saved";
|
|||
const deletedSuccessMessage = "The user federation provider has been deleted.";
|
||||
const deleteModalTitle = "Delete user federation provider?";
|
||||
const disableModalTitle = "Disable user federation provider?";
|
||||
const changeSuccessMsg =
|
||||
"Successfully changed the priority order of user federation providers";
|
||||
|
||||
describe("User Fed Kerberos tests", () => {
|
||||
beforeEach(() => {
|
||||
|
@ -142,16 +150,27 @@ describe("User Fed Kerberos tests", () => {
|
|||
sidebarPage.goToUserFederation();
|
||||
});
|
||||
|
||||
it("Change the priority order of Kerberos providers", () => {
|
||||
const priorityDialog = new PriorityDialog();
|
||||
const providers = [firstKerberosName, secondKerberosName];
|
||||
|
||||
sidebarPage.goToUserFederation();
|
||||
providersPage.clickMenuCommand(addProviderMenu, initCapProvider);
|
||||
|
||||
sidebarPage.goToUserFederation();
|
||||
priorityDialog.openDialog().checkOrder(providers);
|
||||
priorityDialog.clickSave();
|
||||
masthead.checkNotificationMessage(changeSuccessMsg, true);
|
||||
});
|
||||
|
||||
it("Delete a Kerberos provider from card view using the card's menu", () => {
|
||||
providersPage.deleteCardFromCard(secondKerberosName);
|
||||
|
||||
modalUtils.checkModalTitle(deleteModalTitle).confirmModal();
|
||||
masthead.checkNotificationMessage(deletedSuccessMessage);
|
||||
});
|
||||
|
||||
it("Delete a Kerberos provider using the Settings view's Action menu", () => {
|
||||
providersPage.deleteCardFromMenu(firstKerberosName);
|
||||
|
||||
modalUtils.checkModalTitle(deleteModalTitle).confirmModal();
|
||||
masthead.checkNotificationMessage(deletedSuccessMessage);
|
||||
});
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
const expect = chai.expect;
|
||||
|
||||
export default class PriorityDialog {
|
||||
private managePriorityOrder = "viewHeader-lower-btn";
|
||||
private list = "manageOrderDataList";
|
||||
|
||||
openDialog() {
|
||||
cy.findByTestId(this.managePriorityOrder).click({ force: true });
|
||||
return this;
|
||||
}
|
||||
|
||||
moveRowTo(from: string, to: string) {
|
||||
cy.findByTestId(from).trigger("dragstart").trigger("dragleave");
|
||||
|
||||
cy.findByTestId(to)
|
||||
.trigger("dragenter")
|
||||
.trigger("dragover")
|
||||
.trigger("drop")
|
||||
.trigger("dragend");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
clickSave() {
|
||||
cy.get("#modal-confirm").click();
|
||||
return this;
|
||||
}
|
||||
|
||||
checkOrder(providerNames: string[]) {
|
||||
cy.get(`[data-testid=${this.list}] li`).should((providers) => {
|
||||
expect(providers).to.have.length(providerNames.length);
|
||||
for (let index = 0; index < providerNames.length; index++) {
|
||||
expect(providers.eq(index)).to.contain(providerNames[index]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import {
|
||||
Badge,
|
||||
Button,
|
||||
Divider,
|
||||
Dropdown,
|
||||
DropdownPosition,
|
||||
|
@ -38,6 +39,7 @@ export type ViewHeaderProps = {
|
|||
dropdownItems?: ReactElement[];
|
||||
lowerDropdownItems?: any;
|
||||
lowerDropdownMenuTitle?: any;
|
||||
lowerButton?: any;
|
||||
isEnabled?: boolean;
|
||||
onToggle?: (value: boolean) => void;
|
||||
divider?: boolean;
|
||||
|
@ -61,6 +63,7 @@ export const ViewHeader = ({
|
|||
dropdownItems,
|
||||
lowerDropdownMenuTitle,
|
||||
lowerDropdownItems,
|
||||
lowerButton,
|
||||
isEnabled = true,
|
||||
onToggle,
|
||||
divider = true,
|
||||
|
@ -191,6 +194,15 @@ export const ViewHeader = ({
|
|||
dropdownItems={lowerDropdownItems}
|
||||
/>
|
||||
)}
|
||||
{lowerButton && (
|
||||
<Button
|
||||
variant={lowerButton.variant}
|
||||
onClick={lowerButton.onClick}
|
||||
data-testid="viewHeader-lower-btn"
|
||||
>
|
||||
{lowerButton.lowerButtonTitle}
|
||||
</Button>
|
||||
)}
|
||||
</PageSection>
|
||||
{divider && <Divider component="div" />}
|
||||
</>
|
||||
|
|
|
@ -34,7 +34,7 @@ import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
|||
import { upperCaseFormatter } from "../util";
|
||||
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
||||
import { ProviderIconMapper } from "./ProviderIconMapper";
|
||||
import { ManageOderDialog } from "./ManageOrderDialog";
|
||||
import { ManageOrderDialog } from "./ManageOrderDialog";
|
||||
import { toIdentityProvider } from "./routes/IdentityProvider";
|
||||
import { toIdentityProviderCreate } from "./routes/IdentityProviderCreate";
|
||||
import helpUrls from "../help-urls";
|
||||
|
@ -161,7 +161,7 @@ export default function IdentityProvidersSection() {
|
|||
<>
|
||||
<DeleteConfirm />
|
||||
{manageDisplayDialog && (
|
||||
<ManageOderDialog
|
||||
<ManageOrderDialog
|
||||
onClose={() => setManageDisplayDialog(false)}
|
||||
providers={providers.filter((p) => p.enabled)}
|
||||
/>
|
||||
|
|
|
@ -21,15 +21,15 @@ import type IdentityProviderRepresentation from "@keycloak/keycloak-admin-client
|
|||
import { useAdminClient } from "../context/auth/AdminClient";
|
||||
import { useAlerts } from "../components/alert/Alerts";
|
||||
|
||||
type ManageOderDialogProps = {
|
||||
type ManageOrderDialogProps = {
|
||||
providers: IdentityProviderRepresentation[];
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
export const ManageOderDialog = ({
|
||||
export const ManageOrderDialog = ({
|
||||
providers,
|
||||
onClose,
|
||||
}: ManageOderDialogProps) => {
|
||||
}: ManageOrderDialogProps) => {
|
||||
const { t } = useTranslation("identity-providers");
|
||||
const adminClient = useAdminClient();
|
||||
const { addAlert, addError } = useAlerts();
|
||||
|
@ -98,7 +98,7 @@ export const ManageOderDialog = ({
|
|||
]}
|
||||
>
|
||||
<TextContent className="pf-u-pb-lg">
|
||||
<Text>{t("oderDialogIntro")}</Text>
|
||||
<Text>{t("orderDialogIntro")}</Text>
|
||||
</TextContent>
|
||||
|
||||
<DataList
|
||||
|
|
|
@ -41,7 +41,7 @@ export default {
|
|||
displayOrder: "Display order",
|
||||
createSuccess: "Identity provider successfully created",
|
||||
createError: "Could not create the identity provider: {{error}}",
|
||||
oderDialogIntro:
|
||||
orderDialogIntro:
|
||||
"The order that the providers are listed in the login page or the account console. You can drag the row handles to change the order.",
|
||||
manageOrderTableAria:
|
||||
"List of identity providers in the order listed on the login page",
|
||||
|
|
147
src/user-federation/ManagePriorityDialog.tsx
Normal file
147
src/user-federation/ManagePriorityDialog.tsx
Normal file
|
@ -0,0 +1,147 @@
|
|||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { sortBy } from "lodash-es";
|
||||
import {
|
||||
AlertVariant,
|
||||
Button,
|
||||
ButtonVariant,
|
||||
DataList,
|
||||
DataListCell,
|
||||
DataListControl,
|
||||
DataListDragButton,
|
||||
DataListItem,
|
||||
DataListItemCells,
|
||||
DataListItemRow,
|
||||
Modal,
|
||||
ModalVariant,
|
||||
TextContent,
|
||||
Text,
|
||||
} from "@patternfly/react-core";
|
||||
import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
|
||||
import { useAdminClient } from "../context/auth/AdminClient";
|
||||
import { useAlerts } from "../components/alert/Alerts";
|
||||
|
||||
type ManagePriorityDialogProps = {
|
||||
components: ComponentRepresentation[];
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
export const ManagePriorityDialog = ({
|
||||
components,
|
||||
onClose,
|
||||
}: ManagePriorityDialogProps) => {
|
||||
const { t } = useTranslation("user-federation");
|
||||
const adminClient = useAdminClient();
|
||||
const { addAlert, addError } = useAlerts();
|
||||
|
||||
const [id, setId] = useState("");
|
||||
const [liveText, setLiveText] = useState("");
|
||||
const [order, setOrder] = useState(
|
||||
components.map((component) => component.name!)
|
||||
);
|
||||
|
||||
const onDragStart = (id: string) => {
|
||||
setId(id);
|
||||
setLiveText(t("common:onDragStart", { item: id }));
|
||||
};
|
||||
|
||||
const onDragMove = () => {
|
||||
setLiveText(t("common:onDragMove", { item: id }));
|
||||
};
|
||||
|
||||
const onDragCancel = () => {
|
||||
setLiveText(t("common:onDragCancel"));
|
||||
};
|
||||
|
||||
const onDragFinish = (providerOrder: string[]) => {
|
||||
setLiveText(t("common:onDragFinish", { list: providerOrder }));
|
||||
setOrder(providerOrder);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
variant={ModalVariant.small}
|
||||
title={t("managePriorityOrder")}
|
||||
isOpen={true}
|
||||
onClose={onClose}
|
||||
actions={[
|
||||
<Button
|
||||
id="modal-confirm"
|
||||
key="confirm"
|
||||
onClick={() => {
|
||||
order.map(async (name, index) => {
|
||||
const component = components!.find((c) => c.name === name)!;
|
||||
component.config!.priority = [index.toString()];
|
||||
try {
|
||||
const id = component.id!;
|
||||
await adminClient.components.update({ id }, component);
|
||||
addAlert(t("orderChangeSuccess"), AlertVariant.success);
|
||||
} catch (error) {
|
||||
addError("orderChangeError", error);
|
||||
}
|
||||
});
|
||||
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
{t("common:save")}
|
||||
</Button>,
|
||||
<Button
|
||||
id="modal-cancel"
|
||||
key="cancel"
|
||||
variant={ButtonVariant.link}
|
||||
onClick={onClose}
|
||||
>
|
||||
{t("common:cancel")}
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<TextContent className="pf-u-pb-lg">
|
||||
<Text>{t("managePriorityInfo")}</Text>
|
||||
</TextContent>
|
||||
|
||||
<DataList
|
||||
aria-label={t("manageOrderTableAria")}
|
||||
data-testid="manageOrderDataList"
|
||||
isCompact
|
||||
onDragFinish={onDragFinish}
|
||||
onDragStart={onDragStart}
|
||||
onDragMove={onDragMove}
|
||||
onDragCancel={onDragCancel}
|
||||
itemOrder={order}
|
||||
>
|
||||
{sortBy(components, "config.priority").map((component) => (
|
||||
<DataListItem
|
||||
aria-labelledby={component.name}
|
||||
id={component.name}
|
||||
key={component.name}
|
||||
>
|
||||
<DataListItemRow>
|
||||
<DataListControl>
|
||||
<DataListDragButton
|
||||
aria-label="Reorder"
|
||||
aria-labelledby={component.name}
|
||||
aria-describedby={t("manageOrderItemAria")}
|
||||
aria-pressed="false"
|
||||
/>
|
||||
</DataListControl>
|
||||
<DataListItemCells
|
||||
dataListCells={[
|
||||
<DataListCell
|
||||
key={`${component.name}-cell`}
|
||||
data-testid={component.name}
|
||||
>
|
||||
<span id={component.name}>{component.name}</span>
|
||||
</DataListCell>,
|
||||
]}
|
||||
/>
|
||||
</DataListItemRow>
|
||||
</DataListItem>
|
||||
))}
|
||||
</DataList>
|
||||
<div className="pf-screen-reader" aria-live="assertive">
|
||||
{liveText}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
|
@ -20,6 +20,7 @@ import { useTranslation } from "react-i18next";
|
|||
import { useHistory } from "react-router-dom";
|
||||
import { useAlerts } from "../components/alert/Alerts";
|
||||
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
||||
import { ManagePriorityDialog } from "./ManagePriorityDialog";
|
||||
import { KeycloakCard } from "../components/keycloak-card/KeycloakCard";
|
||||
import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||
import { useAdminClient, useFetch } from "../context/auth/AdminClient";
|
||||
|
@ -43,6 +44,8 @@ export default function UserFederationSection() {
|
|||
|
||||
const history = useHistory();
|
||||
|
||||
const [manageDisplayDialog, setManageDisplayDialog] = useState(false);
|
||||
|
||||
const providers =
|
||||
useServerInfo().componentTypes?.[
|
||||
"org.keycloak.storage.UserStorageProvider"
|
||||
|
@ -80,10 +83,11 @@ export default function UserFederationSection() {
|
|||
[]
|
||||
);
|
||||
|
||||
// const learnMoreLinkProps = {
|
||||
// title: t("common:learnMore"),
|
||||
// href: "https://www.keycloak.org/docs/latest/server_admin/index.html#_user-storage-federation",
|
||||
// };
|
||||
const lowerButtonProps = {
|
||||
variant: "link",
|
||||
onClick: () => setManageDisplayDialog(true),
|
||||
lowerButtonTitle: t("managePriorities"),
|
||||
};
|
||||
|
||||
let cards;
|
||||
|
||||
|
@ -109,8 +113,14 @@ export default function UserFederationSection() {
|
|||
toggleDeleteDialog();
|
||||
};
|
||||
|
||||
const cardSorter = (card1: any, card2: any) => {
|
||||
const a = `${card1.name}`;
|
||||
const b = `${card2.name}`;
|
||||
return a < b ? -1 : 1;
|
||||
};
|
||||
|
||||
if (userFederations) {
|
||||
cards = userFederations.map((userFederation, index) => {
|
||||
cards = userFederations.sort(cardSorter).map((userFederation, index) => {
|
||||
const ufCardDropdownItems = [
|
||||
<DropdownItem
|
||||
key={`${index}-cardDelete`}
|
||||
|
@ -149,6 +159,13 @@ export default function UserFederationSection() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<DeleteConfirm />
|
||||
{manageDisplayDialog && userFederations && (
|
||||
<ManagePriorityDialog
|
||||
onClose={() => setManageDisplayDialog(false)}
|
||||
components={userFederations.filter((p) => p.config?.enabled)}
|
||||
/>
|
||||
)}
|
||||
<ViewHeader
|
||||
titleKey="userFederation"
|
||||
subKey="user-federation:userFederationExplain"
|
||||
|
@ -157,15 +174,13 @@ export default function UserFederationSection() {
|
|||
? {
|
||||
lowerDropdownItems: ufAddProviderDropdownItems,
|
||||
lowerDropdownMenuTitle: "user-federation:addNewProvider",
|
||||
lowerButton: lowerButtonProps,
|
||||
}
|
||||
: {})}
|
||||
/>
|
||||
<PageSection>
|
||||
{userFederations && userFederations.length > 0 ? (
|
||||
<>
|
||||
<DeleteConfirm />
|
||||
<Gallery hasGutter>{cards}</Gallery>
|
||||
</>
|
||||
<Gallery hasGutter>{cards}</Gallery>
|
||||
) : (
|
||||
<>
|
||||
<TextContent>
|
||||
|
|
|
@ -95,6 +95,14 @@ export default {
|
|||
"Error when trying to connect to LDAP. See server.log for details. {{error}}",
|
||||
|
||||
learnMore: "Learn more",
|
||||
managePriorities: "Manage priorities",
|
||||
managePriorityOrder: "Manage priority order",
|
||||
managePriorityInfo:
|
||||
"Priority is the order of providers when doing a user lookup. You can drag the row handlers to change the priorities.",
|
||||
orderChangeSuccess:
|
||||
"Successfully changed the priority order of user federation providers",
|
||||
orderChangeError:
|
||||
"Could not change the priority order of user federation providers {{error}}",
|
||||
addNewProvider: "Add new provider",
|
||||
addCustomProvider: "Add custom provider",
|
||||
providerDetails: "Provider details",
|
||||
|
|
Loading…
Reference in a new issue