108 lines
3.6 KiB
TypeScript
108 lines
3.6 KiB
TypeScript
import type KeycloakAdminClient from "@keycloak/keycloak-admin-client";
|
|
import { Page } from "@patternfly/react-core";
|
|
import type Keycloak from "keycloak-js";
|
|
import { PropsWithChildren, Suspense } from "react";
|
|
import { ErrorBoundary } from "react-error-boundary";
|
|
import { HashRouter as Router, Route, Routes } from "react-router-dom";
|
|
|
|
import { AlertProvider } from "./components/alert/Alerts";
|
|
import { PageBreadCrumbs } from "./components/bread-crumb/PageBreadCrumbs";
|
|
import { ErrorRenderer } from "./components/error/ErrorRenderer";
|
|
import { Help } from "./components/help-enabler/HelpHeader";
|
|
import { KeycloakSpinner } from "./components/keycloak-spinner/KeycloakSpinner";
|
|
import { AccessContextProvider, useAccess } from "./context/access/Access";
|
|
import { AdminClientContext } from "./context/auth/AdminClient";
|
|
import { RealmContextProvider } from "./context/realm-context/RealmContext";
|
|
import { RealmsProvider } from "./context/RealmsContext";
|
|
import { ServerInfoProvider } from "./context/server-info/ServerInfoProvider";
|
|
import { WhoAmIContextProvider } from "./context/whoami/WhoAmI";
|
|
import { ForbiddenSection } from "./ForbiddenSection";
|
|
import { SubGroups } from "./groups/SubGroupsContext";
|
|
import { Header } from "./PageHeader";
|
|
import { PageNav } from "./PageNav";
|
|
import { RouteDef, routes } from "./route-config";
|
|
|
|
export const mainPageContentId = "kc-main-content-page-container";
|
|
|
|
export type AdminClientProps = {
|
|
keycloak: Keycloak;
|
|
adminClient: KeycloakAdminClient;
|
|
};
|
|
|
|
const AppContexts = ({
|
|
children,
|
|
keycloak,
|
|
adminClient,
|
|
}: PropsWithChildren<AdminClientProps>) => (
|
|
<Router>
|
|
<AdminClientContext.Provider value={{ keycloak, adminClient }}>
|
|
<WhoAmIContextProvider>
|
|
<RealmsProvider>
|
|
<RealmContextProvider>
|
|
<AccessContextProvider>
|
|
<Help>
|
|
<AlertProvider>
|
|
<SubGroups>{children}</SubGroups>
|
|
</AlertProvider>
|
|
</Help>
|
|
</AccessContextProvider>
|
|
</RealmContextProvider>
|
|
</RealmsProvider>
|
|
</WhoAmIContextProvider>
|
|
</AdminClientContext.Provider>
|
|
</Router>
|
|
);
|
|
|
|
// If someone tries to go directly to a route they don't
|
|
// have access to, show forbidden page.
|
|
type SecuredRouteProps = { route: RouteDef };
|
|
const SecuredRoute = ({ route }: SecuredRouteProps) => {
|
|
const { hasAccess } = useAccess();
|
|
const accessAllowed =
|
|
route.access instanceof Array
|
|
? hasAccess(...route.access)
|
|
: hasAccess(route.access);
|
|
|
|
if (accessAllowed)
|
|
return (
|
|
<Suspense fallback={<KeycloakSpinner />}>
|
|
<route.component />
|
|
</Suspense>
|
|
);
|
|
|
|
return <ForbiddenSection permissionNeeded={route.access} />;
|
|
};
|
|
|
|
export const App = ({ keycloak, adminClient }: AdminClientProps) => {
|
|
return (
|
|
<AppContexts keycloak={keycloak} adminClient={adminClient}>
|
|
<Page
|
|
header={<Header />}
|
|
isManagedSidebar
|
|
sidebar={<PageNav />}
|
|
breadcrumb={<PageBreadCrumbs />}
|
|
mainContainerId={mainPageContentId}
|
|
>
|
|
<ErrorBoundary
|
|
FallbackComponent={ErrorRenderer}
|
|
onReset={() =>
|
|
(window.location.href =
|
|
window.location.origin + window.location.pathname)
|
|
}
|
|
>
|
|
<ServerInfoProvider>
|
|
<Routes>
|
|
{routes.map((route, i) => (
|
|
<Route
|
|
key={i}
|
|
path={route.path}
|
|
element={<SecuredRoute route={route} />}
|
|
/>
|
|
))}
|
|
</Routes>
|
|
</ServerInfoProvider>
|
|
</ErrorBoundary>
|
|
</Page>
|
|
</AppContexts>
|
|
);
|
|
};
|