From e28aa90fcb6e2603ec1222ca01da18dcde7ede2d Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Wed, 8 May 2024 10:23:43 +0200 Subject: [PATCH] use context for adminClient (#28693) * use context for adminClient Now we can reuse the components as we can use the adminClient from the context Signed-off-by: Erik Jan de Wit * split environment into base, admin and account Signed-off-by: Erik Jan de Wit * added type to useEnvironment Signed-off-by: Erik Jan de Wit --------- Signed-off-by: Erik Jan de Wit --- .../src/account-security/AccountRow.tsx | 4 +- .../src/account-security/DeviceActivity.tsx | 3 +- .../src/account-security/LinkedAccounts.tsx | 2 +- .../src/account-security/SigningIn.tsx | 2 +- js/apps/account-ui/src/api.ts | 7 +- js/apps/account-ui/src/api/methods.ts | 24 +++-- js/apps/account-ui/src/api/request.ts | 10 +- .../src/applications/Applications.tsx | 3 +- .../src/content/ContentComponent.tsx | 2 +- js/apps/account-ui/src/groups/Groups.tsx | 2 +- js/apps/account-ui/src/i18n.ts | 3 +- js/apps/account-ui/src/index.ts | 1 - .../src/personal-info/PersonalInfo.tsx | 9 +- .../src/resources/EditTheResource.tsx | 3 +- .../src/resources/PermissionRequest.tsx | 4 +- .../account-ui/src/resources/ResourcesTab.tsx | 4 +- .../src/resources/ShareTheResource.tsx | 2 +- js/apps/account-ui/src/root/Header.tsx | 10 +- .../account-ui/src/root/KeycloakContext.tsx | 93 ------------------ js/apps/account-ui/src/root/PageNav.tsx | 14 ++- js/apps/account-ui/src/root/Root.tsx | 3 +- js/apps/account-ui/src/routes.tsx | 3 +- js/apps/admin-ui/pom.xml | 2 +- js/apps/admin-ui/src/App.tsx | 66 ++++++++----- js/apps/admin-ui/src/PageHeader.tsx | 9 +- js/apps/admin-ui/src/Root.tsx | 8 ++ js/apps/admin-ui/src/admin-client.ts | 51 +++++++--- .../authentication/AuthenticationSection.tsx | 4 +- .../src/authentication/BindFlowDialog.tsx | 5 +- .../src/authentication/DuplicateFlowModal.tsx | 5 +- .../src/authentication/EditFlowModal.tsx | 5 +- .../src/authentication/FlowDetails.tsx | 4 +- .../src/authentication/RequiredActions.tsx | 5 +- .../components/AddFlowDropdown.tsx | 5 +- .../components/ExecutionConfigModal.tsx | 4 +- .../src/authentication/components/UsedBy.tsx | 6 +- .../components/modals/AddStepModal.tsx | 5 +- .../components/modals/AddSubFlowModal.tsx | 4 +- .../src/authentication/form/CreateFlow.tsx | 5 +- .../authentication/policies/CibaPolicy.tsx | 4 +- .../src/authentication/policies/OtpPolicy.tsx | 4 +- .../policies/PasswordPolicy.tsx | 11 ++- .../src/authentication/policies/Policies.tsx | 5 +- .../policies/WebauthnPolicy.tsx | 4 +- .../src/client-scopes/ChangeTypeDropdown.tsx | 7 +- .../src/client-scopes/ClientScopesSection.tsx | 11 ++- .../src/client-scopes/CreateClientScope.tsx | 11 ++- .../src/client-scopes/EditClientScope.tsx | 9 +- .../client-scopes/details/MappingDetails.tsx | 6 +- .../admin-ui/src/clients/ClientDetails.tsx | 5 +- .../admin-ui/src/clients/ClientSessions.tsx | 4 +- .../admin-ui/src/clients/ClientsSection.tsx | 6 +- .../src/clients/add/NewClientForm.tsx | 5 +- .../src/clients/advanced/AddHostDialog.tsx | 4 +- .../src/clients/advanced/AdvancedSettings.tsx | 9 +- .../advanced/AuthenticationOverrides.tsx | 4 +- .../src/clients/advanced/ClusteringPanel.tsx | 5 +- .../authorization/AuthorizationEvaluate.tsx | 5 +- .../authorization/AuthorizationExport.tsx | 6 +- .../authorization/DeleteScopeDialog.tsx | 5 +- .../src/clients/authorization/DetailCell.tsx | 7 +- .../authorization/PermissionDetails.tsx | 5 +- .../src/clients/authorization/Permissions.tsx | 5 +- .../src/clients/authorization/Policies.tsx | 5 +- .../clients/authorization/ResourceDetails.tsx | 10 +- .../src/clients/authorization/Resources.tsx | 5 +- .../authorization/ResourcesPolicySelect.tsx | 15 +-- .../clients/authorization/ScopeDetails.tsx | 4 +- .../src/clients/authorization/ScopePicker.tsx | 5 +- .../src/clients/authorization/ScopeSelect.tsx | 5 +- .../src/clients/authorization/Scopes.tsx | 5 +- .../src/clients/authorization/Settings.tsx | 4 +- .../authorization/policy/ClientScope.tsx | 5 +- .../clients/authorization/policy/Group.tsx | 15 +-- .../authorization/policy/PolicyDetails.tsx | 5 +- .../src/clients/authorization/policy/Role.tsx | 5 +- .../src/clients/credentials/ClientSecret.tsx | 6 +- .../src/clients/credentials/Credentials.tsx | 4 +- .../src/clients/import/ImportForm.tsx | 5 +- .../CreateInitialAccessToken.tsx | 4 +- .../initial-access/InitialAccessTokenList.tsx | 5 +- .../src/clients/keys/ExportSamlKeyDialog.tsx | 5 +- js/apps/admin-ui/src/clients/keys/Keys.tsx | 4 +- .../src/clients/keys/SamlImportKeyDialog.tsx | 6 +- .../admin-ui/src/clients/keys/SamlKeys.tsx | 4 +- .../src/clients/keys/SamlKeysDialog.tsx | 9 +- .../registration/ClientRegistrationList.tsx | 5 +- .../clients/registration/DetailProvider.tsx | 4 +- .../src/clients/roles/CreateClientRole.tsx | 5 +- .../src/clients/scopes/ClientScopes.tsx | 17 +++- .../src/clients/scopes/DedicatedScope.tsx | 8 +- .../src/clients/scopes/DedicatedScopes.tsx | 5 +- .../src/clients/scopes/EvaluateScopes.tsx | 7 +- .../service-account/ServiceAccount.tsx | 5 +- .../client-scope/ClientScopeTypes.tsx | 23 +++-- .../src/components/client/ClientSelect.tsx | 5 +- .../download-dialog/DownloadDialog.tsx | 5 +- .../UserProfileAttributeListComponent.tsx | 5 +- .../components/group/GroupPickerDialog.tsx | 4 +- .../permission-tab/PermissionTab.tsx | 5 +- .../role-mapping/AddRoleMappingModal.tsx | 8 +- .../components/role-mapping/RoleMapping.tsx | 12 ++- .../src/components/role-mapping/queries.ts | 32 ++++-- .../src/components/role-mapping/resource.ts | 42 +++++--- .../src/components/roles-list/RolesList.tsx | 5 +- .../src/components/users/UserDataTable.tsx | 7 +- .../src/components/users/UserSelect.tsx | 5 +- .../admin-ui/src/components/users/resource.ts | 4 +- .../admin-ui/src/context/RealmsContext.tsx | 18 ++-- .../src/context/auth/admin-ui-endpoint.ts | 6 +- .../context/realm-context/RealmContext.tsx | 10 +- .../server-info/ServerInfoProvider.tsx | 6 +- .../admin-ui/src/context/whoami/WhoAmI.tsx | 10 +- js/apps/admin-ui/src/dashboard/Dashboard.tsx | 37 +++---- js/apps/admin-ui/src/environment.ts | 70 ------------- js/apps/admin-ui/src/events/AdminEvents.tsx | 5 +- js/apps/admin-ui/src/events/EventsSection.tsx | 9 +- .../admin-ui/src/groups/GroupAttributes.tsx | 5 +- .../admin-ui/src/groups/GroupRoleMapping.tsx | 5 +- js/apps/admin-ui/src/groups/GroupTable.tsx | 5 +- js/apps/admin-ui/src/groups/GroupsModal.tsx | 4 +- js/apps/admin-ui/src/groups/GroupsSection.tsx | 5 +- js/apps/admin-ui/src/groups/Members.tsx | 5 +- js/apps/admin-ui/src/groups/MembersModal.tsx | 5 +- .../src/groups/components/DeleteGroup.tsx | 5 +- .../src/groups/components/GroupTree.tsx | 7 +- .../src/groups/components/MoveDialog.tsx | 16 ++- js/apps/admin-ui/src/i18n/i18n.ts | 7 +- .../IdentityProvidersSection.tsx | 4 +- .../identity-providers/ManageOrderDialog.tsx | 4 +- .../add/AddIdentityProvider.tsx | 4 +- .../src/identity-providers/add/AddMapper.tsx | 5 +- .../add/AddOpenIdConnect.tsx | 5 +- .../identity-providers/add/AddSamlConnect.tsx | 5 +- .../add/AdvancedSettings.tsx | 5 +- .../identity-providers/add/DetailSettings.tsx | 7 +- .../add/OpenIdConnectSettings.tsx | 5 +- .../add/SamlConnectSettings.tsx | 9 +- .../add/SamlGeneralSettings.tsx | 8 +- .../component/DiscoveryEndpointField.tsx | 5 +- .../component/RedirectUrl.tsx | 5 +- js/apps/admin-ui/src/index.ts | 2 + js/apps/admin-ui/src/keycloak.ts | 22 ----- js/apps/admin-ui/src/main.tsx | 5 +- js/apps/admin-ui/src/page/Page.tsx | 6 +- js/apps/admin-ui/src/page/PageHandler.tsx | 4 +- js/apps/admin-ui/src/page/PageList.tsx | 4 +- .../src/realm-roles/CreateRealmRole.tsx | 5 +- .../src/realm-roles/RealmRoleTabs.tsx | 5 +- .../src/realm-roles/RealmRolesSection.tsx | 5 +- .../src/realm-roles/UsersInRoleTab.tsx | 5 +- .../realm-settings/AddClientProfileModal.tsx | 5 +- .../src/realm-settings/ClientProfileForm.tsx | 4 +- .../src/realm-settings/DefaultGroupsTab.tsx | 5 +- .../admin-ui/src/realm-settings/EmailTab.tsx | 6 +- .../src/realm-settings/ExecutorForm.tsx | 5 +- .../src/realm-settings/GeneralTab.tsx | 7 +- .../admin-ui/src/realm-settings/LoginTab.tsx | 4 +- .../realm-settings/NewAttributeSettings.tsx | 4 +- .../src/realm-settings/NewClientPolicy.tsx | 5 +- .../NewClientPolicyCondition.tsx | 4 +- .../src/realm-settings/PartialExport.tsx | 5 +- .../src/realm-settings/PartialImport.tsx | 5 +- .../src/realm-settings/PoliciesTab.tsx | 5 +- .../src/realm-settings/ProfilesTab.tsx | 5 +- .../realm-settings/RealmSettingsSection.tsx | 5 +- .../src/realm-settings/RealmSettingsTabs.tsx | 20 ++-- .../src/realm-settings/UserRegistration.tsx | 5 +- .../realm-settings/event-config/EventsTab.tsx | 5 +- .../src/realm-settings/keys/KeysListTab.tsx | 8 +- .../realm-settings/keys/KeysProvidersTab.tsx | 7 +- .../src/realm-settings/keys/KeysTab.tsx | 4 +- .../keys/key-providers/KeyProviderForm.tsx | 4 +- .../localization/EffectiveMessageBundles.tsx | 5 +- .../localization/RealmOverrides.tsx | 12 ++- .../user-profile/AttributesTab.tsx | 8 +- .../user-profile/UserProfileContext.tsx | 5 +- .../attribute/AddTranslationsDialog.tsx | 18 ++-- .../attribute/AttributeGeneralSettings.tsx | 5 +- .../admin-ui/src/realm/add/NewRealmForm.tsx | 5 +- js/apps/admin-ui/src/routes.tsx | 7 +- .../admin-ui/src/sessions/RevocationModal.tsx | 5 +- .../admin-ui/src/sessions/SessionsSection.tsx | 8 +- .../admin-ui/src/sessions/SessionsTable.tsx | 12 ++- .../CreateUserFederationLdapSettings.tsx | 5 +- .../user-federation/ManagePriorityDialog.tsx | 4 +- .../UserFederationKerberosSettings.tsx | 5 +- .../UserFederationLdapSettings.tsx | 5 +- .../user-federation/UserFederationSection.tsx | 5 +- .../custom/CustomProviderSettings.tsx | 4 +- .../kerberos/KerberosSettingsRequired.tsx | 5 +- .../ldap/LdapSettingsAdvanced.tsx | 5 +- .../ldap/LdapSettingsConnection.tsx | 4 +- .../ldap/LdapSettingsGeneral.tsx | 5 +- .../ldap/mappers/LdapMapperDetails.tsx | 5 +- .../ldap/mappers/LdapMapperList.tsx | 5 +- .../user-federation/shared/ExtendedHeader.tsx | 5 +- .../src/user-federation/shared/Header.tsx | 5 +- js/apps/admin-ui/src/user/CreateUser.tsx | 5 +- js/apps/admin-ui/src/user/EditUser.tsx | 24 ++--- .../admin-ui/src/user/FederatedUserLink.tsx | 5 +- js/apps/admin-ui/src/user/UserConsents.tsx | 5 +- js/apps/admin-ui/src/user/UserCredentials.tsx | 5 +- js/apps/admin-ui/src/user/UserForm.tsx | 5 +- js/apps/admin-ui/src/user/UserGroups.tsx | 5 +- js/apps/admin-ui/src/user/UserIdPModal.tsx | 5 +- .../src/user/UserIdentityProviderLinks.tsx | 4 +- js/apps/admin-ui/src/user/UserRoleMapping.tsx | 5 +- js/apps/admin-ui/src/user/UserSessions.tsx | 5 +- .../user/user-credentials/InlineLabelEdit.tsx | 4 +- .../RequiredActionMultiSelect.tsx | 5 +- .../ResetCredentialDialog.tsx | 5 +- .../user-credentials/ResetPasswordDialog.tsx | 5 +- js/apps/admin-ui/src/utils/useCurrentUser.ts | 4 +- js/libs/ui-shared/src/context/ErrorPage.tsx | 60 ++++++++++++ .../ui-shared/src/context/KeycloakContext.tsx | 97 +++++++++++++++++++ .../ui-shared/src/context}/environment.ts | 37 +++++-- js/libs/ui-shared/src/main.ts | 12 +++ 218 files changed, 1110 insertions(+), 756 deletions(-) delete mode 100644 js/apps/account-ui/src/root/KeycloakContext.tsx create mode 100644 js/apps/admin-ui/src/Root.tsx delete mode 100644 js/apps/admin-ui/src/environment.ts create mode 100644 js/apps/admin-ui/src/index.ts delete mode 100644 js/apps/admin-ui/src/keycloak.ts create mode 100644 js/libs/ui-shared/src/context/ErrorPage.tsx create mode 100644 js/libs/ui-shared/src/context/KeycloakContext.tsx rename js/{apps/account-ui/src => libs/ui-shared/src/context}/environment.ts (72%) diff --git a/js/apps/account-ui/src/account-security/AccountRow.tsx b/js/apps/account-ui/src/account-security/AccountRow.tsx index 5f5d7e3543..06ce9396a0 100644 --- a/js/apps/account-ui/src/account-security/AccountRow.tsx +++ b/js/apps/account-ui/src/account-security/AccountRow.tsx @@ -12,11 +12,9 @@ import { } from "@patternfly/react-core"; import { LinkIcon, UnlinkIcon } from "@patternfly/react-icons"; import { useTranslation } from "react-i18next"; -import { IconMapper, useAlerts } from "@keycloak/keycloak-ui-shared"; - +import { IconMapper, useAlerts, useEnvironment } from "@keycloak/keycloak-ui-shared"; import { linkAccount, unLinkAccount } from "../api/methods"; import { LinkedAccountRepresentation } from "../api/representations"; -import { useEnvironment } from "../root/KeycloakContext"; type AccountRowProps = { account: LinkedAccountRepresentation; diff --git a/js/apps/account-ui/src/account-security/DeviceActivity.tsx b/js/apps/account-ui/src/account-security/DeviceActivity.tsx index bccff2a442..23e03253ca 100644 --- a/js/apps/account-ui/src/account-security/DeviceActivity.tsx +++ b/js/apps/account-ui/src/account-security/DeviceActivity.tsx @@ -23,7 +23,7 @@ import { } from "@patternfly/react-icons"; import { useState } from "react"; import { useTranslation } from "react-i18next"; -import { ContinueCancelModal, useAlerts } from "@keycloak/keycloak-ui-shared"; +import { ContinueCancelModal, useAlerts, useEnvironment } from "@keycloak/keycloak-ui-shared"; import { deleteSession, getDevices } from "../api/methods"; import { ClientRepresentation, @@ -32,7 +32,6 @@ import { } from "../api/representations"; import { Page } from "../components/page/Page"; import { TFuncKey } from "../i18n"; -import { useEnvironment } from "../root/KeycloakContext"; import { formatDate } from "../utils/formatDate"; import { usePromise } from "../utils/usePromise"; diff --git a/js/apps/account-ui/src/account-security/LinkedAccounts.tsx b/js/apps/account-ui/src/account-security/LinkedAccounts.tsx index b526f8f4f6..277b457ba0 100644 --- a/js/apps/account-ui/src/account-security/LinkedAccounts.tsx +++ b/js/apps/account-ui/src/account-security/LinkedAccounts.tsx @@ -7,7 +7,7 @@ import { EmptyRow } from "../components/datalist/EmptyRow"; import { Page } from "../components/page/Page"; import { usePromise } from "../utils/usePromise"; import { AccountRow } from "./AccountRow"; -import { useEnvironment } from "../root/KeycloakContext"; +import { useEnvironment } from "@keycloak/keycloak-ui-shared"; export const LinkedAccounts = () => { const { t } = useTranslation(); diff --git a/js/apps/account-ui/src/account-security/SigningIn.tsx b/js/apps/account-ui/src/account-security/SigningIn.tsx index 6d90669c75..e2f5afdd99 100644 --- a/js/apps/account-ui/src/account-security/SigningIn.tsx +++ b/js/apps/account-ui/src/account-security/SigningIn.tsx @@ -18,6 +18,7 @@ import { import { EllipsisVIcon } from "@patternfly/react-icons"; import { CSSProperties, useState } from "react"; import { Trans, useTranslation } from "react-i18next"; +import { useEnvironment } from "@keycloak/keycloak-ui-shared"; import { getCredentials } from "../api/methods"; import { CredentialContainer, @@ -26,7 +27,6 @@ import { import { EmptyRow } from "../components/datalist/EmptyRow"; import { Page } from "../components/page/Page"; import { TFuncKey } from "../i18n"; -import { useEnvironment } from "../root/KeycloakContext"; import { formatDate } from "../utils/formatDate"; import { usePromise } from "../utils/usePromise"; diff --git a/js/apps/account-ui/src/api.ts b/js/apps/account-ui/src/api.ts index ddc3694c7e..5583a03049 100644 --- a/js/apps/account-ui/src/api.ts +++ b/js/apps/account-ui/src/api.ts @@ -1,9 +1,10 @@ +import { KeycloakContext } from "@keycloak/keycloak-ui-shared"; +import { BaseEnvironment } from "@keycloak/keycloak-ui-shared/dist/context/environment"; import { CallOptions } from "./api/methods"; import { Links, parseLinks } from "./api/parse-links"; import { parseResponse } from "./api/parse-response"; import { Permission, Resource, Scope } from "./api/representations"; import { request } from "./api/request"; -import { KeycloakContext } from "./root/KeycloakContext"; export const fetchResources = async ( { signal, context }: CallOptions, @@ -43,7 +44,7 @@ export const fetchPermission = async ( }; export const updateRequest = ( - context: KeycloakContext, + context: KeycloakContext, resourceId: string, username: string, scopes: Scope[] | string[], @@ -54,7 +55,7 @@ export const updateRequest = ( }); export const updatePermissions = ( - context: KeycloakContext, + context: KeycloakContext, resourceId: string, permissions: Permission[], ) => diff --git a/js/apps/account-ui/src/api/methods.ts b/js/apps/account-ui/src/api/methods.ts index edc8450590..45ac408786 100644 --- a/js/apps/account-ui/src/api/methods.ts +++ b/js/apps/account-ui/src/api/methods.ts @@ -1,4 +1,8 @@ -import { KeycloakContext } from "../root/KeycloakContext"; +import { + AccountEnvironment, + KeycloakContext, +} from "@keycloak/keycloak-ui-shared"; +import { BaseEnvironment } from "@keycloak/keycloak-ui-shared/dist/context/environment"; import { joinPath } from "../utils/joinPath"; import { parseResponse } from "./parse-response"; import { @@ -13,7 +17,7 @@ import { import { request } from "./request"; export type CallOptions = { - context: KeycloakContext; + context: KeycloakContext; signal?: AbortSignal; }; @@ -41,7 +45,7 @@ export async function getSupportedLocales({ } export async function savePersonalInfo( - context: KeycloakContext, + context: KeycloakContext, info: UserRepresentation, ): Promise { const response = await request("/", context, { body: info, method: "POST" }); @@ -81,11 +85,17 @@ export async function getApplications({ return parseResponse(response); } -export async function deleteConsent(context: KeycloakContext, id: string) { +export async function deleteConsent( + context: KeycloakContext, + id: string, +) { return request(`/applications/${id}/consent`, context, { method: "DELETE" }); } -export async function deleteSession(context: KeycloakContext, id?: string) { +export async function deleteSession( + context: KeycloakContext, + id?: string, +) { return request(`/sessions${id ? `/${id}` : ""}`, context, { method: "DELETE", }); @@ -104,7 +114,7 @@ export async function getLinkedAccounts({ signal, context }: CallOptions) { } export async function unLinkAccount( - context: KeycloakContext, + context: KeycloakContext, account: LinkedAccountRepresentation, ) { const response = await request( @@ -119,7 +129,7 @@ export async function unLinkAccount( } export async function linkAccount( - context: KeycloakContext, + context: KeycloakContext, account: LinkedAccountRepresentation, ) { const redirectUri = encodeURIComponent( diff --git a/js/apps/account-ui/src/api/request.ts b/js/apps/account-ui/src/api/request.ts index 3c5bc50564..b73b3f98fe 100644 --- a/js/apps/account-ui/src/api/request.ts +++ b/js/apps/account-ui/src/api/request.ts @@ -1,8 +1,8 @@ -import { Environment } from "../environment"; +import { KeycloakContext } from "@keycloak/keycloak-ui-shared"; +import { BaseEnvironment } from "@keycloak/keycloak-ui-shared/dist/context/environment"; import Keycloak from "keycloak-js"; -import { CONTENT_TYPE_HEADER, CONTENT_TYPE_JSON } from "./constants"; import { joinPath } from "../utils/joinPath"; -import { KeycloakContext } from "../root/KeycloakContext"; +import { CONTENT_TYPE_HEADER, CONTENT_TYPE_JSON } from "./constants"; export type RequestOptions = { signal?: AbortSignal; @@ -35,7 +35,7 @@ async function _request( export async function request( path: string, - { environment, keycloak }: KeycloakContext, + { environment, keycloak }: KeycloakContext, opts: RequestOptions = {}, ) { return _request(url(environment, path), { @@ -44,7 +44,7 @@ export async function request( }); } -export const url = (environment: Environment, path: string) => +export const url = (environment: BaseEnvironment, path: string) => new URL( joinPath(environment.authUrl, "realms", environment.realm, "account", path), ); diff --git a/js/apps/account-ui/src/applications/Applications.tsx b/js/apps/account-ui/src/applications/Applications.tsx index d9f6bde65e..6a6e3b2156 100644 --- a/js/apps/account-ui/src/applications/Applications.tsx +++ b/js/apps/account-ui/src/applications/Applications.tsx @@ -22,12 +22,11 @@ import { } from "@patternfly/react-icons"; import { useState } from "react"; import { useTranslation } from "react-i18next"; -import { ContinueCancelModal, useAlerts } from "@keycloak/keycloak-ui-shared"; +import { ContinueCancelModal, useAlerts, useEnvironment } from "@keycloak/keycloak-ui-shared"; import { deleteConsent, getApplications } from "../api/methods"; import { ClientRepresentation } from "../api/representations"; import { Page } from "../components/page/Page"; import { TFuncKey } from "../i18n"; -import { useEnvironment } from "../root/KeycloakContext"; import { formatDate } from "../utils/formatDate"; import { usePromise } from "../utils/usePromise"; diff --git a/js/apps/account-ui/src/content/ContentComponent.tsx b/js/apps/account-ui/src/content/ContentComponent.tsx index 652a565530..6b0d9df5ac 100644 --- a/js/apps/account-ui/src/content/ContentComponent.tsx +++ b/js/apps/account-ui/src/content/ContentComponent.tsx @@ -1,7 +1,7 @@ import { Spinner } from "@patternfly/react-core"; import { Suspense, lazy, useMemo, useState } from "react"; import { useParams } from "react-router-dom"; -import { useEnvironment } from "../root/KeycloakContext"; +import { useEnvironment } from "@keycloak/keycloak-ui-shared"; import { MenuItem } from "../root/PageNav"; import { ContentComponentParams } from "../routes"; import { joinPath } from "../utils/joinPath"; diff --git a/js/apps/account-ui/src/groups/Groups.tsx b/js/apps/account-ui/src/groups/Groups.tsx index 959634d590..c84135dd18 100644 --- a/js/apps/account-ui/src/groups/Groups.tsx +++ b/js/apps/account-ui/src/groups/Groups.tsx @@ -8,10 +8,10 @@ import { } from "@patternfly/react-core"; import { useState } from "react"; import { useTranslation } from "react-i18next"; +import { useEnvironment } from "@keycloak/keycloak-ui-shared"; import { getGroups } from "../api/methods"; import { Group } from "../api/representations"; import { Page } from "../components/page/Page"; -import { useEnvironment } from "../root/KeycloakContext"; import { usePromise } from "../utils/usePromise"; export const Groups = () => { diff --git a/js/apps/account-ui/src/i18n.ts b/js/apps/account-ui/src/i18n.ts index b8c42565fa..651a7ee9df 100644 --- a/js/apps/account-ui/src/i18n.ts +++ b/js/apps/account-ui/src/i18n.ts @@ -1,8 +1,7 @@ import { LanguageDetectorModule, createInstance } from "i18next"; import HttpBackend from "i18next-http-backend"; import { initReactI18next } from "react-i18next"; - -import { environment } from "./environment"; +import { environment } from "@keycloak/keycloak-ui-shared"; import { joinPath } from "./utils/joinPath"; const DEFAULT_LOCALE = "en"; diff --git a/js/apps/account-ui/src/index.ts b/js/apps/account-ui/src/index.ts index c8016dbc59..eae5ccf4b9 100644 --- a/js/apps/account-ui/src/index.ts +++ b/js/apps/account-ui/src/index.ts @@ -1,7 +1,6 @@ export { PersonalInfo } from "./personal-info/PersonalInfo"; export { ErrorPage } from "./root/ErrorPage"; export { Header } from "./root/Header"; -export { KeycloakProvider, useEnvironment } from "./root/KeycloakContext"; export { PageNav } from "./root/PageNav"; export { DeviceActivity } from "./account-security/DeviceActivity"; export { LinkedAccounts } from "./account-security/LinkedAccounts"; diff --git a/js/apps/account-ui/src/personal-info/PersonalInfo.tsx b/js/apps/account-ui/src/personal-info/PersonalInfo.tsx index 94d4ce39d7..37e932b854 100644 --- a/js/apps/account-ui/src/personal-info/PersonalInfo.tsx +++ b/js/apps/account-ui/src/personal-info/PersonalInfo.tsx @@ -1,9 +1,11 @@ import { + AccountEnvironment, UserProfileFields, beerify, debeerify, setUserProfileServerError, useAlerts, + useEnvironment, } from "@keycloak/keycloak-ui-shared"; import { ActionGroup, @@ -18,7 +20,6 @@ import { TFunction } from "i18next"; import { useState } from "react"; import { ErrorOption, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; - import { getPersonalInfo, getSupportedLocales, @@ -29,14 +30,12 @@ import { UserRepresentation, } from "../api/representations"; import { Page } from "../components/page/Page"; -import { environment } from "../environment"; import { TFuncKey, i18n } from "../i18n"; -import { useEnvironment } from "../root/KeycloakContext"; import { usePromise } from "../utils/usePromise"; export const PersonalInfo = () => { const { t } = useTranslation(); - const context = useEnvironment(); + const context = useEnvironment(); const [userProfileMetadata, setUserProfileMetadata] = useState(); const [supportedLocales, setSupportedLocales] = useState([]); @@ -106,7 +105,7 @@ export const PersonalInfo = () => { form={form} userProfileMetadata={userProfileMetadata} supportedLocales={supportedLocales} - currentLocale={environment.locale} + currentLocale={context.environment.locale} t={ ((key: unknown, params) => t(key as TFuncKey, params as any)) as TFunction diff --git a/js/apps/account-ui/src/resources/EditTheResource.tsx b/js/apps/account-ui/src/resources/EditTheResource.tsx index e9b07bd0b4..b8f2511d1e 100644 --- a/js/apps/account-ui/src/resources/EditTheResource.tsx +++ b/js/apps/account-ui/src/resources/EditTheResource.tsx @@ -2,15 +2,14 @@ import { Button, Form, Modal } from "@patternfly/react-core"; import { Fragment, useEffect } from "react"; import { FormProvider, useFieldArray, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; - import { SelectControl, TextControl, useAlerts, + useEnvironment, } from "@keycloak/keycloak-ui-shared"; import { updatePermissions } from "../api"; import type { Permission, Resource } from "../api/representations"; -import { useEnvironment } from "../root/KeycloakContext"; type EditTheResourceProps = { resource: Resource; diff --git a/js/apps/account-ui/src/resources/PermissionRequest.tsx b/js/apps/account-ui/src/resources/PermissionRequest.tsx index 7a0f80dc41..a9f4ac06ea 100644 --- a/js/apps/account-ui/src/resources/PermissionRequest.tsx +++ b/js/apps/account-ui/src/resources/PermissionRequest.tsx @@ -11,11 +11,9 @@ import { UserCheckIcon } from "@patternfly/react-icons"; import { Table, Tbody, Td, Th, Thead, Tr } from "@patternfly/react-table"; import { useState } from "react"; import { useTranslation } from "react-i18next"; -import { useAlerts } from "@keycloak/keycloak-ui-shared"; - +import { useAlerts, useEnvironment } from "@keycloak/keycloak-ui-shared"; import { fetchPermission, updateRequest } from "../api"; import { Permission, Resource } from "../api/representations"; -import { useEnvironment } from "../root/KeycloakContext"; type PermissionRequestProps = { resource: Resource; diff --git a/js/apps/account-ui/src/resources/ResourcesTab.tsx b/js/apps/account-ui/src/resources/ResourcesTab.tsx index ade3331998..1e76646471 100644 --- a/js/apps/account-ui/src/resources/ResourcesTab.tsx +++ b/js/apps/account-ui/src/resources/ResourcesTab.tsx @@ -32,13 +32,11 @@ import { } from "@patternfly/react-table"; import { useState } from "react"; import { useTranslation } from "react-i18next"; - -import { ContinueCancelModal, useAlerts } from "@keycloak/keycloak-ui-shared"; +import { ContinueCancelModal, useAlerts, useEnvironment } from "@keycloak/keycloak-ui-shared"; import { fetchPermission, fetchResources, updatePermissions } from "../api"; import { getPermissionRequests } from "../api/methods"; import { Links } from "../api/parse-links"; import { Permission, Resource } from "../api/representations"; -import { useEnvironment } from "../root/KeycloakContext"; import { usePromise } from "../utils/usePromise"; import { EditTheResource } from "./EditTheResource"; import { PermissionRequest } from "./PermissionRequest"; diff --git a/js/apps/account-ui/src/resources/ShareTheResource.tsx b/js/apps/account-ui/src/resources/ShareTheResource.tsx index 6001514fc7..b644e3ced2 100644 --- a/js/apps/account-ui/src/resources/ShareTheResource.tsx +++ b/js/apps/account-ui/src/resources/ShareTheResource.tsx @@ -22,10 +22,10 @@ import { FormErrorText, SelectControl, useAlerts, + useEnvironment, } from "@keycloak/keycloak-ui-shared"; import { updateRequest } from "../api"; import { Permission, Resource } from "../api/representations"; -import { useEnvironment } from "../root/KeycloakContext"; import { SharedWith } from "./SharedWith"; type ShareTheResourceProps = { diff --git a/js/apps/account-ui/src/root/Header.tsx b/js/apps/account-ui/src/root/Header.tsx index 38d0de6939..8e6c6de2eb 100644 --- a/js/apps/account-ui/src/root/Header.tsx +++ b/js/apps/account-ui/src/root/Header.tsx @@ -2,11 +2,13 @@ import { Button } from "@patternfly/react-core"; import { ExternalLinkSquareAltIcon } from "@patternfly/react-icons"; import { useTranslation } from "react-i18next"; import { useHref } from "react-router-dom"; -import { KeycloakMasthead, label } from "@keycloak/keycloak-ui-shared"; - -import { environment } from "../environment"; +import { + KeycloakMasthead, + environment, + label, + useEnvironment, +} from "@keycloak/keycloak-ui-shared"; import { joinPath } from "../utils/joinPath"; -import { useEnvironment } from "./KeycloakContext"; import style from "./header.module.css"; diff --git a/js/apps/account-ui/src/root/KeycloakContext.tsx b/js/apps/account-ui/src/root/KeycloakContext.tsx deleted file mode 100644 index a1f8bc3a42..0000000000 --- a/js/apps/account-ui/src/root/KeycloakContext.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { Spinner } from "@patternfly/react-core"; -import Keycloak from "keycloak-js"; -import { - PropsWithChildren, - createContext, - useContext, - useEffect, - useMemo, - useRef, - useState, -} from "react"; -import { AlertProvider, Help } from "@keycloak/keycloak-ui-shared"; -import { Environment } from "../environment"; -import { ErrorPage } from "./ErrorPage"; - -export type KeycloakContext = KeycloakContextProps & { - keycloak: Keycloak; -}; - -const KeycloakEnvContext = createContext( - undefined, -); - -export const useEnvironment = () => { - const context = useContext(KeycloakEnvContext); - if (!context) - throw Error( - "no environment provider in the hierarchy make sure to add the provider", - ); - return context; -}; - -type KeycloakContextProps = { - environment: Environment; -}; - -export const KeycloakProvider = ({ - environment, - children, -}: PropsWithChildren) => { - const calledOnce = useRef(false); - const [init, setInit] = useState(false); - const [error, setError] = useState(); - const keycloak = useMemo( - () => { - const keycloak = new Keycloak({ - url: environment.authUrl, - realm: environment.realm, - clientId: environment.clientId, - }); - - keycloak.onAuthLogout = () => keycloak.login(); - - return keycloak; - }, - [environment], - ); - - useEffect(() => { - // only needed in dev mode - if (calledOnce.current) { - return; - } - - const init = () => keycloak.init({ - onLoad: "check-sso", - pkceMethod: "S256", - responseMode: "query", - }); - - init() - .then(() => setInit(true)) - .catch((error) => setError(error)); - - calledOnce.current = true; - }, [keycloak]); - - if (error) { - return ; - } - - if (!init) { - return ; - } - - return ( - - - {children} - - - ); -}; diff --git a/js/apps/account-ui/src/root/PageNav.tsx b/js/apps/account-ui/src/root/PageNav.tsx index 7b196fb0eb..c0be3b8015 100644 --- a/js/apps/account-ui/src/root/PageNav.tsx +++ b/js/apps/account-ui/src/root/PageNav.tsx @@ -4,8 +4,8 @@ import { NavItem, NavList, PageSidebar, - Spinner, PageSidebarBody, + Spinner, } from "@patternfly/react-core"; import { PropsWithChildren, @@ -22,11 +22,15 @@ import { useLinkClickHandler, useLocation, } from "react-router-dom"; +import { + AccountEnvironment, + environment, + useEnvironment, + type Feature, +} from "@keycloak/keycloak-ui-shared"; import fetchContentJson from "../content/fetchContent"; -import { environment, type Feature } from "../environment"; import { TFuncKey } from "../i18n"; import { usePromise } from "../utils/usePromise"; -import { useEnvironment } from "./KeycloakContext"; type RootMenuItem = { label: TFuncKey; @@ -45,7 +49,7 @@ export type MenuItem = RootMenuItem | MenuItemWithChildren; export const PageNav = () => { const [menuItems, setMenuItems] = useState(); - const context = useEnvironment(); + const context = useEnvironment(); usePromise((signal) => fetchContentJson({ signal, context }), setMenuItems); return ( @@ -82,7 +86,7 @@ function NavMenuItem({ menuItem }: NavMenuItemProps) { const { t } = useTranslation(); const { environment: { features }, - } = useEnvironment(); + } = useEnvironment(); const { pathname } = useLocation(); const isActive = useMemo( () => matchMenuItem(pathname, menuItem), diff --git a/js/apps/account-ui/src/root/Root.tsx b/js/apps/account-ui/src/root/Root.tsx index 2a298538b0..8527a544fb 100644 --- a/js/apps/account-ui/src/root/Root.tsx +++ b/js/apps/account-ui/src/root/Root.tsx @@ -1,9 +1,8 @@ +import { KeycloakProvider, environment } from "@keycloak/keycloak-ui-shared"; import { Page, Spinner } from "@patternfly/react-core"; import { Suspense } from "react"; import { Outlet } from "react-router-dom"; -import { environment } from "../environment"; import { Header } from "./Header"; -import { KeycloakProvider } from "./KeycloakContext"; import { PageNav } from "./PageNav"; export const Root = () => { diff --git a/js/apps/account-ui/src/routes.tsx b/js/apps/account-ui/src/routes.tsx index 64541c81f2..2e14a2227a 100644 --- a/js/apps/account-ui/src/routes.tsx +++ b/js/apps/account-ui/src/routes.tsx @@ -1,9 +1,8 @@ import { lazy } from "react"; import type { IndexRouteObject, RouteObject } from "react-router-dom"; - +import { environment } from "@keycloak/keycloak-ui-shared"; import { ErrorPage } from "./root/ErrorPage"; import { Root } from "./root/Root"; -import { environment } from "./environment"; const DeviceActivity = lazy(() => import("./account-security/DeviceActivity")); const LinkedAccounts = lazy(() => import("./account-security/LinkedAccounts")); diff --git a/js/apps/admin-ui/pom.xml b/js/apps/admin-ui/pom.xml index 2ea545c7a6..d6a62f1cac 100644 --- a/js/apps/admin-ui/pom.xml +++ b/js/apps/admin-ui/pom.xml @@ -124,7 +124,7 @@ { - "loginRealm": "${loginRealm!"master"}", + "realm": "${loginRealm!"master"}", "clientId": "${clientId}", "authServerUrl": "${authServerUrl}", "authUrl": "${authUrl}", diff --git a/js/apps/admin-ui/src/App.tsx b/js/apps/admin-ui/src/App.tsx index 266dbc121f..09a6601990 100644 --- a/js/apps/admin-ui/src/App.tsx +++ b/js/apps/admin-ui/src/App.tsx @@ -1,10 +1,14 @@ +import KeycloakAdminClient from "@keycloak/keycloak-admin-client"; import { Page } from "@patternfly/react-core"; -import { PropsWithChildren, Suspense } from "react"; +import { PropsWithChildren, Suspense, useEffect, useState } from "react"; import { Outlet } from "react-router-dom"; -import { Help, mainPageContentId } from "@keycloak/keycloak-ui-shared"; - +import { + mainPageContentId, + useEnvironment, +} from "@keycloak/keycloak-ui-shared"; import { Header } from "./PageHeader"; import { PageNav } from "./PageNav"; +import { AdminClientContext, initAdminClient } from "./admin-client"; import { AlertProvider } from "./components/alert/Alerts"; import { PageBreadCrumbs } from "./components/bread-crumb/PageBreadCrumbs"; import { ErrorRenderer } from "./components/error/ErrorRenderer"; @@ -30,11 +34,9 @@ const AppContexts = ({ children }: PropsWithChildren) => ( - - - {children} - - + + {children} + @@ -45,23 +47,37 @@ const AppContexts = ({ children }: PropsWithChildren) => ( ); export const App = () => { + const { keycloak, environment } = useEnvironment(); + const [adminClient, setAdminClient] = useState(); + + useEffect(() => { + const init = async () => { + const client = await initAdminClient(keycloak, environment); + setAdminClient(client); + }; + init().catch(console.error); + }, []); + + if (!adminClient) return ; return ( - - } - isManagedSidebar - sidebar={} - breadcrumb={} - mainContainerId={mainPageContentId} - > - - }> - - - - - - - + + + } + isManagedSidebar + sidebar={} + breadcrumb={} + mainContainerId={mainPageContentId} + > + + }> + + + + + + + + ); }; diff --git a/js/apps/admin-ui/src/PageHeader.tsx b/js/apps/admin-ui/src/PageHeader.tsx index 11a3256160..7efbd719c9 100644 --- a/js/apps/admin-ui/src/PageHeader.tsx +++ b/js/apps/admin-ui/src/PageHeader.tsx @@ -18,16 +18,15 @@ import { BarsIcon, EllipsisVIcon, HelpIcon } from "@patternfly/react-icons"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Link, useHref } from "react-router-dom"; -import { useHelp } from "@keycloak/keycloak-ui-shared"; - +import { useEnvironment, useHelp } from "@keycloak/keycloak-ui-shared"; import { HelpHeader } from "./components/help-enabler/HelpHeader"; import { useRealm } from "./context/realm-context/RealmContext"; import { useWhoAmI } from "./context/whoami/WhoAmI"; import { toDashboard } from "./dashboard/routes/Dashboard"; -import environment from "./environment"; -import { keycloak } from "./keycloak"; const ManageAccountDropdownItem = () => { + const { keycloak } = useEnvironment(); + const { t } = useTranslation(); return ( { }; const SignOutDropdownItem = () => { + const { keycloak } = useEnvironment(); const { t } = useTranslation(); return ( { }; export const Header = () => { + const { environment, keycloak } = useEnvironment(); const { t } = useTranslation(); const { realm } = useRealm(); diff --git a/js/apps/admin-ui/src/Root.tsx b/js/apps/admin-ui/src/Root.tsx new file mode 100644 index 0000000000..2bc35c6b3b --- /dev/null +++ b/js/apps/admin-ui/src/Root.tsx @@ -0,0 +1,8 @@ +import { KeycloakProvider, environment } from "@keycloak/keycloak-ui-shared"; +import { App } from "./App"; + +export const Root = () => ( + + + +); diff --git a/js/apps/admin-ui/src/admin-client.ts b/js/apps/admin-ui/src/admin-client.ts index 5de4672b01..0bd4d02cd5 100644 --- a/js/apps/admin-ui/src/admin-client.ts +++ b/js/apps/admin-ui/src/admin-client.ts @@ -1,20 +1,41 @@ import KeycloakAdminClient from "@keycloak/keycloak-admin-client"; +import { + createNamedContext, + useRequiredContext, +} from "@keycloak/keycloak-ui-shared"; +import { BaseEnvironment } from "@keycloak/keycloak-ui-shared/dist/context/environment"; +import type Keycloak from "keycloak-js"; -import environment from "./environment"; -import { keycloak } from "./keycloak"; +export type AdminClientProps = { + keycloak: Keycloak; + adminClient: KeycloakAdminClient; +}; -export const adminClient = new KeycloakAdminClient(); +export const AdminClientContext = createNamedContext< + AdminClientProps | undefined +>("AdminClientContext", undefined); -adminClient.setConfig({ realmName: environment.loginRealm }); -adminClient.baseUrl = environment.authUrl; -adminClient.registerTokenProvider({ - async getAccessToken() { - try { - await keycloak.updateToken(5); - } catch (error) { - keycloak.login(); - } +export const useAdminClient = () => useRequiredContext(AdminClientContext); - return keycloak.token; - }, -}); +export async function initAdminClient( + keycloak: Keycloak, + environment: BaseEnvironment, +) { + const adminClient = new KeycloakAdminClient(); + + adminClient.setConfig({ realmName: environment.realm }); + adminClient.baseUrl = environment.authUrl; + adminClient.registerTokenProvider({ + async getAccessToken() { + try { + await keycloak.updateToken(5); + } catch (error) { + keycloak.login(); + } + + return keycloak.token; + }, + }); + + return adminClient; +} diff --git a/js/apps/admin-ui/src/authentication/AuthenticationSection.tsx b/js/apps/admin-ui/src/authentication/AuthenticationSection.tsx index 34d98a17f0..0a9c2a9f4e 100644 --- a/js/apps/admin-ui/src/authentication/AuthenticationSection.tsx +++ b/js/apps/admin-ui/src/authentication/AuthenticationSection.tsx @@ -15,8 +15,6 @@ import { sortBy } from "lodash-es"; import { useState } from "react"; import { Trans, useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; - -import { adminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner"; @@ -42,6 +40,7 @@ import { Policies } from "./policies/Policies"; import { AuthenticationTab, toAuthentication } from "./routes/Authentication"; import { toCreateFlow } from "./routes/CreateFlow"; import { toFlow } from "./routes/Flow"; +import { useAdminClient } from "../admin-client"; type UsedBy = "SPECIFIC_CLIENTS" | "SPECIFIC_PROVIDERS" | "DEFAULT"; @@ -83,6 +82,7 @@ const AliasRenderer = ({ id, alias, usedBy, builtIn }: AuthenticationType) => { }; export default function AuthenticationSection() { + const { adminClient } = useAdminClient(); const { t } = useTranslation(); const { realm: realmName } = useRealm(); const [key, setKey] = useState(0); diff --git a/js/apps/admin-ui/src/authentication/BindFlowDialog.tsx b/js/apps/admin-ui/src/authentication/BindFlowDialog.tsx index 3d21a6f499..dea63cae9c 100644 --- a/js/apps/admin-ui/src/authentication/BindFlowDialog.tsx +++ b/js/apps/admin-ui/src/authentication/BindFlowDialog.tsx @@ -10,11 +10,10 @@ import { SelectVariant } from "@patternfly/react-core/deprecated"; import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { SelectControl } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useRealm } from "../context/realm-context/RealmContext"; import { REALM_FLOWS } from "./AuthenticationSection"; +import { useAdminClient } from "../admin-client"; type BindingForm = { bindingType: keyof RealmRepresentation; @@ -26,6 +25,8 @@ type BindFlowDialogProps = { }; export const BindFlowDialog = ({ flowAlias, onClose }: BindFlowDialogProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const form = useForm(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/authentication/DuplicateFlowModal.tsx b/js/apps/admin-ui/src/authentication/DuplicateFlowModal.tsx index 06a0fcb913..e02195748d 100644 --- a/js/apps/admin-ui/src/authentication/DuplicateFlowModal.tsx +++ b/js/apps/admin-ui/src/authentication/DuplicateFlowModal.tsx @@ -11,8 +11,7 @@ import { useEffect } from "react"; import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useRealm } from "../context/realm-context/RealmContext"; import { NameDescription } from "./form/NameDescription"; @@ -31,6 +30,8 @@ export const DuplicateFlowModal = ({ toggleDialog, onComplete, }: DuplicateFlowModalProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const form = useForm({ mode: "onChange" }); const { setValue, getValues, handleSubmit } = form; diff --git a/js/apps/admin-ui/src/authentication/EditFlowModal.tsx b/js/apps/admin-ui/src/authentication/EditFlowModal.tsx index 954e9000bb..5ed7ac903a 100644 --- a/js/apps/admin-ui/src/authentication/EditFlowModal.tsx +++ b/js/apps/admin-ui/src/authentication/EditFlowModal.tsx @@ -9,9 +9,8 @@ import { } from "@patternfly/react-core"; import { useEffect } from "react"; import { FormProvider, useForm } from "react-hook-form"; - import { useTranslation } from "react-i18next"; -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { NameDescription } from "./form/NameDescription"; @@ -21,6 +20,8 @@ type EditFlowModalProps = { }; export const EditFlowModal = ({ flow, toggleDialog }: EditFlowModalProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const form = useForm({ mode: "onChange" }); diff --git a/js/apps/admin-ui/src/authentication/FlowDetails.tsx b/js/apps/admin-ui/src/authentication/FlowDetails.tsx index bdbc05c121..f00e0aea39 100644 --- a/js/apps/admin-ui/src/authentication/FlowDetails.tsx +++ b/js/apps/admin-ui/src/authentication/FlowDetails.tsx @@ -21,7 +21,7 @@ import { DomainIcon, TableIcon } from "@patternfly/react-icons"; import { useState } from "react"; import { Trans, useTranslation } from "react-i18next"; import { useNavigate, useParams } from "react-router-dom"; -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { ViewHeader } from "../components/view-header/ViewHeader"; @@ -52,6 +52,8 @@ export const providerConditionFilter = ( ) => value.displayName?.startsWith("Condition "); export default function FlowDetails() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { realm } = useRealm(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/authentication/RequiredActions.tsx b/js/apps/admin-ui/src/authentication/RequiredActions.tsx index aacd8c247b..81eb48d3d0 100644 --- a/js/apps/admin-ui/src/authentication/RequiredActions.tsx +++ b/js/apps/admin-ui/src/authentication/RequiredActions.tsx @@ -3,8 +3,7 @@ import type RequiredActionProviderSimpleRepresentation from "@keycloak/keycloak- import { AlertVariant, Switch } from "@patternfly/react-core"; import { useState } from "react"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner"; import { toKey } from "../util"; @@ -22,6 +21,8 @@ type Row = { }; export const RequiredActions = () => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/authentication/components/AddFlowDropdown.tsx b/js/apps/admin-ui/src/authentication/components/AddFlowDropdown.tsx index 0add04d719..fbe80911a4 100644 --- a/js/apps/admin-ui/src/authentication/components/AddFlowDropdown.tsx +++ b/js/apps/admin-ui/src/authentication/components/AddFlowDropdown.tsx @@ -8,8 +8,7 @@ import { import { PlusIcon } from "@patternfly/react-icons"; import { useState } from "react"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useFetch } from "../../utils/useFetch"; import type { ExpandableExecution } from "../execution-model"; import { AddStepModal, FlowType } from "./modals/AddStepModal"; @@ -29,6 +28,8 @@ export const AddFlowDropdown = ({ onAddExecution, onAddFlow, }: AddFlowDropdownProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const [open, setOpen] = useState(false); diff --git a/js/apps/admin-ui/src/authentication/components/ExecutionConfigModal.tsx b/js/apps/admin-ui/src/authentication/components/ExecutionConfigModal.tsx index cbaf9b99e5..83719b9e7b 100644 --- a/js/apps/admin-ui/src/authentication/components/ExecutionConfigModal.tsx +++ b/js/apps/admin-ui/src/authentication/components/ExecutionConfigModal.tsx @@ -15,7 +15,7 @@ import { useEffect, useState } from "react"; import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { TextControl } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { DynamicComponents } from "../../components/dynamic/DynamicComponents"; import { convertFormValuesToObject, convertToFormValues } from "../../util"; @@ -34,6 +34,8 @@ type ExecutionConfigModalProps = { export const ExecutionConfigModal = ({ execution, }: ExecutionConfigModalProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/authentication/components/UsedBy.tsx b/js/apps/admin-ui/src/authentication/components/UsedBy.tsx index e33ae75aa9..7e2d7b6386 100644 --- a/js/apps/admin-ui/src/authentication/components/UsedBy.tsx +++ b/js/apps/admin-ui/src/authentication/components/UsedBy.tsx @@ -10,7 +10,7 @@ import { } from "@patternfly/react-core"; import { CheckCircleIcon } from "@patternfly/react-icons"; import { useTranslation } from "react-i18next"; - +import { useAdminClient } from "../../admin-client"; import { fetchUsedBy } from "../../components/role-mapping/resource"; import { KeycloakDataTable } from "../../components/table-toolbar/KeycloakDataTable"; import useToggle from "../../utils/useToggle"; @@ -36,6 +36,8 @@ type UsedByModalProps = { }; const UsedByModal = ({ id, isSpecificClient, onClose }: UsedByModalProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const loader = async ( @@ -43,7 +45,7 @@ const UsedByModal = ({ id, isSpecificClient, onClose }: UsedByModalProps) => { max?: number, search?: string, ): Promise<{ name: string }[]> => { - const result = await fetchUsedBy({ + const result = await fetchUsedBy(adminClient, { id, type: isSpecificClient ? "clients" : "idp", first: first || 0, diff --git a/js/apps/admin-ui/src/authentication/components/modals/AddStepModal.tsx b/js/apps/admin-ui/src/authentication/components/modals/AddStepModal.tsx index 08a1071c03..dd04ca17cd 100644 --- a/js/apps/admin-ui/src/authentication/components/modals/AddStepModal.tsx +++ b/js/apps/admin-ui/src/authentication/components/modals/AddStepModal.tsx @@ -10,12 +10,11 @@ import { } from "@patternfly/react-core"; import { useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../../../admin-client"; import { PaginatingTableToolbar } from "../../../components/table-toolbar/PaginatingTableToolbar"; import { useFetch } from "../../../utils/useFetch"; import useLocaleSort, { mapByKey } from "../../../utils/useLocaleSort"; import { providerConditionFilter } from "../../FlowDetails"; +import { useAdminClient } from "../../../admin-client"; type AuthenticationProviderListProps = { list?: AuthenticationProviderRepresentation[]; @@ -56,6 +55,8 @@ type AddStepModalProps = { }; export const AddStepModal = ({ name, type, onSelect }: AddStepModalProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const [value, setValue] = useState(); diff --git a/js/apps/admin-ui/src/authentication/components/modals/AddSubFlowModal.tsx b/js/apps/admin-ui/src/authentication/components/modals/AddSubFlowModal.tsx index b0db6a7745..4a9ffa060d 100644 --- a/js/apps/admin-ui/src/authentication/components/modals/AddSubFlowModal.tsx +++ b/js/apps/admin-ui/src/authentication/components/modals/AddSubFlowModal.tsx @@ -10,7 +10,7 @@ import { useEffect, useState } from "react"; import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { SelectControl, TextControl } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../../../admin-client"; +import { useAdminClient } from "../../../admin-client"; import { useFetch } from "../../../utils/useFetch"; type AddSubFlowProps = { @@ -33,6 +33,8 @@ export const AddSubFlowModal = ({ onConfirm, onCancel, }: AddSubFlowProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const form = useForm(); const [formProviders, setFormProviders] = diff --git a/js/apps/admin-ui/src/authentication/form/CreateFlow.tsx b/js/apps/admin-ui/src/authentication/form/CreateFlow.tsx index e4278ec6ab..4244187bd3 100644 --- a/js/apps/admin-ui/src/authentication/form/CreateFlow.tsx +++ b/js/apps/admin-ui/src/authentication/form/CreateFlow.tsx @@ -8,9 +8,8 @@ import { import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; - import { SelectControl } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { FormAccess } from "../../components/form/FormAccess"; import { ViewHeader } from "../../components/view-header/ViewHeader"; @@ -22,6 +21,8 @@ import { NameDescription } from "./NameDescription"; const TYPES = ["basic-flow", "client-flow"] as const; export default function CreateFlow() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const navigate = useNavigate(); const { realm } = useRealm(); diff --git a/js/apps/admin-ui/src/authentication/policies/CibaPolicy.tsx b/js/apps/admin-ui/src/authentication/policies/CibaPolicy.tsx index 37f7cf7f04..b31c82245a 100644 --- a/js/apps/admin-ui/src/authentication/policies/CibaPolicy.tsx +++ b/js/apps/admin-ui/src/authentication/policies/CibaPolicy.tsx @@ -10,7 +10,7 @@ import { useEffect } from "react"; import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { SelectControl, TextControl } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { FormAccess } from "../../components/form/FormAccess"; import { useRealm } from "../../context/realm-context/RealmContext"; @@ -33,6 +33,8 @@ type FormFields = Omit< >; export const CibaPolicy = ({ realm, realmUpdated }: CibaPolicyProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const form = useForm({ mode: "onChange" }); const { realm: realmName } = useRealm(); diff --git a/js/apps/admin-ui/src/authentication/policies/OtpPolicy.tsx b/js/apps/admin-ui/src/authentication/policies/OtpPolicy.tsx index a32ea1ffdf..7798be785c 100644 --- a/js/apps/admin-ui/src/authentication/policies/OtpPolicy.tsx +++ b/js/apps/admin-ui/src/authentication/policies/OtpPolicy.tsx @@ -19,7 +19,7 @@ import { SelectControl, SwitchControl, } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { FormAccess } from "../../components/form/FormAccess"; import { TimeSelectorControl } from "../../components/time-selector/TimeSelectorControl"; @@ -43,6 +43,8 @@ type FormFields = Omit< >; export const OtpPolicy = ({ realm, realmUpdated }: OtpPolicyProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const form = useForm({ mode: "onChange", defaultValues: realm }); const { diff --git a/js/apps/admin-ui/src/authentication/policies/PasswordPolicy.tsx b/js/apps/admin-ui/src/authentication/policies/PasswordPolicy.tsx index 44b5454c54..7b6f0ffca2 100644 --- a/js/apps/admin-ui/src/authentication/policies/PasswordPolicy.tsx +++ b/js/apps/admin-ui/src/authentication/policies/PasswordPolicy.tsx @@ -7,15 +7,15 @@ import { ButtonVariant, Divider, EmptyState, + EmptyStateActions, EmptyStateBody, + EmptyStateFooter, + EmptyStateHeader, EmptyStateIcon, PageSection, Toolbar, ToolbarContent, ToolbarItem, - EmptyStateActions, - EmptyStateHeader, - EmptyStateFooter, Select, SelectOption, MenuToggle, @@ -25,8 +25,7 @@ import { PlusCircleIcon } from "@patternfly/react-icons"; import { useEffect, useMemo, useState } from "react"; import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { FormAccess } from "../../components/form/FormAccess"; import { useRealm } from "../../context/realm-context/RealmContext"; @@ -94,6 +93,8 @@ export const PasswordPolicy = ({ realm, realmUpdated, }: PasswordPolicyProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { passwordPolicies } = useServerInfo(); diff --git a/js/apps/admin-ui/src/authentication/policies/Policies.tsx b/js/apps/admin-ui/src/authentication/policies/Policies.tsx index bdb1bf384b..fcc94bca01 100644 --- a/js/apps/admin-ui/src/authentication/policies/Policies.tsx +++ b/js/apps/admin-ui/src/authentication/policies/Policies.tsx @@ -2,8 +2,7 @@ import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/r import { Tab, Tabs, TabTitleText } from "@patternfly/react-core"; import { useState } from "react"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner"; import { useRealm } from "../../context/realm-context/RealmContext"; import { useFetch } from "../../utils/useFetch"; @@ -13,6 +12,8 @@ import { PasswordPolicy } from "./PasswordPolicy"; import { WebauthnPolicy } from "./WebauthnPolicy"; export const Policies = () => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const [subTab, setSubTab] = useState(1); const { realm: realmName } = useRealm(); diff --git a/js/apps/admin-ui/src/authentication/policies/WebauthnPolicy.tsx b/js/apps/admin-ui/src/authentication/policies/WebauthnPolicy.tsx index edab37171c..6add1969b2 100644 --- a/js/apps/admin-ui/src/authentication/policies/WebauthnPolicy.tsx +++ b/js/apps/admin-ui/src/authentication/policies/WebauthnPolicy.tsx @@ -22,7 +22,6 @@ import { TextControl, useHelp, } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { FormAccess } from "../../components/form/FormAccess"; import { MultiLineInput } from "../../components/multi-line-input/MultiLineInput"; @@ -30,6 +29,7 @@ import { TimeSelectorControl } from "../../components/time-selector/TimeSelector import { useRealm } from "../../context/realm-context/RealmContext"; import { convertFormValuesToObject, convertToFormValues } from "../../util"; +import { useAdminClient } from "../../admin-client"; import "./webauthn-policy.css"; const SIGNATURE_ALGORITHMS = [ @@ -108,6 +108,8 @@ export const WebauthnPolicy = ({ realmUpdated, isPasswordLess = false, }: WebauthnPolicyProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const { realm: realmName } = useRealm(); diff --git a/js/apps/admin-ui/src/client-scopes/ChangeTypeDropdown.tsx b/js/apps/admin-ui/src/client-scopes/ChangeTypeDropdown.tsx index 63cab6264a..7afd9d775e 100644 --- a/js/apps/admin-ui/src/client-scopes/ChangeTypeDropdown.tsx +++ b/js/apps/admin-ui/src/client-scopes/ChangeTypeDropdown.tsx @@ -2,7 +2,7 @@ import { AlertVariant } from "@patternfly/react-core"; import { Select } from "@patternfly/react-core/deprecated"; import { useState } from "react"; import { useTranslation } from "react-i18next"; - +import { useAdminClient } from "../admin-client"; import type { Row } from "../clients/scopes/ClientScopes"; import { useAlerts } from "../components/alert/Alerts"; import { @@ -24,6 +24,8 @@ export const ChangeTypeDropdown = ({ selectedRows, refresh, }: ChangeTypeDropdownProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const [open, setOpen] = useState(false); @@ -44,12 +46,13 @@ export const ChangeTypeDropdown = ({ selectedRows.map((row) => { return clientId ? changeClientScope( + adminClient, clientId, row, row.type, value as ClientScope, ) - : changeScope(row, value as ClientScope); + : changeScope(adminClient, row, value as ClientScope); }), ); setOpen(false); diff --git a/js/apps/admin-ui/src/client-scopes/ClientScopesSection.tsx b/js/apps/admin-ui/src/client-scopes/ClientScopesSection.tsx index 1d49d6ca9b..302fc34275 100644 --- a/js/apps/admin-ui/src/client-scopes/ClientScopesSection.tsx +++ b/js/apps/admin-ui/src/client-scopes/ClientScopesSection.tsx @@ -14,8 +14,7 @@ import { cellWidth } from "@patternfly/react-table"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import type { Row } from "../clients/scopes/ClientScopes"; import { getProtocolName } from "../clients/utils"; import { useAlerts } from "../components/alert/Alerts"; @@ -56,6 +55,8 @@ type TypeSelectorProps = ClientScopeDefaultOptionalType & { }; const TypeSelector = (scope: TypeSelectorProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); @@ -66,7 +67,7 @@ const TypeSelector = (scope: TypeSelectorProps) => { all onSelect={async (value) => { try { - await changeScope(scope, value as AllClientScopeType); + await changeScope(adminClient, scope, value as AllClientScopeType); addAlert(t("clientScopeSuccess"), AlertVariant.success); scope.refresh(); } catch (error) { @@ -90,6 +91,8 @@ const ClientScopeDetailLink = ({ }; export default function ClientScopesSection() { + const { adminClient } = useAdminClient(); + const { realm } = useRealm(); const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); @@ -165,7 +168,7 @@ export default function ClientScopesSection() { try { for (const scope of selectedScopes) { try { - await removeScope(scope); + await removeScope(adminClient, scope); } catch (error: any) { console.warn( "could not remove scope", diff --git a/js/apps/admin-ui/src/client-scopes/CreateClientScope.tsx b/js/apps/admin-ui/src/client-scopes/CreateClientScope.tsx index 715e1e228b..569f89bf2b 100644 --- a/js/apps/admin-ui/src/client-scopes/CreateClientScope.tsx +++ b/js/apps/admin-ui/src/client-scopes/CreateClientScope.tsx @@ -1,8 +1,7 @@ import { AlertVariant, PageSection } from "@patternfly/react-core"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { ClientScopeDefaultOptionalType, @@ -15,6 +14,8 @@ import { ScopeForm } from "./details/ScopeForm"; import { toClientScope } from "./routes/ClientScope"; export default function CreateClientScope() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const navigate = useNavigate(); const { realm } = useRealm(); @@ -37,7 +38,11 @@ export default function CreateClientScope() { throw new Error(t("notFound")); } - await changeScope({ ...clientScope, id: scope.id }, clientScope.type); + await changeScope( + adminClient, + { ...clientScope, id: scope.id }, + clientScope.type, + ); addAlert(t("createClientScopeSuccess", AlertVariant.success)); diff --git a/js/apps/admin-ui/src/client-scopes/EditClientScope.tsx b/js/apps/admin-ui/src/client-scopes/EditClientScope.tsx index 0e1f73785e..218553250b 100644 --- a/js/apps/admin-ui/src/client-scopes/EditClientScope.tsx +++ b/js/apps/admin-ui/src/client-scopes/EditClientScope.tsx @@ -15,8 +15,7 @@ import { useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { useHelp } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { AllClientScopes, @@ -43,10 +42,12 @@ import { ClientScopeTab, toClientScope, } from "./routes/ClientScope"; -import { toMapper } from "./routes/Mapper"; import { toClientScopes } from "./routes/ClientScopes"; +import { toMapper } from "./routes/Mapper"; export default function EditClientScope() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const navigate = useNavigate(); const { realm } = useRealm(); @@ -118,7 +119,7 @@ export default function EditClientScope() { try { await adminClient.clientScopes.update({ id }, clientScope); - await changeScope({ ...clientScope, id }, clientScope.type); + await changeScope(adminClient, { ...clientScope, id }, clientScope.type); addAlert(t("updateSuccessClientScope"), AlertVariant.success); } catch (error) { diff --git a/js/apps/admin-ui/src/client-scopes/details/MappingDetails.tsx b/js/apps/admin-ui/src/client-scopes/details/MappingDetails.tsx index 4ba51cfc01..991aefdd9d 100644 --- a/js/apps/admin-ui/src/client-scopes/details/MappingDetails.tsx +++ b/js/apps/admin-ui/src/client-scopes/details/MappingDetails.tsx @@ -15,7 +15,8 @@ import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link, useMatch, useNavigate } from "react-router-dom"; import { TextControl } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; +import { toDedicatedScope } from "../../clients/routes/DedicatedScopeDetails"; import { useAlerts } from "../../components/alert/Alerts"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { DynamicComponents } from "../../components/dynamic/DynamicComponents"; @@ -28,9 +29,10 @@ import { useFetch } from "../../utils/useFetch"; import { useParams } from "../../utils/useParams"; import { toClientScope } from "../routes/ClientScope"; import { MapperParams, MapperRoute } from "../routes/Mapper"; -import { toDedicatedScope } from "../../clients/routes/DedicatedScopeDetails"; export default function MappingDetails() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/clients/ClientDetails.tsx b/js/apps/admin-ui/src/clients/ClientDetails.tsx index 19cca0ed41..065f8cc54f 100644 --- a/js/apps/admin-ui/src/clients/ClientDetails.tsx +++ b/js/apps/admin-ui/src/clients/ClientDetails.tsx @@ -16,8 +16,7 @@ import { useMemo, useState } from "react"; import { Controller, FormProvider, useForm, useWatch } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { ConfirmDialogModal, @@ -188,6 +187,8 @@ export type FormFields = Omit< >; export default function ClientDetails() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const { realm } = useRealm(); diff --git a/js/apps/admin-ui/src/clients/ClientSessions.tsx b/js/apps/admin-ui/src/clients/ClientSessions.tsx index bcdd0f653c..58cb83f3d9 100644 --- a/js/apps/admin-ui/src/clients/ClientSessions.tsx +++ b/js/apps/admin-ui/src/clients/ClientSessions.tsx @@ -2,7 +2,7 @@ import type ClientRepresentation from "@keycloak/keycloak-admin-client/lib/defs/ import type UserSessionRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userSessionRepresentation"; import { PageSection } from "@patternfly/react-core"; import { useTranslation } from "react-i18next"; -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import type { LoaderFunction } from "../components/table-toolbar/KeycloakDataTable"; import SessionsTable from "../sessions/SessionsTable"; @@ -11,6 +11,8 @@ type ClientSessionsProps = { }; export const ClientSessions = ({ client }: ClientSessionsProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const loader: LoaderFunction = async ( diff --git a/js/apps/admin-ui/src/clients/ClientsSection.tsx b/js/apps/admin-ui/src/clients/ClientsSection.tsx index 590646b1a3..870963e7f5 100644 --- a/js/apps/admin-ui/src/clients/ClientsSection.tsx +++ b/js/apps/admin-ui/src/clients/ClientsSection.tsx @@ -14,8 +14,7 @@ import { IRowData, TableText, cellWidth } from "@patternfly/react-table"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { FormattedLink } from "../components/external-link/FormattedLink"; @@ -74,6 +73,7 @@ const ClientDescription = (client: ClientRepresentation) => ( ); const ClientHomeLink = (client: ClientRepresentation) => { + const { adminClient } = useAdminClient(); const href = convertClientToUrl(client, adminClient.baseUrl); if (!href) { @@ -117,6 +117,8 @@ const ToolbarItems = () => { }; export default function ClientsSection() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const { realm } = useRealm(); diff --git a/js/apps/admin-ui/src/clients/add/NewClientForm.tsx b/js/apps/admin-ui/src/clients/add/NewClientForm.tsx index 98ccf315c5..6745de4407 100644 --- a/js/apps/admin-ui/src/clients/add/NewClientForm.tsx +++ b/js/apps/admin-ui/src/clients/add/NewClientForm.tsx @@ -8,8 +8,7 @@ import { useState } from "react"; import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { FormAccess } from "../../components/form/FormAccess"; import { ViewHeader } from "../../components/view-header/ViewHeader"; @@ -23,6 +22,8 @@ import { GeneralSettings } from "./GeneralSettings"; import { LoginSettings } from "./LoginSettings"; export default function NewClientForm() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { realm } = useRealm(); const navigate = useNavigate(); diff --git a/js/apps/admin-ui/src/clients/advanced/AddHostDialog.tsx b/js/apps/admin-ui/src/clients/advanced/AddHostDialog.tsx index 515724a401..d7bddbc0d6 100644 --- a/js/apps/admin-ui/src/clients/advanced/AddHostDialog.tsx +++ b/js/apps/admin-ui/src/clients/advanced/AddHostDialog.tsx @@ -8,7 +8,7 @@ import { import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { TextControl } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; type FormFields = { @@ -28,6 +28,8 @@ export const AddHostDialog = ({ onAdded, onClose, }: AddHostDialogProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const form = useForm(); const { diff --git a/js/apps/admin-ui/src/clients/advanced/AdvancedSettings.tsx b/js/apps/admin-ui/src/clients/advanced/AdvancedSettings.tsx index 707f06a730..512ac3911c 100644 --- a/js/apps/admin-ui/src/clients/advanced/AdvancedSettings.tsx +++ b/js/apps/admin-ui/src/clients/advanced/AdvancedSettings.tsx @@ -8,21 +8,20 @@ import { import { useState } from "react"; import { Controller, useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; -import { FormAccess } from "../../components/form/FormAccess"; import { HelpItem } from "@keycloak/keycloak-ui-shared"; +import { useAdminClient } from "../../admin-client"; import { DefaultSwitchControl } from "../../components/SwitchControl"; -import { adminClient } from "../../admin-client"; +import { FormAccess } from "../../components/form/FormAccess"; import { KeyValueInput } from "../../components/key-value-form/KeyValueInput"; import { MultiLineInput } from "../../components/multi-line-input/MultiLineInput"; import { TimeSelector } from "../../components/time-selector/TimeSelector"; import { useRealm } from "../../context/realm-context/RealmContext"; import { convertAttributeNameToForm } from "../../util"; import { useFetch } from "../../utils/useFetch"; +import useIsFeatureEnabled, { Feature } from "../../utils/useIsFeatureEnabled"; import { FormFields } from "../ClientDetails"; import { TokenLifespan } from "./TokenLifespan"; -import useIsFeatureEnabled, { Feature } from "../../utils/useIsFeatureEnabled"; - type AdvancedSettingsProps = { save: () => void; reset: () => void; @@ -36,6 +35,8 @@ export const AdvancedSettings = ({ protocol, hasConfigureAccess, }: AdvancedSettingsProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const [open, setOpen] = useState(false); diff --git a/js/apps/admin-ui/src/clients/advanced/AuthenticationOverrides.tsx b/js/apps/admin-ui/src/clients/advanced/AuthenticationOverrides.tsx index 5462c9e096..58054321be 100644 --- a/js/apps/admin-ui/src/clients/advanced/AuthenticationOverrides.tsx +++ b/js/apps/admin-ui/src/clients/advanced/AuthenticationOverrides.tsx @@ -4,7 +4,7 @@ import { sortBy } from "lodash-es"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { SelectControl } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { FormAccess } from "../../components/form/FormAccess"; import { useFetch } from "../../utils/useFetch"; @@ -21,6 +21,8 @@ export const AuthenticationOverrides = ({ reset, hasConfigureAccess, }: AuthenticationOverridesProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const [flows, setFlows] = useState([]); diff --git a/js/apps/admin-ui/src/clients/advanced/ClusteringPanel.tsx b/js/apps/admin-ui/src/clients/advanced/ClusteringPanel.tsx index c76f9370a9..4461bb63b8 100644 --- a/js/apps/admin-ui/src/clients/advanced/ClusteringPanel.tsx +++ b/js/apps/admin-ui/src/clients/advanced/ClusteringPanel.tsx @@ -11,8 +11,7 @@ import { import { useState } from "react"; import { useTranslation } from "react-i18next"; import { HelpItem } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { FormAccess } from "../../components/form/FormAccess"; @@ -35,6 +34,8 @@ export const ClusteringPanel = ({ save, client: { id, registeredNodes, access }, }: AdvancedProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const formatDate = useFormatDate(); diff --git a/js/apps/admin-ui/src/clients/authorization/AuthorizationEvaluate.tsx b/js/apps/admin-ui/src/clients/authorization/AuthorizationEvaluate.tsx index 9a24254a51..2770243b9d 100644 --- a/js/apps/admin-ui/src/clients/authorization/AuthorizationEvaluate.tsx +++ b/js/apps/admin-ui/src/clients/authorization/AuthorizationEvaluate.tsx @@ -30,9 +30,8 @@ import { HelpItem, TextControl, } from "@keycloak/keycloak-ui-shared"; - import { ForbiddenSection } from "../../ForbiddenSection"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { ClientSelect } from "../../components/client/ClientSelect"; import { FormAccess } from "../../components/form/FormAccess"; @@ -98,6 +97,8 @@ export const AuthorizationEvaluate = (props: Props) => { }; const AuthorizationEvaluateContent = ({ client }: Props) => { + const { adminClient } = useAdminClient(); + const form = useForm({ mode: "onChange" }); const { control, diff --git a/js/apps/admin-ui/src/clients/authorization/AuthorizationExport.tsx b/js/apps/admin-ui/src/clients/authorization/AuthorizationExport.tsx index 9efbb29da7..80e259516a 100644 --- a/js/apps/admin-ui/src/clients/authorization/AuthorizationExport.tsx +++ b/js/apps/admin-ui/src/clients/authorization/AuthorizationExport.tsx @@ -9,16 +9,18 @@ import { saveAs } from "file-saver"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { TextAreaControl } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { FormAccess } from "../../components/form/FormAccess"; import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner"; -import { useFetch } from "../../utils/useFetch"; import { prettyPrintJSON } from "../../util"; +import { useFetch } from "../../utils/useFetch"; import { useParams } from "../../utils/useParams"; import type { ClientParams } from "../routes/Client"; export const AuthorizationExport = () => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { clientId } = useParams(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/clients/authorization/DeleteScopeDialog.tsx b/js/apps/admin-ui/src/clients/authorization/DeleteScopeDialog.tsx index 9aced7b97c..6eec82659d 100644 --- a/js/apps/admin-ui/src/clients/authorization/DeleteScopeDialog.tsx +++ b/js/apps/admin-ui/src/clients/authorization/DeleteScopeDialog.tsx @@ -1,8 +1,7 @@ import type ScopeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/scopeRepresentation"; import { Alert, AlertVariant } from "@patternfly/react-core"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { ConfirmDialogModal } from "../../components/confirm-dialog/ConfirmDialog"; import type { PermissionScopeRepresentation } from "./Scopes"; @@ -25,6 +24,8 @@ export const DeleteScopeDialog = ({ open, toggleDialog, }: DeleteScopeDialogProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/clients/authorization/DetailCell.tsx b/js/apps/admin-ui/src/clients/authorization/DetailCell.tsx index db6c7b9b7b..4c05f837c3 100644 --- a/js/apps/admin-ui/src/clients/authorization/DetailCell.tsx +++ b/js/apps/admin-ui/src/clients/authorization/DetailCell.tsx @@ -1,11 +1,10 @@ import type ResourceServerRepresentation from "@keycloak/keycloak-admin-client/lib/defs/resourceServerRepresentation"; import { DescriptionList } from "@patternfly/react-core"; import { useState } from "react"; -import { adminClient } from "../../admin-client"; - +import { useAdminClient } from "../../admin-client"; import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner"; -import { useFetch } from "../../utils/useFetch"; import { useRealm } from "../../context/realm-context/RealmContext"; +import { useFetch } from "../../utils/useFetch"; import { toPermissionDetails } from "../routes/PermissionDetails"; import { toScopeDetails } from "../routes/Scope"; import { DetailDescription, DetailDescriptionLink } from "./DetailDescription"; @@ -21,6 +20,8 @@ type DetailCellProps = { }; export const DetailCell = ({ id, clientId, uris }: DetailCellProps) => { + const { adminClient } = useAdminClient(); + const { realm } = useRealm(); const [scope, setScope] = useState(); const [permissions, setPermissions] = diff --git a/js/apps/admin-ui/src/clients/authorization/PermissionDetails.tsx b/js/apps/admin-ui/src/clients/authorization/PermissionDetails.tsx index ac95d5db00..911056f83a 100644 --- a/js/apps/admin-ui/src/clients/authorization/PermissionDetails.tsx +++ b/js/apps/admin-ui/src/clients/authorization/PermissionDetails.tsx @@ -21,8 +21,7 @@ import { TextAreaControl, TextControl, } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { FormAccess } from "../../components/form/FormAccess"; @@ -46,6 +45,8 @@ type FormFields = PolicyRepresentation & { }; export default function PermissionDetails() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const form = useForm({ diff --git a/js/apps/admin-ui/src/clients/authorization/Permissions.tsx b/js/apps/admin-ui/src/clients/authorization/Permissions.tsx index 3cf3a8f363..a2cd3841ff 100644 --- a/js/apps/admin-ui/src/clients/authorization/Permissions.tsx +++ b/js/apps/admin-ui/src/clients/authorization/Permissions.tsx @@ -26,8 +26,7 @@ import { import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner"; @@ -73,6 +72,8 @@ export const AuthorizationPermissions = ({ clientId, isDisabled = false, }: PermissionsProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const navigate = useNavigate(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/clients/authorization/Policies.tsx b/js/apps/admin-ui/src/clients/authorization/Policies.tsx index fc1145e163..3d73b0b263 100644 --- a/js/apps/admin-ui/src/clients/authorization/Policies.tsx +++ b/js/apps/admin-ui/src/clients/authorization/Policies.tsx @@ -20,8 +20,7 @@ import { import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner"; @@ -66,6 +65,8 @@ export const AuthorizationPolicies = ({ clientId, isDisabled = false, }: PoliciesProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const { realm } = useRealm(); diff --git a/js/apps/admin-ui/src/clients/authorization/ResourceDetails.tsx b/js/apps/admin-ui/src/clients/authorization/ResourceDetails.tsx index 99e72e1ae7..960d5582f6 100644 --- a/js/apps/admin-ui/src/clients/authorization/ResourceDetails.tsx +++ b/js/apps/admin-ui/src/clients/authorization/ResourceDetails.tsx @@ -16,23 +16,23 @@ import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; import { HelpItem, TextControl } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; +import { DefaultSwitchControl } from "../../components/SwitchControl"; import { useAlerts } from "../../components/alert/Alerts"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { FormAccess } from "../../components/form/FormAccess"; -import type { KeyValueType } from "../../components/key-value-form/key-value-convert"; import { KeyValueInput } from "../../components/key-value-form/KeyValueInput"; +import type { KeyValueType } from "../../components/key-value-form/key-value-convert"; import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner"; import { MultiLineInput } from "../../components/multi-line-input/MultiLineInput"; import { ViewHeader } from "../../components/view-header/ViewHeader"; +import { useAccess } from "../../context/access/Access"; import { convertFormValuesToObject, convertToFormValues } from "../../util"; import { useFetch } from "../../utils/useFetch"; import { useParams } from "../../utils/useParams"; import { toAuthorizationTab } from "../routes/AuthenticationTab"; import { ResourceDetailsParams, toResourceDetails } from "../routes/Resource"; import { ScopePicker } from "./ScopePicker"; -import { DefaultSwitchControl } from "../../components/SwitchControl"; -import { useAccess } from "../../context/access/Access"; import "./resource-details.css"; type SubmittedResource = Omit< @@ -43,6 +43,8 @@ type SubmittedResource = Omit< }; export default function ResourceDetails() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const [client, setClient] = useState(); const [resource, setResource] = useState(); diff --git a/js/apps/admin-ui/src/clients/authorization/Resources.tsx b/js/apps/admin-ui/src/clients/authorization/Resources.tsx index 091e295bf3..09cf169c8c 100644 --- a/js/apps/admin-ui/src/clients/authorization/Resources.tsx +++ b/js/apps/admin-ui/src/clients/authorization/Resources.tsx @@ -19,8 +19,7 @@ import { import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner"; @@ -54,6 +53,8 @@ export const AuthorizationResources = ({ clientId, isDisabled = false, }: ResourcesProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const navigate = useNavigate(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/clients/authorization/ResourcesPolicySelect.tsx b/js/apps/admin-ui/src/clients/authorization/ResourcesPolicySelect.tsx index ff3a6e6fc2..5e91e2a0a6 100644 --- a/js/apps/admin-ui/src/clients/authorization/ResourcesPolicySelect.tsx +++ b/js/apps/admin-ui/src/clients/authorization/ResourcesPolicySelect.tsx @@ -1,3 +1,4 @@ +import PolicyProviderRepresentation from "@keycloak/keycloak-admin-client/lib/defs/policyProviderRepresentation"; import type PolicyRepresentation from "@keycloak/keycloak-admin-client/lib/defs/policyRepresentation"; import type ResourceRepresentation from "@keycloak/keycloak-admin-client/lib/defs/resourceRepresentation"; import type { @@ -17,18 +18,16 @@ import { useFormContext, } from "react-hook-form"; import { useTranslation } from "react-i18next"; - import { Link, useNavigate } from "react-router-dom"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; +import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { useRealm } from "../../context/realm-context/RealmContext"; import { useFetch } from "../../utils/useFetch"; -import { toPolicyDetails } from "../routes/PolicyDetails"; -import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; -import { toCreatePolicy } from "../routes/NewPolicy"; -import { NewPolicyDialog } from "./NewPolicyDialog"; import useToggle from "../../utils/useToggle"; -import PolicyProviderRepresentation from "@keycloak/keycloak-admin-client/lib/defs/policyProviderRepresentation"; +import { toCreatePolicy } from "../routes/NewPolicy"; +import { toPolicyDetails } from "../routes/PolicyDetails"; import { toResourceDetails } from "../routes/Resource"; +import { NewPolicyDialog } from "./NewPolicyDialog"; type Type = "resources" | "policies"; @@ -76,6 +75,8 @@ export const ResourcesPolicySelect = ({ preSelected, isRequired = false, }: ResourcesPolicySelectProps) => { + const { adminClient } = useAdminClient(); + const { realm } = useRealm(); const { t } = useTranslation(); const navigate = useNavigate(); diff --git a/js/apps/admin-ui/src/clients/authorization/ScopeDetails.tsx b/js/apps/admin-ui/src/clients/authorization/ScopeDetails.tsx index 79e17a3759..4614ff0b0d 100644 --- a/js/apps/admin-ui/src/clients/authorization/ScopeDetails.tsx +++ b/js/apps/admin-ui/src/clients/authorization/ScopeDetails.tsx @@ -12,7 +12,7 @@ import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; import { TextControl } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { FormAccess } from "../../components/form/FormAccess"; import { ViewHeader } from "../../components/view-header/ViewHeader"; @@ -26,6 +26,8 @@ import { DeleteScopeDialog } from "./DeleteScopeDialog"; type FormFields = Omit; export default function ScopeDetails() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { id, scopeId, realm } = useParams(); const navigate = useNavigate(); diff --git a/js/apps/admin-ui/src/clients/authorization/ScopePicker.tsx b/js/apps/admin-ui/src/clients/authorization/ScopePicker.tsx index 7681d73982..4edfb898ad 100644 --- a/js/apps/admin-ui/src/clients/authorization/ScopePicker.tsx +++ b/js/apps/admin-ui/src/clients/authorization/ScopePicker.tsx @@ -9,8 +9,7 @@ import { useState } from "react"; import { Controller, useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { HelpItem } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useFetch } from "../../utils/useFetch"; type Scope = { @@ -19,6 +18,8 @@ type Scope = { }; export const ScopePicker = ({ clientId }: { clientId: string }) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { control } = useFormContext(); diff --git a/js/apps/admin-ui/src/clients/authorization/ScopeSelect.tsx b/js/apps/admin-ui/src/clients/authorization/ScopeSelect.tsx index 58fb94ae79..d45a0edfd3 100644 --- a/js/apps/admin-ui/src/clients/authorization/ScopeSelect.tsx +++ b/js/apps/admin-ui/src/clients/authorization/ScopeSelect.tsx @@ -7,8 +7,7 @@ import { import { useRef, useState } from "react"; import { Controller, useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useFetch } from "../../utils/useFetch"; type ScopeSelectProps = { @@ -22,6 +21,8 @@ export const ScopeSelect = ({ resourceId, preSelected, }: ScopeSelectProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { diff --git a/js/apps/admin-ui/src/clients/authorization/Scopes.tsx b/js/apps/admin-ui/src/clients/authorization/Scopes.tsx index d1c9ecbff3..ee8707cd8b 100644 --- a/js/apps/admin-ui/src/clients/authorization/Scopes.tsx +++ b/js/apps/admin-ui/src/clients/authorization/Scopes.tsx @@ -18,8 +18,7 @@ import { import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner"; import { ListEmptyState } from "../../components/list-empty-state/ListEmptyState"; import { PaginatingTableToolbar } from "../../components/table-toolbar/PaginatingTableToolbar"; @@ -53,6 +52,8 @@ export const AuthorizationScopes = ({ clientId, isDisabled = false, }: ScopesProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const navigate = useNavigate(); const { realm } = useRealm(); diff --git a/js/apps/admin-ui/src/clients/authorization/Settings.tsx b/js/apps/admin-ui/src/clients/authorization/Settings.tsx index 984b8b0e2d..8f252f5f17 100644 --- a/js/apps/admin-ui/src/clients/authorization/Settings.tsx +++ b/js/apps/admin-ui/src/clients/authorization/Settings.tsx @@ -11,7 +11,7 @@ import { useState } from "react"; import { Controller, FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { HelpItem } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { DefaultSwitchControl } from "../../components/SwitchControl"; import { useAlerts } from "../../components/alert/Alerts"; import { FixedButtonsGroup } from "../../components/form/FixedButtonGroup"; @@ -35,6 +35,8 @@ export type FormFields = Omit< >; export const AuthorizationSettings = ({ clientId }: { clientId: string }) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const [resource, setResource] = useState(); const [importDialog, toggleImportDialog] = useToggle(); diff --git a/js/apps/admin-ui/src/clients/authorization/policy/ClientScope.tsx b/js/apps/admin-ui/src/clients/authorization/policy/ClientScope.tsx index 1d96ec98c3..425ef99a60 100644 --- a/js/apps/admin-ui/src/clients/authorization/policy/ClientScope.tsx +++ b/js/apps/admin-ui/src/clients/authorization/policy/ClientScope.tsx @@ -6,8 +6,7 @@ import { useState } from "react"; import { Controller, useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { FormErrorText, HelpItem } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../../admin-client"; +import { useAdminClient } from "../../../admin-client"; import { useFetch } from "../../../utils/useFetch"; import useLocaleSort, { mapByKey } from "../../../utils/useLocaleSort"; import { AddScopeDialog } from "../../scopes/AddScopeDialog"; @@ -18,6 +17,8 @@ export type RequiredIdValue = { }; export const ClientScope = () => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { control, diff --git a/js/apps/admin-ui/src/clients/authorization/policy/Group.tsx b/js/apps/admin-ui/src/clients/authorization/policy/Group.tsx index 1eddc66ffd..b990c6606e 100644 --- a/js/apps/admin-ui/src/clients/authorization/policy/Group.tsx +++ b/js/apps/admin-ui/src/clients/authorization/policy/Group.tsx @@ -1,17 +1,16 @@ import type GroupRepresentation from "@keycloak/keycloak-admin-client/lib/defs/groupRepresentation"; +import { + FormErrorText, + HelpItem, + TextControl, +} from "@keycloak/keycloak-ui-shared"; import { Button, Checkbox, FormGroup } from "@patternfly/react-core"; import { MinusCircleIcon } from "@patternfly/react-icons"; import { Table, Tbody, Td, Th, Thead, Tr } from "@patternfly/react-table"; import { useState } from "react"; import { Controller, useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; -import { - FormErrorText, - HelpItem, - TextControl, -} from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../../admin-client"; +import { useAdminClient } from "../../../admin-client"; import { GroupPickerDialog } from "../../../components/group/GroupPickerDialog"; import { useFetch } from "../../../utils/useFetch"; @@ -26,6 +25,8 @@ export type GroupValue = { }; export const Group = () => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { control, diff --git a/js/apps/admin-ui/src/clients/authorization/policy/PolicyDetails.tsx b/js/apps/admin-ui/src/clients/authorization/policy/PolicyDetails.tsx index efcfc3b794..5e16808d92 100644 --- a/js/apps/admin-ui/src/clients/authorization/policy/PolicyDetails.tsx +++ b/js/apps/admin-ui/src/clients/authorization/policy/PolicyDetails.tsx @@ -11,8 +11,7 @@ import { useState } from "react"; import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; - -import { adminClient } from "../../../admin-client"; +import { useAdminClient } from "../../../admin-client"; import { useAlerts } from "../../../components/alert/Alerts"; import { useConfirmDialog } from "../../../components/confirm-dialog/ConfirmDialog"; import { FormAccess } from "../../../components/form/FormAccess"; @@ -62,6 +61,8 @@ const COMPONENTS: { export const isValidComponentType = (value: string) => value in COMPONENTS; export default function PolicyDetails() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { id, realm, policyId, policyType } = useParams(); const navigate = useNavigate(); diff --git a/js/apps/admin-ui/src/clients/authorization/policy/Role.tsx b/js/apps/admin-ui/src/clients/authorization/policy/Role.tsx index 3d0cc7ff9f..fda78f3ae7 100644 --- a/js/apps/admin-ui/src/clients/authorization/policy/Role.tsx +++ b/js/apps/admin-ui/src/clients/authorization/policy/Role.tsx @@ -5,8 +5,7 @@ import { useState } from "react"; import { Controller, useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { FormErrorText, HelpItem } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../../admin-client"; +import { useAdminClient } from "../../../admin-client"; import { DefaultSwitchControl } from "../../../components/SwitchControl"; import { AddRoleMappingModal } from "../../../components/role-mapping/AddRoleMappingModal"; import { Row, ServiceRole } from "../../../components/role-mapping/RoleMapping"; @@ -14,6 +13,8 @@ import { useFetch } from "../../../utils/useFetch"; import type { RequiredIdValue } from "./ClientScope"; export const Role = () => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { control, diff --git a/js/apps/admin-ui/src/clients/credentials/ClientSecret.tsx b/js/apps/admin-ui/src/clients/credentials/ClientSecret.tsx index abc8bd24c3..70dd1f25c4 100644 --- a/js/apps/admin-ui/src/clients/credentials/ClientSecret.tsx +++ b/js/apps/admin-ui/src/clients/credentials/ClientSecret.tsx @@ -4,15 +4,15 @@ import { Button, FormGroup, InputGroup, + InputGroupItem, Split, SplitItem, - InputGroupItem, } from "@patternfly/react-core"; import { useState } from "react"; import { useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { PasswordInput } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { useAccess } from "../../context/access/Access"; @@ -89,6 +89,8 @@ const ExpireDateFormatter = ({ time }: { time: number }) => { }; export const ClientSecret = ({ client, secret, toggle }: ClientSecretProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/clients/credentials/Credentials.tsx b/js/apps/admin-ui/src/clients/credentials/Credentials.tsx index 44f0e9c4c4..3fe9b2ad01 100644 --- a/js/apps/admin-ui/src/clients/credentials/Credentials.tsx +++ b/js/apps/admin-ui/src/clients/credentials/Credentials.tsx @@ -19,7 +19,7 @@ import { useState } from "react"; import { useFormContext, useWatch } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { HelpItem, SelectControl } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { FormAccess } from "../../components/form/FormAccess"; @@ -40,6 +40,8 @@ export type CredentialsProps = { }; export const Credentials = ({ client, save, refresh }: CredentialsProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const clientId = client.id!; diff --git a/js/apps/admin-ui/src/clients/import/ImportForm.tsx b/js/apps/admin-ui/src/clients/import/ImportForm.tsx index 19a0a15b26..33bf23c304 100644 --- a/js/apps/admin-ui/src/clients/import/ImportForm.tsx +++ b/js/apps/admin-ui/src/clients/import/ImportForm.tsx @@ -12,8 +12,7 @@ import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; import { TextControl } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { FormAccess } from "../../components/form/FormAccess"; import { FileUploadForm } from "../../components/json-file-upload/FileUploadForm"; @@ -34,6 +33,8 @@ import { toClients } from "../routes/Clients"; const isXml = (text: string) => text.match(/(<.[^(><.)]+>)/g); export default function ImportForm() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const navigate = useNavigate(); const { realm } = useRealm(); diff --git a/js/apps/admin-ui/src/clients/initial-access/CreateInitialAccessToken.tsx b/js/apps/admin-ui/src/clients/initial-access/CreateInitialAccessToken.tsx index acf078a0df..1fe3b80b75 100644 --- a/js/apps/admin-ui/src/clients/initial-access/CreateInitialAccessToken.tsx +++ b/js/apps/admin-ui/src/clients/initial-access/CreateInitialAccessToken.tsx @@ -10,7 +10,7 @@ import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; import { NumberControl } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { FormAccess } from "../../components/form/FormAccess"; import { TimeSelectorControl } from "../../components/time-selector/TimeSelectorControl"; @@ -20,6 +20,8 @@ import { toClients } from "../routes/Clients"; import { AccessTokenDialog } from "./AccessTokenDialog"; export default function CreateInitialAccessToken() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const form = useForm({ mode: "onChange" }); const { diff --git a/js/apps/admin-ui/src/clients/initial-access/InitialAccessTokenList.tsx b/js/apps/admin-ui/src/clients/initial-access/InitialAccessTokenList.tsx index b73b146c92..2589fafd59 100644 --- a/js/apps/admin-ui/src/clients/initial-access/InitialAccessTokenList.tsx +++ b/js/apps/admin-ui/src/clients/initial-access/InitialAccessTokenList.tsx @@ -4,8 +4,7 @@ import { wrappable } from "@patternfly/react-table"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; - -import { adminClient } from "../../admin-client"; +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"; @@ -18,6 +17,8 @@ import useFormatDate, { FORMAT_DATE_AND_TIME } from "../../utils/useFormatDate"; import { toCreateInitialAccessToken } from "../routes/CreateInitialAccessToken"; export const InitialAccessTokenList = () => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/clients/keys/ExportSamlKeyDialog.tsx b/js/apps/admin-ui/src/clients/keys/ExportSamlKeyDialog.tsx index 1d8f7312d4..121b3b41f1 100644 --- a/js/apps/admin-ui/src/clients/keys/ExportSamlKeyDialog.tsx +++ b/js/apps/admin-ui/src/clients/keys/ExportSamlKeyDialog.tsx @@ -3,8 +3,7 @@ import { Button, Form, Modal } from "@patternfly/react-core"; import { saveAs } from "file-saver"; import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { useRealm } from "../../context/realm-context/RealmContext"; import { KeyForm, getFileExtension } from "./GenerateKeyDialog"; @@ -21,6 +20,8 @@ export const ExportSamlKeyDialog = ({ close, keyType, }: ExportSamlKeyDialogProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { realm } = useRealm(); diff --git a/js/apps/admin-ui/src/clients/keys/Keys.tsx b/js/apps/admin-ui/src/clients/keys/Keys.tsx index 506cd7701e..0d098f6ddb 100644 --- a/js/apps/admin-ui/src/clients/keys/Keys.tsx +++ b/js/apps/admin-ui/src/clients/keys/Keys.tsx @@ -17,7 +17,7 @@ import { useState } from "react"; import { useFormContext, useWatch } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { TextControl } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { DefaultSwitchControl } from "../../components/SwitchControl"; import { useAlerts } from "../../components/alert/Alerts"; import { FormAccess } from "../../components/form/FormAccess"; @@ -38,6 +38,8 @@ type KeysProps = { const attr = "jwt.credential"; export const Keys = ({ clientId, save, hasConfigureAccess }: KeysProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { control, diff --git a/js/apps/admin-ui/src/clients/keys/SamlImportKeyDialog.tsx b/js/apps/admin-ui/src/clients/keys/SamlImportKeyDialog.tsx index 83b2259731..52b7f43522 100644 --- a/js/apps/admin-ui/src/clients/keys/SamlImportKeyDialog.tsx +++ b/js/apps/admin-ui/src/clients/keys/SamlImportKeyDialog.tsx @@ -1,7 +1,7 @@ import { AlertVariant } from "@patternfly/react-core"; import { FormProvider, useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; - +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { ConfirmDialogModal } from "../../components/confirm-dialog/ConfirmDialog"; import { KeyForm } from "./GenerateKeyDialog"; @@ -19,6 +19,8 @@ export const SamlImportKeyDialog = ({ attr, onClose, }: SamlImportKeyDialogProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const form = useFormContext(); const { handleSubmit } = form; @@ -26,7 +28,7 @@ export const SamlImportKeyDialog = ({ const { addAlert, addError } = useAlerts(); const submit = (form: SamlKeysDialogForm) => { - submitForm(form, id, attr, (error) => { + submitForm(adminClient, form, id, attr, (error) => { if (error) { addError("importError", error); } else { diff --git a/js/apps/admin-ui/src/clients/keys/SamlKeys.tsx b/js/apps/admin-ui/src/clients/keys/SamlKeys.tsx index 47cf39e807..023f762cf6 100644 --- a/js/apps/admin-ui/src/clients/keys/SamlKeys.tsx +++ b/js/apps/admin-ui/src/clients/keys/SamlKeys.tsx @@ -17,7 +17,7 @@ import { Fragment, useState } from "react"; import { Controller, useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { FormPanel, HelpItem } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { FormAccess } from "../../components/form/FormAccess"; @@ -154,6 +154,8 @@ const KeySection = ({ }; export const SamlKeys = ({ clientId, save }: SamlKeysProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const [isChanged, setIsChanged] = useState(); const [keyInfo, setKeyInfo] = useState(); diff --git a/js/apps/admin-ui/src/clients/keys/SamlKeysDialog.tsx b/js/apps/admin-ui/src/clients/keys/SamlKeysDialog.tsx index ec82b473c8..4fc209bac3 100644 --- a/js/apps/admin-ui/src/clients/keys/SamlKeysDialog.tsx +++ b/js/apps/admin-ui/src/clients/keys/SamlKeysDialog.tsx @@ -1,3 +1,4 @@ +import KeycloakAdminClient from "@keycloak/keycloak-admin-client"; import type CertificateRepresentation from "@keycloak/keycloak-admin-client/lib/defs/certificateRepresentation"; import type KeyStoreConfig from "@keycloak/keycloak-admin-client/lib/defs/keystoreConfig"; import { @@ -22,8 +23,7 @@ import { useState } from "react"; import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { HelpItem } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { Certificate } from "./Certificate"; import { KeyForm } from "./GenerateKeyDialog"; @@ -41,6 +41,7 @@ export type SamlKeysDialogForm = KeyStoreConfig & { }; export const submitForm = async ( + adminClient: KeycloakAdminClient, form: SamlKeysDialogForm, id: string, attr: KeyTypes, @@ -70,6 +71,8 @@ export const SamlKeysDialog = ({ onClose, onCancel, }: SamlKeysDialogProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const [type, setType] = useState(false); const [keys, setKeys] = useState(); @@ -82,7 +85,7 @@ export const SamlKeysDialog = ({ const { addAlert, addError } = useAlerts(); const submit = (form: SamlKeysDialogForm) => { - submitForm(form, id, attr, (error) => { + submitForm(adminClient, form, id, attr, (error) => { if (error) { addError("importError", error); } else { diff --git a/js/apps/admin-ui/src/clients/registration/ClientRegistrationList.tsx b/js/apps/admin-ui/src/clients/registration/ClientRegistrationList.tsx index df9a52137b..1b4517f337 100644 --- a/js/apps/admin-ui/src/clients/registration/ClientRegistrationList.tsx +++ b/js/apps/admin-ui/src/clients/registration/ClientRegistrationList.tsx @@ -3,8 +3,7 @@ import { Button, ButtonVariant, ToolbarItem } from "@patternfly/react-core"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Link, useNavigate, useParams } from "react-router-dom"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { @@ -44,6 +43,8 @@ const DetailLink = (comp: ComponentRepresentation) => { export const ClientRegistrationList = ({ subType, }: ClientRegistrationListProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { subTab } = useParams(); const navigate = useNavigate(); diff --git a/js/apps/admin-ui/src/clients/registration/DetailProvider.tsx b/js/apps/admin-ui/src/clients/registration/DetailProvider.tsx index addd31208b..6a3eda8ea4 100644 --- a/js/apps/admin-ui/src/clients/registration/DetailProvider.tsx +++ b/js/apps/admin-ui/src/clients/registration/DetailProvider.tsx @@ -12,7 +12,7 @@ import { FormProvider, useForm, useWatch } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; import { TextControl } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { DynamicComponents } from "../../components/dynamic/DynamicComponents"; @@ -29,6 +29,8 @@ import { import { toClientRegistration } from "../routes/ClientRegistration"; export default function DetailProvider() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { id, providerId, subTab } = useParams(); const navigate = useNavigate(); diff --git a/js/apps/admin-ui/src/clients/roles/CreateClientRole.tsx b/js/apps/admin-ui/src/clients/roles/CreateClientRole.tsx index 6b30d2f91a..9a8f1e0240 100644 --- a/js/apps/admin-ui/src/clients/roles/CreateClientRole.tsx +++ b/js/apps/admin-ui/src/clients/roles/CreateClientRole.tsx @@ -3,8 +3,7 @@ import { AlertVariant } from "@patternfly/react-core"; import { FormProvider, SubmitHandler, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useNavigate, useParams } from "react-router-dom"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { AttributeForm } from "../../components/key-value-form/AttributeForm"; import { RoleForm } from "../../components/role-form/RoleForm"; @@ -14,6 +13,8 @@ import { toClientRole } from "../routes/ClientRole"; import { NewRoleParams } from "../routes/NewRole"; export default function CreateClientRole() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const form = useForm({ mode: "onChange" }); const navigate = useNavigate(); diff --git a/js/apps/admin-ui/src/clients/scopes/ClientScopes.tsx b/js/apps/admin-ui/src/clients/scopes/ClientScopes.tsx index e70ee3a4d7..fbc595541e 100644 --- a/js/apps/admin-ui/src/clients/scopes/ClientScopes.tsx +++ b/js/apps/admin-ui/src/clients/scopes/ClientScopes.tsx @@ -13,8 +13,7 @@ import { import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { ChangeTypeDropdown } from "../../client-scopes/ChangeTypeDropdown"; import { SearchDropdown, @@ -73,6 +72,8 @@ const TypeSelector = ({ fineGrainedAccess, ...scope }: TypeSelectorProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); @@ -89,6 +90,7 @@ const TypeSelector = ({ onSelect={async (value) => { try { await changeClientScope( + adminClient, clientId, scope, scope.type, @@ -110,6 +112,8 @@ export const ClientScopes = ({ clientName, fineGrainedAccess, }: ClientScopesProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const { realm } = useRealm(); @@ -205,6 +209,7 @@ export const ClientScopes = ({ onConfirm: async () => { try { await removeClientScope( + adminClient, clientId, selectedRows[0], selectedRows[0].type as ClientScope, @@ -230,7 +235,12 @@ export const ClientScopes = ({ await Promise.all( scopes.map( async (scope) => - await addClientScope(clientId, scope.scope, scope.type!), + await addClientScope( + adminClient, + clientId, + scope.scope, + scope.type!, + ), ), ); addAlert(t("clientScopeSuccess"), AlertVariant.success); @@ -301,6 +311,7 @@ export const ClientScopes = ({ await Promise.all( selectedRows.map((row) => removeClientScope( + adminClient, clientId, { ...row }, row.type as ClientScope, diff --git a/js/apps/admin-ui/src/clients/scopes/DedicatedScope.tsx b/js/apps/admin-ui/src/clients/scopes/DedicatedScope.tsx index 909e495c24..55b066e54b 100644 --- a/js/apps/admin-ui/src/clients/scopes/DedicatedScope.tsx +++ b/js/apps/admin-ui/src/clients/scopes/DedicatedScope.tsx @@ -9,12 +9,10 @@ import { } from "@patternfly/react-core"; import { useState } from "react"; import { useTranslation } from "react-i18next"; - -import { FormAccess } from "../../components/form/FormAccess"; import { HelpItem } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; +import { FormAccess } from "../../components/form/FormAccess"; import { RoleMapping, Row } from "../../components/role-mapping/RoleMapping"; import { useAccess } from "../../context/access/Access"; @@ -25,6 +23,8 @@ type DedicatedScopeProps = { export const DedicatedScope = ({ client: initialClient, }: DedicatedScopeProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/clients/scopes/DedicatedScopes.tsx b/js/apps/admin-ui/src/clients/scopes/DedicatedScopes.tsx index d8bcd1f6a5..2ab59822c2 100644 --- a/js/apps/admin-ui/src/clients/scopes/DedicatedScopes.tsx +++ b/js/apps/admin-ui/src/clients/scopes/DedicatedScopes.tsx @@ -10,8 +10,7 @@ import { import { useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { MapperList } from "../../client-scopes/details/MapperList"; import { useAlerts } from "../../components/alert/Alerts"; import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner"; @@ -31,6 +30,8 @@ import { toMapper } from "../routes/Mapper"; import { DedicatedScope } from "./DedicatedScope"; export default function DedicatedScopes() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const navigate = useNavigate(); const { realm, clientId } = useParams(); diff --git a/js/apps/admin-ui/src/clients/scopes/EvaluateScopes.tsx b/js/apps/admin-ui/src/clients/scopes/EvaluateScopes.tsx index b16ccd27f4..1ffa8cb258 100644 --- a/js/apps/admin-ui/src/clients/scopes/EvaluateScopes.tsx +++ b/js/apps/admin-ui/src/clients/scopes/EvaluateScopes.tsx @@ -28,16 +28,15 @@ import { useEffect, useRef, useState } from "react"; import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { HelpItem, useHelp } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { KeycloakDataTable } from "../../components/table-toolbar/KeycloakDataTable"; import { UserSelect } from "../../components/users/UserSelect"; +import { useAccess } from "../../context/access/Access"; import { useRealm } from "../../context/realm-context/RealmContext"; import { useServerInfo } from "../../context/server-info/ServerInfoProvider"; import { prettyPrintJSON } from "../../util"; import { useFetch } from "../../utils/useFetch"; import { GeneratedCodeTab } from "./GeneratedCodeTab"; -import { useAccess } from "../../context/access/Access"; import "./evaluate.css"; @@ -116,6 +115,8 @@ const EffectiveRoles = ({ }; export const EvaluateScopes = ({ clientId, protocol }: EvaluateScopesProps) => { + const { adminClient } = useAdminClient(); + const prefix = "openid"; const { t } = useTranslation(); const { enabled } = useHelp(); diff --git a/js/apps/admin-ui/src/clients/service-account/ServiceAccount.tsx b/js/apps/admin-ui/src/clients/service-account/ServiceAccount.tsx index d9bb335465..ac4a25da1c 100644 --- a/js/apps/admin-ui/src/clients/service-account/ServiceAccount.tsx +++ b/js/apps/admin-ui/src/clients/service-account/ServiceAccount.tsx @@ -6,8 +6,7 @@ import { InfoCircleIcon } from "@patternfly/react-icons"; import { useState } from "react"; import { Trans, useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner"; import { RoleMapping, Row } from "../../components/role-mapping/RoleMapping"; @@ -23,6 +22,8 @@ type ServiceAccountProps = { }; export const ServiceAccount = ({ client }: ServiceAccountProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const { realm } = useRealm(); diff --git a/js/apps/admin-ui/src/components/client-scope/ClientScopeTypes.tsx b/js/apps/admin-ui/src/components/client-scope/ClientScopeTypes.tsx index f7e6ace55b..d6fdf95fe5 100644 --- a/js/apps/admin-ui/src/components/client-scope/ClientScopeTypes.tsx +++ b/js/apps/admin-ui/src/components/client-scope/ClientScopeTypes.tsx @@ -1,5 +1,6 @@ import type ClientScopeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientScopeRepresentation"; +import KeycloakAdminClient from "@keycloak/keycloak-admin-client"; import { DropdownItem, Select, @@ -9,8 +10,6 @@ import { import type { TFunction } from "i18next"; import { useState } from "react"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../../admin-client"; import { toUpperCase } from "../../util"; export enum ClientScope { @@ -96,23 +95,25 @@ export type ClientScopeDefaultOptionalType = ClientScopeRepresentation & { }; export const changeScope = async ( + adminClient: KeycloakAdminClient, clientScope: ClientScopeDefaultOptionalType, changeTo: AllClientScopeType, ) => { - await removeScope(clientScope); - await addScope(clientScope, changeTo); + await removeScope(adminClient, clientScope); + await addScope(adminClient, clientScope, changeTo); }; -const castAdminClient = () => +const castAdminClient = (adminClient: KeycloakAdminClient) => adminClient.clientScopes as unknown as { [index: string]: Function; }; export const removeScope = async ( + adminClient: KeycloakAdminClient, clientScope: ClientScopeDefaultOptionalType, ) => { if (clientScope.type !== AllClientScopes.none) - await castAdminClient()[ + await castAdminClient(adminClient)[ `delDefault${ clientScope.type === ClientScope.optional ? "Optional" : "" }ClientScope` @@ -122,11 +123,12 @@ export const removeScope = async ( }; const addScope = async ( + adminClient: KeycloakAdminClient, clientScope: ClientScopeDefaultOptionalType, type: AllClientScopeType, ) => { if (type !== AllClientScopes.none) - await castAdminClient()[ + await castAdminClient(adminClient)[ `addDefault${type === ClientScope.optional ? "Optional" : ""}ClientScope` ]({ id: clientScope.id!, @@ -134,18 +136,20 @@ const addScope = async ( }; export const changeClientScope = async ( + adminClient: KeycloakAdminClient, clientId: string, clientScope: ClientScopeRepresentation, type: AllClientScopeType, changeTo: ClientScopeType, ) => { if (type !== "none") { - await removeClientScope(clientId, clientScope, type); + await removeClientScope(adminClient, clientId, clientScope, type); } - await addClientScope(clientId, clientScope, changeTo); + await addClientScope(adminClient, clientId, clientScope, changeTo); }; export const removeClientScope = async ( + adminClient: KeycloakAdminClient, clientId: string, clientScope: ClientScopeRepresentation, type: ClientScope, @@ -159,6 +163,7 @@ export const removeClientScope = async ( }; export const addClientScope = async ( + adminClient: KeycloakAdminClient, clientId: string, clientScope: ClientScopeRepresentation, type: ClientScopeType, diff --git a/js/apps/admin-ui/src/components/client/ClientSelect.tsx b/js/apps/admin-ui/src/components/client/ClientSelect.tsx index 8074384e73..4f27111243 100644 --- a/js/apps/admin-ui/src/components/client/ClientSelect.tsx +++ b/js/apps/admin-ui/src/components/client/ClientSelect.tsx @@ -4,8 +4,7 @@ import { SelectProps, SelectVariant } from "@patternfly/react-core/deprecated"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { SelectControl } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useFetch } from "../../utils/useFetch"; import type { ComponentProps } from "../dynamic/components"; @@ -20,6 +19,8 @@ export const ClientSelect = ({ required = false, variant = SelectVariant.typeahead, }: ClientSelectProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const [clients, setClients] = useState([]); diff --git a/js/apps/admin-ui/src/components/download-dialog/DownloadDialog.tsx b/js/apps/admin-ui/src/components/download-dialog/DownloadDialog.tsx index dd66c0030e..cf86ed9ae2 100644 --- a/js/apps/admin-ui/src/components/download-dialog/DownloadDialog.tsx +++ b/js/apps/admin-ui/src/components/download-dialog/DownloadDialog.tsx @@ -16,8 +16,7 @@ import { saveAs } from "file-saver"; import { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { HelpItem, useHelp } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useRealm } from "../../context/realm-context/RealmContext"; import { useServerInfo } from "../../context/server-info/ServerInfoProvider"; import { addTrailingSlash, prettyPrintJSON } from "../../util"; @@ -38,6 +37,8 @@ export const DownloadDialog = ({ toggleDialog, protocol = "openid-connect", }: DownloadDialogProps) => { + const { adminClient } = useAdminClient(); + const { realm } = useRealm(); const { t } = useTranslation(); const { enabled } = useHelp(); diff --git a/js/apps/admin-ui/src/components/dynamic/UserProfileAttributeListComponent.tsx b/js/apps/admin-ui/src/components/dynamic/UserProfileAttributeListComponent.tsx index 8aec190250..8d1b986a74 100644 --- a/js/apps/admin-ui/src/components/dynamic/UserProfileAttributeListComponent.tsx +++ b/js/apps/admin-ui/src/components/dynamic/UserProfileAttributeListComponent.tsx @@ -4,8 +4,7 @@ import { useState } from "react"; import { useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { FormErrorText, HelpItem } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useFetch } from "../../utils/useFetch"; import { KeySelect } from "../key-value-form/KeySelect"; import { convertToName } from "./DynamicComponents"; @@ -17,6 +16,8 @@ export const UserProfileAttributeListComponent = ({ helpText, required = false, }: ComponentProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { formState: { errors }, diff --git a/js/apps/admin-ui/src/components/group/GroupPickerDialog.tsx b/js/apps/admin-ui/src/components/group/GroupPickerDialog.tsx index 2415d46818..90a1506da8 100644 --- a/js/apps/admin-ui/src/components/group/GroupPickerDialog.tsx +++ b/js/apps/admin-ui/src/components/group/GroupPickerDialog.tsx @@ -20,7 +20,7 @@ import { import { AngleRightIcon } from "@patternfly/react-icons"; import { Fragment, useState } from "react"; import { useTranslation } from "react-i18next"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useFetch } from "../../utils/useFetch"; import { ListEmptyState } from "../list-empty-state/ListEmptyState"; import { PaginatingTableToolbar } from "../table-toolbar/PaginatingTableToolbar"; @@ -53,6 +53,8 @@ export const GroupPickerDialog = ({ onClose, onConfirm, }: GroupPickerDialogProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const [selectedRows, setSelectedRows] = useState([]); diff --git a/js/apps/admin-ui/src/components/permission-tab/PermissionTab.tsx b/js/apps/admin-ui/src/components/permission-tab/PermissionTab.tsx index b5f7749c0f..890e923189 100644 --- a/js/apps/admin-ui/src/components/permission-tab/PermissionTab.tsx +++ b/js/apps/admin-ui/src/components/permission-tab/PermissionTab.tsx @@ -21,8 +21,7 @@ import { useState } from "react"; import { Trans, useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; import { HelpItem } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { toPermissionDetails } from "../../clients/routes/PermissionDetails"; import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner"; import { useRealm } from "../../context/realm-context/RealmContext"; @@ -45,6 +44,8 @@ type PermissionsTabProps = { }; export const PermissionsTab = ({ id, type }: PermissionsTabProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const navigate = useNavigate(); const { realm } = useRealm(); diff --git a/js/apps/admin-ui/src/components/role-mapping/AddRoleMappingModal.tsx b/js/apps/admin-ui/src/components/role-mapping/AddRoleMappingModal.tsx index 42b5760af1..53d88bfc87 100644 --- a/js/apps/admin-ui/src/components/role-mapping/AddRoleMappingModal.tsx +++ b/js/apps/admin-ui/src/components/role-mapping/AddRoleMappingModal.tsx @@ -12,7 +12,7 @@ import { import { FilterIcon } from "@patternfly/react-icons"; import { useState } from "react"; import { useTranslation } from "react-i18next"; - +import { useAdminClient } from "../../admin-client"; import { useAccess } from "../../context/access/Access"; import useLocaleSort from "../../utils/useLocaleSort"; import { ListEmptyState } from "../list-empty-state/ListEmptyState"; @@ -42,6 +42,8 @@ export const AddRoleMappingModal = ({ onAssign, onClose, }: AddRoleMappingModalProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { hasAccess } = useAccess(); const canViewRealmRoles = hasAccess("view-realm") || hasAccess("query-users"); @@ -72,7 +74,7 @@ export const AddRoleMappingModal = ({ params.search = search; } - const roles = await getAvailableRoles(type, { ...params, id }); + const roles = await getAvailableRoles(adminClient, type, { ...params, id }); const sorted = localeSort(roles, compareRow); return sorted.map((row) => { return { @@ -87,7 +89,7 @@ export const AddRoleMappingModal = ({ max?: number, search?: string, ): Promise => { - const roles = await getAvailableClientRoles({ + const roles = await getAvailableClientRoles(adminClient, { id, type, first: first || 0, diff --git a/js/apps/admin-ui/src/components/role-mapping/RoleMapping.tsx b/js/apps/admin-ui/src/components/role-mapping/RoleMapping.tsx index 3df678e218..5b8172f6ad 100644 --- a/js/apps/admin-ui/src/components/role-mapping/RoleMapping.tsx +++ b/js/apps/admin-ui/src/components/role-mapping/RoleMapping.tsx @@ -12,7 +12,7 @@ import { import { cellWidth } from "@patternfly/react-table"; import { useState } from "react"; import { useTranslation } from "react-i18next"; - +import { useAdminClient } from "../../admin-client"; import { emptyFormatter, upperCaseFormatter } from "../../util"; import { useAlerts } from "../alert/Alerts"; import { useConfirmDialog } from "../confirm-dialog/ConfirmDialog"; @@ -86,6 +86,8 @@ export const RoleMapping = ({ isManager = true, save, }: RoleMappingProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); @@ -105,10 +107,10 @@ export const RoleMapping = ({ let effectiveRoles: Row[] = []; let effectiveClientRoles: Row[] = []; if (!hide) { - effectiveRoles = await getEffectiveRoles(type, id); + effectiveRoles = await getEffectiveRoles(adminClient, type, id); effectiveClientRoles = ( - await getEffectiveClientRoles({ + await getEffectiveClientRoles(adminClient, { type, id, }) @@ -118,7 +120,7 @@ export const RoleMapping = ({ })); } - const roles = await getMapping(type, id); + const roles = await getMapping(adminClient, type, id); const realmRolesMapping = roles.realmMappings?.map((role) => ({ role })) || []; const clientMapping = Object.values(roles.clientMappings || {}) @@ -150,7 +152,7 @@ export const RoleMapping = ({ }, onConfirm: async () => { try { - await Promise.all(deleteMapping(type, id, selected)); + await Promise.all(deleteMapping(adminClient, type, id, selected)); addAlert(t("clientScopeRemoveSuccess"), AlertVariant.success); refresh(); } catch (error) { diff --git a/js/apps/admin-ui/src/components/role-mapping/queries.ts b/js/apps/admin-ui/src/components/role-mapping/queries.ts index 7c34723443..f6eeb24043 100644 --- a/js/apps/admin-ui/src/components/role-mapping/queries.ts +++ b/js/apps/admin-ui/src/components/role-mapping/queries.ts @@ -6,8 +6,6 @@ import type { Clients } from "@keycloak/keycloak-admin-client/lib/resources/clie import type { Groups } from "@keycloak/keycloak-admin-client/lib/resources/groups"; import type { Roles } from "@keycloak/keycloak-admin-client/lib/resources/roles"; import type { Users } from "@keycloak/keycloak-admin-client/lib/resources/users"; - -import { adminClient } from "../../admin-client"; import { Row } from "./RoleMapping"; export type ResourcesKey = keyof KeycloakAdminClient; @@ -97,23 +95,34 @@ type queryType = | ListAvailableFunction | ListEffectiveFunction; -const castAdminClient = (resource: ResourcesKey) => +const castAdminClient = ( + adminClient: KeycloakAdminClient, + resource: ResourcesKey, +) => adminClient[resource] as unknown as { [index in queryType]: (...params: any) => Promise; }; const applyQuery = ( + adminClient: KeycloakAdminClient, type: ResourcesKey, query: queryType, ...params: object[] -): Promise => castAdminClient(type)[query](...params); +): Promise => + castAdminClient(adminClient, type)[query](...params); -export const deleteMapping = (type: ResourcesKey, id: string, rows: Row[]) => +export const deleteMapping = ( + adminClient: KeycloakAdminClient, + type: ResourcesKey, + id: string, + rows: Row[], +) => rows.map((row) => { const role = { id: row.role.id!, name: row.role.name! }; const query = mapping[type]?.delete[row.client ? 0 : 1]!; return applyQuery( + adminClient, type, query, { @@ -127,11 +136,12 @@ export const deleteMapping = (type: ResourcesKey, id: string, rows: Row[]) => }); export const getMapping = async ( + adminClient: KeycloakAdminClient, type: ResourcesKey, id: string, ): Promise => { const query = mapping[type]!.listEffective[0]; - const result = applyQuery(type, query, { id }); + const result = applyQuery(adminClient, type, query, { id }); if (type !== "roles") { return result as MappingsRepresentation; } @@ -156,30 +166,32 @@ export const getMapping = async ( }; export const getEffectiveRoles = async ( + adminClient: KeycloakAdminClient, type: ResourcesKey, id: string, ): Promise => { const query = mapping[type]!.listEffective[1]; if (type !== "roles") { - return (await applyQuery(type, query, { id })).map((role) => ({ + return (await applyQuery(adminClient, type, query, { id })).map((role) => ({ role, })); } - const roles = await applyQuery(type, query, { id }); + const roles = await applyQuery(adminClient, type, query, { id }); const parentRoles = await Promise.all( roles .filter((r) => r.composite) - .map((r) => applyQuery(type, query, { id: r.id })), + .map((r) => applyQuery(adminClient, type, query, { id: r.id })), ); return [...roles, ...parentRoles.flat()].map((role) => ({ role })); }; export const getAvailableRoles = async ( + adminClient: KeycloakAdminClient, type: ResourcesKey, params: Record, ): Promise => { const query = mapping[type]!.listAvailable[1]; - return (await applyQuery(type, query, params)).map((role) => ({ + return (await applyQuery(adminClient, type, query, params)).map((role) => ({ role, })); }; diff --git a/js/apps/admin-ui/src/components/role-mapping/resource.ts b/js/apps/admin-ui/src/components/role-mapping/resource.ts index e0dc11d6f3..a460a4fcbf 100644 --- a/js/apps/admin-ui/src/components/role-mapping/resource.ts +++ b/js/apps/admin-ui/src/components/role-mapping/resource.ts @@ -1,6 +1,7 @@ import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation"; import { fetchAdminUI } from "../../context/auth/admin-ui-endpoint"; +import KeycloakAdminClient from "@keycloak/keycloak-admin-client"; type IDQuery = { id: string; @@ -27,29 +28,27 @@ type ClientRole = { clientId: string; }; -const fetchEndpoint = async ({ - id, - type, - first, - max, - search, - endpoint, -}: Query): Promise => - fetchAdminUI(`/ui-ext/${endpoint}/${type}/${id}`, { +const fetchEndpoint = async ( + adminClient: KeycloakAdminClient, + { id, type, first, max, search, endpoint }: Query, +): Promise => + fetchAdminUI(adminClient, `/ui-ext/${endpoint}/${type}/${id}`, { first: (first || 0).toString(), max: (max || 10).toString(), search: search || "", }); export const getAvailableClientRoles = ( + adminClient: KeycloakAdminClient, query: PaginatingQuery, ): Promise => - fetchEndpoint({ ...query, endpoint: "available-roles" }); + fetchEndpoint(adminClient, { ...query, endpoint: "available-roles" }); export const getEffectiveClientRoles = ( + adminClient: KeycloakAdminClient, query: EffectiveClientRolesQuery, ): Promise => - fetchEndpoint({ ...query, endpoint: "effective-roles" }); + fetchEndpoint(adminClient, { ...query, endpoint: "effective-roles" }); type UserQuery = { lastName?: string; @@ -69,8 +68,21 @@ export type BruteUser = UserRepresentation & { bruteForceStatus?: Record; }; -export const findUsers = (query: UserQuery): Promise => - fetchAdminUI("ui-ext/brute-force-user", query as Record); +export const findUsers = ( + adminClient: KeycloakAdminClient, + query: UserQuery, +): Promise => + fetchAdminUI( + adminClient, + "ui-ext/brute-force-user", + query as Record, + ); -export const fetchUsedBy = (query: PaginatingQuery): Promise => - fetchEndpoint({ ...query, endpoint: "authentication-management" }); +export const fetchUsedBy = ( + adminClient: KeycloakAdminClient, + query: PaginatingQuery, +): Promise => + fetchEndpoint(adminClient, { + ...query, + endpoint: "authentication-management", + }); diff --git a/js/apps/admin-ui/src/components/roles-list/RolesList.tsx b/js/apps/admin-ui/src/components/roles-list/RolesList.tsx index e248435633..6c3ea5dda4 100644 --- a/js/apps/admin-ui/src/components/roles-list/RolesList.tsx +++ b/js/apps/admin-ui/src/components/roles-list/RolesList.tsx @@ -5,8 +5,7 @@ import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Link, To, useNavigate } from "react-router-dom"; import { HelpItem } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useRealm } from "../../context/realm-context/RealmContext"; import { toRealmSettings } from "../../realm-settings/routes/RealmSettings"; import { emptyFormatter, upperCaseFormatter } from "../../util"; @@ -71,6 +70,8 @@ export const RolesList = ({ toDetail, isReadOnly, }: RolesListProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const navigate = useNavigate(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/components/users/UserDataTable.tsx b/js/apps/admin-ui/src/components/users/UserDataTable.tsx index 85acabccdb..cb527ed05a 100644 --- a/js/apps/admin-ui/src/components/users/UserDataTable.tsx +++ b/js/apps/admin-ui/src/components/users/UserDataTable.tsx @@ -27,8 +27,7 @@ import type { IRowData } from "@patternfly/react-table"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useRealm } from "../../context/realm-context/RealmContext"; import { SearchType } from "../../user/details/SearchFilter"; import { toAddUser } from "../../user/routes/AddUser"; @@ -95,6 +94,8 @@ const ValidatedEmail = (user: UserRepresentation) => { }; export function UserDataTable() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const { realm: realmName } = useRealm(); @@ -159,7 +160,7 @@ export function UserDataTable() { } try { - return await findUsers({ + return await findUsers(adminClient, { briefRepresentation: true, ...params, }); diff --git a/js/apps/admin-ui/src/components/users/UserSelect.tsx b/js/apps/admin-ui/src/components/users/UserSelect.tsx index 4c72b781a5..6b43710701 100644 --- a/js/apps/admin-ui/src/components/users/UserSelect.tsx +++ b/js/apps/admin-ui/src/components/users/UserSelect.tsx @@ -11,8 +11,7 @@ import { useCallback, useState } from "react"; import { Controller, useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { FormErrorText, HelpItem } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useFetch } from "../../utils/useFetch"; import useToggle from "../../utils/useToggle"; import type { ComponentProps } from "../dynamic/components"; @@ -30,6 +29,8 @@ export const UserSelect = ({ isRequired, variant = SelectVariant.typeaheadMulti, }: UserSelectProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { control, diff --git a/js/apps/admin-ui/src/components/users/resource.ts b/js/apps/admin-ui/src/components/users/resource.ts index 55d98e20d2..afc52da43e 100644 --- a/js/apps/admin-ui/src/components/users/resource.ts +++ b/js/apps/admin-ui/src/components/users/resource.ts @@ -1,6 +1,8 @@ +import KeycloakAdminClient from "@keycloak/keycloak-admin-client"; import { fetchAdminUI } from "../../context/auth/admin-ui-endpoint"; export const getUnmanagedAttributes = ( + adminClient: KeycloakAdminClient, id: string, ): Promise | undefined> => - fetchAdminUI(`ui-ext/users/${id}/unmanagedAttributes`); + fetchAdminUI(adminClient, `ui-ext/users/${id}/unmanagedAttributes`); diff --git a/js/apps/admin-ui/src/context/RealmsContext.tsx b/js/apps/admin-ui/src/context/RealmsContext.tsx index 261948b7d2..27951ce09f 100644 --- a/js/apps/admin-ui/src/context/RealmsContext.tsx +++ b/js/apps/admin-ui/src/context/RealmsContext.tsx @@ -1,16 +1,16 @@ import { NetworkError } from "@keycloak/keycloak-admin-client"; -import { PropsWithChildren, useCallback, useMemo, useState } from "react"; import { createNamedContext, - useRequiredContext, label, + useEnvironment, + useRequiredContext, } from "@keycloak/keycloak-ui-shared"; - -import { keycloak } from "../keycloak"; -import { useFetch } from "../utils/useFetch"; -import { fetchAdminUI } from "./auth/admin-ui-endpoint"; -import useLocaleSort from "../utils/useLocaleSort"; +import { PropsWithChildren, useCallback, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; +import { useAdminClient } from "../admin-client"; +import { useFetch } from "../utils/useFetch"; +import useLocaleSort from "../utils/useLocaleSort"; +import { fetchAdminUI } from "./auth/admin-ui-endpoint"; type RealmsContextProps = { /** A list of all the realms. */ @@ -30,6 +30,9 @@ export const RealmsContext = createNamedContext( ); export const RealmsProvider = ({ children }: PropsWithChildren) => { + const { keycloak } = useEnvironment(); + const { adminClient } = useAdminClient(); + const [realms, setRealms] = useState([]); const [refreshCount, setRefreshCount] = useState(0); const localeSort = useLocaleSort(); @@ -43,6 +46,7 @@ export const RealmsProvider = ({ children }: PropsWithChildren) => { async () => { try { return await fetchAdminUI( + adminClient, "ui-ext/realms/names", {}, ); diff --git a/js/apps/admin-ui/src/context/auth/admin-ui-endpoint.ts b/js/apps/admin-ui/src/context/auth/admin-ui-endpoint.ts index 20860f10cb..367d6ea70a 100644 --- a/js/apps/admin-ui/src/context/auth/admin-ui-endpoint.ts +++ b/js/apps/admin-ui/src/context/auth/admin-ui-endpoint.ts @@ -1,9 +1,11 @@ -import { fetchWithError } from "@keycloak/keycloak-admin-client"; -import { adminClient } from "../../admin-client"; +import KeycloakAdminClient, { + fetchWithError, +} from "@keycloak/keycloak-admin-client"; import { getAuthorizationHeaders } from "../../utils/getAuthorizationHeaders"; import { joinPath } from "../../utils/joinPath"; export async function fetchAdminUI( + adminClient: KeycloakAdminClient, endpoint: string, query?: Record, ): Promise { diff --git a/js/apps/admin-ui/src/context/realm-context/RealmContext.tsx b/js/apps/admin-ui/src/context/realm-context/RealmContext.tsx index 6927f3ae52..a0ac8e39a3 100644 --- a/js/apps/admin-ui/src/context/realm-context/RealmContext.tsx +++ b/js/apps/admin-ui/src/context/realm-context/RealmContext.tsx @@ -2,12 +2,11 @@ import { PropsWithChildren, useEffect, useMemo } from "react"; import { useMatch } from "react-router-dom"; import { createNamedContext, + useEnvironment, useRequiredContext, } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { DashboardRouteWithRealm } from "../../dashboard/routes/Dashboard"; -import environment from "../../environment"; type RealmContextType = { realm: string; @@ -19,6 +18,9 @@ export const RealmContext = createNamedContext( ); export const RealmContextProvider = ({ children }: PropsWithChildren) => { + const { adminClient } = useAdminClient(); + const { environment } = useEnvironment(); + const routeMatch = useMatch({ path: DashboardRouteWithRealm.path, end: false, @@ -26,7 +28,7 @@ export const RealmContextProvider = ({ children }: PropsWithChildren) => { const realmParam = routeMatch?.params.realm; const realm = useMemo( - () => decodeURIComponent(realmParam ?? environment.loginRealm), + () => decodeURIComponent(realmParam ?? environment.realm), [realmParam], ); diff --git a/js/apps/admin-ui/src/context/server-info/ServerInfoProvider.tsx b/js/apps/admin-ui/src/context/server-info/ServerInfoProvider.tsx index dc8962ddd3..fe64a4fc2b 100644 --- a/js/apps/admin-ui/src/context/server-info/ServerInfoProvider.tsx +++ b/js/apps/admin-ui/src/context/server-info/ServerInfoProvider.tsx @@ -1,11 +1,10 @@ import type { ServerInfoRepresentation } from "@keycloak/keycloak-admin-client/lib/defs/serverInfoRepesentation"; -import { PropsWithChildren, useState } from "react"; import { createNamedContext, useRequiredContext, } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { PropsWithChildren, useState } from "react"; +import { useAdminClient } from "../../admin-client"; import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner"; import { sortProviders } from "../../util"; import { useFetch } from "../../utils/useFetch"; @@ -20,6 +19,7 @@ export const useLoginProviders = () => sortProviders(useServerInfo().providers!["login-protocol"].providers); export const ServerInfoProvider = ({ children }: PropsWithChildren) => { + const { adminClient } = useAdminClient(); const [serverInfo, setServerInfo] = useState(); useFetch(adminClient.serverInfo.find, setServerInfo, []); diff --git a/js/apps/admin-ui/src/context/whoami/WhoAmI.tsx b/js/apps/admin-ui/src/context/whoami/WhoAmI.tsx index 78ad3da589..7dac7b999d 100644 --- a/js/apps/admin-ui/src/context/whoami/WhoAmI.tsx +++ b/js/apps/admin-ui/src/context/whoami/WhoAmI.tsx @@ -3,11 +3,10 @@ import type { AccessType } from "@keycloak/keycloak-admin-client/lib/defs/whoAmI import { PropsWithChildren, useState } from "react"; import { createNamedContext, + useEnvironment, useRequiredContext, } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; -import environment from "../../environment"; +import { useAdminClient } from "../../admin-client"; import { DEFAULT_LOCALE, i18n } from "../../i18n/i18n"; import { useFetch } from "../../utils/useFetch"; import { useRealm } from "../realm-context/RealmContext"; @@ -72,6 +71,9 @@ export const WhoAmIContext = createNamedContext( export const useWhoAmI = () => useRequiredContext(WhoAmIContext); export const WhoAmIContextProvider = ({ children }: PropsWithChildren) => { + const { adminClient } = useAdminClient(); + const { environment } = useEnvironment(); + const [whoAmI, setWhoAmI] = useState(new WhoAmI()); const { realm } = useRealm(); const [key, setKey] = useState(0); @@ -79,7 +81,7 @@ export const WhoAmIContextProvider = ({ children }: PropsWithChildren) => { useFetch( () => adminClient.whoAmI.find({ - realm: environment.loginRealm, + realm: environment.realm, currentRealm: realm!, }), (me) => { diff --git a/js/apps/admin-ui/src/dashboard/Dashboard.tsx b/js/apps/admin-ui/src/dashboard/Dashboard.tsx index 624e9de2b2..03eeb07606 100644 --- a/js/apps/admin-ui/src/dashboard/Dashboard.tsx +++ b/js/apps/admin-ui/src/dashboard/Dashboard.tsx @@ -1,5 +1,8 @@ -import { useMemo, useState } from "react"; -import { useTranslation } from "react-i18next"; +import FeatureRepresentation, { + FeatureType, +} from "@keycloak/keycloak-admin-client/lib/defs/featureRepresentation"; +import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation"; +import { HelpItem, label, useEnvironment } from "@keycloak/keycloak-ui-shared"; import { ActionList, ActionListItem, @@ -14,6 +17,7 @@ import { DescriptionListTerm, EmptyState, EmptyStateBody, + EmptyStateHeader, Grid, GridItem, Label, @@ -27,32 +31,29 @@ import { TextContent, TextVariants, Title, - EmptyStateHeader, } from "@patternfly/react-core"; - -import FeatureRepresentation, { - FeatureType, -} from "@keycloak/keycloak-admin-client/lib/defs/featureRepresentation"; -import { useRealm } from "../context/realm-context/RealmContext"; -import { useServerInfo } from "../context/server-info/ServerInfoProvider"; -import { HelpItem, label } from "@keycloak/keycloak-ui-shared"; -import environment from "../environment"; +import { useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useAdminClient } from "../admin-client"; import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner"; -import useLocaleSort, { mapByKey } from "../utils/useLocaleSort"; import { RoutableTabs, useRoutableTab, } from "../components/routable-tabs/RoutableTabs"; -import { DashboardTab, toDashboard } from "./routes/Dashboard"; +import { useRealm } from "../context/realm-context/RealmContext"; +import { useServerInfo } from "../context/server-info/ServerInfoProvider"; +import helpUrls from "../help-urls"; +import { useFetch } from "../utils/useFetch"; +import useLocaleSort, { mapByKey } from "../utils/useLocaleSort"; import { ProviderInfo } from "./ProviderInfo"; +import { DashboardTab, toDashboard } from "./routes/Dashboard"; import "./dashboard.css"; -import { useFetch } from "../utils/useFetch"; -import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation"; -import { adminClient } from "../admin-client"; -import helpUrls from "../help-urls"; const EmptyDashboard = () => { + const { adminClient } = useAdminClient(); + const { environment } = useEnvironment(); + const { t } = useTranslation(); const { realm } = useRealm(); const [realmInfo, setRealmInfo] = useState(); @@ -99,6 +100,8 @@ const FeatureItem = ({ feature }: FeatureItemProps) => { }; const Dashboard = () => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { realm } = useRealm(); const serverInfo = useServerInfo(); diff --git a/js/apps/admin-ui/src/environment.ts b/js/apps/admin-ui/src/environment.ts deleted file mode 100644 index 413f315f94..0000000000 --- a/js/apps/admin-ui/src/environment.ts +++ /dev/null @@ -1,70 +0,0 @@ -export type Environment = { - /** The realm used to authenticate the user to the Admin Console. */ - loginRealm: string; - /** The identifier of the client used to authenticate the user to the Admin Console. */ - clientId: string; - /** The URL to the root of the auth server. */ - authServerUrl: string; - /** The URL to the path of the auth server where client requests can be sent. */ - authUrl: string; - /** The URL to the base of the Admin UI. */ - consoleBaseUrl: string; - /** The URL to resources such as the files in the `public` directory. */ - resourceUrl: string; - /** The name of the master realm. */ - masterRealm: string; - /** The version hash of the auth server. */ - resourceVersion: string; - /** Indicates the src for the Brand image */ - logo: string; - /** Indicates the url to be followed when Brand image is clicked */ - logoUrl: string; -}; - -// During development the realm can be passed as a query parameter when redirecting back from Keycloak. -const realm = new URLSearchParams(window.location.search).get("realm"); - -// The default environment, used during development. -const defaultEnvironment: Environment = { - loginRealm: realm ?? "master", - clientId: "security-admin-console-v2", - authServerUrl: "http://localhost:8180", - authUrl: "http://localhost:8180", - consoleBaseUrl: "/admin/master/console/", - resourceUrl: ".", - masterRealm: "master", - resourceVersion: "unknown", - logo: "/logo.svg", - logoUrl: "", -}; - -// Merge the default and injected environment variables together. -const environment: Environment = { - ...defaultEnvironment, - ...getInjectedEnvironment(), -}; - -export default environment; - -/** - * Extracts the environment variables that are passed if the application is running as a Keycloak theme. - * These variables are injected by Keycloak into the `index.ftl` as a script tag, the contents of which can be parsed as JSON. - */ -function getInjectedEnvironment(): Record { - const element = document.getElementById("environment"); - - // If the element cannot be found, return an empty record. - if (!element?.textContent) { - return {}; - } - - // Attempt to parse the contents as JSON and return its value. - try { - return JSON.parse(element.textContent); - } catch (error) { - console.error("Unable to parse environment variables."); - } - - // Otherwise, return an empty record. - return {}; -} diff --git a/js/apps/admin-ui/src/events/AdminEvents.tsx b/js/apps/admin-ui/src/events/AdminEvents.tsx index d8cb7f5b9c..1cf34c5b9e 100644 --- a/js/apps/admin-ui/src/events/AdminEvents.tsx +++ b/js/apps/admin-ui/src/events/AdminEvents.tsx @@ -32,9 +32,8 @@ import { pickBy } from "lodash-es"; import { PropsWithChildren, useMemo, useState } from "react"; import { Controller, FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; - import { TextControl } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import DropdownPanel from "../components/dropdown-panel/DropdownPanel"; import { ListEmptyState } from "../components/list-empty-state/ListEmptyState"; import { @@ -97,6 +96,8 @@ const DisplayDialog = ({ }; export const AdminEvents = () => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { realm } = useRealm(); const serverInfo = useServerInfo(); diff --git a/js/apps/admin-ui/src/events/EventsSection.tsx b/js/apps/admin-ui/src/events/EventsSection.tsx index 47d721cd16..2010801273 100644 --- a/js/apps/admin-ui/src/events/EventsSection.tsx +++ b/js/apps/admin-ui/src/events/EventsSection.tsx @@ -33,8 +33,9 @@ import { useState } from "react"; import { Controller, FormProvider, useForm } from "react-hook-form"; import { Trans, useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; - -import { adminClient } from "../admin-client"; +import { TextControl } from "@keycloak/keycloak-ui-shared"; +import { useAdminClient } from "../admin-client"; +import DropdownPanel from "../components/dropdown-panel/DropdownPanel"; import { ListEmptyState } from "../components/list-empty-state/ListEmptyState"; import { RoutableTabs, @@ -42,7 +43,6 @@ import { } from "../components/routable-tabs/RoutableTabs"; import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable"; import { ViewHeader } from "../components/view-header/ViewHeader"; -import DropdownPanel from "../components/dropdown-panel/DropdownPanel"; import { useRealm } from "../context/realm-context/RealmContext"; import helpUrls from "../help-urls"; import { toRealmSettings } from "../realm-settings/routes/RealmSettings"; @@ -51,7 +51,6 @@ import { useFetch } from "../utils/useFetch"; import useFormatDate, { FORMAT_DATE_AND_TIME } from "../utils/useFormatDate"; import { AdminEvents } from "./AdminEvents"; import { EventsTab, toEvents } from "./routes/Events"; -import { TextControl } from "@keycloak/keycloak-ui-shared"; import "./events.css"; @@ -134,6 +133,8 @@ const UserDetailLink = (event: EventRepresentation) => { }; export default function EventsSection() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { realm } = useRealm(); const formatDate = useFormatDate(); diff --git a/js/apps/admin-ui/src/groups/GroupAttributes.tsx b/js/apps/admin-ui/src/groups/GroupAttributes.tsx index 6a0e4def53..d7d78db60d 100644 --- a/js/apps/admin-ui/src/groups/GroupAttributes.tsx +++ b/js/apps/admin-ui/src/groups/GroupAttributes.tsx @@ -8,8 +8,7 @@ import { useState } from "react"; import { useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useLocation } from "react-router-dom"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { AttributeForm, @@ -21,6 +20,8 @@ import { useFetch } from "../utils/useFetch"; import { getLastId } from "./groupIdUtils"; export const GroupAttributes = () => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const form = useForm({ diff --git a/js/apps/admin-ui/src/groups/GroupRoleMapping.tsx b/js/apps/admin-ui/src/groups/GroupRoleMapping.tsx index 08925882cd..80ba78f9d0 100644 --- a/js/apps/admin-ui/src/groups/GroupRoleMapping.tsx +++ b/js/apps/admin-ui/src/groups/GroupRoleMapping.tsx @@ -1,8 +1,7 @@ import type { RoleMappingPayload } from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation"; import { AlertVariant } from "@patternfly/react-core"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { RoleMapping, Row } from "../components/role-mapping/RoleMapping"; @@ -12,6 +11,8 @@ type GroupRoleMappingProps = { }; export const GroupRoleMapping = ({ id, name }: GroupRoleMappingProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/groups/GroupTable.tsx b/js/apps/admin-ui/src/groups/GroupTable.tsx index 148bc76f30..a2952416f0 100644 --- a/js/apps/admin-ui/src/groups/GroupTable.tsx +++ b/js/apps/admin-ui/src/groups/GroupTable.tsx @@ -7,7 +7,7 @@ import { SearchInput, ToolbarItem } from "@patternfly/react-core"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Link, useLocation } from "react-router-dom"; - +import { useAdminClient } from "../admin-client"; import { ListEmptyState } from "../components/list-empty-state/ListEmptyState"; import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable"; import { useAccess } from "../context/access/Access"; @@ -18,13 +18,14 @@ import { DeleteGroup } from "./components/DeleteGroup"; import { GroupToolbar } from "./components/GroupToolbar"; import { MoveDialog } from "./components/MoveDialog"; import { getLastId } from "./groupIdUtils"; -import { adminClient } from "../admin-client"; type GroupTableProps = { refresh: () => void; }; export const GroupTable = ({ refresh: viewRefresh }: GroupTableProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const [selectedRows, setSelectedRows] = useState([]); diff --git a/js/apps/admin-ui/src/groups/GroupsModal.tsx b/js/apps/admin-ui/src/groups/GroupsModal.tsx index 9eac2d0a26..d84d0d051f 100644 --- a/js/apps/admin-ui/src/groups/GroupsModal.tsx +++ b/js/apps/admin-ui/src/groups/GroupsModal.tsx @@ -10,7 +10,7 @@ import { import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { TextControl } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; type GroupsModalProps = { @@ -26,6 +26,8 @@ export const GroupsModal = ({ handleModalToggle, refresh, }: GroupsModalProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/groups/GroupsSection.tsx b/js/apps/admin-ui/src/groups/GroupsSection.tsx index c44e65563b..7b982444a5 100644 --- a/js/apps/admin-ui/src/groups/GroupsSection.tsx +++ b/js/apps/admin-ui/src/groups/GroupsSection.tsx @@ -18,12 +18,11 @@ import { AngleLeftIcon, TreeIcon } from "@patternfly/react-icons"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { useLocation, useNavigate } from "react-router-dom"; - +import { useAdminClient } from "../admin-client"; import { GroupBreadCrumbs } from "../components/bread-crumb/GroupBreadCrumbs"; import { PermissionsTab } from "../components/permission-tab/PermissionTab"; import { ViewHeader } from "../components/view-header/ViewHeader"; import { useAccess } from "../context/access/Access"; -import { adminClient } from "../admin-client"; import { useRealm } from "../context/realm-context/RealmContext"; import helpUrls from "../help-urls"; import { useFetch } from "../utils/useFetch"; @@ -43,6 +42,8 @@ import { toGroups } from "./routes/Groups"; import "./GroupsSection.css"; export default function GroupsSection() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const [activeTab, setActiveTab] = useState(0); diff --git a/js/apps/admin-ui/src/groups/Members.tsx b/js/apps/admin-ui/src/groups/Members.tsx index 55ecf0a55d..f1bafb6be4 100644 --- a/js/apps/admin-ui/src/groups/Members.tsx +++ b/js/apps/admin-ui/src/groups/Members.tsx @@ -16,8 +16,7 @@ import { uniqBy } from "lodash-es"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Link, useLocation } from "react-router-dom"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { GroupPath } from "../components/group/GroupPath"; import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner"; @@ -62,6 +61,8 @@ const UserDetailLink = (user: MembersOf) => { }; export const Members = () => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/groups/MembersModal.tsx b/js/apps/admin-ui/src/groups/MembersModal.tsx index ae85b23906..fd3621763b 100644 --- a/js/apps/admin-ui/src/groups/MembersModal.tsx +++ b/js/apps/admin-ui/src/groups/MembersModal.tsx @@ -8,8 +8,7 @@ import { import { differenceBy } from "lodash-es"; import { useState } from "react"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { ListEmptyState } from "../components/list-empty-state/ListEmptyState"; import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable"; @@ -21,6 +20,8 @@ type MemberModalProps = { }; export const MemberModal = ({ groupId, onClose }: MemberModalProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const [selectedRows, setSelectedRows] = useState([]); diff --git a/js/apps/admin-ui/src/groups/components/DeleteGroup.tsx b/js/apps/admin-ui/src/groups/components/DeleteGroup.tsx index 571ee0eb15..20de96b7cc 100644 --- a/js/apps/admin-ui/src/groups/components/DeleteGroup.tsx +++ b/js/apps/admin-ui/src/groups/components/DeleteGroup.tsx @@ -1,8 +1,7 @@ import type GroupRepresentation from "@keycloak/keycloak-admin-client/lib/defs/groupRepresentation"; import { ButtonVariant } from "@patternfly/react-core"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { ConfirmDialogModal } from "../../components/confirm-dialog/ConfirmDialog"; @@ -19,6 +18,8 @@ export const DeleteGroup = ({ toggleDialog, refresh, }: DeleteConfirmProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/groups/components/GroupTree.tsx b/js/apps/admin-ui/src/groups/components/GroupTree.tsx index 978b72594a..2750fa26d8 100644 --- a/js/apps/admin-ui/src/groups/components/GroupTree.tsx +++ b/js/apps/admin-ui/src/groups/components/GroupTree.tsx @@ -4,10 +4,10 @@ import { Button, Checkbox, InputGroup, + InputGroupItem, Tooltip, TreeView, TreeViewDataItem, - InputGroupItem, } from "@patternfly/react-core"; import { Dropdown, @@ -21,6 +21,7 @@ import { unionBy } from "lodash-es"; import { useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner"; import { PaginatingTableToolbar } from "../../components/table-toolbar/PaginatingTableToolbar"; @@ -138,6 +139,8 @@ export const GroupTree = ({ refresh: viewRefresh, canViewDetails, }: GroupTreeProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { realm } = useRealm(); const navigate = useNavigate(); @@ -190,6 +193,7 @@ export const GroupTree = ({ useFetch( async () => { const groups = await fetchAdminUI( + adminClient, "groups", Object.assign( { @@ -204,6 +208,7 @@ export const GroupTree = ({ let subGroups: GroupRepresentation[] = []; if (activeItem) { subGroups = await fetchAdminUI( + adminClient, `groups/${activeItem.id}/children`, { first: `${firstSub}`, diff --git a/js/apps/admin-ui/src/groups/components/MoveDialog.tsx b/js/apps/admin-ui/src/groups/components/MoveDialog.tsx index 3672f8278b..596dfc5df1 100644 --- a/js/apps/admin-ui/src/groups/components/MoveDialog.tsx +++ b/js/apps/admin-ui/src/groups/components/MoveDialog.tsx @@ -1,7 +1,7 @@ +import type KeycloakAdminClient from "@keycloak/keycloak-admin-client"; import type GroupRepresentation from "@keycloak/keycloak-admin-client/lib/defs/groupRepresentation"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { GroupPickerDialog } from "../../components/group/GroupPickerDialog"; @@ -11,23 +11,31 @@ type MoveDialogProps = { refresh: () => void; }; -const moveToRoot = (source: GroupRepresentation) => +const moveToRoot = ( + adminClient: KeycloakAdminClient, + source: GroupRepresentation, +) => source.id ? adminClient.groups.updateRoot(source) : adminClient.groups.create(source); const moveToGroup = async ( + adminClient: KeycloakAdminClient, source: GroupRepresentation, dest: GroupRepresentation, ) => adminClient.groups.updateChildGroup({ id: dest.id! }, source); export const MoveDialog = ({ source, onClose, refresh }: MoveDialogProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const moveGroup = async (group?: GroupRepresentation[]) => { try { - await (group ? moveToGroup(source, group[0]) : moveToRoot(source)); + await (group + ? moveToGroup(adminClient, source, group[0]) + : moveToRoot(adminClient, source)); refresh(); addAlert(t("moveGroupSuccess")); } catch (error) { diff --git a/js/apps/admin-ui/src/i18n/i18n.ts b/js/apps/admin-ui/src/i18n/i18n.ts index 4118799d8c..bc68e4418f 100644 --- a/js/apps/admin-ui/src/i18n/i18n.ts +++ b/js/apps/admin-ui/src/i18n/i18n.ts @@ -1,8 +1,7 @@ import { createInstance } from "i18next"; -import { initReactI18next } from "react-i18next"; - import HttpBackend from "i18next-http-backend"; -import environment from "../environment"; +import { initReactI18next } from "react-i18next"; +import { environment } from "@keycloak/keycloak-ui-shared"; import { joinPath } from "../utils/joinPath"; type KeyValue = { key: string; value: string }; @@ -19,7 +18,7 @@ export const i18n = createInstance({ backend: { loadPath: joinPath( environment.authServerUrl, - `resources/${environment.loginRealm}/admin/{{lng}}`, + `resources/${environment.realm}/admin/{{lng}}`, ), parse: (data: string) => { const messages = JSON.parse(data); diff --git a/js/apps/admin-ui/src/identity-providers/IdentityProvidersSection.tsx b/js/apps/admin-ui/src/identity-providers/IdentityProvidersSection.tsx index c4af5b8d26..b378ddc1b0 100644 --- a/js/apps/admin-ui/src/identity-providers/IdentityProvidersSection.tsx +++ b/js/apps/admin-ui/src/identity-providers/IdentityProvidersSection.tsx @@ -26,7 +26,7 @@ import { Fragment, useState } from "react"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; import { IconMapper } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { ClickableCard } from "../components/keycloak-card/ClickableCard"; @@ -73,6 +73,8 @@ const DetailLink = (identityProvider: IdentityProviderRepresentation) => { }; export default function IdentityProvidersSection() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const identityProviders = groupBy( useServerInfo().identityProviders, diff --git a/js/apps/admin-ui/src/identity-providers/ManageOrderDialog.tsx b/js/apps/admin-ui/src/identity-providers/ManageOrderDialog.tsx index b9d6bb4399..deec48b566 100644 --- a/js/apps/admin-ui/src/identity-providers/ManageOrderDialog.tsx +++ b/js/apps/admin-ui/src/identity-providers/ManageOrderDialog.tsx @@ -21,7 +21,7 @@ import { import { sortBy } from "lodash-es"; import { useState } from "react"; import { useTranslation } from "react-i18next"; -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner"; import { useFetch } from "../utils/useFetch"; @@ -31,6 +31,8 @@ type ManageOrderDialogProps = { }; export const ManageOrderDialog = ({ onClose }: ManageOrderDialogProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/identity-providers/add/AddIdentityProvider.tsx b/js/apps/admin-ui/src/identity-providers/add/AddIdentityProvider.tsx index f3c217c08f..59bc8241e2 100644 --- a/js/apps/admin-ui/src/identity-providers/add/AddIdentityProvider.tsx +++ b/js/apps/admin-ui/src/identity-providers/add/AddIdentityProvider.tsx @@ -9,7 +9,7 @@ import { useMemo } from "react"; import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { DynamicComponents } from "../../components/dynamic/DynamicComponents"; import { FormAccess } from "../../components/form/FormAccess"; @@ -24,6 +24,8 @@ import { toIdentityProviders } from "../routes/IdentityProviders"; import { GeneralSettings } from "./GeneralSettings"; export default function AddIdentityProvider() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { providerId } = useParams(); const form = useForm({ mode: "onChange" }); diff --git a/js/apps/admin-ui/src/identity-providers/add/AddMapper.tsx b/js/apps/admin-ui/src/identity-providers/add/AddMapper.tsx index ec90e45b5d..4a93369848 100644 --- a/js/apps/admin-ui/src/identity-providers/add/AddMapper.tsx +++ b/js/apps/admin-ui/src/identity-providers/add/AddMapper.tsx @@ -13,9 +13,8 @@ import { useState } from "react"; import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; - import { TextControl } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { DynamicComponents } from "../../components/dynamic/DynamicComponents"; @@ -43,6 +42,8 @@ export type Role = RoleRepresentation & { }; export default function AddMapper() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const form = useForm({ diff --git a/js/apps/admin-ui/src/identity-providers/add/AddOpenIdConnect.tsx b/js/apps/admin-ui/src/identity-providers/add/AddOpenIdConnect.tsx index d56e15884a..ad83cebb63 100644 --- a/js/apps/admin-ui/src/identity-providers/add/AddOpenIdConnect.tsx +++ b/js/apps/admin-ui/src/identity-providers/add/AddOpenIdConnect.tsx @@ -8,8 +8,7 @@ import { import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link, useLocation, useNavigate } from "react-router-dom"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { FormAccess } from "../../components/form/FormAccess"; import { ViewHeader } from "../../components/view-header/ViewHeader"; @@ -25,6 +24,8 @@ type DiscoveryIdentity = IdentityProviderRepresentation & { }; export default function AddOpenIdConnect() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const navigate = useNavigate(); const { pathname } = useLocation(); diff --git a/js/apps/admin-ui/src/identity-providers/add/AddSamlConnect.tsx b/js/apps/admin-ui/src/identity-providers/add/AddSamlConnect.tsx index 9bfd4ea044..eee4648f22 100644 --- a/js/apps/admin-ui/src/identity-providers/add/AddSamlConnect.tsx +++ b/js/apps/admin-ui/src/identity-providers/add/AddSamlConnect.tsx @@ -8,8 +8,7 @@ import { import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { FormAccess } from "../../components/form/FormAccess"; import { ViewHeader } from "../../components/view-header/ViewHeader"; @@ -24,6 +23,8 @@ type DiscoveryIdentityProvider = IdentityProviderRepresentation & { }; export default function AddSamlConnect() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const navigate = useNavigate(); const id = "saml"; diff --git a/js/apps/admin-ui/src/identity-providers/add/AdvancedSettings.tsx b/js/apps/admin-ui/src/identity-providers/add/AdvancedSettings.tsx index a40d24ec34..5c7248cc3e 100644 --- a/js/apps/admin-ui/src/identity-providers/add/AdvancedSettings.tsx +++ b/js/apps/admin-ui/src/identity-providers/add/AdvancedSettings.tsx @@ -19,8 +19,7 @@ import { HelpItem, SelectControl, } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useFetch } from "../../utils/useFetch"; import useIsFeatureEnabled, { Feature } from "../../utils/useIsFeatureEnabled"; import type { FieldProps } from "../component/FormGroupField"; @@ -34,6 +33,8 @@ const LoginFlow = ({ defaultValue, labelForEmpty = "none", }: FieldProps & { defaultValue: string; labelForEmpty?: string }) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { control } = useFormContext(); diff --git a/js/apps/admin-ui/src/identity-providers/add/DetailSettings.tsx b/js/apps/admin-ui/src/identity-providers/add/DetailSettings.tsx index 0ea480bc11..67c728fdf3 100644 --- a/js/apps/admin-ui/src/identity-providers/add/DetailSettings.tsx +++ b/js/apps/admin-ui/src/identity-providers/add/DetailSettings.tsx @@ -26,8 +26,6 @@ import { import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; import { ScrollForm } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { DynamicComponents } from "../../components/dynamic/DynamicComponents"; @@ -68,6 +66,7 @@ import { OIDCAuthentication } from "./OIDCAuthentication"; import { OIDCGeneralSettings } from "./OIDCGeneralSettings"; import { ReqAuthnConstraints } from "./ReqAuthnConstraintsSettings"; import { SamlGeneralSettings } from "./SamlGeneralSettings"; +import { useAdminClient } from "../../admin-client"; type HeaderProps = { onChange: (value: boolean) => void; @@ -85,6 +84,8 @@ type IdPWithMapperAttributes = IdentityProviderMapperRepresentation & { }; const Header = ({ onChange, value, save, toggleDeleteDialog }: HeaderProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { alias: displayName } = useParams<{ alias: string }>(); const [provider, setProvider] = useState(); @@ -250,6 +251,8 @@ const MapperLink = ({ name, mapperId, provider }: MapperLinkProps) => { }; export default function DetailSettings() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { alias, providerId } = useParams(); const isFeatureEnabled = useIsFeatureEnabled(); diff --git a/js/apps/admin-ui/src/identity-providers/add/OpenIdConnectSettings.tsx b/js/apps/admin-ui/src/identity-providers/add/OpenIdConnectSettings.tsx index 2c44aeef32..a3412183ee 100644 --- a/js/apps/admin-ui/src/identity-providers/add/OpenIdConnectSettings.tsx +++ b/js/apps/admin-ui/src/identity-providers/add/OpenIdConnectSettings.tsx @@ -2,13 +2,14 @@ import { FormGroup, Title } from "@patternfly/react-core"; import { useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { FormErrorText, HelpItem } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { JsonFileUpload } from "../../components/json-file-upload/JsonFileUpload"; import { DiscoveryEndpointField } from "../component/DiscoveryEndpointField"; import { DiscoverySettings } from "./DiscoverySettings"; export const OpenIdConnectSettings = () => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const id = "oidc"; diff --git a/js/apps/admin-ui/src/identity-providers/add/SamlConnectSettings.tsx b/js/apps/admin-ui/src/identity-providers/add/SamlConnectSettings.tsx index 1fefcdb1a0..3d58064509 100644 --- a/js/apps/admin-ui/src/identity-providers/add/SamlConnectSettings.tsx +++ b/js/apps/admin-ui/src/identity-providers/add/SamlConnectSettings.tsx @@ -4,15 +4,15 @@ import { FormGroup, Title } from "@patternfly/react-core"; import { useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { + AdminEnvironment, FormErrorText, HelpItem, TextControl, + useEnvironment, } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { FileUploadForm } from "../../components/json-file-upload/FileUploadForm"; import { useRealm } from "../../context/realm-context/RealmContext"; -import environment from "../../environment"; import { addTrailingSlash } from "../../util"; import { getAuthorizationHeaders } from "../../utils/getAuthorizationHeaders"; import { DiscoveryEndpointField } from "../component/DiscoveryEndpointField"; @@ -23,6 +23,9 @@ type FormFields = IdentityProviderRepresentation & { }; export const SamlConnectSettings = () => { + const { adminClient } = useAdminClient(); + const { environment } = useEnvironment(); + const { t } = useTranslation(); const id = "saml"; diff --git a/js/apps/admin-ui/src/identity-providers/add/SamlGeneralSettings.tsx b/js/apps/admin-ui/src/identity-providers/add/SamlGeneralSettings.tsx index 996ef2f463..eab5ded67f 100644 --- a/js/apps/admin-ui/src/identity-providers/add/SamlGeneralSettings.tsx +++ b/js/apps/admin-ui/src/identity-providers/add/SamlGeneralSettings.tsx @@ -1,10 +1,13 @@ import { FormGroup } from "@patternfly/react-core"; import { useFormContext, useWatch } from "react-hook-form"; import { useTranslation } from "react-i18next"; -import { HelpItem, TextControl } from "@keycloak/keycloak-ui-shared"; +import { + HelpItem, + TextControl, + useEnvironment, +} from "@keycloak/keycloak-ui-shared"; import { FormattedLink } from "../../components/external-link/FormattedLink"; import { useRealm } from "../../context/realm-context/RealmContext"; -import environment from "../../environment"; import { DisplayOrder } from "../component/DisplayOrder"; import { RedirectUrl } from "../component/RedirectUrl"; @@ -19,6 +22,7 @@ export const SamlGeneralSettings = ({ }: SamlGeneralSettingsProps) => { const { t } = useTranslation(); const { realm } = useRealm(); + const { environment } = useEnvironment(); const { control } = useFormContext(); const alias = useWatch({ control, name: "alias" }); diff --git a/js/apps/admin-ui/src/identity-providers/component/DiscoveryEndpointField.tsx b/js/apps/admin-ui/src/identity-providers/component/DiscoveryEndpointField.tsx index 65f6b10aa4..9a283e43c3 100644 --- a/js/apps/admin-ui/src/identity-providers/component/DiscoveryEndpointField.tsx +++ b/js/apps/admin-ui/src/identity-providers/component/DiscoveryEndpointField.tsx @@ -4,8 +4,7 @@ import { ReactNode, useMemo, useState } from "react"; import { useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { HelpItem, TextControl } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; type DiscoveryEndpointFieldProps = { id: string; @@ -18,6 +17,8 @@ export const DiscoveryEndpointField = ({ fileUpload, children, }: DiscoveryEndpointFieldProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { setValue, diff --git a/js/apps/admin-ui/src/identity-providers/component/RedirectUrl.tsx b/js/apps/admin-ui/src/identity-providers/component/RedirectUrl.tsx index 6daf644977..3f110aa37f 100644 --- a/js/apps/admin-ui/src/identity-providers/component/RedirectUrl.tsx +++ b/js/apps/admin-ui/src/identity-providers/component/RedirectUrl.tsx @@ -1,12 +1,13 @@ import { ClipboardCopy, FormGroup } from "@patternfly/react-core"; import { useTranslation } from "react-i18next"; import { HelpItem } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useRealm } from "../../context/realm-context/RealmContext"; import { addTrailingSlash } from "../../util"; export const RedirectUrl = ({ id }: { id: string }) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { realm } = useRealm(); diff --git a/js/apps/admin-ui/src/index.ts b/js/apps/admin-ui/src/index.ts new file mode 100644 index 0000000000..169f7432e9 --- /dev/null +++ b/js/apps/admin-ui/src/index.ts @@ -0,0 +1,2 @@ +export * as ClientsSection from "./clients/ClientsSection"; +export * as ClientScopeSection from "./client-scopes/ClientScopesSection"; diff --git a/js/apps/admin-ui/src/keycloak.ts b/js/apps/admin-ui/src/keycloak.ts deleted file mode 100644 index 76925593fa..0000000000 --- a/js/apps/admin-ui/src/keycloak.ts +++ /dev/null @@ -1,22 +0,0 @@ -import Keycloak from "keycloak-js"; - -import environment from "./environment"; - -export const keycloak = new Keycloak({ - url: environment.authServerUrl, - realm: environment.loginRealm, - clientId: environment.clientId, -}); - -keycloak.onAuthLogout = () => keycloak.login(); - -export async function initKeycloak() { - const authenticated = await keycloak.init({ - onLoad: "check-sso", - }); - - // Force the user to login if not authenticated. - if (!authenticated) { - await keycloak.login(); - } -} diff --git a/js/apps/admin-ui/src/main.tsx b/js/apps/admin-ui/src/main.tsx index bcdfb4e1c6..76c6acb362 100644 --- a/js/apps/admin-ui/src/main.tsx +++ b/js/apps/admin-ui/src/main.tsx @@ -1,18 +1,15 @@ -import "@patternfly/react-core/dist/styles/base.css"; import "@patternfly/patternfly/patternfly-addons.css"; +import "@patternfly/react-core/dist/styles/base.css"; import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import { createHashRouter, RouterProvider } from "react-router-dom"; - import { i18n } from "./i18n/i18n"; -import { initKeycloak } from "./keycloak"; import { RootRoute } from "./routes"; import "./index.css"; // Initialize required components before rendering app. -await initKeycloak(); await i18n.init(); const router = createHashRouter([RootRoute]); diff --git a/js/apps/admin-ui/src/page/Page.tsx b/js/apps/admin-ui/src/page/Page.tsx index 4cacf14da1..e0c3278a34 100644 --- a/js/apps/admin-ui/src/page/Page.tsx +++ b/js/apps/admin-ui/src/page/Page.tsx @@ -2,17 +2,19 @@ import { ButtonVariant } from "@patternfly/react-core"; import { DropdownItem } from "@patternfly/react-core/deprecated"; import { useTranslation } from "react-i18next"; import { useNavigate, useParams } from "react-router-dom"; -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { ViewHeader } from "../components/view-header/ViewHeader"; +import { useRealm } from "../context/realm-context/RealmContext"; import { useServerInfo } from "../context/server-info/ServerInfoProvider"; import { PageHandler } from "./PageHandler"; import { PAGE_PROVIDER } from "./PageList"; import { PageParams, toPage } from "./routes"; -import { useRealm } from "../context/realm-context/RealmContext"; export default function Page() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { componentTypes } = useServerInfo(); const { realm } = useRealm(); diff --git a/js/apps/admin-ui/src/page/PageHandler.tsx b/js/apps/admin-ui/src/page/PageHandler.tsx index 746014aaf3..cc13e4f249 100644 --- a/js/apps/admin-ui/src/page/PageHandler.tsx +++ b/js/apps/admin-ui/src/page/PageHandler.tsx @@ -6,7 +6,7 @@ import { useState } from "react"; import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { DynamicComponents } from "../components/dynamic/DynamicComponents"; import { useRealm } from "../context/realm-context/RealmContext"; @@ -26,6 +26,8 @@ export const PageHandler = ({ providerType, page: { id: providerId, ...page }, }: PageHandlerProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const form = useForm(); const { realm: realmName } = useRealm(); diff --git a/js/apps/admin-ui/src/page/PageList.tsx b/js/apps/admin-ui/src/page/PageList.tsx index 190b395674..634be22ecd 100644 --- a/js/apps/admin-ui/src/page/PageList.tsx +++ b/js/apps/admin-ui/src/page/PageList.tsx @@ -11,7 +11,7 @@ import { IRowData } from "@patternfly/react-table"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Link, useNavigate, useParams } from "react-router-dom"; -import { adminClient } from "../admin-client"; +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"; @@ -37,6 +37,8 @@ const DetailLink = (obj: ComponentRepresentation) => { ); }; export default function PageList() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const navigate = useNavigate(); diff --git a/js/apps/admin-ui/src/realm-roles/CreateRealmRole.tsx b/js/apps/admin-ui/src/realm-roles/CreateRealmRole.tsx index dae15d6489..492d5f8210 100644 --- a/js/apps/admin-ui/src/realm-roles/CreateRealmRole.tsx +++ b/js/apps/admin-ui/src/realm-roles/CreateRealmRole.tsx @@ -3,8 +3,7 @@ import { AlertVariant } from "@patternfly/react-core"; import { FormProvider, SubmitHandler, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { AttributeForm } from "../components/key-value-form/AttributeForm"; import { RoleForm } from "../components/role-form/RoleForm"; @@ -13,6 +12,8 @@ import { toRealmRole } from "./routes/RealmRole"; import { toRealmRoles } from "./routes/RealmRoles"; export default function CreateRealmRole() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const form = useForm({ mode: "onChange" }); const navigate = useNavigate(); diff --git a/js/apps/admin-ui/src/realm-roles/RealmRoleTabs.tsx b/js/apps/admin-ui/src/realm-roles/RealmRoleTabs.tsx index dee779025a..8a29210514 100644 --- a/js/apps/admin-ui/src/realm-roles/RealmRoleTabs.tsx +++ b/js/apps/admin-ui/src/realm-roles/RealmRoleTabs.tsx @@ -17,8 +17,7 @@ import { } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useLocation, useMatch, useNavigate } from "react-router-dom"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { toClient } from "../clients/routes/Client"; import { ClientRoleParams, @@ -56,6 +55,8 @@ import { RealmRoleRoute, RealmRoleTab, toRealmRole } from "./routes/RealmRole"; import { toRealmRoles } from "./routes/RealmRoles"; export default function RealmRoleTabs() { + const { adminClient } = useAdminClient(); + const isFeatureEnabled = useIsFeatureEnabled(); const { t } = useTranslation(); const form = useForm({ diff --git a/js/apps/admin-ui/src/realm-roles/RealmRolesSection.tsx b/js/apps/admin-ui/src/realm-roles/RealmRolesSection.tsx index 3ae89f0bbc..466ab3c3c1 100644 --- a/js/apps/admin-ui/src/realm-roles/RealmRolesSection.tsx +++ b/js/apps/admin-ui/src/realm-roles/RealmRolesSection.tsx @@ -1,6 +1,5 @@ import { PageSection } from "@patternfly/react-core"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { RolesList } from "../components/roles-list/RolesList"; import { ViewHeader } from "../components/view-header/ViewHeader"; import { useAccess } from "../context/access/Access"; @@ -10,6 +9,8 @@ import { toAddRole } from "./routes/AddRole"; import { toRealmRole } from "./routes/RealmRole"; export default function RealmRolesSection() { + const { adminClient } = useAdminClient(); + const { realm } = useRealm(); const { hasAccess } = useAccess(); const isManager = hasAccess("manage-realm"); diff --git a/js/apps/admin-ui/src/realm-roles/UsersInRoleTab.tsx b/js/apps/admin-ui/src/realm-roles/UsersInRoleTab.tsx index 0c7b7ee185..f7b9a923e7 100644 --- a/js/apps/admin-ui/src/realm-roles/UsersInRoleTab.tsx +++ b/js/apps/admin-ui/src/realm-roles/UsersInRoleTab.tsx @@ -3,8 +3,7 @@ import { QuestionCircleIcon } from "@patternfly/react-icons"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { useHelp } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import type { ClientRoleParams } from "../clients/routes/ClientRole"; import { ListEmptyState } from "../components/list-empty-state/ListEmptyState"; import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable"; @@ -13,6 +12,8 @@ import { emptyFormatter, upperCaseFormatter } from "../util"; import { useParams } from "../utils/useParams"; export const UsersInRoleTab = () => { + const { adminClient } = useAdminClient(); + const navigate = useNavigate(); const { realm } = useRealm(); diff --git a/js/apps/admin-ui/src/realm-settings/AddClientProfileModal.tsx b/js/apps/admin-ui/src/realm-settings/AddClientProfileModal.tsx index 981f48cf71..313acfbc80 100644 --- a/js/apps/admin-ui/src/realm-settings/AddClientProfileModal.tsx +++ b/js/apps/admin-ui/src/realm-settings/AddClientProfileModal.tsx @@ -3,8 +3,7 @@ import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/ro import { Button, Label, Modal, ModalVariant } from "@patternfly/react-core"; import { useState } from "react"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner"; import { ListEmptyState } from "../components/list-empty-state/ListEmptyState"; import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable"; @@ -32,6 +31,8 @@ export type AddClientProfileModalProps = { }; export const AddClientProfileModal = (props: AddClientProfileModalProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const [selectedRows, setSelectedRows] = useState([]); diff --git a/js/apps/admin-ui/src/realm-settings/ClientProfileForm.tsx b/js/apps/admin-ui/src/realm-settings/ClientProfileForm.tsx index 544931557b..d9a406f34b 100644 --- a/js/apps/admin-ui/src/realm-settings/ClientProfileForm.tsx +++ b/js/apps/admin-ui/src/realm-settings/ClientProfileForm.tsx @@ -29,7 +29,7 @@ import { TextAreaControl, TextControl, } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { FormAccess } from "../components/form/FormAccess"; @@ -54,6 +54,8 @@ const defaultValues: ClientProfileForm = { }; export default function ClientProfileForm() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const navigate = useNavigate(); const form = useForm({ diff --git a/js/apps/admin-ui/src/realm-settings/DefaultGroupsTab.tsx b/js/apps/admin-ui/src/realm-settings/DefaultGroupsTab.tsx index 49700f6f50..4332667f41 100644 --- a/js/apps/admin-ui/src/realm-settings/DefaultGroupsTab.tsx +++ b/js/apps/admin-ui/src/realm-settings/DefaultGroupsTab.tsx @@ -18,8 +18,7 @@ import { useState } from "react"; import { Trans, useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; import { useHelp } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { GroupPickerDialog } from "../components/group/GroupPickerDialog"; @@ -35,6 +34,8 @@ import { useFetch } from "../utils/useFetch"; import useToggle from "../utils/useToggle"; export const DefaultsGroupsTab = () => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const [isKebabOpen, toggleKebab] = useToggle(); diff --git a/js/apps/admin-ui/src/realm-settings/EmailTab.tsx b/js/apps/admin-ui/src/realm-settings/EmailTab.tsx index e7c13238c9..a58a5561c4 100644 --- a/js/apps/admin-ui/src/realm-settings/EmailTab.tsx +++ b/js/apps/admin-ui/src/realm-settings/EmailTab.tsx @@ -15,11 +15,11 @@ import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; import { FormPanel, + PasswordControl, SwitchControl, TextControl, - PasswordControl, } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { FormAccess } from "../components/form/FormAccess"; import { useRealm } from "../context/realm-context/RealmContext"; @@ -41,6 +41,8 @@ export const RealmSettingsEmailTab = ({ realm, save, }: RealmSettingsEmailTabProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { realm: realmName } = useRealm(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/realm-settings/ExecutorForm.tsx b/js/apps/admin-ui/src/realm-settings/ExecutorForm.tsx index bd87a06b0f..0ca4afd7d7 100644 --- a/js/apps/admin-ui/src/realm-settings/ExecutorForm.tsx +++ b/js/apps/admin-ui/src/realm-settings/ExecutorForm.tsx @@ -18,8 +18,7 @@ import { Controller, FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; import { HelpItem } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { DynamicComponents } from "../components/dynamic/DynamicComponents"; import { FormAccess } from "../components/form/FormAccess"; @@ -41,6 +40,8 @@ const defaultValues: ExecutorForm = { }; export default function ExecutorForm() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const navigate = useNavigate(); const { realm, profileName } = useParams(); diff --git a/js/apps/admin-ui/src/realm-settings/GeneralTab.tsx b/js/apps/admin-ui/src/realm-settings/GeneralTab.tsx index c78ccaa64c..e9778c6cc0 100644 --- a/js/apps/admin-ui/src/realm-settings/GeneralTab.tsx +++ b/js/apps/admin-ui/src/realm-settings/GeneralTab.tsx @@ -21,8 +21,7 @@ import { SelectControl, TextControl, } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { DefaultSwitchControl } from "../components/SwitchControl"; import { FormattedLink } from "../components/external-link/FormattedLink"; import { FormAccess } from "../components/form/FormAccess"; @@ -46,6 +45,8 @@ export const RealmSettingsGeneralTab = ({ realm, save, }: RealmSettingsGeneralTabProps) => { + const { adminClient } = useAdminClient(); + const { realm: realmName } = useRealm(); const [userProfileConfig, setUserProfileConfig] = useState(); @@ -93,6 +94,8 @@ function RealmSettingsGeneralTabForm({ save, userProfileConfig, }: RealmSettingsGeneralTabFormProps) { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { realm: realmName } = useRealm(); const form = useForm(); diff --git a/js/apps/admin-ui/src/realm-settings/LoginTab.tsx b/js/apps/admin-ui/src/realm-settings/LoginTab.tsx index 679064f828..f4929a98fd 100644 --- a/js/apps/admin-ui/src/realm-settings/LoginTab.tsx +++ b/js/apps/admin-ui/src/realm-settings/LoginTab.tsx @@ -2,7 +2,7 @@ import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/r import { FormGroup, PageSection, Switch } from "@patternfly/react-core"; import { useTranslation } from "react-i18next"; import { FormPanel, HelpItem } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { FormAccess } from "../components/form/FormAccess"; import { useRealm } from "../context/realm-context/RealmContext"; @@ -18,6 +18,8 @@ export const RealmSettingsLoginTab = ({ realm, refresh, }: RealmSettingsLoginTabProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/realm-settings/NewAttributeSettings.tsx b/js/apps/admin-ui/src/realm-settings/NewAttributeSettings.tsx index 00b98d632f..fc9a5acef5 100644 --- a/js/apps/admin-ui/src/realm-settings/NewAttributeSettings.tsx +++ b/js/apps/admin-ui/src/realm-settings/NewAttributeSettings.tsx @@ -15,7 +15,7 @@ import { FormProvider, useForm, useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; import { ScrollForm } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { FixedButtonsGroup } from "../components/form/FixedButtonGroup"; import { ViewHeader } from "../components/view-header/ViewHeader"; @@ -156,6 +156,8 @@ const CreateAttributeFormContent = ({ }; export default function NewAttributeSettings() { + const { adminClient } = useAdminClient(); + const { realm: realmName, attributeName } = useParams(); const form = useForm(); const { t } = useTranslation(); diff --git a/js/apps/admin-ui/src/realm-settings/NewClientPolicy.tsx b/js/apps/admin-ui/src/realm-settings/NewClientPolicy.tsx index 5d54ea7a66..ee788fe74d 100644 --- a/js/apps/admin-ui/src/realm-settings/NewClientPolicy.tsx +++ b/js/apps/admin-ui/src/realm-settings/NewClientPolicy.tsx @@ -30,8 +30,7 @@ import { KeycloakTextArea, TextControl, } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { FormAccess } from "../components/form/FormAccess"; @@ -69,6 +68,8 @@ type PolicyDetailAttributes = { }; export default function NewClientPolicy() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { realm } = useRealm(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/realm-settings/NewClientPolicyCondition.tsx b/js/apps/admin-ui/src/realm-settings/NewClientPolicyCondition.tsx index 0ed6688e63..4d8d425fa4 100644 --- a/js/apps/admin-ui/src/realm-settings/NewClientPolicyCondition.tsx +++ b/js/apps/admin-ui/src/realm-settings/NewClientPolicyCondition.tsx @@ -20,7 +20,7 @@ import { Controller, FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link, useNavigate, useParams } from "react-router-dom"; import { HelpItem } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { DynamicComponents } from "../components/dynamic/DynamicComponents"; import { FormAccess } from "../components/form/FormAccess"; @@ -39,6 +39,8 @@ type ConfigProperty = ConfigPropertyRepresentation & { }; export default function NewClientPolicyCondition() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const navigate = useNavigate(); diff --git a/js/apps/admin-ui/src/realm-settings/PartialExport.tsx b/js/apps/admin-ui/src/realm-settings/PartialExport.tsx index 439d6ffca9..f9b17bc0fb 100644 --- a/js/apps/admin-ui/src/realm-settings/PartialExport.tsx +++ b/js/apps/admin-ui/src/realm-settings/PartialExport.tsx @@ -14,8 +14,7 @@ import { import { saveAs } from "file-saver"; import { useState } from "react"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useRealm } from "../context/realm-context/RealmContext"; import { prettyPrintJSON } from "../util"; @@ -31,6 +30,8 @@ export const PartialExportDialog = ({ isOpen, onClose, }: PartialExportDialogProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { realm } = useRealm(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/realm-settings/PartialImport.tsx b/js/apps/admin-ui/src/realm-settings/PartialImport.tsx index 3e7e02ebc0..caf4e6887c 100644 --- a/js/apps/admin-ui/src/realm-settings/PartialImport.tsx +++ b/js/apps/admin-ui/src/realm-settings/PartialImport.tsx @@ -37,8 +37,7 @@ import { useState, } from "react"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { JsonFileUpload } from "../components/json-file-upload/JsonFileUpload"; import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable"; @@ -71,6 +70,8 @@ const INITIAL_RESOURCES: Readonly = { }; export const PartialImportDialog = (props: PartialImportProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { realm } = useRealm(); diff --git a/js/apps/admin-ui/src/realm-settings/PoliciesTab.tsx b/js/apps/admin-ui/src/realm-settings/PoliciesTab.tsx index 726b94143f..a8ea343240 100644 --- a/js/apps/admin-ui/src/realm-settings/PoliciesTab.tsx +++ b/js/apps/admin-ui/src/realm-settings/PoliciesTab.tsx @@ -17,8 +17,7 @@ import { useState } from "react"; import { Controller, useForm, type UseFormReturn } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner"; @@ -41,6 +40,8 @@ type ClientPolicy = ClientPolicyRepresentation & { }; export const PoliciesTab = () => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const { realm } = useRealm(); diff --git a/js/apps/admin-ui/src/realm-settings/ProfilesTab.tsx b/js/apps/admin-ui/src/realm-settings/ProfilesTab.tsx index 90176a8d5a..f5f9df2085 100644 --- a/js/apps/admin-ui/src/realm-settings/ProfilesTab.tsx +++ b/js/apps/admin-ui/src/realm-settings/ProfilesTab.tsx @@ -19,8 +19,7 @@ import { omit } from "lodash-es"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner"; @@ -42,6 +41,8 @@ type ClientProfile = ClientProfileRepresentation & { }; export default function ProfilesTab() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { realm } = useRealm(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/realm-settings/RealmSettingsSection.tsx b/js/apps/admin-ui/src/realm-settings/RealmSettingsSection.tsx index 9731bf2e0d..65a99ce2f5 100644 --- a/js/apps/admin-ui/src/realm-settings/RealmSettingsSection.tsx +++ b/js/apps/admin-ui/src/realm-settings/RealmSettingsSection.tsx @@ -1,7 +1,6 @@ import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation"; import { useState } from "react"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner"; import { useFetch } from "../utils/useFetch"; import { useParams } from "../utils/useParams"; @@ -9,6 +8,8 @@ import { RealmSettingsTabs } from "./RealmSettingsTabs"; import type { RealmSettingsParams } from "./routes/RealmSettings"; export default function RealmSettingsSection() { + const { adminClient } = useAdminClient(); + const { realm: realmName } = useParams(); const [realm, setRealm] = useState(); const [key, setKey] = useState(0); diff --git a/js/apps/admin-ui/src/realm-settings/RealmSettingsTabs.tsx b/js/apps/admin-ui/src/realm-settings/RealmSettingsTabs.tsx index f9df89fd17..bbb6983967 100644 --- a/js/apps/admin-ui/src/realm-settings/RealmSettingsTabs.tsx +++ b/js/apps/admin-ui/src/realm-settings/RealmSettingsTabs.tsx @@ -1,4 +1,7 @@ +import { fetchWithError } from "@keycloak/keycloak-admin-client"; import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation"; +import { UserProfileConfig } from "@keycloak/keycloak-admin-client/lib/defs/userProfileMetadata"; +import { AdminEnvironment, useEnvironment } from "@keycloak/keycloak-ui-shared"; import { AlertVariant, ButtonVariant, @@ -15,11 +18,7 @@ import { useEffect, useMemo, useState } from "react"; import { Controller, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; -import { useAccess } from "../context/access/Access"; - -import { fetchWithError } from "@keycloak/keycloak-admin-client"; -import { UserProfileConfig } from "@keycloak/keycloak-admin-client/lib/defs/userProfileMetadata"; -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import type { KeyValueType } from "../components/key-value-form/key-value-convert"; @@ -29,17 +28,17 @@ import { } from "../components/routable-tabs/RoutableTabs"; import { ViewHeader } from "../components/view-header/ViewHeader"; import { useRealms } from "../context/RealmsContext"; +import { useAccess } from "../context/access/Access"; import { useRealm } from "../context/realm-context/RealmContext"; import { toDashboard } from "../dashboard/routes/Dashboard"; -import environment from "../environment"; import helpUrls from "../help-urls"; +import { DEFAULT_LOCALE } from "../i18n/i18n"; import { convertFormValuesToObject, convertToFormValues } from "../util"; import { getAuthorizationHeaders } from "../utils/getAuthorizationHeaders"; import { joinPath } from "../utils/joinPath"; import useIsFeatureEnabled, { Feature } from "../utils/useIsFeatureEnabled"; import { RealmSettingsEmailTab } from "./EmailTab"; import { RealmSettingsGeneralTab } from "./GeneralTab"; -import { LocalizationTab } from "./localization/LocalizationTab"; import { RealmSettingsLoginTab } from "./LoginTab"; import { PartialExportDialog } from "./PartialExport"; import { PartialImportDialog } from "./PartialImport"; @@ -51,11 +50,11 @@ import { RealmSettingsTokensTab } from "./TokensTab"; import { UserRegistration } from "./UserRegistration"; import { EventsTab } from "./event-config/EventsTab"; import { KeysTab } from "./keys/KeysTab"; +import { LocalizationTab } from "./localization/LocalizationTab"; import { ClientPoliciesTab, toClientPolicies } from "./routes/ClientPolicies"; import { RealmSettingsTab, toRealmSettings } from "./routes/RealmSettings"; import { SecurityDefenses } from "./security-defences/SecurityDefenses"; import { UserProfileTab } from "./user-profile/UserProfileTab"; -import { DEFAULT_LOCALE } from "../i18n/i18n"; export interface UIRealmRepresentation extends RealmRepresentation { upConfig?: UserProfileConfig; @@ -76,6 +75,9 @@ const RealmSettingsHeader = ({ realmName, refresh, }: RealmSettingsHeaderProps) => { + const { adminClient } = useAdminClient(); + const { environment } = useEnvironment(); + const { t } = useTranslation(); const { refresh: refreshRealms } = useRealms(); const { addAlert, addError } = useAlerts(); @@ -184,6 +186,8 @@ export const RealmSettingsTabs = ({ realm, refresh, }: RealmSettingsTabsProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const { realm: realmName } = useRealm(); diff --git a/js/apps/admin-ui/src/realm-settings/UserRegistration.tsx b/js/apps/admin-ui/src/realm-settings/UserRegistration.tsx index ab1cfc1fc6..34fbf722ff 100644 --- a/js/apps/admin-ui/src/realm-settings/UserRegistration.tsx +++ b/js/apps/admin-ui/src/realm-settings/UserRegistration.tsx @@ -3,8 +3,7 @@ import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/ro import { AlertVariant, Tab, Tabs, TabTitleText } from "@patternfly/react-core"; import { useState } from "react"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner"; import { RoleMapping } from "../components/role-mapping/RoleMapping"; @@ -13,6 +12,8 @@ import { useFetch } from "../utils/useFetch"; import { DefaultsGroupsTab } from "./DefaultGroupsTab"; export const UserRegistration = () => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const [realm, setRealm] = useState(); const [activeTab, setActiveTab] = useState(10); diff --git a/js/apps/admin-ui/src/realm-settings/event-config/EventsTab.tsx b/js/apps/admin-ui/src/realm-settings/event-config/EventsTab.tsx index c146c4fa02..df67455b5e 100644 --- a/js/apps/admin-ui/src/realm-settings/event-config/EventsTab.tsx +++ b/js/apps/admin-ui/src/realm-settings/event-config/EventsTab.tsx @@ -12,8 +12,7 @@ import { isEqual } from "lodash-es"; import { useState } from "react"; import { useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { FormAccess } from "../../components/form/FormAccess"; @@ -34,6 +33,8 @@ type EventsConfigForm = RealmEventsConfigRepresentation & { }; export const EventsTab = ({ realm }: EventsTabProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const form = useForm(); const { setValue, handleSubmit } = form; diff --git a/js/apps/admin-ui/src/realm-settings/keys/KeysListTab.tsx b/js/apps/admin-ui/src/realm-settings/keys/KeysListTab.tsx index 5a42726257..8b31d3488b 100644 --- a/js/apps/admin-ui/src/realm-settings/keys/KeysListTab.tsx +++ b/js/apps/admin-ui/src/realm-settings/keys/KeysListTab.tsx @@ -11,8 +11,7 @@ import { cellWidth } from "@patternfly/react-table"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner"; import { ListEmptyState } from "../../components/list-empty-state/ListEmptyState"; @@ -20,13 +19,12 @@ import { KeycloakDataTable } from "../../components/table-toolbar/KeycloakDataTa import { useRealm } from "../../context/realm-context/RealmContext"; import { emptyFormatter } from "../../util"; import { useFetch } from "../../utils/useFetch"; +import useFormatDate from "../../utils/useFormatDate"; import useToggle from "../../utils/useToggle"; import { toKeysTab } from "../routes/KeysTab"; import "../realm-settings-section.css"; -import useFormatDate from "../../utils/useFormatDate"; - const FILTER_OPTIONS = ["ACTIVE", "PASSIVE", "DISABLED"] as const; type FilterType = (typeof FILTER_OPTIONS)[number]; @@ -81,6 +79,8 @@ const SelectFilter = ({ onFilter }: SelectFilterProps) => { }; export const KeysListTab = ({ realmComponents }: KeysListTabProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const navigate = useNavigate(); const formatDate = useFormatDate(); diff --git a/js/apps/admin-ui/src/realm-settings/keys/KeysProvidersTab.tsx b/js/apps/admin-ui/src/realm-settings/keys/KeysProvidersTab.tsx index 1ace147691..2d9407c3e8 100644 --- a/js/apps/admin-ui/src/realm-settings/keys/KeysProvidersTab.tsx +++ b/js/apps/admin-ui/src/realm-settings/keys/KeysProvidersTab.tsx @@ -6,19 +6,18 @@ import { Button, ButtonVariant, InputGroup, + InputGroupItem, PageSection, TextInput, Toolbar, ToolbarGroup, ToolbarItem, - InputGroupItem, } from "@patternfly/react-core"; import { SearchIcon } from "@patternfly/react-icons"; import { KeyboardEvent, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { DraggableTable } from "../../authentication/components/DraggableTable"; import { useAlerts } from "../../components/alert/Alerts"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; @@ -50,6 +49,8 @@ export const KeysProvidersTab = ({ realmComponents, refresh, }: KeysProvidersTabProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const { realm } = useRealm(); diff --git a/js/apps/admin-ui/src/realm-settings/keys/KeysTab.tsx b/js/apps/admin-ui/src/realm-settings/keys/KeysTab.tsx index a04a3d3db9..d67563a4f3 100644 --- a/js/apps/admin-ui/src/realm-settings/keys/KeysTab.tsx +++ b/js/apps/admin-ui/src/realm-settings/keys/KeysTab.tsx @@ -3,7 +3,7 @@ import { Tab, TabTitleText } from "@patternfly/react-core"; import { useState } from "react"; import { useTranslation } from "react-i18next"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner"; import { RoutableTabs, @@ -30,6 +30,8 @@ const sortByPriority = (components: ComponentRepresentation[]) => { }; export const KeysTab = () => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { realm: realmName } = useRealm(); diff --git a/js/apps/admin-ui/src/realm-settings/keys/key-providers/KeyProviderForm.tsx b/js/apps/admin-ui/src/realm-settings/keys/key-providers/KeyProviderForm.tsx index 752c2f0441..4fff435312 100644 --- a/js/apps/admin-ui/src/realm-settings/keys/key-providers/KeyProviderForm.tsx +++ b/js/apps/admin-ui/src/realm-settings/keys/key-providers/KeyProviderForm.tsx @@ -9,7 +9,7 @@ import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { TextControl } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../../../admin-client"; +import { useAdminClient } from "../../../admin-client"; import { useAlerts } from "../../../components/alert/Alerts"; import { DynamicComponents } from "../../../components/dynamic/DynamicComponents"; import { FormAccess } from "../../../components/form/FormAccess"; @@ -31,6 +31,8 @@ export const KeyProviderForm = ({ providerType, onClose, }: KeyProviderFormProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { id } = useParams<{ id: string }>(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/realm-settings/localization/EffectiveMessageBundles.tsx b/js/apps/admin-ui/src/realm-settings/localization/EffectiveMessageBundles.tsx index b7ab458acc..5dc7b79f49 100644 --- a/js/apps/admin-ui/src/realm-settings/localization/EffectiveMessageBundles.tsx +++ b/js/apps/admin-ui/src/realm-settings/localization/EffectiveMessageBundles.tsx @@ -22,8 +22,7 @@ import { pickBy } from "lodash-es"; import { useMemo, useState } from "react"; import { Controller, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import DropdownPanel from "../../components/dropdown-panel/DropdownPanel"; import { ListEmptyState } from "../../components/list-empty-state/ListEmptyState"; import { KeycloakDataTable } from "../../components/table-toolbar/KeycloakDataTable"; @@ -57,6 +56,8 @@ export const EffectiveMessageBundles = ({ defaultSupportedLocales, defaultLocales, }: EffectiveMessageBundlesProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { realm } = useRealm(); const serverInfo = useServerInfo(); diff --git a/js/apps/admin-ui/src/realm-settings/localization/RealmOverrides.tsx b/js/apps/admin-ui/src/realm-settings/localization/RealmOverrides.tsx index 6b190b2b79..a362f70a6c 100644 --- a/js/apps/admin-ui/src/realm-settings/localization/RealmOverrides.tsx +++ b/js/apps/admin-ui/src/realm-settings/localization/RealmOverrides.tsx @@ -1,3 +1,4 @@ +import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation"; import { AlertVariant, Button, @@ -5,11 +6,11 @@ import { Divider, Form, FormGroup, - TextContent, Text, - ToolbarItem, - TextVariants, + TextContent, TextInput, + TextVariants, + ToolbarItem, } from "@patternfly/react-core"; import { Dropdown, @@ -37,12 +38,11 @@ import { Thead, Tr, } from "@patternfly/react-table"; -import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation"; import { cloneDeep, isEqual, uniqWith } from "lodash-es"; import { ChangeEvent, useEffect, useState, type FormEvent } from "react"; import { useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { KeyValueType } from "../../components/key-value-form/key-value-convert"; @@ -82,6 +82,8 @@ export const RealmOverrides = ({ realm, tableData, }: RealmOverridesProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const [addTranslationModalOpen, setAddTranslationModalOpen] = useState(false); const [filterDropdownOpen, setFilterDropdownOpen] = useState(false); diff --git a/js/apps/admin-ui/src/realm-settings/user-profile/AttributesTab.tsx b/js/apps/admin-ui/src/realm-settings/user-profile/AttributesTab.tsx index e137bcd3b5..7afba7a562 100644 --- a/js/apps/admin-ui/src/realm-settings/user-profile/AttributesTab.tsx +++ b/js/apps/admin-ui/src/realm-settings/user-profile/AttributesTab.tsx @@ -18,17 +18,17 @@ import { uniqBy } from "lodash-es"; import { useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; +import { useAdminClient } from "../../admin-client"; import { DraggableTable } from "../../authentication/components/DraggableTable"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinner"; import { useRealm } from "../../context/realm-context/RealmContext"; +import { DEFAULT_LOCALE } from "../../i18n/i18n"; +import { useFetch } from "../../utils/useFetch"; import useToggle from "../../utils/useToggle"; import { toAddAttribute } from "../routes/AddAttribute"; import { toAttribute } from "../routes/Attribute"; import { useUserProfile } from "./UserProfileContext"; -import { useFetch } from "../../utils/useFetch"; -import { adminClient } from "../../admin-client"; -import { DEFAULT_LOCALE } from "../../i18n/i18n"; const RESTRICTED_ATTRIBUTES = ["username", "email"]; @@ -41,6 +41,8 @@ type AttributesTabProps = { }; export const AttributesTab = ({ setTableData }: AttributesTabProps) => { + const { adminClient } = useAdminClient(); + const { config, save } = useUserProfile(); const { realm: realmName } = useRealm(); const { t } = useTranslation(); diff --git a/js/apps/admin-ui/src/realm-settings/user-profile/UserProfileContext.tsx b/js/apps/admin-ui/src/realm-settings/user-profile/UserProfileContext.tsx index 256f577ddc..98252a5f3f 100644 --- a/js/apps/admin-ui/src/realm-settings/user-profile/UserProfileContext.tsx +++ b/js/apps/admin-ui/src/realm-settings/user-profile/UserProfileContext.tsx @@ -6,8 +6,7 @@ import { createNamedContext, useRequiredContext, } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { useRealm } from "../../context/realm-context/RealmContext"; import { useFetch } from "../../utils/useFetch"; @@ -33,6 +32,8 @@ export const UserProfileContext = createNamedContext< >("UserProfileContext", undefined); export const UserProfileProvider = ({ children }: PropsWithChildren) => { + const { adminClient } = useAdminClient(); + const { realm } = useRealm(); const { addAlert, addError } = useAlerts(); const { t } = useTranslation(); diff --git a/js/apps/admin-ui/src/realm-settings/user-profile/attribute/AddTranslationsDialog.tsx b/js/apps/admin-ui/src/realm-settings/user-profile/attribute/AddTranslationsDialog.tsx index a1d14826ac..648c26a925 100644 --- a/js/apps/admin-ui/src/realm-settings/user-profile/attribute/AddTranslationsDialog.tsx +++ b/js/apps/admin-ui/src/realm-settings/user-profile/attribute/AddTranslationsDialog.tsx @@ -1,4 +1,5 @@ import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation"; +import { TextControl } from "@keycloak/keycloak-ui-shared"; import { Button, Flex, @@ -12,20 +13,19 @@ import { TextContent, TextVariants, } from "@patternfly/react-core"; -import { Table, Tbody, Td, Th, Thead, Tr } from "@patternfly/react-table"; import { SearchIcon } from "@patternfly/react-icons"; +import { Table, Tbody, Td, Th, Thead, Tr } from "@patternfly/react-table"; import { useEffect, useMemo, useState } from "react"; -import { useTranslation } from "react-i18next"; import { FormProvider, useForm, useWatch } from "react-hook-form"; +import { useTranslation } from "react-i18next"; +import { useAdminClient } from "../../../admin-client"; +import { ListEmptyState } from "../../../components/list-empty-state/ListEmptyState"; +import { PaginatingTableToolbar } from "../../../components/table-toolbar/PaginatingTableToolbar"; import { useRealm } from "../../../context/realm-context/RealmContext"; import { useWhoAmI } from "../../../context/whoami/WhoAmI"; -import { adminClient } from "../../../admin-client"; -import { PaginatingTableToolbar } from "../../../components/table-toolbar/PaginatingTableToolbar"; -import { ListEmptyState } from "../../../components/list-empty-state/ListEmptyState"; -import { useFetch } from "../../../utils/useFetch"; -import { localeToDisplayName } from "../../../util"; import { DEFAULT_LOCALE } from "../../../i18n/i18n"; -import { TextControl } from "@keycloak/keycloak-ui-shared"; +import { localeToDisplayName } from "../../../util"; +import { useFetch } from "../../../utils/useFetch"; type TranslationForm = { locale: string; @@ -52,6 +52,8 @@ export const AddTranslationsDialog = ({ toggleDialog, onTranslationsAdded, }: AddTranslationsDialogProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { realm: realmName } = useRealm(); const [realm, setRealm] = useState(); diff --git a/js/apps/admin-ui/src/realm-settings/user-profile/attribute/AttributeGeneralSettings.tsx b/js/apps/admin-ui/src/realm-settings/user-profile/attribute/AttributeGeneralSettings.tsx index c74c07d080..2b586dddf6 100644 --- a/js/apps/admin-ui/src/realm-settings/user-profile/attribute/AttributeGeneralSettings.tsx +++ b/js/apps/admin-ui/src/realm-settings/user-profile/attribute/AttributeGeneralSettings.tsx @@ -23,8 +23,7 @@ import { useEffect, useState } from "react"; import { Controller, useFormContext, useWatch } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { FormErrorText, HelpItem } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../../admin-client"; +import { useAdminClient } from "../../../admin-client"; import { FormAccess } from "../../../components/form/FormAccess"; import { KeycloakSpinner } from "../../../components/keycloak-spinner/KeycloakSpinner"; import { useRealm } from "../../../context/realm-context/RealmContext"; @@ -62,6 +61,8 @@ export const AttributeGeneralSettings = ({ onHandlingTranslationData, onHandlingGeneratedDisplayName, }: AttributeGeneralSettingsProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { realm: realmName } = useRealm(); const form = useFormContext(); diff --git a/js/apps/admin-ui/src/realm/add/NewRealmForm.tsx b/js/apps/admin-ui/src/realm/add/NewRealmForm.tsx index 47d2851f2b..77c0699cac 100644 --- a/js/apps/admin-ui/src/realm/add/NewRealmForm.tsx +++ b/js/apps/admin-ui/src/realm/add/NewRealmForm.tsx @@ -5,8 +5,7 @@ import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { TextControl } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { DefaultSwitchControl } from "../../components/SwitchControl"; import { useAlerts } from "../../components/alert/Alerts"; import { FormAccess } from "../../components/form/FormAccess"; @@ -18,6 +17,8 @@ import { toDashboard } from "../../dashboard/routes/Dashboard"; import { convertFormValuesToObject, convertToFormValues } from "../../util"; export default function NewRealmForm() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const navigate = useNavigate(); const { refresh, whoAmI } = useWhoAmI(); diff --git a/js/apps/admin-ui/src/routes.tsx b/js/apps/admin-ui/src/routes.tsx index 077d04842a..94c2516271 100644 --- a/js/apps/admin-ui/src/routes.tsx +++ b/js/apps/admin-ui/src/routes.tsx @@ -2,9 +2,8 @@ import type { AccessType } from "@keycloak/keycloak-admin-client/lib/defs/whoAmI import type { TFunction } from "i18next"; import type { ComponentType } from "react"; import type { NonIndexRouteObject, RouteObject } from "react-router-dom"; - -import { App } from "./App"; import { PageNotFoundSection } from "./PageNotFoundSection"; +import { Root } from "./Root"; import authenticationRoutes from "./authentication/routes"; import clientScopesRoutes from "./client-scopes/routes"; import clientRoutes from "./clients/routes"; @@ -12,13 +11,13 @@ import dashboardRoutes from "./dashboard/routes"; import eventRoutes from "./events/routes"; import groupsRoutes from "./groups/routes"; import identityProviders from "./identity-providers/routes"; +import pageRoutes from "./page/routes"; import realmRoleRoutes from "./realm-roles/routes"; import realmSettingRoutes from "./realm-settings/routes"; import realmRoutes from "./realm/routes"; import sessionRoutes from "./sessions/routes"; import userFederationRoutes from "./user-federation/routes"; import userRoutes from "./user/routes"; -import pageRoutes from "./page/routes"; export type AppRouteObjectHandle = { access: AccessType | AccessType[]; @@ -58,6 +57,6 @@ export const routes: AppRouteObject[] = [ export const RootRoute: RouteObject = { path: "/", - element: , + element: , children: routes, }; diff --git a/js/apps/admin-ui/src/sessions/RevocationModal.tsx b/js/apps/admin-ui/src/sessions/RevocationModal.tsx index 1f41e98144..1f63677ce9 100644 --- a/js/apps/admin-ui/src/sessions/RevocationModal.tsx +++ b/js/apps/admin-ui/src/sessions/RevocationModal.tsx @@ -14,8 +14,7 @@ import { import { useState } from "react"; import { useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useRealm } from "../context/realm-context/RealmContext"; import { useFetch } from "../utils/useFetch"; @@ -29,6 +28,8 @@ export const RevocationModal = ({ handleModalToggle, save, }: RevocationModalProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert } = useAlerts(); diff --git a/js/apps/admin-ui/src/sessions/SessionsSection.tsx b/js/apps/admin-ui/src/sessions/SessionsSection.tsx index 0bad690c73..191ba9ec4e 100644 --- a/js/apps/admin-ui/src/sessions/SessionsSection.tsx +++ b/js/apps/admin-ui/src/sessions/SessionsSection.tsx @@ -8,17 +8,16 @@ import { import { FilterIcon } from "@patternfly/react-icons"; import { useState } from "react"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { ViewHeader } from "../components/view-header/ViewHeader"; import { fetchAdminUI } from "../context/auth/admin-ui-endpoint"; import { useRealm } from "../context/realm-context/RealmContext"; import helpUrls from "../help-urls"; +import useToggle from "../utils/useToggle"; import { RevocationModal } from "./RevocationModal"; import SessionsTable from "./SessionsTable"; -import useToggle from "../utils/useToggle"; import "./SessionsSection.css"; @@ -61,6 +60,8 @@ const SessionFilter = ({ filterType, onChange }: SessionFilterProps) => { }; export default function SessionsSection() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const [key, setKey] = useState(0); @@ -78,6 +79,7 @@ export default function SessionsSection() { const loader = async (first?: number, max?: number, search?: string) => { const data = await fetchAdminUI( + adminClient, "ui-ext/sessions", { first: `${first}`, diff --git a/js/apps/admin-ui/src/sessions/SessionsTable.tsx b/js/apps/admin-ui/src/sessions/SessionsTable.tsx index 602b717ff7..419d962fdc 100644 --- a/js/apps/admin-ui/src/sessions/SessionsTable.tsx +++ b/js/apps/admin-ui/src/sessions/SessionsTable.tsx @@ -1,4 +1,5 @@ import type UserSessionRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userSessionRepresentation"; +import { useEnvironment } from "@keycloak/keycloak-ui-shared"; import { Button, Label, @@ -9,11 +10,11 @@ import { Tooltip, } from "@patternfly/react-core"; import { CubesIcon, InfoCircleIcon } from "@patternfly/react-icons"; +import { IRowData } from "@patternfly/react-table"; import { MouseEvent, ReactNode, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { Link, useMatch, useNavigate } from "react-router-dom"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { toClient } from "../clients/routes/Client"; import { useAlerts } from "../components/alert/Alerts"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; @@ -26,12 +27,10 @@ import { } from "../components/table-toolbar/KeycloakDataTable"; import { useRealm } from "../context/realm-context/RealmContext"; import { useWhoAmI } from "../context/whoami/WhoAmI"; -import { keycloak } from "../keycloak"; -import { toUser, UserRoute } from "../user/routes/User"; +import { UserRoute, toUser } from "../user/routes/User"; import { toUsers } from "../user/routes/Users"; import { isLightweightUser } from "../user/utils"; import useFormatDate from "../utils/useFormatDate"; -import { IRowData } from "@patternfly/react-table"; export type ColumnName = | "username" @@ -98,6 +97,9 @@ export default function SessionsTable({ isSearching, isPaginated, }: SessionsTableProps) { + const { keycloak } = useEnvironment(); + const { adminClient } = useAdminClient(); + const { realm } = useRealm(); const { whoAmI } = useWhoAmI(); const navigate = useNavigate(); diff --git a/js/apps/admin-ui/src/user-federation/CreateUserFederationLdapSettings.tsx b/js/apps/admin-ui/src/user-federation/CreateUserFederationLdapSettings.tsx index 79db5316d0..3f271505bd 100644 --- a/js/apps/admin-ui/src/user-federation/CreateUserFederationLdapSettings.tsx +++ b/js/apps/admin-ui/src/user-federation/CreateUserFederationLdapSettings.tsx @@ -2,8 +2,7 @@ import { AlertVariant, PageSection } from "@patternfly/react-core"; import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useRealm } from "../context/realm-context/RealmContext"; import { @@ -15,6 +14,8 @@ import { toUserFederation } from "./routes/UserFederation"; import { ExtendedHeader } from "./shared/ExtendedHeader"; export default function CreateUserFederationLdapSettings() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const form = useForm({ mode: "onChange" }); const navigate = useNavigate(); diff --git a/js/apps/admin-ui/src/user-federation/ManagePriorityDialog.tsx b/js/apps/admin-ui/src/user-federation/ManagePriorityDialog.tsx index 3e1e5cec6d..ca64943cca 100644 --- a/js/apps/admin-ui/src/user-federation/ManagePriorityDialog.tsx +++ b/js/apps/admin-ui/src/user-federation/ManagePriorityDialog.tsx @@ -21,7 +21,7 @@ import { import { sortBy } from "lodash-es"; import { useState } from "react"; import { useTranslation } from "react-i18next"; -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; type ManagePriorityDialogProps = { @@ -33,6 +33,8 @@ export const ManagePriorityDialog = ({ components, onClose, }: ManagePriorityDialogProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/user-federation/UserFederationKerberosSettings.tsx b/js/apps/admin-ui/src/user-federation/UserFederationKerberosSettings.tsx index 37833f9c98..694b8af7a4 100644 --- a/js/apps/admin-ui/src/user-federation/UserFederationKerberosSettings.tsx +++ b/js/apps/admin-ui/src/user-federation/UserFederationKerberosSettings.tsx @@ -9,8 +9,7 @@ import { import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useRealm } from "../context/realm-context/RealmContext"; import { useFetch } from "../utils/useFetch"; @@ -21,6 +20,8 @@ import { Header } from "./shared/Header"; import { SettingsCache } from "./shared/SettingsCache"; export default function UserFederationKerberosSettings() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const form = useForm({ mode: "onChange" }); const navigate = useNavigate(); diff --git a/js/apps/admin-ui/src/user-federation/UserFederationLdapSettings.tsx b/js/apps/admin-ui/src/user-federation/UserFederationLdapSettings.tsx index 7ca1815970..af241c6543 100644 --- a/js/apps/admin-ui/src/user-federation/UserFederationLdapSettings.tsx +++ b/js/apps/admin-ui/src/user-federation/UserFederationLdapSettings.tsx @@ -9,8 +9,7 @@ import { useState } from "react"; import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useParams } from "react-router-dom"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner"; import { @@ -34,6 +33,8 @@ import { toUserFederationLdapMapper } from "./routes/UserFederationLdapMapper"; import { ExtendedHeader } from "./shared/ExtendedHeader"; export default function UserFederationLdapSettings() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const form = useForm({ mode: "onChange" }); const { realm } = useRealm(); diff --git a/js/apps/admin-ui/src/user-federation/UserFederationSection.tsx b/js/apps/admin-ui/src/user-federation/UserFederationSection.tsx index de781a1675..ad9637a987 100644 --- a/js/apps/admin-ui/src/user-federation/UserFederationSection.tsx +++ b/js/apps/admin-ui/src/user-federation/UserFederationSection.tsx @@ -18,8 +18,7 @@ import { DatabaseIcon } from "@patternfly/react-icons"; import { useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { ClickableCard } from "../components/keycloak-card/ClickableCard"; @@ -39,6 +38,8 @@ import { toUserFederationLdap } from "./routes/UserFederationLdap"; import "./user-federation.css"; export default function UserFederationSection() { + const { adminClient } = useAdminClient(); + const [userFederations, setUserFederations] = useState(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/user-federation/custom/CustomProviderSettings.tsx b/js/apps/admin-ui/src/user-federation/custom/CustomProviderSettings.tsx index dfb5229dcc..4eb1449689 100644 --- a/js/apps/admin-ui/src/user-federation/custom/CustomProviderSettings.tsx +++ b/js/apps/admin-ui/src/user-federation/custom/CustomProviderSettings.tsx @@ -10,7 +10,7 @@ import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link, useNavigate } from "react-router-dom"; import { TextControl } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { DynamicComponents } from "../../components/dynamic/DynamicComponents"; import { FormAccess } from "../../components/form/FormAccess"; @@ -28,6 +28,8 @@ import { SyncSettings } from "./SyncSettings"; import "./custom-provider-settings.css"; export default function CustomProviderSettings() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { id, providerId } = useParams(); const navigate = useNavigate(); diff --git a/js/apps/admin-ui/src/user-federation/kerberos/KerberosSettingsRequired.tsx b/js/apps/admin-ui/src/user-federation/kerberos/KerberosSettingsRequired.tsx index 2d3ee3bf8a..39981b1874 100644 --- a/js/apps/admin-ui/src/user-federation/kerberos/KerberosSettingsRequired.tsx +++ b/js/apps/admin-ui/src/user-federation/kerberos/KerberosSettingsRequired.tsx @@ -14,7 +14,7 @@ import { } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { HelpItem, TextControl } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { FormAccess } from "../../components/form/FormAccess"; import { WizardSectionHeader } from "../../components/wizard-section-header/WizardSectionHeader"; import { useRealm } from "../../context/realm-context/RealmContext"; @@ -31,8 +31,9 @@ export const KerberosSettingsRequired = ({ showSectionHeading = false, showSectionDescription = false, }: KerberosSettingsRequiredProps) => { - const { t } = useTranslation(); + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { realm } = useRealm(); const [isEditModeDropdownOpen, setIsEditModeDropdownOpen] = useState(false); diff --git a/js/apps/admin-ui/src/user-federation/ldap/LdapSettingsAdvanced.tsx b/js/apps/admin-ui/src/user-federation/ldap/LdapSettingsAdvanced.tsx index 9136ceeaaa..7da9811f02 100644 --- a/js/apps/admin-ui/src/user-federation/ldap/LdapSettingsAdvanced.tsx +++ b/js/apps/admin-ui/src/user-federation/ldap/LdapSettingsAdvanced.tsx @@ -2,8 +2,7 @@ import { Button, FormGroup, Switch } from "@patternfly/react-core"; import { Controller, UseFormReturn } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { HelpItem } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { FormAccess } from "../../components/form/FormAccess"; import { WizardSectionHeader } from "../../components/wizard-section-header/WizardSectionHeader"; @@ -25,6 +24,8 @@ export const LdapSettingsAdvanced = ({ showSectionHeading = false, showSectionDescription = false, }: LdapSettingsAdvancedProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { realm } = useRealm(); diff --git a/js/apps/admin-ui/src/user-federation/ldap/LdapSettingsConnection.tsx b/js/apps/admin-ui/src/user-federation/ldap/LdapSettingsConnection.tsx index 44f4fa000d..8ff020990a 100644 --- a/js/apps/admin-ui/src/user-federation/ldap/LdapSettingsConnection.tsx +++ b/js/apps/admin-ui/src/user-federation/ldap/LdapSettingsConnection.tsx @@ -24,7 +24,7 @@ import { PasswordControl, TextControl, } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { FormAccess } from "../../components/form/FormAccess"; import { WizardSectionHeader } from "../../components/wizard-section-header/WizardSectionHeader"; @@ -66,6 +66,8 @@ export const LdapSettingsConnection = ({ showSectionHeading = false, showSectionDescription = false, }: LdapSettingsConnectionProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { realm } = useRealm(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/user-federation/ldap/LdapSettingsGeneral.tsx b/js/apps/admin-ui/src/user-federation/ldap/LdapSettingsGeneral.tsx index eaa3af774f..c755cdc2e6 100644 --- a/js/apps/admin-ui/src/user-federation/ldap/LdapSettingsGeneral.tsx +++ b/js/apps/admin-ui/src/user-federation/ldap/LdapSettingsGeneral.tsx @@ -9,8 +9,7 @@ import { useState } from "react"; import { Controller, FormProvider, UseFormReturn } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { HelpItem, TextControl } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { FormAccess } from "../../components/form/FormAccess"; import { WizardSectionHeader } from "../../components/wizard-section-header/WizardSectionHeader"; import { useRealm } from "../../context/realm-context/RealmContext"; @@ -29,6 +28,8 @@ export const LdapSettingsGeneral = ({ showSectionDescription = false, vendorEdit = false, }: LdapSettingsGeneralProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { realm } = useRealm(); diff --git a/js/apps/admin-ui/src/user-federation/ldap/mappers/LdapMapperDetails.tsx b/js/apps/admin-ui/src/user-federation/ldap/mappers/LdapMapperDetails.tsx index 6a641ad247..8baa8dd0c6 100644 --- a/js/apps/admin-ui/src/user-federation/ldap/mappers/LdapMapperDetails.tsx +++ b/js/apps/admin-ui/src/user-federation/ldap/mappers/LdapMapperDetails.tsx @@ -20,8 +20,7 @@ import { Controller, FormProvider, useForm, useWatch } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { HelpItem, TextControl } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../../admin-client"; +import { useAdminClient } from "../../../admin-client"; import { useAlerts } from "../../../components/alert/Alerts"; import { useConfirmDialog } from "../../../components/confirm-dialog/ConfirmDialog"; import { @@ -39,6 +38,8 @@ import { toUserFederationLdap } from "../../routes/UserFederationLdap"; import { UserFederationLdapMapperParams } from "../../routes/UserFederationLdapMapper"; export default function LdapMapperDetails() { + const { adminClient } = useAdminClient(); + const form = useForm(); const [mapping, setMapping] = useState(); const [components, setComponents] = useState(); diff --git a/js/apps/admin-ui/src/user-federation/ldap/mappers/LdapMapperList.tsx b/js/apps/admin-ui/src/user-federation/ldap/mappers/LdapMapperList.tsx index 031ae5a034..2a8c5febb8 100644 --- a/js/apps/admin-ui/src/user-federation/ldap/mappers/LdapMapperList.tsx +++ b/js/apps/admin-ui/src/user-federation/ldap/mappers/LdapMapperList.tsx @@ -8,8 +8,7 @@ import { import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Link, To, useNavigate, useParams } from "react-router-dom"; - -import { adminClient } from "../../../admin-client"; +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"; @@ -34,6 +33,8 @@ const MapperLink = ({ toDetail, ...mapper }: MapperLinkProps) => ( ); export const LdapMapperList = ({ toCreate, toDetail }: LdapMapperListProps) => { + const { adminClient } = useAdminClient(); + const navigate = useNavigate(); const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/user-federation/shared/ExtendedHeader.tsx b/js/apps/admin-ui/src/user-federation/shared/ExtendedHeader.tsx index dbf58f06ab..bb7b1d0614 100644 --- a/js/apps/admin-ui/src/user-federation/shared/ExtendedHeader.tsx +++ b/js/apps/admin-ui/src/user-federation/shared/ExtendedHeader.tsx @@ -6,8 +6,7 @@ import { import { useFormContext, useWatch } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useParams } from "react-router-dom"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { Header } from "./Header"; @@ -25,6 +24,8 @@ export const ExtendedHeader = ({ save, noDivider = false, }: ExtendedHeaderProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { id } = useParams<{ id: string }>(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/user-federation/shared/Header.tsx b/js/apps/admin-ui/src/user-federation/shared/Header.tsx index 39b714f51c..20b18bd6f3 100644 --- a/js/apps/admin-ui/src/user-federation/shared/Header.tsx +++ b/js/apps/admin-ui/src/user-federation/shared/Header.tsx @@ -4,8 +4,7 @@ import { ReactElement } from "react"; import { Controller, useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useNavigate, useParams } from "react-router-dom"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { ViewHeader } from "../../components/view-header/ViewHeader"; @@ -26,6 +25,8 @@ export const Header = ({ noDivider = false, dropdownItems = [], }: HeaderProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { id } = useParams>(); const navigate = useNavigate(); diff --git a/js/apps/admin-ui/src/user/CreateUser.tsx b/js/apps/admin-ui/src/user/CreateUser.tsx index e8cfdfcba9..afa589bca9 100644 --- a/js/apps/admin-ui/src/user/CreateUser.tsx +++ b/js/apps/admin-ui/src/user/CreateUser.tsx @@ -11,8 +11,7 @@ import { isUserProfileError, setUserProfileServerError, } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner"; import { ViewHeader } from "../components/view-header/ViewHeader"; @@ -25,6 +24,8 @@ import { toUser } from "./routes/User"; import "./user-section.css"; export default function CreateUser() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const navigate = useNavigate(); diff --git a/js/apps/admin-ui/src/user/EditUser.tsx b/js/apps/admin-ui/src/user/EditUser.tsx index 464007dd87..87dddee3a5 100644 --- a/js/apps/admin-ui/src/user/EditUser.tsx +++ b/js/apps/admin-ui/src/user/EditUser.tsx @@ -1,8 +1,8 @@ -import type { - UserProfileMetadata, - UserProfileConfig, -} from "@keycloak/keycloak-admin-client/lib/defs/userProfileMetadata"; import RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation"; +import type { + UserProfileConfig, + UserProfileMetadata, +} from "@keycloak/keycloak-admin-client/lib/defs/userProfileMetadata"; import { AlertVariant, ButtonVariant, @@ -23,15 +23,16 @@ import { isUserProfileError, setUserProfileServerError, } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; +import { KeyValueType } from "../components/key-value-form/key-value-convert"; import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner"; import { RoutableTabs, useRoutableTab, } from "../components/routable-tabs/RoutableTabs"; +import { getUnmanagedAttributes } from "../components/users/resource"; import { ViewHeader } from "../components/view-header/ViewHeader"; import { useAccess } from "../context/access/Access"; import { useRealm } from "../context/realm-context/RealmContext"; @@ -47,20 +48,21 @@ import { UserIdentityProviderLinks } from "./UserIdentityProviderLinks"; import { UserRoleMapping } from "./UserRoleMapping"; import { UserSessions } from "./UserSessions"; import { + UIUserRepresentation, UserFormFields, + filterManagedAttributes, toUserFormFields, toUserRepresentation, - filterManagedAttributes, - UIUserRepresentation, } from "./form-state"; import { UserParams, UserTab, toUser } from "./routes/User"; import { toUsers } from "./routes/Users"; import { isLightweightUser } from "./utils"; -import { getUnmanagedAttributes } from "../components/users/resource"; + import "./user-section.css"; -import { KeyValueType } from "../components/key-value-form/key-value-convert"; export default function EditUser() { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const navigate = useNavigate(); @@ -115,7 +117,7 @@ export default function EditUser() { userProfileMetadata: true, }) as UIUserRepresentation | undefined, adminClient.attackDetection.findOne({ id: id! }), - getUnmanagedAttributes(id!), + getUnmanagedAttributes(adminClient, id!), adminClient.users.getProfile({ realm: realmName }), ]), ([realm, userData, attackDetection, unmanagedAttributes, upConfig]) => { diff --git a/js/apps/admin-ui/src/user/FederatedUserLink.tsx b/js/apps/admin-ui/src/user/FederatedUserLink.tsx index c0f0532643..a3c1541283 100644 --- a/js/apps/admin-ui/src/user/FederatedUserLink.tsx +++ b/js/apps/admin-ui/src/user/FederatedUserLink.tsx @@ -3,8 +3,7 @@ import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/us import { Button } from "@patternfly/react-core"; import { useState } from "react"; import { Link } from "react-router-dom"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAccess } from "../context/access/Access"; import { useRealm } from "../context/realm-context/RealmContext"; import { toCustomUserFederation } from "../user-federation/routes/CustomUserFederation"; @@ -15,6 +14,8 @@ type FederatedUserLinkProps = { }; export const FederatedUserLink = ({ user }: FederatedUserLinkProps) => { + const { adminClient } = useAdminClient(); + const access = useAccess(); const { realm } = useRealm(); diff --git a/js/apps/admin-ui/src/user/UserConsents.tsx b/js/apps/admin-ui/src/user/UserConsents.tsx index 6a579d9ef7..7ce1719413 100644 --- a/js/apps/admin-ui/src/user/UserConsents.tsx +++ b/js/apps/admin-ui/src/user/UserConsents.tsx @@ -10,8 +10,7 @@ import { cellWidth } from "@patternfly/react-table"; import { sortBy } from "lodash-es"; import { useState } from "react"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../admin-client"; +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"; @@ -24,6 +23,8 @@ import useFormatDate from "../utils/useFormatDate"; import { useParams } from "../utils/useParams"; export const UserConsents = () => { + const { adminClient } = useAdminClient(); + const [selectedClient, setSelectedClient] = useState(); const { t } = useTranslation(); diff --git a/js/apps/admin-ui/src/user/UserCredentials.tsx b/js/apps/admin-ui/src/user/UserCredentials.tsx index 6217f62aea..24f80581d9 100644 --- a/js/apps/admin-ui/src/user/UserCredentials.tsx +++ b/js/apps/admin-ui/src/user/UserCredentials.tsx @@ -19,8 +19,7 @@ import { } from "react"; import { useTranslation } from "react-i18next"; import { HelpItem } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner"; @@ -97,6 +96,8 @@ const UserCredentialsRow = ({ ); export const UserCredentials = ({ user, setUser }: UserCredentialsProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const [key, setKey] = useState(0); diff --git a/js/apps/admin-ui/src/user/UserForm.tsx b/js/apps/admin-ui/src/user/UserForm.tsx index 62c15b8cda..f8264a80fa 100644 --- a/js/apps/admin-ui/src/user/UserForm.tsx +++ b/js/apps/admin-ui/src/user/UserForm.tsx @@ -26,8 +26,7 @@ import { useEffect, useState } from "react"; import { Controller, FormProvider, UseFormReturn } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { DefaultSwitchControl } from "../components/SwitchControl"; import { useAlerts } from "../components/alert/Alerts"; import { FormAccess } from "../components/form/FormAccess"; @@ -68,6 +67,8 @@ export const UserForm = ({ save, onGroupsUpdate, }: UserFormProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const formatDate = useFormatDate(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/user/UserGroups.tsx b/js/apps/admin-ui/src/user/UserGroups.tsx index 72fe9eefeb..c947f93b0f 100644 --- a/js/apps/admin-ui/src/user/UserGroups.tsx +++ b/js/apps/admin-ui/src/user/UserGroups.tsx @@ -13,8 +13,7 @@ import { intersectionBy, sortBy, uniqBy } from "lodash-es"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { useHelp } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { GroupPath } from "../components/group/GroupPath"; @@ -29,6 +28,8 @@ type UserGroupsProps = { }; export const UserGroups = ({ user }: UserGroupsProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const [key, setKey] = useState(0); diff --git a/js/apps/admin-ui/src/user/UserIdPModal.tsx b/js/apps/admin-ui/src/user/UserIdPModal.tsx index e8b632e5d0..c6e1cf6e70 100644 --- a/js/apps/admin-ui/src/user/UserIdPModal.tsx +++ b/js/apps/admin-ui/src/user/UserIdPModal.tsx @@ -13,8 +13,7 @@ import { capitalize } from "lodash-es"; import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { TextControl } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; type UserIdpModalProps = { @@ -30,6 +29,8 @@ export const UserIdpModal = ({ onClose, onRefresh, }: UserIdpModalProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); const form = useForm({ diff --git a/js/apps/admin-ui/src/user/UserIdentityProviderLinks.tsx b/js/apps/admin-ui/src/user/UserIdentityProviderLinks.tsx index 5c90bf32fb..2c599d8061 100644 --- a/js/apps/admin-ui/src/user/UserIdentityProviderLinks.tsx +++ b/js/apps/admin-ui/src/user/UserIdentityProviderLinks.tsx @@ -15,7 +15,7 @@ import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; import { FormPanel } from "@keycloak/keycloak-ui-shared"; -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable"; @@ -32,6 +32,8 @@ type UserIdentityProviderLinksProps = { export const UserIdentityProviderLinks = ({ userId, }: UserIdentityProviderLinksProps) => { + const { adminClient } = useAdminClient(); + const [key, setKey] = useState(0); const [federatedId, setFederatedId] = useState(""); const [isLinkIdPModalOpen, setIsLinkIdPModalOpen] = useState(false); diff --git a/js/apps/admin-ui/src/user/UserRoleMapping.tsx b/js/apps/admin-ui/src/user/UserRoleMapping.tsx index 5b4a17fd4a..b13f57d0d2 100644 --- a/js/apps/admin-ui/src/user/UserRoleMapping.tsx +++ b/js/apps/admin-ui/src/user/UserRoleMapping.tsx @@ -1,8 +1,7 @@ import type { RoleMappingPayload } from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation"; import { AlertVariant } from "@patternfly/react-core"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { RoleMapping, Row } from "../components/role-mapping/RoleMapping"; @@ -12,6 +11,8 @@ type UserRoleMappingProps = { }; export const UserRoleMapping = ({ id, name }: UserRoleMappingProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { addAlert, addError } = useAlerts(); diff --git a/js/apps/admin-ui/src/user/UserSessions.tsx b/js/apps/admin-ui/src/user/UserSessions.tsx index 64e7422418..f97aa765c3 100644 --- a/js/apps/admin-ui/src/user/UserSessions.tsx +++ b/js/apps/admin-ui/src/user/UserSessions.tsx @@ -1,13 +1,14 @@ import { PageSection } from "@patternfly/react-core"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useRealm } from "../context/realm-context/RealmContext"; import SessionsTable from "../sessions/SessionsTable"; import { useParams } from "../utils/useParams"; import type { UserParams } from "./routes/User"; export const UserSessions = () => { + const { adminClient } = useAdminClient(); + const { id } = useParams(); const { realm } = useRealm(); const { t } = useTranslation(); diff --git a/js/apps/admin-ui/src/user/user-credentials/InlineLabelEdit.tsx b/js/apps/admin-ui/src/user/user-credentials/InlineLabelEdit.tsx index 910d1b966a..2e058564b2 100644 --- a/js/apps/admin-ui/src/user/user-credentials/InlineLabelEdit.tsx +++ b/js/apps/admin-ui/src/user/user-credentials/InlineLabelEdit.tsx @@ -9,7 +9,7 @@ import { import { CheckIcon, PencilAltIcon, TimesIcon } from "@patternfly/react-icons"; import { useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; type UserLabelForm = { @@ -29,6 +29,8 @@ export const InlineLabelEdit = ({ isEditable, toggle, }: InlineLabelEditProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const { register, handleSubmit } = useForm(); diff --git a/js/apps/admin-ui/src/user/user-credentials/RequiredActionMultiSelect.tsx b/js/apps/admin-ui/src/user/user-credentials/RequiredActionMultiSelect.tsx index 54fdbbaf80..453fbef967 100644 --- a/js/apps/admin-ui/src/user/user-credentials/RequiredActionMultiSelect.tsx +++ b/js/apps/admin-ui/src/user/user-credentials/RequiredActionMultiSelect.tsx @@ -4,8 +4,7 @@ import { useState } from "react"; import { FieldPathByValue, FieldValues } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { SelectControl } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useFetch } from "../../utils/useFetch"; export type RequiredActionMultiSelectProps< @@ -25,6 +24,8 @@ export const RequiredActionMultiSelect = < label, help, }: RequiredActionMultiSelectProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const [requiredActions, setRequiredActions] = useState< RequiredActionProviderRepresentation[] diff --git a/js/apps/admin-ui/src/user/user-credentials/ResetCredentialDialog.tsx b/js/apps/admin-ui/src/user/user-credentials/ResetCredentialDialog.tsx index ebc3422d32..755556c4a3 100644 --- a/js/apps/admin-ui/src/user/user-credentials/ResetCredentialDialog.tsx +++ b/js/apps/admin-ui/src/user/user-credentials/ResetCredentialDialog.tsx @@ -3,8 +3,7 @@ import { AlertVariant, Form, ModalVariant } from "@patternfly/react-core"; import { isEmpty } from "lodash-es"; import { FormProvider, useForm, useWatch } from "react-hook-form"; import { useTranslation } from "react-i18next"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { useAlerts } from "../../components/alert/Alerts"; import { ConfirmDialogModal } from "../../components/confirm-dialog/ConfirmDialog"; import { LifespanField } from "./LifespanField"; @@ -29,6 +28,8 @@ export const ResetCredentialDialog = ({ userId, onClose, }: ResetCredentialDialogProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const form = useForm({ defaultValues: credResetFormDefaultValues, diff --git a/js/apps/admin-ui/src/user/user-credentials/ResetPasswordDialog.tsx b/js/apps/admin-ui/src/user/user-credentials/ResetPasswordDialog.tsx index 5c30f1eb78..b83d3cb128 100644 --- a/js/apps/admin-ui/src/user/user-credentials/ResetPasswordDialog.tsx +++ b/js/apps/admin-ui/src/user/user-credentials/ResetPasswordDialog.tsx @@ -9,8 +9,7 @@ import { import { FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { FormErrorText, PasswordInput } from "@keycloak/keycloak-ui-shared"; - -import { adminClient } from "../../admin-client"; +import { useAdminClient } from "../../admin-client"; import { DefaultSwitchControl } from "../../components/SwitchControl"; import { useAlerts } from "../../components/alert/Alerts"; import { @@ -46,6 +45,8 @@ export const ResetPasswordDialog = ({ refresh, onClose, }: ResetPasswordDialogProps) => { + const { adminClient } = useAdminClient(); + const { t } = useTranslation(); const form = useForm({ defaultValues: credFormDefaultValues, diff --git a/js/apps/admin-ui/src/utils/useCurrentUser.ts b/js/apps/admin-ui/src/utils/useCurrentUser.ts index eb92777010..044de3f3ac 100644 --- a/js/apps/admin-ui/src/utils/useCurrentUser.ts +++ b/js/apps/admin-ui/src/utils/useCurrentUser.ts @@ -1,11 +1,11 @@ import UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation"; import { useState } from "react"; - -import { adminClient } from "../admin-client"; +import { useAdminClient } from "../admin-client"; import { useWhoAmI } from "../context/whoami/WhoAmI"; import { useFetch } from "./useFetch"; export function useCurrentUser() { + const { adminClient } = useAdminClient(); const { whoAmI } = useWhoAmI(); const [currentUser, setCurrentUser] = useState(); diff --git a/js/libs/ui-shared/src/context/ErrorPage.tsx b/js/libs/ui-shared/src/context/ErrorPage.tsx new file mode 100644 index 0000000000..6d66acedcd --- /dev/null +++ b/js/libs/ui-shared/src/context/ErrorPage.tsx @@ -0,0 +1,60 @@ +import { + Button, + Modal, + ModalVariant, + Page, + Text, + TextContent, + TextVariants, +} from "@patternfly/react-core"; +import { useTranslation } from "react-i18next"; + +type ErrorPageProps = { + error?: unknown; +}; + +export const ErrorPage = (props: ErrorPageProps) => { + const { t } = useTranslation(); + const error = props.error; + const errorMessage = getErrorMessage(error); + + function onRetry() { + location.href = location.origin + location.pathname; + } + + return ( + + + {t("tryAgain")} + , + ]} + > + + {t("somethingWentWrongDescription")} + {errorMessage && ( + {errorMessage} + )} + + + + ); +}; + +function getErrorMessage(error: unknown): string | null { + if (typeof error === "string") { + return error; + } + + if (error instanceof Error) { + return error.message; + } + + return null; +} diff --git a/js/libs/ui-shared/src/context/KeycloakContext.tsx b/js/libs/ui-shared/src/context/KeycloakContext.tsx new file mode 100644 index 0000000000..d19660ff41 --- /dev/null +++ b/js/libs/ui-shared/src/context/KeycloakContext.tsx @@ -0,0 +1,97 @@ +import { Spinner } from "@patternfly/react-core"; +import Keycloak from "keycloak-js"; +import { + PropsWithChildren, + createContext, + useContext, + useEffect, + useMemo, + useRef, + useState, +} from "react"; +import { AlertProvider } from "../alerts/Alerts"; +import { ErrorPage } from "./ErrorPage"; +import { Help } from "./HelpContext"; +import { BaseEnvironment } from "./environment"; + +export type KeycloakContext = + KeycloakContextProps & { + keycloak: Keycloak; + }; + +const createKeycloakEnvContext = () => + createContext | undefined>(undefined); + +let KeycloakEnvContext: any; + +export const useEnvironment = < + T extends BaseEnvironment = BaseEnvironment, +>() => { + const context = useContext>(KeycloakEnvContext); + if (!context) + throw Error( + "no environment provider in the hierarchy make sure to add the provider", + ); + return context; +}; + +interface KeycloakContextProps { + environment: T; +} + +export const KeycloakProvider = ({ + environment, + children, +}: PropsWithChildren>) => { + KeycloakEnvContext = createKeycloakEnvContext(); + const calledOnce = useRef(false); + const [init, setInit] = useState(false); + const [error, setError] = useState(); + const keycloak = useMemo(() => { + const keycloak = new Keycloak({ + url: environment.authUrl, + realm: environment.realm, + clientId: environment.clientId, + }); + + keycloak.onAuthLogout = () => keycloak.login(); + + return keycloak; + }, [environment]); + + useEffect(() => { + // only needed in dev mode + if (calledOnce.current) { + return; + } + + const init = () => + keycloak.init({ + onLoad: "check-sso", + pkceMethod: "S256", + responseMode: "query", + }); + + init() + .then(() => setInit(true)) + .catch((error) => setError(error)); + + calledOnce.current = true; + }, [keycloak]); + + if (error) { + return ; + } + + if (!init) { + return ; + } + + return ( + + + {children} + + + ); +}; diff --git a/js/apps/account-ui/src/environment.ts b/js/libs/ui-shared/src/context/environment.ts similarity index 72% rename from js/apps/account-ui/src/environment.ts rename to js/libs/ui-shared/src/context/environment.ts index 89770744d1..aef682a4ac 100644 --- a/js/apps/account-ui/src/environment.ts +++ b/js/libs/ui-shared/src/context/environment.ts @@ -1,5 +1,4 @@ -import { matchPath } from "react-router-dom"; -import { DEFAULT_REALM, ROOT_PATH } from "./constants"; +export const DEFAULT_REALM = "master"; export type Feature = { isRegistrationEmailAsUsername: boolean; @@ -15,7 +14,7 @@ export type Feature = { isViewGroupsEnabled: boolean; }; -export type Environment = { +export type BaseEnvironment = { /** The URL to the root of the auth server. */ authUrl: string; /** The URL to the root of the account console. */ @@ -30,6 +29,20 @@ export type Environment = { logo: string; /** Indicates the url to be followed when Brand image is clicked */ logoUrl: string; +}; + +export type AdminEnvironment = BaseEnvironment & { + /** The URL to the root of the auth server. */ + authServerUrl: string; + /** The name of the master realm. */ + masterRealm: string; + /** The URL to the base of the Admin UI. */ + consoleBaseUrl: string; + /** The version hash of the auth server. */ + resourceVersion: string; +}; + +export type AccountEnvironment = BaseEnvironment & { /** The locale of the user */ locale: string; /** Feature flags */ @@ -40,18 +53,24 @@ export type Environment = { referrerUrl?: string; }; -// Detect the current realm from the URL. -const match = matchPath(ROOT_PATH, location.pathname); +// During development the realm can be passed as a query parameter when redirecting back from Keycloak. +const realm = + new URLSearchParams(window.location.search).get("realm") || + location.pathname.match("/realms/(.*?)/account")?.[1]; -const defaultEnvironment: Environment = { +const defaultEnvironment: AdminEnvironment & AccountEnvironment = { authUrl: "http://localhost:8180", - baseUrl: `http://localhost:8180/realms/${match?.params.realm ?? DEFAULT_REALM}/account/`, - realm: match?.params.realm ?? DEFAULT_REALM, + authServerUrl: "http://localhost:8180", + baseUrl: `http://localhost:8180/realms/${realm ?? DEFAULT_REALM}/account/`, + realm: realm ?? DEFAULT_REALM, clientId: "security-admin-console-v2", resourceUrl: "http://localhost:8080", logo: "/logo.svg", logoUrl: "/", locale: "en", + consoleBaseUrl: "/admin/master/console/", + masterRealm: "master", + resourceVersion: "unknown", features: { isRegistrationEmailAsUsername: false, isEditUserNameAllowed: true, @@ -68,7 +87,7 @@ const defaultEnvironment: Environment = { }; // Merge the default and injected environment variables together. -const environment: Environment = { +const environment = { ...defaultEnvironment, ...getInjectedEnvironment(), }; diff --git a/js/libs/ui-shared/src/main.ts b/js/libs/ui-shared/src/main.ts index 192313337e..5eee13d93b 100644 --- a/js/libs/ui-shared/src/main.ts +++ b/js/libs/ui-shared/src/main.ts @@ -1,5 +1,17 @@ export { AlertProvider, useAlerts } from "./alerts/Alerts"; +export { ErrorPage } from "./context/ErrorPage"; export { Help, useHelp } from "./context/HelpContext"; +export { + KeycloakProvider, + useEnvironment, + type KeycloakContext, +} from "./context/KeycloakContext"; +export { + environment, + type AccountEnvironment, + type AdminEnvironment, + type Feature, +} from "./context/environment"; export { ContinueCancelModal } from "./continue-cancel/ContinueCancelModal"; export { FormErrorText,