diff --git a/apps/admin-ui/src/components/role-mapping/resource.ts b/apps/admin-ui/src/components/role-mapping/resource.ts index 18e9937f11..f914805343 100644 --- a/apps/admin-ui/src/components/role-mapping/resource.ts +++ b/apps/admin-ui/src/components/role-mapping/resource.ts @@ -1,19 +1,23 @@ -import KeycloakAdminClient from "@keycloak/keycloak-admin-client"; +import type KeycloakAdminClient from "@keycloak/keycloak-admin-client"; import { fetchAdminUI } from "../../context/auth/admin-ui-endpoint"; +import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation"; type BaseQuery = { adminClient: KeycloakAdminClient; +}; + +type IDQuery = BaseQuery & { id: string; type: string; }; -type PaginatingQuery = BaseQuery & { +type PaginatingQuery = IDQuery & { first: number; max: number; search?: string; }; -type EffectiveClientRolesQuery = BaseQuery; +type EffectiveClientRolesQuery = IDQuery; type Query = Partial> & { adminClient: KeycloakAdminClient; @@ -36,13 +40,12 @@ const fetchEndpoint = async ({ max, search, endpoint, -}: Query): Promise => { - return fetchAdminUI(adminClient, `/admin-ui-${endpoint}/${type}/${id}`, { +}: Query): Promise => + fetchAdminUI(adminClient, `/admin-ui-${endpoint}/${type}/${id}`, { first: (first || 0).toString(), max: (max || 10).toString(), search: search || "", }); -}; export const getAvailableClientRoles = ( query: PaginatingQuery @@ -54,5 +57,33 @@ export const getEffectiveClientRoles = ( ): Promise => fetchEndpoint({ ...query, endpoint: "effective-roles" }); +type UserQuery = BaseQuery & { + lastName?: string; + firstName?: string; + email?: string; + username?: string; + emailVerified?: boolean; + idpAlias?: string; + idpUserId?: string; + enabled?: boolean; + briefRepresentation?: boolean; + exact?: boolean; + q?: string; +}; + +export type BruteUser = UserRepresentation & { + bruteForceStatus?: Record; +}; + +export const findUsers = ({ + adminClient, + ...query +}: UserQuery): Promise => + fetchAdminUI( + adminClient, + "admin-ui-brute-force-user", + query as Record + ); + export const fetchUsedBy = (query: PaginatingQuery): Promise => fetchEndpoint({ ...query, endpoint: "authentication-management" }); diff --git a/apps/admin-ui/src/user/UsersSection.tsx b/apps/admin-ui/src/user/UsersSection.tsx index 59d149c78a..996cf58e97 100644 --- a/apps/admin-ui/src/user/UsersSection.tsx +++ b/apps/admin-ui/src/user/UsersSection.tsx @@ -1,3 +1,7 @@ +import { useState } from "react"; +import { useHistory } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom-v5-compat"; +import { useTranslation } from "react-i18next"; import { AlertVariant, Button, @@ -26,13 +30,10 @@ import { WarningTriangleIcon, } from "@patternfly/react-icons"; import type { IRowData } from "@patternfly/react-table"; -import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation"; + import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation"; import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation"; -import { useState } from "react"; -import { useTranslation } from "react-i18next"; -import { useHistory } from "react-router-dom"; -import { Link, useNavigate } from "react-router-dom-v5-compat"; +import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation"; import { useAlerts } from "../components/alert/Alerts"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { ListEmptyState } from "../components/list-empty-state/ListEmptyState"; @@ -52,13 +53,10 @@ import { RoutableTabs, } from "../components/routable-tabs/RoutableTabs"; import { useAccess } from "../context/access/Access"; +import { BruteUser, findUsers } from "../components/role-mapping/resource"; import "./user-section.css"; -type BruteUser = UserRepresentation & { - brute?: Record; -}; - export default function UsersSection() { const { t } = useTranslation("users"); const { adminClient } = useAdminClient(); @@ -128,24 +126,11 @@ export default function UsersSection() { } try { - const users = await adminClient.users.find({ + return await findUsers({ + adminClient, briefRepresentation: true, ...params, }); - if (realm?.bruteForceProtected) { - const brutes = await Promise.all( - users.map((user: BruteUser) => - adminClient.attackDetection.findOne({ - id: user.id!, - }) - ) - ); - for (let index = 0; index < users.length; index++) { - const user: BruteUser = users[index]; - user.brute = brutes[index]; - } - } - return users; } catch (error) { if (userStorage?.length) { addError("users:noUsersFoundErrorStorage", error); @@ -198,12 +183,12 @@ export default function UsersSection() { {t("disabled")} )} - {user.brute?.disabled && ( + {user.bruteForceStatus?.disabled && ( )} - {user.enabled && !user.brute?.disabled && "—"} + {user.enabled && !user.bruteForceStatus?.disabled && "—"} ); }; @@ -226,7 +211,7 @@ export default function UsersSection() { const goToCreate = () => navigate(toAddUser({ realm: realmName })); - if (!userStorage) { + if (!userStorage || !realm) { return ; } @@ -240,7 +225,7 @@ export default function UsersSection() { {t("addUser")} - {!realm?.bruteForceProtected ? ( + {!realm.bruteForceProtected ? (