Split realm context into two (#1523)
This commit is contained in:
parent
2b45a83109
commit
d7362a97a0
8 changed files with 96 additions and 68 deletions
|
@ -15,6 +15,7 @@ import { routes, RouteDef } from "./route-config";
|
||||||
import { PageBreadCrumbs } from "./components/bread-crumb/PageBreadCrumbs";
|
import { PageBreadCrumbs } from "./components/bread-crumb/PageBreadCrumbs";
|
||||||
import { ForbiddenSection } from "./ForbiddenSection";
|
import { ForbiddenSection } from "./ForbiddenSection";
|
||||||
import { SubGroups } from "./groups/SubGroupsContext";
|
import { SubGroups } from "./groups/SubGroupsContext";
|
||||||
|
import { RealmsProvider } from "./context/RealmsContext";
|
||||||
import { RealmContextProvider } from "./context/realm-context/RealmContext";
|
import { RealmContextProvider } from "./context/realm-context/RealmContext";
|
||||||
import { ErrorRenderer } from "./components/error/ErrorRenderer";
|
import { ErrorRenderer } from "./components/error/ErrorRenderer";
|
||||||
import { AdminClient } from "./context/auth/AdminClient";
|
import { AdminClient } from "./context/auth/AdminClient";
|
||||||
|
@ -34,6 +35,7 @@ const AppContexts: FunctionComponent<AdminClientProps> = ({
|
||||||
<Router>
|
<Router>
|
||||||
<AdminClient.Provider value={adminClient}>
|
<AdminClient.Provider value={adminClient}>
|
||||||
<WhoAmIContextProvider>
|
<WhoAmIContextProvider>
|
||||||
|
<RealmsProvider>
|
||||||
<RealmContextProvider>
|
<RealmContextProvider>
|
||||||
<AccessContextProvider>
|
<AccessContextProvider>
|
||||||
<Help>
|
<Help>
|
||||||
|
@ -45,6 +47,7 @@ const AppContexts: FunctionComponent<AdminClientProps> = ({
|
||||||
</Help>
|
</Help>
|
||||||
</AccessContextProvider>
|
</AccessContextProvider>
|
||||||
</RealmContextProvider>
|
</RealmContextProvider>
|
||||||
|
</RealmsProvider>
|
||||||
</WhoAmIContextProvider>
|
</WhoAmIContextProvider>
|
||||||
</AdminClient.Provider>
|
</AdminClient.Provider>
|
||||||
</Router>
|
</Router>
|
||||||
|
|
|
@ -47,13 +47,7 @@ export const MockAdminClient: FunctionComponent<{ mock?: object }> = (
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<WhoAmIContextProvider>
|
<WhoAmIContextProvider>
|
||||||
<RealmContext.Provider
|
<RealmContext.Provider value={{ realm: "master" }}>
|
||||||
value={{
|
|
||||||
realm: "master",
|
|
||||||
realms: [],
|
|
||||||
refresh: () => Promise.resolve(),
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<AccessContextProvider>{props.children}</AccessContextProvider>
|
<AccessContextProvider>{props.children}</AccessContextProvider>
|
||||||
</RealmContext.Provider>
|
</RealmContext.Provider>
|
||||||
</WhoAmIContextProvider>
|
</WhoAmIContextProvider>
|
||||||
|
|
|
@ -22,13 +22,7 @@ describe("FormAccess", () => {
|
||||||
whoAmI: new WhoAmI(whoami as WhoAmIRepresentation),
|
whoAmI: new WhoAmI(whoami as WhoAmIRepresentation),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<RealmContext.Provider
|
<RealmContext.Provider value={{ realm }}>
|
||||||
value={{
|
|
||||||
realm,
|
|
||||||
realms: [],
|
|
||||||
refresh: () => Promise.resolve(),
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<AccessContextProvider>
|
<AccessContextProvider>
|
||||||
<FormAccess role="manage-clients">
|
<FormAccess role="manage-clients">
|
||||||
<FormGroup label="test" fieldId="field">
|
<FormGroup label="test" fieldId="field">
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { useTranslation } from "react-i18next";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
|
|
||||||
import { useRealm } from "../../context/realm-context/RealmContext";
|
import { useRealm } from "../../context/realm-context/RealmContext";
|
||||||
|
import { useRealms } from "../../context/RealmsContext";
|
||||||
import { useWhoAmI } from "../../context/whoami/WhoAmI";
|
import { useWhoAmI } from "../../context/whoami/WhoAmI";
|
||||||
import { toDashboard } from "../../dashboard/routes/Dashboard";
|
import { toDashboard } from "../../dashboard/routes/Dashboard";
|
||||||
import { toAddRealm } from "../../realm/routes/AddRealm";
|
import { toAddRealm } from "../../realm/routes/AddRealm";
|
||||||
|
@ -25,7 +26,8 @@ import { RecentUsed } from "./recent-used";
|
||||||
import "./realm-selector.css";
|
import "./realm-selector.css";
|
||||||
|
|
||||||
export const RealmSelector = () => {
|
export const RealmSelector = () => {
|
||||||
const { realm, realms } = useRealm();
|
const { realm } = useRealm();
|
||||||
|
const { realms } = useRealms();
|
||||||
const { whoAmI } = useWhoAmI();
|
const { whoAmI } = useWhoAmI();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
|
|
58
src/context/RealmsContext.tsx
Normal file
58
src/context/RealmsContext.tsx
Normal 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);
|
|
@ -1,6 +1,4 @@
|
||||||
import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
|
import React, { FunctionComponent, useEffect, useMemo } from "react";
|
||||||
import _ from "lodash";
|
|
||||||
import React, { FunctionComponent, useEffect, useMemo, useState } from "react";
|
|
||||||
import { useRouteMatch } from "react-router-dom";
|
import { useRouteMatch } from "react-router-dom";
|
||||||
import { RecentUsed } from "../../components/realm-selector/recent-used";
|
import { RecentUsed } from "../../components/realm-selector/recent-used";
|
||||||
import {
|
import {
|
||||||
|
@ -9,12 +7,10 @@ import {
|
||||||
} from "../../dashboard/routes/Dashboard";
|
} from "../../dashboard/routes/Dashboard";
|
||||||
import environment from "../../environment";
|
import environment from "../../environment";
|
||||||
import useRequiredContext from "../../utils/useRequiredContext";
|
import useRequiredContext from "../../utils/useRequiredContext";
|
||||||
import { useAdminClient, useFetch } from "../auth/AdminClient";
|
import { useAdminClient } from "../auth/AdminClient";
|
||||||
|
|
||||||
type RealmContextType = {
|
type RealmContextType = {
|
||||||
realm: string;
|
realm: string;
|
||||||
realms: RealmRepresentation[];
|
|
||||||
refresh: () => Promise<void>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RealmContext = React.createContext<RealmContextType | undefined>(
|
export const RealmContext = React.createContext<RealmContextType | undefined>(
|
||||||
|
@ -22,6 +18,8 @@ export const RealmContext = React.createContext<RealmContextType | undefined>(
|
||||||
);
|
);
|
||||||
|
|
||||||
export const RealmContextProvider: FunctionComponent = ({ children }) => {
|
export const RealmContextProvider: FunctionComponent = ({ children }) => {
|
||||||
|
const adminClient = useAdminClient();
|
||||||
|
const recentUsed = useMemo(() => new RecentUsed(), []);
|
||||||
const routeMatch = useRouteMatch<DashboardParams>(DashboardRoute.path);
|
const routeMatch = useRouteMatch<DashboardParams>(DashboardRoute.path);
|
||||||
const realmParam = routeMatch?.params.realm;
|
const realmParam = routeMatch?.params.realm;
|
||||||
const realm = useMemo(
|
const realm = useMemo(
|
||||||
|
@ -29,43 +27,16 @@ export const RealmContextProvider: FunctionComponent = ({ children }) => {
|
||||||
[realmParam]
|
[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.
|
// Configure admin client to use selected realm when it changes.
|
||||||
useEffect(() => adminClient.setConfig({ realmName: realm }), [realm]);
|
useEffect(() => adminClient.setConfig({ realmName: realm }), [realm]);
|
||||||
|
|
||||||
// Keep track of recently used realms when selected realm changes.
|
// Keep track of recently used realms when selected realm changes.
|
||||||
useEffect(() => recentUsed.setRecentUsed(realm), [realm]);
|
useEffect(() => recentUsed.setRecentUsed(realm), [realm]);
|
||||||
|
|
||||||
|
const value = useMemo(() => ({ realm }), [realm]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RealmContext.Provider
|
<RealmContext.Provider value={value}>{children}</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>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/de
|
||||||
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
||||||
import { KeycloakTabs } from "../components/keycloak-tabs/KeycloakTabs";
|
import { KeycloakTabs } from "../components/keycloak-tabs/KeycloakTabs";
|
||||||
import { useRealm } from "../context/realm-context/RealmContext";
|
import { useRealm } from "../context/realm-context/RealmContext";
|
||||||
|
import { useRealms } from "../context/RealmsContext";
|
||||||
import { ViewHeader } from "../components/view-header/ViewHeader";
|
import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||||
import { useAdminClient } from "../context/auth/AdminClient";
|
import { useAdminClient } from "../context/auth/AdminClient";
|
||||||
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
||||||
|
@ -48,6 +49,8 @@ import { toRealmSettings } from "./routes/RealmSettings";
|
||||||
import { LocalizationTab } from "./LocalizationTab";
|
import { LocalizationTab } from "./LocalizationTab";
|
||||||
import { HelpItem } from "../components/help-enabler/HelpItem";
|
import { HelpItem } from "../components/help-enabler/HelpItem";
|
||||||
import { DEFAULT_LOCALE } from "../i18n";
|
import { DEFAULT_LOCALE } from "../i18n";
|
||||||
|
import { toDashboard } from "../dashboard/routes/Dashboard";
|
||||||
|
import environment from "../environment";
|
||||||
|
|
||||||
type RealmSettingsHeaderProps = {
|
type RealmSettingsHeaderProps = {
|
||||||
onChange: (value: boolean) => void;
|
onChange: (value: boolean) => void;
|
||||||
|
@ -66,6 +69,7 @@ const RealmSettingsHeader = ({
|
||||||
}: RealmSettingsHeaderProps) => {
|
}: RealmSettingsHeaderProps) => {
|
||||||
const { t } = useTranslation("realm-settings");
|
const { t } = useTranslation("realm-settings");
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
|
const { refresh: refreshRealms } = useRealms();
|
||||||
const { addAlert, addError } = useAlerts();
|
const { addAlert, addError } = useAlerts();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const [partialImportOpen, setPartialImportOpen] = useState(false);
|
const [partialImportOpen, setPartialImportOpen] = useState(false);
|
||||||
|
@ -90,7 +94,8 @@ const RealmSettingsHeader = ({
|
||||||
try {
|
try {
|
||||||
await adminClient.realms.del({ realm: realmName });
|
await adminClient.realms.del({ realm: realmName });
|
||||||
addAlert(t("deletedSuccess"), AlertVariant.success);
|
addAlert(t("deletedSuccess"), AlertVariant.success);
|
||||||
history.push("/master/");
|
await refreshRealms();
|
||||||
|
history.push(toDashboard({ realm: environment.masterRealm }));
|
||||||
refresh();
|
refresh();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
addError("realm-settings:deleteError", error);
|
addError("realm-settings:deleteError", error);
|
||||||
|
@ -163,7 +168,8 @@ export const RealmSettingsTabs = ({
|
||||||
const { t } = useTranslation("realm-settings");
|
const { t } = useTranslation("realm-settings");
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const { addAlert, addError } = useAlerts();
|
const { addAlert, addError } = useAlerts();
|
||||||
const { realm: realmName, refresh: refreshRealm } = useRealm();
|
const { realm: realmName } = useRealm();
|
||||||
|
const { refresh: refreshRealms } = useRealms();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
const kpComponentTypes =
|
const kpComponentTypes =
|
||||||
|
@ -215,7 +221,7 @@ export const RealmSettingsTabs = ({
|
||||||
setupForm(realm);
|
setupForm(realm);
|
||||||
const isRealmRenamed = realmName !== realm.realm;
|
const isRealmRenamed = realmName !== realm.realm;
|
||||||
if (isRealmRenamed) {
|
if (isRealmRenamed) {
|
||||||
await refreshRealm();
|
await refreshRealms();
|
||||||
history.push(toRealmSettings({ realm: realm.realm! }));
|
history.push(toRealmSettings({ realm: realm.realm! }));
|
||||||
}
|
}
|
||||||
addAlert(t("saveSuccess"), AlertVariant.success);
|
addAlert(t("saveSuccess"), AlertVariant.success);
|
||||||
|
|
|
@ -17,7 +17,7 @@ import { FormAccess } from "../../components/form-access/FormAccess";
|
||||||
import { JsonFileUpload } from "../../components/json-file-upload/JsonFileUpload";
|
import { JsonFileUpload } from "../../components/json-file-upload/JsonFileUpload";
|
||||||
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
||||||
import { useAdminClient } from "../../context/auth/AdminClient";
|
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 { useWhoAmI } from "../../context/whoami/WhoAmI";
|
||||||
import { toDashboard } from "../../dashboard/routes/Dashboard";
|
import { toDashboard } from "../../dashboard/routes/Dashboard";
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ export default function NewRealmForm() {
|
||||||
const { t } = useTranslation("realm");
|
const { t } = useTranslation("realm");
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { refresh } = useWhoAmI();
|
const { refresh } = useWhoAmI();
|
||||||
const { refresh: realmRefresh } = useRealm();
|
const { refresh: refreshRealms } = useRealms();
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const { addAlert, addError } = useAlerts();
|
const { addAlert, addError } = useAlerts();
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ export default function NewRealmForm() {
|
||||||
addAlert(t("saveRealmSuccess"), AlertVariant.success);
|
addAlert(t("saveRealmSuccess"), AlertVariant.success);
|
||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
await realmRefresh();
|
await refreshRealms();
|
||||||
history.push(toDashboard({ realm: realm.realm }));
|
history.push(toDashboard({ realm: realm.realm }));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
addError("realm:saveRealmError", error);
|
addError("realm:saveRealmError", error);
|
||||||
|
|
Loading…
Reference in a new issue