Split realm context into two (#1523)

This commit is contained in:
Jon Koops 2021-11-11 17:04:04 +01:00 committed by GitHub
parent 2b45a83109
commit d7362a97a0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 96 additions and 68 deletions

View file

@ -15,6 +15,7 @@ import { routes, RouteDef } from "./route-config";
import { PageBreadCrumbs } from "./components/bread-crumb/PageBreadCrumbs";
import { ForbiddenSection } from "./ForbiddenSection";
import { SubGroups } from "./groups/SubGroupsContext";
import { RealmsProvider } from "./context/RealmsContext";
import { RealmContextProvider } from "./context/realm-context/RealmContext";
import { ErrorRenderer } from "./components/error/ErrorRenderer";
import { AdminClient } from "./context/auth/AdminClient";
@ -34,6 +35,7 @@ const AppContexts: FunctionComponent<AdminClientProps> = ({
<Router>
<AdminClient.Provider value={adminClient}>
<WhoAmIContextProvider>
<RealmsProvider>
<RealmContextProvider>
<AccessContextProvider>
<Help>
@ -45,6 +47,7 @@ const AppContexts: FunctionComponent<AdminClientProps> = ({
</Help>
</AccessContextProvider>
</RealmContextProvider>
</RealmsProvider>
</WhoAmIContextProvider>
</AdminClient.Provider>
</Router>

View file

@ -47,13 +47,7 @@ export const MockAdminClient: FunctionComponent<{ mock?: object }> = (
}
>
<WhoAmIContextProvider>
<RealmContext.Provider
value={{
realm: "master",
realms: [],
refresh: () => Promise.resolve(),
}}
>
<RealmContext.Provider value={{ realm: "master" }}>
<AccessContextProvider>{props.children}</AccessContextProvider>
</RealmContext.Provider>
</WhoAmIContextProvider>

View file

@ -22,13 +22,7 @@ describe("FormAccess", () => {
whoAmI: new WhoAmI(whoami as WhoAmIRepresentation),
}}
>
<RealmContext.Provider
value={{
realm,
realms: [],
refresh: () => Promise.resolve(),
}}
>
<RealmContext.Provider value={{ realm }}>
<AccessContextProvider>
<FormAccess role="manage-clients">
<FormGroup label="test" fieldId="field">

View file

@ -16,6 +16,7 @@ import { useTranslation } from "react-i18next";
import { useHistory } from "react-router-dom";
import { useRealm } from "../../context/realm-context/RealmContext";
import { useRealms } from "../../context/RealmsContext";
import { useWhoAmI } from "../../context/whoami/WhoAmI";
import { toDashboard } from "../../dashboard/routes/Dashboard";
import { toAddRealm } from "../../realm/routes/AddRealm";
@ -25,7 +26,8 @@ import { RecentUsed } from "./recent-used";
import "./realm-selector.css";
export const RealmSelector = () => {
const { realm, realms } = useRealm();
const { realm } = useRealm();
const { realms } = useRealms();
const { whoAmI } = useWhoAmI();
const [open, setOpen] = useState(false);
const [search, setSearch] = useState("");

View file

@ -0,0 +1,58 @@
import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
import { sortBy } from "lodash";
import React, {
createContext,
FunctionComponent,
useCallback,
useMemo,
useState,
} from "react";
import { RecentUsed } from "../components/realm-selector/recent-used";
import useRequiredContext from "../utils/useRequiredContext";
import { useAdminClient, useFetch } from "./auth/AdminClient";
type RealmsContextProps = {
/** A list of all the realms. */
realms: RealmRepresentation[];
/** Refreshes the realms with the latest information. */
refresh: () => Promise<void>;
};
export const RealmsContext = createContext<RealmsContextProps | undefined>(
undefined
);
export const RealmsProvider: FunctionComponent = ({ children }) => {
const adminClient = useAdminClient();
const [realms, setRealms] = useState<RealmRepresentation[]>([]);
const recentUsed = useMemo(() => new RecentUsed(), []);
function updateRealms(realms: RealmRepresentation[]) {
setRealms(sortBy(realms, "realm"));
recentUsed.clean(realms.map(({ realm }) => realm!));
}
useFetch(
() => adminClient.realms.find(),
(realms) => updateRealms(realms),
[]
);
const refresh = useCallback(async () => {
//this is needed otherwise the realm find function will not return
//new or renamed realms because of the cached realms in the token (perhaps?)
await adminClient.keycloak?.updateToken(Number.MAX_VALUE);
updateRealms(await adminClient.realms.find());
}, []);
const value = useMemo<RealmsContextProps>(
() => ({ realms, refresh }),
[realms, refresh]
);
return (
<RealmsContext.Provider value={value}>{children}</RealmsContext.Provider>
);
};
export const useRealms = () => useRequiredContext(RealmsContext);

View file

@ -1,6 +1,4 @@
import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
import _ from "lodash";
import React, { FunctionComponent, useEffect, useMemo, useState } from "react";
import React, { FunctionComponent, useEffect, useMemo } from "react";
import { useRouteMatch } from "react-router-dom";
import { RecentUsed } from "../../components/realm-selector/recent-used";
import {
@ -9,12 +7,10 @@ import {
} from "../../dashboard/routes/Dashboard";
import environment from "../../environment";
import useRequiredContext from "../../utils/useRequiredContext";
import { useAdminClient, useFetch } from "../auth/AdminClient";
import { useAdminClient } from "../auth/AdminClient";
type RealmContextType = {
realm: string;
realms: RealmRepresentation[];
refresh: () => Promise<void>;
};
export const RealmContext = React.createContext<RealmContextType | undefined>(
@ -22,6 +18,8 @@ export const RealmContext = React.createContext<RealmContextType | undefined>(
);
export const RealmContextProvider: FunctionComponent = ({ children }) => {
const adminClient = useAdminClient();
const recentUsed = useMemo(() => new RecentUsed(), []);
const routeMatch = useRouteMatch<DashboardParams>(DashboardRoute.path);
const realmParam = routeMatch?.params.realm;
const realm = useMemo(
@ -29,43 +27,16 @@ export const RealmContextProvider: FunctionComponent = ({ children }) => {
[realmParam]
);
const [realms, setRealms] = useState<RealmRepresentation[]>([]);
const adminClient = useAdminClient();
const recentUsed = new RecentUsed();
const updateRealmsList = (realms: RealmRepresentation[]) => {
setRealms(_.sortBy(realms, "realm"));
recentUsed.clean(realms.map((r) => r.realm!));
};
useFetch(
() => adminClient.realms.find(),
(realms) => updateRealmsList(realms),
[]
);
// Configure admin client to use selected realm when it changes.
useEffect(() => adminClient.setConfig({ realmName: realm }), [realm]);
// Keep track of recently used realms when selected realm changes.
useEffect(() => recentUsed.setRecentUsed(realm), [realm]);
const value = useMemo(() => ({ realm }), [realm]);
return (
<RealmContext.Provider
value={{
realm,
realms,
refresh: async () => {
//this is needed otherwise the realm find function will not return
//new or renamed realms because of the cached realms in the token (perhaps?)
await adminClient.keycloak?.updateToken(Number.MAX_VALUE);
const list = await adminClient.realms.find();
updateRealmsList(list);
},
}}
>
{children}
</RealmContext.Provider>
<RealmContext.Provider value={value}>{children}</RealmContext.Provider>
);
};

View file

@ -19,6 +19,7 @@ import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/de
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
import { KeycloakTabs } from "../components/keycloak-tabs/KeycloakTabs";
import { useRealm } from "../context/realm-context/RealmContext";
import { useRealms } from "../context/RealmsContext";
import { ViewHeader } from "../components/view-header/ViewHeader";
import { useAdminClient } from "../context/auth/AdminClient";
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
@ -48,6 +49,8 @@ import { toRealmSettings } from "./routes/RealmSettings";
import { LocalizationTab } from "./LocalizationTab";
import { HelpItem } from "../components/help-enabler/HelpItem";
import { DEFAULT_LOCALE } from "../i18n";
import { toDashboard } from "../dashboard/routes/Dashboard";
import environment from "../environment";
type RealmSettingsHeaderProps = {
onChange: (value: boolean) => void;
@ -66,6 +69,7 @@ const RealmSettingsHeader = ({
}: RealmSettingsHeaderProps) => {
const { t } = useTranslation("realm-settings");
const adminClient = useAdminClient();
const { refresh: refreshRealms } = useRealms();
const { addAlert, addError } = useAlerts();
const history = useHistory();
const [partialImportOpen, setPartialImportOpen] = useState(false);
@ -90,7 +94,8 @@ const RealmSettingsHeader = ({
try {
await adminClient.realms.del({ realm: realmName });
addAlert(t("deletedSuccess"), AlertVariant.success);
history.push("/master/");
await refreshRealms();
history.push(toDashboard({ realm: environment.masterRealm }));
refresh();
} catch (error) {
addError("realm-settings:deleteError", error);
@ -163,7 +168,8 @@ export const RealmSettingsTabs = ({
const { t } = useTranslation("realm-settings");
const adminClient = useAdminClient();
const { addAlert, addError } = useAlerts();
const { realm: realmName, refresh: refreshRealm } = useRealm();
const { realm: realmName } = useRealm();
const { refresh: refreshRealms } = useRealms();
const history = useHistory();
const kpComponentTypes =
@ -215,7 +221,7 @@ export const RealmSettingsTabs = ({
setupForm(realm);
const isRealmRenamed = realmName !== realm.realm;
if (isRealmRenamed) {
await refreshRealm();
await refreshRealms();
history.push(toRealmSettings({ realm: realm.realm! }));
}
addAlert(t("saveSuccess"), AlertVariant.success);

View file

@ -17,7 +17,7 @@ import { FormAccess } from "../../components/form-access/FormAccess";
import { JsonFileUpload } from "../../components/json-file-upload/JsonFileUpload";
import { ViewHeader } from "../../components/view-header/ViewHeader";
import { useAdminClient } from "../../context/auth/AdminClient";
import { useRealm } from "../../context/realm-context/RealmContext";
import { useRealms } from "../../context/RealmsContext";
import { useWhoAmI } from "../../context/whoami/WhoAmI";
import { toDashboard } from "../../dashboard/routes/Dashboard";
@ -25,7 +25,7 @@ export default function NewRealmForm() {
const { t } = useTranslation("realm");
const history = useHistory();
const { refresh } = useWhoAmI();
const { refresh: realmRefresh } = useRealm();
const { refresh: refreshRealms } = useRealms();
const adminClient = useAdminClient();
const { addAlert, addError } = useAlerts();
@ -45,7 +45,7 @@ export default function NewRealmForm() {
addAlert(t("saveRealmSuccess"), AlertVariant.success);
refresh();
await realmRefresh();
await refreshRealms();
history.push(toDashboard({ realm: realm.realm }));
} catch (error) {
addError("realm:saveRealmError", error);