Fix custom userFed that doesn't implement CredentialInputUpdater (#20062)

* Fix custom userFed that doesn't implement CredentialInputUpdater
Fixes #19491

* Cleanup from Jon's review

* Specify dependency

* Revert "Specify dependency"

This reverts commit a52d5d993aa1f9cf4503e6101eb49c20b27b38fc.
This commit is contained in:
Stan Silvert 2023-05-02 17:09:44 -04:00 committed by GitHub
parent fb4513d320
commit 51b0eec685
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 75 additions and 104 deletions

View file

@ -35,9 +35,10 @@ import { InlineLabelEdit } from "./user-credentials/InlineLabelEdit";
import styles from "@patternfly/react-styles/css/components/Table/table";
import { CredentialRow } from "./user-credentials/CredentialRow";
import { toUpperCase } from "../util";
import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner";
import { FederatedUserLink } from "./FederatedUserLink";
import "./user-credentials.css";
import { FederatedCredentials } from "./user-credentials/FederatedCredentials";
type UserCredentialsProps = {
user: UserRepresentation;
@ -329,6 +330,26 @@ export const UserCredentials = ({ user }: UserCredentialsProps) => {
}
};
const useFederatedCredentials = user.federationLink || user.origin;
const [credentialTypes, setCredentialTypes] = useState<string[]>([]);
useFetch(
() => adminClient.users.getUserStorageCredentialTypes({ id: user.id! }),
setCredentialTypes,
[]
);
if (!credentialTypes) {
return <KeycloakSpinner />;
}
const hasCredentialTypes = credentialTypes.length > 0;
const noCredentials = groupedUserCredentials.length === 0;
const noFederatedCredentials =
!user.credentials || user.credentials.length === 0;
const emptyState =
noCredentials && noFederatedCredentials && !hasCredentialTypes;
return (
<>
{isOpen && (
@ -346,7 +367,7 @@ export const UserCredentials = ({ user }: UserCredentialsProps) => {
/>
)}
<DeleteConfirm />
{user.email && (
{user.email && !emptyState && (
<Button
className="kc-resetCredentialBtn-header"
variant="primary"
@ -372,7 +393,7 @@ export const UserCredentials = ({ user }: UserCredentialsProps) => {
<Divider />
</>
)}
{groupedUserCredentials.length !== 0 && (
{groupedUserCredentials.length !== 0 && !hasCredentialTypes && (
<PageSection variant={PageSectionVariants.light}>
<TableComposable variant={"compact"}>
<Thead>
@ -485,30 +506,58 @@ export const UserCredentials = ({ user }: UserCredentialsProps) => {
</TableComposable>
</PageSection>
)}
{(user.federationLink || user.origin) && (
<FederatedCredentials user={user} onSetPassword={toggleModal} />
{useFederatedCredentials && hasCredentialTypes && (
<PageSection variant={PageSectionVariants.light}>
<TableComposable variant="compact">
<Thead>
<Tr>
<Th>{t("type")}</Th>
<Th>{t("providedBy")}</Th>
<Th />
</Tr>
</Thead>
<Tbody>
{credentialTypes.map((credential) => (
<Tr key={credential}>
<Td>
<b>{credential}</b>
</Td>
<Td>
<FederatedUserLink user={user} />
</Td>
{credential === "password" && (
<Td modifier="fitContent">
<Button variant="secondary" onClick={toggleModal}>
{t("setPassword")}
</Button>
</Td>
)}
</Tr>
))}
</Tbody>
</TableComposable>
</PageSection>
)}
{emptyState && (
<ListEmptyState
hasIcon
message={t("noCredentials")}
instructions={t("noCredentialsText")}
primaryActionText={t("setPassword")}
onPrimaryAction={toggleModal}
secondaryActions={
user.email
? [
{
text: t("credentialResetBtn"),
onClick: toggleCredentialsResetModal,
type: ButtonVariant.link,
},
]
: undefined
}
/>
)}
{groupedUserCredentials.length === 0 &&
!(user.federationLink || user.origin) && (
<ListEmptyState
hasIcon
message={t("noCredentials")}
instructions={t("noCredentialsText")}
primaryActionText={t("setPassword")}
onPrimaryAction={toggleModal}
secondaryActions={
user.email
? [
{
text: t("credentialResetBtn"),
onClick: toggleCredentialsResetModal,
type: ButtonVariant.link,
},
]
: undefined
}
/>
)}
</>
);
};

View file

@ -1,78 +0,0 @@
import {
Button,
PageSection,
PageSectionVariants,
} from "@patternfly/react-core";
import {
TableComposable,
Tbody,
Td,
Th,
Thead,
Tr,
} from "@patternfly/react-table";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation";
import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner";
import { useAdminClient, useFetch } from "../../context/auth/AdminClient";
import { FederatedUserLink } from "../FederatedUserLink";
type FederatedCredentialsProps = {
user: UserRepresentation;
onSetPassword: () => void;
};
export const FederatedCredentials = ({
user,
onSetPassword,
}: FederatedCredentialsProps) => {
const { t } = useTranslation("users");
const { adminClient } = useAdminClient();
const [credentialTypes, setCredentialTypes] = useState<string[]>();
useFetch(
() => adminClient.users.getUserStorageCredentialTypes({ id: user.id! }),
setCredentialTypes,
[]
);
if (!credentialTypes) {
return <KeycloakSpinner />;
}
return (
<PageSection variant={PageSectionVariants.light}>
<TableComposable variant={"compact"}>
<Thead>
<Tr>
<Th>{t("type")}</Th>
<Th>{t("providedBy")}</Th>
<Th />
</Tr>
</Thead>
<Tbody>
{credentialTypes.map((credential) => (
<Tr key={credential}>
<Td>
<b>{credential}</b>
</Td>
<Td>
<FederatedUserLink user={user} />
</Td>
{credential === "password" && (
<Td modifier="fitContent">
<Button variant="secondary" onClick={onSetPassword}>
{t("setPassword")}
</Button>
</Td>
)}
</Tr>
))}
</Tbody>
</TableComposable>
</PageSection>
);
};