keycloak-scim/js/apps/admin-ui/src/organizations/IdentityProviders.tsx
Erik Jan de Wit f088b0009c
initial ui for organizations (#29643)
* initial screen

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* more screens

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* added members tab

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* added the backend

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* added member add / invite models

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* initial version of the identity provider section

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* add link and unlink providers

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* small fix

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* PR comments

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* Do not validate broker domain when the domain is an empty string

Closes #29759

Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* added filter and value

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* added test

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* added first name last name

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* refresh menu when realm organization is changed

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* changed to record

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* changed to form data

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed lint error

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* Changing name of invitation parameters

Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* Chancing name of parameters on the client

Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* Enable organization at the realm before running tests

Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* Domain help message

Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* Handling model validation errors when creating organizations

Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* Message key for organizationDetails

Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* Do not change kc.org attribute on group

Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* add realm into the context

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* Changing button in invitation model to use Send instead of Save

Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* Better message when validating the organization domain

Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* Fixing compilation error after rebase

Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed test

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* removed wait as it no longer required and skip flacky test

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* skip tests that are flaky

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* stabilize user create test

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

---------

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
Co-authored-by: Pedro Igor <pigor.craveiro@gmail.com>
2024-05-29 14:34:02 +02:00

196 lines
5.5 KiB
TypeScript

import IdentityProviderRepresentation from "@keycloak/keycloak-admin-client/lib/defs/identityProviderRepresentation";
import {
Button,
ButtonVariant,
PageSection,
Switch,
ToolbarItem,
} from "@patternfly/react-core";
import { BellIcon } from "@patternfly/react-icons";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { useParams } from "react-router-dom";
import { useAdminClient } from "../admin-client";
import { useAlerts } from "../components/alert/Alerts";
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
import { useFetch } from "../utils/useFetch";
import useToggle from "../utils/useToggle";
import { LinkIdentityProviderModal } from "./LinkIdentityProviderModal";
import { EditOrganizationParams } from "./routes/EditOrganization";
type ShownOnLoginPageCheckProps = {
row: IdentityProviderRepresentation;
refresh: () => void;
};
const ShownOnLoginPageCheck = ({
row,
refresh,
}: ShownOnLoginPageCheckProps) => {
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const { t } = useTranslation();
const toggle = async (value: boolean) => {
try {
await adminClient.identityProviders.update(
{ alias: row.alias! },
{
...row,
config: {
...row.config,
"kc.org.broker.public": `${value}`,
},
},
);
addAlert(t("linkUpdatedSuccessful"));
refresh();
} catch (error) {
addError("linkUpdatedError", error);
}
};
return (
<Switch
label={t("on")}
labelOff={t("off")}
isChecked={row.config?.["kc.org.broker.public"] === "true"}
onChange={(_, value) => toggle(value)}
/>
);
};
export const IdentityProviders = () => {
const { adminClient } = useAdminClient();
const { t } = useTranslation();
const { id: orgId } = useParams<EditOrganizationParams>();
const { addAlert, addError } = useAlerts();
const [key, setKey] = useState(0);
const refresh = () => setKey(key + 1);
const [hasProviders, setHasProviders] = useState(false);
const [selectedRow, setSelectedRow] =
useState<IdentityProviderRepresentation>();
const [open, toggleOpen] = useToggle();
useFetch(
async () => adminClient.identityProviders.find({ max: 1 }),
(providers) => {
setHasProviders(providers.length === 1);
},
[],
);
const loader = () =>
adminClient.organizations.listIdentityProviders({ orgId: orgId! });
const [toggleUnlinkDialog, UnlinkConfirm] = useConfirmDialog({
titleKey: "identityProviderUnlink",
messageKey: "identityProviderUnlinkConfirm",
continueButtonLabel: "unLinkIdentityProvider",
continueButtonVariant: ButtonVariant.danger,
onConfirm: async () => {
try {
await adminClient.organizations.unLinkIdp({
orgId: orgId!,
alias: selectedRow!.alias! as string,
});
setSelectedRow(undefined);
addAlert(t("unLinkSuccessful"));
refresh();
} catch (error) {
addError("unLinkError", error);
}
},
});
return (
<PageSection variant="light">
<UnlinkConfirm />
{open && (
<LinkIdentityProviderModal
orgId={orgId!}
identityProvider={selectedRow}
onClose={() => {
toggleOpen();
refresh();
}}
/>
)}
{!hasProviders ? (
<ListEmptyState
icon={BellIcon}
message={t("noIdentityProvider")}
instructions={t("noIdentityProviderInstructions")}
/>
) : (
<KeycloakDataTable
key={key}
loader={loader}
ariaLabelKey="identityProviders"
searchPlaceholderKey="searchProvider"
toolbarItem={
<ToolbarItem>
<Button
onClick={() => {
setSelectedRow(undefined);
toggleOpen();
}}
>
{t("linkIdentityProvider")}
</Button>
</ToolbarItem>
}
actions={[
{
title: t("edit"),
onRowClick: (row) => {
setSelectedRow(row);
toggleOpen();
},
},
{
title: t("unLinkIdentityProvider"),
onRowClick: (row) => {
setSelectedRow(row);
toggleUnlinkDialog();
},
},
]}
columns={[
{
name: "alias",
},
{
name: "config['kc.org.domain']",
displayKey: "domain",
},
{
name: "providerId",
displayKey: "providerDetails",
},
{
name: "config['kc.org.broker.public']",
displayKey: "shownOnLoginPage",
cellRenderer: (row) => (
<ShownOnLoginPageCheck row={row} refresh={refresh} />
),
},
]}
emptyState={
<ListEmptyState
message={t("emptyIdentityProviderLink")}
instructions={t("emptyIdentityProviderLinkInstructions")}
primaryActionText={t("linkIdentityProvider")}
onPrimaryAction={toggleOpen}
/>
}
/>
)}
</PageSection>
);
};