Add a way to extend the UI with an Java API (#23772)
* POC to see how we could extend the UI This is very crude and there are still open issues that need to be worked out Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com> * added saving option Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com> * added list and recreate client form Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com> * add tab ui Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com> * integrate tabs Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com> * remove examples Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com> * fixed error messages Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com> * added Feature for ui customization Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com> --------- Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
This commit is contained in:
parent
96c882447d
commit
37790c7956
32 changed files with 729 additions and 155 deletions
|
@ -105,8 +105,11 @@ public class Profile {
|
|||
MULTI_SITE("Multi-site support", Type.PREVIEW),
|
||||
|
||||
OFFLINE_SESSION_PRELOADING("Offline session preloading", Type.DEPRECATED),
|
||||
|
||||
HOSTNAME_V1("Hostname Options V1", Type.DEFAULT),
|
||||
//HOSTNAME_V2("Hostname Options V2", Type.DEFAULT, 2),
|
||||
|
||||
DECLARATIVE_UI("declarative ui spi", Type.EXPERIMENTAL),
|
||||
;
|
||||
|
||||
private final Type type;
|
||||
|
|
|
@ -78,6 +78,7 @@ public class ProfileTest {
|
|||
Profile.Feature.DYNAMIC_SCOPES,
|
||||
Profile.Feature.DOCKER,
|
||||
Profile.Feature.MULTI_SITE,
|
||||
Profile.Feature.DECLARATIVE_UI,
|
||||
Profile.Feature.RECOVERY_CODES,
|
||||
Profile.Feature.SCRIPTS,
|
||||
Profile.Feature.TOKEN_EXCHANGE,
|
||||
|
|
|
@ -2987,3 +2987,15 @@ termsAndConditionsUserAttribute=Terms and conditions accepted timestamp
|
|||
realmOverridesDescription= Realm overrides allow you to specify translations that will take effect for the entire realm. These translations will override any translation specified by a theme.
|
||||
addTranslation=Add translation
|
||||
effectiveMessageBundlesDescription=An effective message bundle is the set of translations for a given language, theme, and theme type. It also takes into account any realm overrides, which will take precedence.
|
||||
clientsClientScopesHelp=The scopes associated with this resource.
|
||||
searchItem=Search item
|
||||
createItem=Create item
|
||||
itemDelete=Delete item
|
||||
itemDeleteConfirm=Are you sure you want to permanently delete the item
|
||||
itemDeleteConfirmTitle=Delete item?
|
||||
itemDeletedSuccess=The item has been deleted
|
||||
itemDeleteError=Could not delete item: {{error}}
|
||||
noItems=There are no items
|
||||
noItemsInstructions=You haven't created any items in this realm. Create a item to get started.
|
||||
itemSaveError=Error could not save item\! {{error}}
|
||||
itemSaveSuccessful=Sucessful saved
|
|
@ -24,6 +24,7 @@ import { AuthWall } from "./root/AuthWall";
|
|||
|
||||
const AppContexts = ({ children }: PropsWithChildren) => (
|
||||
<ErrorBoundaryProvider>
|
||||
<ServerInfoProvider>
|
||||
<RealmsProvider>
|
||||
<RealmContextProvider>
|
||||
<WhoAmIContextProvider>
|
||||
|
@ -39,6 +40,7 @@ const AppContexts = ({ children }: PropsWithChildren) => (
|
|||
</WhoAmIContextProvider>
|
||||
</RealmContextProvider>
|
||||
</RealmsProvider>
|
||||
</ServerInfoProvider>
|
||||
</ErrorBoundaryProvider>
|
||||
);
|
||||
|
||||
|
@ -53,13 +55,11 @@ export const App = () => {
|
|||
mainContainerId={mainPageContentId}
|
||||
>
|
||||
<ErrorBoundaryFallback fallback={ErrorRenderer}>
|
||||
<ServerInfoProvider>
|
||||
<Suspense fallback={<KeycloakSpinner />}>
|
||||
<AuthWall>
|
||||
<Outlet />
|
||||
</AuthWall>
|
||||
</Suspense>
|
||||
</ServerInfoProvider>
|
||||
</ErrorBoundaryFallback>
|
||||
</Page>
|
||||
</AppContexts>
|
||||
|
|
|
@ -9,23 +9,25 @@ import {
|
|||
import { FormEvent } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { NavLink, useMatch, useNavigate } from "react-router-dom";
|
||||
|
||||
import { RealmSelector } from "./components/realm-selector/RealmSelector";
|
||||
import { useAccess } from "./context/access/Access";
|
||||
import { useRealm } from "./context/realm-context/RealmContext";
|
||||
import { useServerInfo } from "./context/server-info/ServerInfoProvider";
|
||||
import { toPage } from "./page/routes";
|
||||
import { AddRealmRoute } from "./realm/routes/AddRealm";
|
||||
import { routes } from "./routes";
|
||||
|
||||
import "./page-nav.css";
|
||||
|
||||
type LeftNavProps = { title: string; path: string };
|
||||
type LeftNavProps = { title: string; path: string; id?: string };
|
||||
|
||||
const LeftNav = ({ title, path }: LeftNavProps) => {
|
||||
const LeftNav = ({ title, path, id }: LeftNavProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { hasAccess } = useAccess();
|
||||
const { realm } = useRealm();
|
||||
const route = routes.find(
|
||||
(route) => route.path.replace(/\/:.+?(\?|(?:(?!\/).)*|$)/g, "") === path,
|
||||
(route) =>
|
||||
route.path.replace(/\/:.+?(\?|(?:(?!\/).)*|$)/g, "") === (id || path),
|
||||
);
|
||||
|
||||
const accessAllowed =
|
||||
|
@ -56,6 +58,9 @@ const LeftNav = ({ title, path }: LeftNavProps) => {
|
|||
export const PageNav = () => {
|
||||
const { t } = useTranslation();
|
||||
const { hasSomeAccess } = useAccess();
|
||||
const { componentTypes } = useServerInfo();
|
||||
const pages =
|
||||
componentTypes?.["org.keycloak.services.ui.extend.UiPageProvider"];
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
|
@ -116,6 +121,14 @@ export const PageNav = () => {
|
|||
<LeftNav title="authentication" path="/authentication" />
|
||||
<LeftNav title="identityProviders" path="/identity-providers" />
|
||||
<LeftNav title="userFederation" path="/user-federation" />
|
||||
{pages?.map((p) => (
|
||||
<LeftNav
|
||||
key={p.id}
|
||||
title={p.id}
|
||||
path={toPage({ providerId: p.id }).pathname!}
|
||||
id="/page-section"
|
||||
/>
|
||||
))}
|
||||
</NavGroup>
|
||||
)}
|
||||
</Nav>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import {
|
||||
Tab,
|
||||
TabProps,
|
||||
Tabs,
|
||||
TabsComponent,
|
||||
|
@ -6,11 +7,22 @@ import {
|
|||
} from "@patternfly/react-core";
|
||||
import {
|
||||
Children,
|
||||
isValidElement,
|
||||
JSXElementConstructor,
|
||||
PropsWithChildren,
|
||||
ReactElement,
|
||||
isValidElement,
|
||||
} from "react";
|
||||
import { Path, useHref, useLocation } from "react-router-dom";
|
||||
import {
|
||||
Path,
|
||||
generatePath,
|
||||
matchPath,
|
||||
useHref,
|
||||
useLocation,
|
||||
useParams,
|
||||
} from "react-router-dom";
|
||||
import { useServerInfo } from "../../context/server-info/ServerInfoProvider";
|
||||
import { PageHandler } from "../../page/PageHandler";
|
||||
import { TAB_PROVIDER } from "../../page/PageList";
|
||||
|
||||
// TODO: Remove the custom 'children' props and type once the following issue has been resolved:
|
||||
// https://github.com/patternfly/patternfly-react/issues/6766
|
||||
|
@ -32,14 +44,31 @@ export const RoutableTabs = ({
|
|||
...otherProps
|
||||
}: RoutableTabsProps) => {
|
||||
const { pathname } = useLocation();
|
||||
const params = useParams();
|
||||
const { componentTypes } = useServerInfo();
|
||||
const tabs = componentTypes?.[TAB_PROVIDER] || [];
|
||||
|
||||
// Extract event keys from children.
|
||||
const matchedTabs = tabs
|
||||
.filter((tab) => matchPath({ path: tab.metadata.path }, pathname))
|
||||
.map((t) => ({
|
||||
...t,
|
||||
pathname: generatePath(t.metadata.path, {
|
||||
...params,
|
||||
...t.metadata.params,
|
||||
}),
|
||||
}));
|
||||
// Extract all keys from matchedTabs
|
||||
const matchedTabsKeys = matchedTabs.map((t) => t.pathname);
|
||||
|
||||
// Extract event keys from children
|
||||
const eventKeys = Children.toArray(children)
|
||||
.filter((child): child is ChildElement => isValidElement(child))
|
||||
.map((child) => child.props.eventKey.toString());
|
||||
|
||||
const allKeys = [...eventKeys, ...matchedTabsKeys];
|
||||
|
||||
// Determine if there is an exact match.
|
||||
const exactMatch = eventKeys.find(
|
||||
const exactMatch = allKeys.find(
|
||||
(eventKey) => eventKey === decodeURI(pathname),
|
||||
);
|
||||
|
||||
|
@ -63,10 +92,33 @@ export const RoutableTabs = ({
|
|||
{...otherProps}
|
||||
>
|
||||
{children}
|
||||
{matchedTabs.map((t) => (
|
||||
<DynamicTab key={t.id} eventKey={t.pathname} title={t.id}>
|
||||
<PageHandler page={t} providerType={TAB_PROVIDER} />
|
||||
</DynamicTab>
|
||||
))}
|
||||
</Tabs>
|
||||
);
|
||||
};
|
||||
|
||||
type DynamicTabProps = {
|
||||
title: string;
|
||||
eventKey: string;
|
||||
};
|
||||
|
||||
const DynamicTab = ({
|
||||
children,
|
||||
...props
|
||||
}: PropsWithChildren<DynamicTabProps>) => {
|
||||
const href = useHref(props.eventKey);
|
||||
|
||||
return (
|
||||
<Tab href={href} {...props}>
|
||||
{children}
|
||||
</Tab>
|
||||
);
|
||||
};
|
||||
|
||||
export const useRoutableTab = (to: Partial<Path>) => ({
|
||||
eventKey: to.pathname ?? "",
|
||||
href: useHref(to),
|
||||
|
|
67
js/apps/admin-ui/src/page/Page.tsx
Normal file
67
js/apps/admin-ui/src/page/Page.tsx
Normal file
|
@ -0,0 +1,67 @@
|
|||
import { ButtonVariant, DropdownItem } from "@patternfly/react-core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { adminClient } from "../admin-client";
|
||||
import { useAlerts } from "../components/alert/Alerts";
|
||||
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
||||
import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||
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 { t } = useTranslation();
|
||||
const { componentTypes } = useServerInfo();
|
||||
const { realm } = useRealm();
|
||||
const pages = componentTypes?.[PAGE_PROVIDER];
|
||||
const navigate = useNavigate();
|
||||
const { id, providerId } = useParams<PageParams>();
|
||||
const { addAlert, addError } = useAlerts();
|
||||
|
||||
const page = pages?.find((p) => p.id === providerId);
|
||||
if (!page) {
|
||||
throw new Error(t("notFound"));
|
||||
}
|
||||
|
||||
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
||||
titleKey: "itemDeleteConfirmTitle",
|
||||
messageKey: "itemDeleteConfirm",
|
||||
continueButtonLabel: "delete",
|
||||
continueButtonVariant: ButtonVariant.danger,
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
await adminClient.components.del({
|
||||
id: id!,
|
||||
});
|
||||
addAlert(t("itemDeletedSuccess"));
|
||||
navigate(toPage({ realm, providerId: providerId! }));
|
||||
} catch (error) {
|
||||
addError("itemSaveError", error);
|
||||
}
|
||||
},
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<DeleteConfirm />
|
||||
<ViewHeader
|
||||
titleKey={id || t("createItem")}
|
||||
dropdownItems={
|
||||
id
|
||||
? [
|
||||
<DropdownItem
|
||||
data-testid="delete-item"
|
||||
key="delete"
|
||||
onClick={() => toggleDeleteDialog()}
|
||||
>
|
||||
{t("delete")}
|
||||
</DropdownItem>,
|
||||
]
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
<PageHandler providerType={PAGE_PROVIDER} id={id} page={page} />
|
||||
</>
|
||||
);
|
||||
}
|
107
js/apps/admin-ui/src/page/PageHandler.tsx
Normal file
107
js/apps/admin-ui/src/page/PageHandler.tsx
Normal file
|
@ -0,0 +1,107 @@
|
|||
import ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
|
||||
import ComponentTypeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentTypeRepresentation";
|
||||
import RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
|
||||
import { ActionGroup, Button, Form, PageSection } from "@patternfly/react-core";
|
||||
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 { useAlerts } from "../components/alert/Alerts";
|
||||
import { DynamicComponents } from "../components/dynamic/DynamicComponents";
|
||||
import { useRealm } from "../context/realm-context/RealmContext";
|
||||
import { useFetch } from "../utils/useFetch";
|
||||
import { PAGE_PROVIDER, TAB_PROVIDER } from "./PageList";
|
||||
import { toPage } from "./routes";
|
||||
|
||||
type PageHandlerProps = {
|
||||
id?: string;
|
||||
providerType: typeof TAB_PROVIDER | typeof PAGE_PROVIDER;
|
||||
page: ComponentTypeRepresentation;
|
||||
};
|
||||
|
||||
export const PageHandler = ({
|
||||
id: idAttribute,
|
||||
providerType,
|
||||
page: { id: providerId, ...page },
|
||||
}: PageHandlerProps) => {
|
||||
const { t } = useTranslation();
|
||||
const form = useForm<ComponentTypeRepresentation>();
|
||||
const { realm: realmName } = useRealm();
|
||||
const [realm, setRealm] = useState<RealmRepresentation>();
|
||||
const { addAlert, addError } = useAlerts();
|
||||
const [id, setId] = useState(idAttribute);
|
||||
|
||||
useFetch(
|
||||
async () =>
|
||||
await Promise.all([
|
||||
adminClient.realms.findOne({ realm: realmName }),
|
||||
id ? adminClient.components.findOne({ id }) : Promise.resolve(),
|
||||
providerType === TAB_PROVIDER
|
||||
? adminClient.components.find({ type: TAB_PROVIDER })
|
||||
: Promise.resolve(),
|
||||
]),
|
||||
([realm, data, tabs]) => {
|
||||
setRealm(realm);
|
||||
const tab = (tabs || []).find((t) => t.providerId === providerId);
|
||||
form.reset(data || tab || {});
|
||||
if (tab) setId(tab.id);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const onSubmit = async (component: ComponentRepresentation) => {
|
||||
if (component.config)
|
||||
Object.entries(component.config).forEach(
|
||||
([key, value]) =>
|
||||
(component.config![key] = Array.isArray(value) ? value : [value]),
|
||||
);
|
||||
try {
|
||||
const updatedComponent = {
|
||||
...component,
|
||||
providerId,
|
||||
providerType,
|
||||
parentId: realm?.id,
|
||||
};
|
||||
if (id) {
|
||||
await adminClient.components.update({ id }, updatedComponent);
|
||||
} else {
|
||||
await adminClient.components.create(updatedComponent);
|
||||
}
|
||||
addAlert("itemSaveSuccessful");
|
||||
} catch (error) {
|
||||
addError("itemSaveError", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<PageSection variant="light">
|
||||
<Form
|
||||
isHorizontal
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="keycloak__form"
|
||||
>
|
||||
<FormProvider {...form}>
|
||||
<DynamicComponents properties={page.properties} />
|
||||
</FormProvider>
|
||||
|
||||
<ActionGroup>
|
||||
<Button data-testid="save" type="submit">
|
||||
{t("save")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="link"
|
||||
component={(props) => (
|
||||
<Link
|
||||
{...props}
|
||||
to={toPage({ realm: realmName, providerId: providerId! })}
|
||||
/>
|
||||
)}
|
||||
>
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
</ActionGroup>
|
||||
</Form>
|
||||
</PageSection>
|
||||
);
|
||||
};
|
140
js/apps/admin-ui/src/page/PageList.tsx
Normal file
140
js/apps/admin-ui/src/page/PageList.tsx
Normal file
|
@ -0,0 +1,140 @@
|
|||
import ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
|
||||
import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
|
||||
import type { ComponentQuery } from "@keycloak/keycloak-admin-client/lib/resources/components";
|
||||
import {
|
||||
Button,
|
||||
ButtonVariant,
|
||||
PageSection,
|
||||
ToolbarItem,
|
||||
} from "@patternfly/react-core";
|
||||
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 { useAlerts } from "../components/alert/Alerts";
|
||||
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
||||
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
|
||||
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
|
||||
import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||
import { useRealm } from "../context/realm-context/RealmContext";
|
||||
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
||||
import { useFetch } from "../utils/useFetch";
|
||||
import { PageListParams, toDetailPage } from "./routes";
|
||||
|
||||
export const PAGE_PROVIDER = "org.keycloak.services.ui.extend.UiPageProvider";
|
||||
export const TAB_PROVIDER = "org.keycloak.services.ui.extend.UiTabProvider";
|
||||
|
||||
const DetailLink = (obj: ComponentRepresentation) => {
|
||||
const { realm } = useRealm();
|
||||
return (
|
||||
<Link
|
||||
key={obj.id}
|
||||
to={toDetailPage({ realm, providerId: obj.providerId!, id: obj.id! })}
|
||||
>
|
||||
{obj.id}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
export default function PageList() {
|
||||
const { t } = useTranslation();
|
||||
const { addAlert, addError } = useAlerts();
|
||||
const navigate = useNavigate();
|
||||
const { providerId } = useParams<PageListParams>();
|
||||
const [key, setKey] = useState(0);
|
||||
const refresh = () => setKey(key + 1);
|
||||
|
||||
const { realm: realmName } = useRealm();
|
||||
const [realm, setRealm] = useState<RealmRepresentation>();
|
||||
const [selectedItem, setSelectedItem] = useState<ComponentRepresentation>();
|
||||
const { componentTypes } = useServerInfo();
|
||||
const pages = componentTypes?.[PAGE_PROVIDER];
|
||||
|
||||
const page = pages?.find((p) => p.id === providerId)!;
|
||||
|
||||
useFetch(
|
||||
async () => adminClient.realms.findOne({ realm: realmName }),
|
||||
setRealm,
|
||||
[],
|
||||
);
|
||||
|
||||
const loader = async () => {
|
||||
const params: ComponentQuery = {
|
||||
parent: realm?.id,
|
||||
type: PAGE_PROVIDER,
|
||||
};
|
||||
return await adminClient.components.find({ ...params });
|
||||
};
|
||||
|
||||
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
||||
titleKey: "itemDeleteConfirmTitle",
|
||||
messageKey: "itemDeleteConfirm",
|
||||
continueButtonLabel: "delete",
|
||||
continueButtonVariant: ButtonVariant.danger,
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
await adminClient.components.del({
|
||||
id: selectedItem!.id!,
|
||||
});
|
||||
addAlert(t("itemDeletedSuccess"));
|
||||
refresh();
|
||||
} catch (error) {
|
||||
addError("itemSaveError", error);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<PageSection variant="light" className="pf-u-p-0">
|
||||
<DeleteConfirm />
|
||||
<ViewHeader titleKey={page.id} subKey={page.helpText} divider={false} />
|
||||
<KeycloakDataTable
|
||||
key={key}
|
||||
toolbarItem={
|
||||
<ToolbarItem>
|
||||
<Button
|
||||
component={(props) => (
|
||||
<Link
|
||||
{...props}
|
||||
to={toDetailPage({ realm: realmName, providerId: page.id })}
|
||||
/>
|
||||
)}
|
||||
>
|
||||
{t("createItem")}
|
||||
</Button>
|
||||
</ToolbarItem>
|
||||
}
|
||||
actionResolver={(item: IRowData) => [
|
||||
{
|
||||
title: t("delete"),
|
||||
onClick() {
|
||||
setSelectedItem(item.data);
|
||||
toggleDeleteDialog();
|
||||
},
|
||||
},
|
||||
]}
|
||||
searchPlaceholderKey="searchItem"
|
||||
loader={loader}
|
||||
columns={[
|
||||
{ name: "id", cellRenderer: DetailLink },
|
||||
...page.properties.slice(0, 3).map((p) => ({
|
||||
name: `config.${p.name}[0]`,
|
||||
displayKey: p.label,
|
||||
})),
|
||||
]}
|
||||
ariaLabelKey="list"
|
||||
emptyState={
|
||||
<ListEmptyState
|
||||
hasIcon
|
||||
message={t("noItems")}
|
||||
instructions={t("noItemsInstructions")}
|
||||
primaryActionText={t("createItem")}
|
||||
onPrimaryAction={() =>
|
||||
navigate(toDetailPage({ realm: realmName, providerId: page.id }))
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</PageSection>
|
||||
);
|
||||
}
|
39
js/apps/admin-ui/src/page/routes.tsx
Normal file
39
js/apps/admin-ui/src/page/routes.tsx
Normal file
|
@ -0,0 +1,39 @@
|
|||
import { Path, generatePath } from "react-router-dom";
|
||||
import type { AppRouteObject } from "../routes";
|
||||
import { lazy } from "react";
|
||||
|
||||
export type PageListParams = { realm?: string; providerId: string };
|
||||
export type PageParams = { realm: string; providerId: string; id?: string };
|
||||
|
||||
const PageList = lazy(() => import("./PageList"));
|
||||
const Page = lazy(() => import("./Page"));
|
||||
|
||||
const PageListRoute: AppRouteObject = {
|
||||
path: "/:realm?/page-section/:providerId",
|
||||
element: <PageList />,
|
||||
breadcrumb: (t) => t("page"),
|
||||
handle: {
|
||||
access: "view-realm",
|
||||
},
|
||||
};
|
||||
|
||||
const PageDetailRoute: AppRouteObject = {
|
||||
path: "/:realm/page/:providerId/:id?",
|
||||
element: <Page />,
|
||||
breadcrumb: (t) => t("page"),
|
||||
handle: {
|
||||
access: "view-realm",
|
||||
},
|
||||
};
|
||||
|
||||
const routes: AppRouteObject[] = [PageListRoute, PageDetailRoute];
|
||||
|
||||
export const toPage = (params: PageListParams): Partial<Path> => ({
|
||||
pathname: generatePath(PageListRoute.path, params),
|
||||
});
|
||||
|
||||
export const toDetailPage = (params: PageParams): Partial<Path> => ({
|
||||
pathname: generatePath(PageDetailRoute.path, params),
|
||||
});
|
||||
|
||||
export default routes;
|
|
@ -18,6 +18,7 @@ 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[];
|
||||
|
@ -51,6 +52,7 @@ export const routes: AppRouteObject[] = [
|
|||
...userRoutes,
|
||||
...groupsRoutes,
|
||||
...dashboardRoutes,
|
||||
...pageRoutes,
|
||||
NotFoundRoute,
|
||||
];
|
||||
|
||||
|
|
|
@ -64,20 +64,21 @@ Feature:
|
|||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation[:v1], js-adapter
|
||||
[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
HTTP(S):
|
||||
|
||||
|
|
|
@ -64,20 +64,21 @@ Feature:
|
|||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation[:v1], js-adapter
|
||||
[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
HTTP(S):
|
||||
|
||||
|
|
|
@ -59,20 +59,21 @@ Feature:
|
|||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation[:v1], js-adapter
|
||||
[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
Config:
|
||||
|
||||
|
|
|
@ -59,20 +59,21 @@ Feature:
|
|||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation[:v1], js-adapter
|
||||
[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
Config:
|
||||
|
||||
|
|
|
@ -59,20 +59,21 @@ Feature:
|
|||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation[:v1], js-adapter
|
||||
[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
Config:
|
||||
|
||||
|
|
|
@ -59,20 +59,21 @@ Feature:
|
|||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation[:v1], js-adapter
|
||||
[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
Config:
|
||||
|
||||
|
|
|
@ -89,20 +89,21 @@ Feature:
|
|||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation[:v1], js-adapter
|
||||
[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
Hostname:
|
||||
|
||||
|
|
|
@ -89,20 +89,21 @@ Feature:
|
|||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1],impersonation[:v1], js-adapter[:v1], kerberos
|
||||
[:v1], linkedin-oauth[:v1], multi-site[:v1], offline-session-preloading[:
|
||||
v1], par[:v1], preview, recovery-codes[:v1], scripts[:v1],
|
||||
step-up-authentication[:v1], token-exchange[:v1], transient-users[:v1],
|
||||
update-email[:v1], web-authn[:v1].
|
||||
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
Hostname:
|
||||
|
||||
|
|
|
@ -89,20 +89,21 @@ Feature:
|
|||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation[:v1], js-adapter
|
||||
[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
Hostname:
|
||||
|
||||
|
|
|
@ -89,20 +89,21 @@ Feature:
|
|||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1],impersonation[:v1], js-adapter[:v1], kerberos
|
||||
[:v1], linkedin-oauth[:v1], multi-site[:v1], offline-session-preloading[:
|
||||
v1], par[:v1], preview, recovery-codes[:v1], scripts[:v1],
|
||||
step-up-authentication[:v1], token-exchange[:v1], transient-users[:v1],
|
||||
update-email[:v1], web-authn[:v1].
|
||||
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
Hostname:
|
||||
|
||||
|
|
|
@ -90,20 +90,21 @@ Feature:
|
|||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation[:v1], js-adapter
|
||||
[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
Hostname:
|
||||
|
||||
|
|
|
@ -90,20 +90,21 @@ Feature:
|
|||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1],impersonation[:v1], js-adapter[:v1], kerberos
|
||||
[:v1], linkedin-oauth[:v1], multi-site[:v1], offline-session-preloading[:
|
||||
v1], par[:v1], preview, recovery-codes[:v1], scripts[:v1],
|
||||
step-up-authentication[:v1], token-exchange[:v1], transient-users[:v1],
|
||||
update-email[:v1], web-authn[:v1].
|
||||
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
Hostname:
|
||||
|
||||
|
|
|
@ -90,20 +90,21 @@ Feature:
|
|||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation[:v1], js-adapter
|
||||
[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
Hostname:
|
||||
|
||||
|
|
|
@ -90,20 +90,21 @@ Feature:
|
|||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
||||
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1],impersonation[:v1], js-adapter[:v1], kerberos
|
||||
[:v1], linkedin-oauth[:v1], multi-site[:v1], offline-session-preloading[:
|
||||
v1], par[:v1], preview, recovery-codes[:v1], scripts[:v1],
|
||||
step-up-authentication[:v1], token-exchange[:v1], transient-users[:v1],
|
||||
update-email[:v1], web-authn[:v1].
|
||||
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||
--features-disabled <feature>
|
||||
Disables a set of one or more features. Possible values are: account-api,
|
||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
||||
transient-users, update-email, web-authn.
|
||||
authorization, ciba, client-policies, client-secret-rotation,
|
||||
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||
step-up-authentication, token-exchange, transient-users, update-email,
|
||||
web-authn.
|
||||
|
||||
Hostname:
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package org.keycloak.services.ui.extend;
|
||||
|
||||
import org.keycloak.provider.ConfiguredProvider;
|
||||
import org.keycloak.provider.Provider;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface UiPageProvider extends Provider, ConfiguredProvider {
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package org.keycloak.services.ui.extend;
|
||||
|
||||
import org.keycloak.component.ComponentFactory;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
public interface UiPageProviderFactory<T> extends ComponentFactory<T, UiPageProvider> {
|
||||
default T create(KeycloakSession session, ComponentModel model) {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package org.keycloak.services.ui.extend;
|
||||
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.Spi;
|
||||
|
||||
public class UiPageSpi implements Spi {
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "ui-page";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Provider> getProviderClass() {
|
||||
return UiPageProvider.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||
return UiPageProviderFactory.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return Profile.isFeatureEnabled(Profile.Feature.DECLARATIVE_UI);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package org.keycloak.services.ui.extend;
|
||||
|
||||
import org.keycloak.provider.ConfiguredProvider;
|
||||
import org.keycloak.provider.Provider;
|
||||
|
||||
public interface UiTabProvider extends Provider, ConfiguredProvider {
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package org.keycloak.services.ui.extend;
|
||||
|
||||
import org.keycloak.component.ComponentFactory;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public interface UiTabProviderFactory<T> extends ComponentFactory<T, UiTabProvider> {
|
||||
default T create(KeycloakSession session, ComponentModel model) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
default Map<String, Object> getTypeMetadata() {
|
||||
Map<String, Object> metadata = new HashMap<>();
|
||||
metadata.put("path", getPath());
|
||||
metadata.put("params", getParams());
|
||||
return metadata;
|
||||
}
|
||||
|
||||
String getPath();
|
||||
|
||||
Map<String, String> getParams();
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package org.keycloak.services.ui.extend;
|
||||
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.Spi;
|
||||
|
||||
public class UiTabSpi implements Spi {
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "ui-tab";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Provider> getProviderClass() {
|
||||
return UiTabProvider.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||
return UiTabProviderFactory.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return Profile.isFeatureEnabled(Profile.Feature.DECLARATIVE_UI);
|
||||
}
|
||||
}
|
|
@ -38,6 +38,8 @@ org.keycloak.scripting.ScriptingSpi
|
|||
org.keycloak.services.managers.BruteForceProtectorSpi
|
||||
org.keycloak.services.resource.AccountResourceSpi
|
||||
org.keycloak.services.resource.RealmResourceSPI
|
||||
org.keycloak.services.ui.extend.UiPageSpi
|
||||
org.keycloak.services.ui.extend.UiTabSpi
|
||||
org.keycloak.sessions.AuthenticationSessionSpi
|
||||
org.keycloak.sessions.StickySessionEncoderSpi
|
||||
org.keycloak.protocol.ClientInstallationSpi
|
||||
|
|
Loading…
Reference in a new issue