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),
|
MULTI_SITE("Multi-site support", Type.PREVIEW),
|
||||||
|
|
||||||
OFFLINE_SESSION_PRELOADING("Offline session preloading", Type.DEPRECATED),
|
OFFLINE_SESSION_PRELOADING("Offline session preloading", Type.DEPRECATED),
|
||||||
|
|
||||||
HOSTNAME_V1("Hostname Options V1", Type.DEFAULT),
|
HOSTNAME_V1("Hostname Options V1", Type.DEFAULT),
|
||||||
//HOSTNAME_V2("Hostname Options V2", Type.DEFAULT, 2),
|
//HOSTNAME_V2("Hostname Options V2", Type.DEFAULT, 2),
|
||||||
|
|
||||||
|
DECLARATIVE_UI("declarative ui spi", Type.EXPERIMENTAL),
|
||||||
;
|
;
|
||||||
|
|
||||||
private final Type type;
|
private final Type type;
|
||||||
|
|
|
@ -78,6 +78,7 @@ public class ProfileTest {
|
||||||
Profile.Feature.DYNAMIC_SCOPES,
|
Profile.Feature.DYNAMIC_SCOPES,
|
||||||
Profile.Feature.DOCKER,
|
Profile.Feature.DOCKER,
|
||||||
Profile.Feature.MULTI_SITE,
|
Profile.Feature.MULTI_SITE,
|
||||||
|
Profile.Feature.DECLARATIVE_UI,
|
||||||
Profile.Feature.RECOVERY_CODES,
|
Profile.Feature.RECOVERY_CODES,
|
||||||
Profile.Feature.SCRIPTS,
|
Profile.Feature.SCRIPTS,
|
||||||
Profile.Feature.TOKEN_EXCHANGE,
|
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.
|
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
|
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.
|
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) => (
|
const AppContexts = ({ children }: PropsWithChildren) => (
|
||||||
<ErrorBoundaryProvider>
|
<ErrorBoundaryProvider>
|
||||||
|
<ServerInfoProvider>
|
||||||
<RealmsProvider>
|
<RealmsProvider>
|
||||||
<RealmContextProvider>
|
<RealmContextProvider>
|
||||||
<WhoAmIContextProvider>
|
<WhoAmIContextProvider>
|
||||||
|
@ -39,6 +40,7 @@ const AppContexts = ({ children }: PropsWithChildren) => (
|
||||||
</WhoAmIContextProvider>
|
</WhoAmIContextProvider>
|
||||||
</RealmContextProvider>
|
</RealmContextProvider>
|
||||||
</RealmsProvider>
|
</RealmsProvider>
|
||||||
|
</ServerInfoProvider>
|
||||||
</ErrorBoundaryProvider>
|
</ErrorBoundaryProvider>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -53,13 +55,11 @@ export const App = () => {
|
||||||
mainContainerId={mainPageContentId}
|
mainContainerId={mainPageContentId}
|
||||||
>
|
>
|
||||||
<ErrorBoundaryFallback fallback={ErrorRenderer}>
|
<ErrorBoundaryFallback fallback={ErrorRenderer}>
|
||||||
<ServerInfoProvider>
|
|
||||||
<Suspense fallback={<KeycloakSpinner />}>
|
<Suspense fallback={<KeycloakSpinner />}>
|
||||||
<AuthWall>
|
<AuthWall>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</AuthWall>
|
</AuthWall>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</ServerInfoProvider>
|
|
||||||
</ErrorBoundaryFallback>
|
</ErrorBoundaryFallback>
|
||||||
</Page>
|
</Page>
|
||||||
</AppContexts>
|
</AppContexts>
|
||||||
|
|
|
@ -9,23 +9,25 @@ import {
|
||||||
import { FormEvent } from "react";
|
import { FormEvent } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { NavLink, useMatch, useNavigate } from "react-router-dom";
|
import { NavLink, useMatch, useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { RealmSelector } from "./components/realm-selector/RealmSelector";
|
import { RealmSelector } from "./components/realm-selector/RealmSelector";
|
||||||
import { useAccess } from "./context/access/Access";
|
import { useAccess } from "./context/access/Access";
|
||||||
import { useRealm } from "./context/realm-context/RealmContext";
|
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 { AddRealmRoute } from "./realm/routes/AddRealm";
|
||||||
import { routes } from "./routes";
|
import { routes } from "./routes";
|
||||||
|
|
||||||
import "./page-nav.css";
|
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 { t } = useTranslation();
|
||||||
const { hasAccess } = useAccess();
|
const { hasAccess } = useAccess();
|
||||||
const { realm } = useRealm();
|
const { realm } = useRealm();
|
||||||
const route = routes.find(
|
const route = routes.find(
|
||||||
(route) => route.path.replace(/\/:.+?(\?|(?:(?!\/).)*|$)/g, "") === path,
|
(route) =>
|
||||||
|
route.path.replace(/\/:.+?(\?|(?:(?!\/).)*|$)/g, "") === (id || path),
|
||||||
);
|
);
|
||||||
|
|
||||||
const accessAllowed =
|
const accessAllowed =
|
||||||
|
@ -56,6 +58,9 @@ const LeftNav = ({ title, path }: LeftNavProps) => {
|
||||||
export const PageNav = () => {
|
export const PageNav = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { hasSomeAccess } = useAccess();
|
const { hasSomeAccess } = useAccess();
|
||||||
|
const { componentTypes } = useServerInfo();
|
||||||
|
const pages =
|
||||||
|
componentTypes?.["org.keycloak.services.ui.extend.UiPageProvider"];
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
@ -116,6 +121,14 @@ export const PageNav = () => {
|
||||||
<LeftNav title="authentication" path="/authentication" />
|
<LeftNav title="authentication" path="/authentication" />
|
||||||
<LeftNav title="identityProviders" path="/identity-providers" />
|
<LeftNav title="identityProviders" path="/identity-providers" />
|
||||||
<LeftNav title="userFederation" path="/user-federation" />
|
<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>
|
</NavGroup>
|
||||||
)}
|
)}
|
||||||
</Nav>
|
</Nav>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
Tab,
|
||||||
TabProps,
|
TabProps,
|
||||||
Tabs,
|
Tabs,
|
||||||
TabsComponent,
|
TabsComponent,
|
||||||
|
@ -6,11 +7,22 @@ import {
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
import {
|
import {
|
||||||
Children,
|
Children,
|
||||||
isValidElement,
|
|
||||||
JSXElementConstructor,
|
JSXElementConstructor,
|
||||||
|
PropsWithChildren,
|
||||||
ReactElement,
|
ReactElement,
|
||||||
|
isValidElement,
|
||||||
} from "react";
|
} 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:
|
// TODO: Remove the custom 'children' props and type once the following issue has been resolved:
|
||||||
// https://github.com/patternfly/patternfly-react/issues/6766
|
// https://github.com/patternfly/patternfly-react/issues/6766
|
||||||
|
@ -32,14 +44,31 @@ export const RoutableTabs = ({
|
||||||
...otherProps
|
...otherProps
|
||||||
}: RoutableTabsProps) => {
|
}: RoutableTabsProps) => {
|
||||||
const { pathname } = useLocation();
|
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)
|
const eventKeys = Children.toArray(children)
|
||||||
.filter((child): child is ChildElement => isValidElement(child))
|
.filter((child): child is ChildElement => isValidElement(child))
|
||||||
.map((child) => child.props.eventKey.toString());
|
.map((child) => child.props.eventKey.toString());
|
||||||
|
|
||||||
|
const allKeys = [...eventKeys, ...matchedTabsKeys];
|
||||||
|
|
||||||
// Determine if there is an exact match.
|
// Determine if there is an exact match.
|
||||||
const exactMatch = eventKeys.find(
|
const exactMatch = allKeys.find(
|
||||||
(eventKey) => eventKey === decodeURI(pathname),
|
(eventKey) => eventKey === decodeURI(pathname),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -63,10 +92,33 @@ export const RoutableTabs = ({
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
{matchedTabs.map((t) => (
|
||||||
|
<DynamicTab key={t.id} eventKey={t.pathname} title={t.id}>
|
||||||
|
<PageHandler page={t} providerType={TAB_PROVIDER} />
|
||||||
|
</DynamicTab>
|
||||||
|
))}
|
||||||
</Tabs>
|
</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>) => ({
|
export const useRoutableTab = (to: Partial<Path>) => ({
|
||||||
eventKey: to.pathname ?? "",
|
eventKey: to.pathname ?? "",
|
||||||
href: useHref(to),
|
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 sessionRoutes from "./sessions/routes";
|
||||||
import userFederationRoutes from "./user-federation/routes";
|
import userFederationRoutes from "./user-federation/routes";
|
||||||
import userRoutes from "./user/routes";
|
import userRoutes from "./user/routes";
|
||||||
|
import pageRoutes from "./page/routes";
|
||||||
|
|
||||||
export type AppRouteObjectHandle = {
|
export type AppRouteObjectHandle = {
|
||||||
access: AccessType | AccessType[];
|
access: AccessType | AccessType[];
|
||||||
|
@ -51,6 +52,7 @@ export const routes: AppRouteObject[] = [
|
||||||
...userRoutes,
|
...userRoutes,
|
||||||
...groupsRoutes,
|
...groupsRoutes,
|
||||||
...dashboardRoutes,
|
...dashboardRoutes,
|
||||||
|
...pageRoutes,
|
||||||
NotFoundRoute,
|
NotFoundRoute,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -64,20 +64,21 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--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],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation[:v1], js-adapter
|
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||||
[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||||
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||||
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||||
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
authorization, ciba, client-policies, client-secret-rotation,
|
||||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||||
transient-users, update-email, web-authn.
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
|
web-authn.
|
||||||
|
|
||||||
HTTP(S):
|
HTTP(S):
|
||||||
|
|
||||||
|
|
|
@ -64,20 +64,21 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--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],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation[:v1], js-adapter
|
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||||
[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||||
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||||
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||||
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
authorization, ciba, client-policies, client-secret-rotation,
|
||||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||||
transient-users, update-email, web-authn.
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
|
web-authn.
|
||||||
|
|
||||||
HTTP(S):
|
HTTP(S):
|
||||||
|
|
||||||
|
|
|
@ -59,20 +59,21 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--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],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation[:v1], js-adapter
|
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||||
[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||||
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||||
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||||
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
authorization, ciba, client-policies, client-secret-rotation,
|
||||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||||
transient-users, update-email, web-authn.
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
|
web-authn.
|
||||||
|
|
||||||
Config:
|
Config:
|
||||||
|
|
||||||
|
|
|
@ -59,20 +59,21 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--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],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation[:v1], js-adapter
|
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||||
[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||||
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||||
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||||
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
authorization, ciba, client-policies, client-secret-rotation,
|
||||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||||
transient-users, update-email, web-authn.
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
|
web-authn.
|
||||||
|
|
||||||
Config:
|
Config:
|
||||||
|
|
||||||
|
|
|
@ -59,20 +59,21 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--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],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation[:v1], js-adapter
|
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||||
[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||||
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||||
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||||
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
authorization, ciba, client-policies, client-secret-rotation,
|
||||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||||
transient-users, update-email, web-authn.
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
|
web-authn.
|
||||||
|
|
||||||
Config:
|
Config:
|
||||||
|
|
||||||
|
|
|
@ -59,20 +59,21 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--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],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation[:v1], js-adapter
|
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||||
[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||||
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||||
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||||
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
authorization, ciba, client-policies, client-secret-rotation,
|
||||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||||
transient-users, update-email, web-authn.
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
|
web-authn.
|
||||||
|
|
||||||
Config:
|
Config:
|
||||||
|
|
||||||
|
|
|
@ -89,20 +89,21 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--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],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation[:v1], js-adapter
|
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||||
[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||||
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||||
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||||
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
authorization, ciba, client-policies, client-secret-rotation,
|
||||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||||
transient-users, update-email, web-authn.
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
|
web-authn.
|
||||||
|
|
||||||
Hostname:
|
Hostname:
|
||||||
|
|
||||||
|
|
|
@ -89,20 +89,21 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--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],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1],impersonation[:v1], js-adapter[:v1], kerberos
|
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||||
[:v1], linkedin-oauth[:v1], multi-site[:v1], offline-session-preloading[:
|
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||||
v1], par[:v1], preview, recovery-codes[:v1], scripts[:v1],
|
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||||
step-up-authentication[:v1], token-exchange[:v1], transient-users[:v1],
|
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||||
update-email[:v1], web-authn[:v1].
|
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
authorization, ciba, client-policies, client-secret-rotation,
|
||||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||||
transient-users, update-email, web-authn.
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
|
web-authn.
|
||||||
|
|
||||||
Hostname:
|
Hostname:
|
||||||
|
|
||||||
|
|
|
@ -89,20 +89,21 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--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],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation[:v1], js-adapter
|
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||||
[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||||
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||||
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||||
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
authorization, ciba, client-policies, client-secret-rotation,
|
||||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||||
transient-users, update-email, web-authn.
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
|
web-authn.
|
||||||
|
|
||||||
Hostname:
|
Hostname:
|
||||||
|
|
||||||
|
|
|
@ -89,20 +89,21 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--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],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1],impersonation[:v1], js-adapter[:v1], kerberos
|
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||||
[:v1], linkedin-oauth[:v1], multi-site[:v1], offline-session-preloading[:
|
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||||
v1], par[:v1], preview, recovery-codes[:v1], scripts[:v1],
|
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||||
step-up-authentication[:v1], token-exchange[:v1], transient-users[:v1],
|
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||||
update-email[:v1], web-authn[:v1].
|
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
authorization, ciba, client-policies, client-secret-rotation,
|
||||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||||
transient-users, update-email, web-authn.
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
|
web-authn.
|
||||||
|
|
||||||
Hostname:
|
Hostname:
|
||||||
|
|
||||||
|
|
|
@ -90,20 +90,21 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--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],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation[:v1], js-adapter
|
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||||
[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||||
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||||
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||||
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
authorization, ciba, client-policies, client-secret-rotation,
|
||||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||||
transient-users, update-email, web-authn.
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
|
web-authn.
|
||||||
|
|
||||||
Hostname:
|
Hostname:
|
||||||
|
|
||||||
|
|
|
@ -90,20 +90,21 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--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],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1],impersonation[:v1], js-adapter[:v1], kerberos
|
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||||
[:v1], linkedin-oauth[:v1], multi-site[:v1], offline-session-preloading[:
|
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||||
v1], par[:v1], preview, recovery-codes[:v1], scripts[:v1],
|
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||||
step-up-authentication[:v1], token-exchange[:v1], transient-users[:v1],
|
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||||
update-email[:v1], web-authn[:v1].
|
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
authorization, ciba, client-policies, client-secret-rotation,
|
||||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||||
transient-users, update-email, web-authn.
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
|
web-authn.
|
||||||
|
|
||||||
Hostname:
|
Hostname:
|
||||||
|
|
||||||
|
|
|
@ -90,20 +90,21 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--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],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation[:v1], js-adapter
|
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||||
[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||||
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||||
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||||
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
authorization, ciba, client-policies, client-secret-rotation,
|
||||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||||
transient-users, update-email, web-authn.
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
|
web-authn.
|
||||||
|
|
||||||
Hostname:
|
Hostname:
|
||||||
|
|
||||||
|
|
|
@ -90,20 +90,21 @@ Feature:
|
||||||
--features <feature> Enables a set of one or more features. Possible values are: account-api[:v1],
|
--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],
|
account2[:v1], account3[:v1], admin-api[:v1], admin-fine-grained-authz[:v1],
|
||||||
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
admin2[:v1], authorization[:v1], ciba[:v1], client-policies[:v1],
|
||||||
client-secret-rotation[:v1], device-flow[:v1], docker[:v1], dpop[:v1],
|
client-secret-rotation[:v1], declarative-ui[:v1], device-flow[:v1], docker[:
|
||||||
dynamic-scopes[:v1], fips[:v1], hostname[:v1],impersonation[:v1], js-adapter[:v1], kerberos
|
v1], dpop[:v1], dynamic-scopes[:v1], fips[:v1], hostname[:v1], impersonation
|
||||||
[:v1], linkedin-oauth[:v1], multi-site[:v1], offline-session-preloading[:
|
[:v1], js-adapter[:v1], kerberos[:v1], linkedin-oauth[:v1], multi-site[:v1],
|
||||||
v1], par[:v1], preview, recovery-codes[:v1], scripts[:v1],
|
offline-session-preloading[:v1], par[:v1], preview, recovery-codes[:v1],
|
||||||
step-up-authentication[:v1], token-exchange[:v1], transient-users[:v1],
|
scripts[:v1], step-up-authentication[:v1], token-exchange[:v1],
|
||||||
update-email[:v1], web-authn[:v1].
|
transient-users[:v1], update-email[:v1], web-authn[:v1].
|
||||||
--features-disabled <feature>
|
--features-disabled <feature>
|
||||||
Disables a set of one or more features. Possible values are: account-api,
|
Disables a set of one or more features. Possible values are: account-api,
|
||||||
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
account2, account3, admin-api, admin-fine-grained-authz, admin2,
|
||||||
authorization, ciba, client-policies, client-secret-rotation, device-flow,
|
authorization, ciba, client-policies, client-secret-rotation,
|
||||||
docker, dpop, dynamic-scopes, fips, impersonation, js-adapter, kerberos,
|
declarative-ui, device-flow, docker, dpop, dynamic-scopes, fips,
|
||||||
linkedin-oauth, multi-site, offline-session-preloading, par, preview,
|
impersonation, js-adapter, kerberos, linkedin-oauth, multi-site,
|
||||||
recovery-codes, scripts, step-up-authentication, token-exchange,
|
offline-session-preloading, par, preview, recovery-codes, scripts,
|
||||||
transient-users, update-email, web-authn.
|
step-up-authentication, token-exchange, transient-users, update-email,
|
||||||
|
web-authn.
|
||||||
|
|
||||||
Hostname:
|
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.managers.BruteForceProtectorSpi
|
||||||
org.keycloak.services.resource.AccountResourceSpi
|
org.keycloak.services.resource.AccountResourceSpi
|
||||||
org.keycloak.services.resource.RealmResourceSPI
|
org.keycloak.services.resource.RealmResourceSPI
|
||||||
|
org.keycloak.services.ui.extend.UiPageSpi
|
||||||
|
org.keycloak.services.ui.extend.UiTabSpi
|
||||||
org.keycloak.sessions.AuthenticationSessionSpi
|
org.keycloak.sessions.AuthenticationSessionSpi
|
||||||
org.keycloak.sessions.StickySessionEncoderSpi
|
org.keycloak.sessions.StickySessionEncoderSpi
|
||||||
org.keycloak.protocol.ClientInstallationSpi
|
org.keycloak.protocol.ClientInstallationSpi
|
||||||
|
|
Loading…
Reference in a new issue