fix merge conflict
This commit is contained in:
commit
bcffc68fdc
57 changed files with 1086 additions and 495 deletions
|
@ -27,7 +27,8 @@
|
||||||
"react-dom": "^16.8.5",
|
"react-dom": "^16.8.5",
|
||||||
"react-hook-form": "^6.8.2",
|
"react-hook-form": "^6.8.2",
|
||||||
"react-i18next": "^11.7.0",
|
"react-i18next": "^11.7.0",
|
||||||
"react-router-dom": "^5.2.0"
|
"react-router-dom": "^5.2.0",
|
||||||
|
"use-react-router-breadcrumbs": "^1.0.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.10.5",
|
"@babel/core": "^7.10.5",
|
||||||
|
|
88
src/App.tsx
88
src/App.tsx
|
@ -5,92 +5,34 @@ import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
|
||||||
import { Header } from "./PageHeader";
|
import { Header } from "./PageHeader";
|
||||||
import { PageNav } from "./PageNav";
|
import { PageNav } from "./PageNav";
|
||||||
import { Help } from "./components/help-enabler/HelpHeader";
|
import { Help } from "./components/help-enabler/HelpHeader";
|
||||||
import { NewRealmForm } from "./realm/add/NewRealmForm";
|
|
||||||
import { NewClientForm } from "./clients/add/NewClientForm";
|
|
||||||
import { NewClientScopeForm } from "./client-scopes/add/NewClientScopeForm";
|
|
||||||
|
|
||||||
import { ImportForm } from "./clients/import/ImportForm";
|
import { RealmContextProvider } from "./context/realm-context/RealmContext";
|
||||||
import { ClientsSection } from "./clients/ClientsSection";
|
import { WhoAmIContextProvider } from "./context/whoami/WhoAmI";
|
||||||
import { ClientScopesSection } from "./client-scopes/ClientScopesSection";
|
|
||||||
import { RealmRolesSection } from "./realm-roles/RealmRolesSection";
|
|
||||||
import { UsersSection } from "./user/UsersSection";
|
|
||||||
import { GroupsSection } from "./groups/GroupsSection";
|
|
||||||
import { SessionsSection } from "./sessions/SessionsSection";
|
|
||||||
import { EventsSection } from "./events/EventsSection";
|
|
||||||
import { RealmSettingsSection } from "./realm-settings/RealmSettingsSection";
|
|
||||||
import { AuthenticationSection } from "./authentication/AuthenticationSection";
|
|
||||||
import { IdentityProvidersSection } from "./identity-providers/IdentityProvidersSection";
|
|
||||||
import { UserFederationSection } from "./user-federation/UserFederationSection";
|
|
||||||
|
|
||||||
import { PageNotFoundSection } from "./PageNotFoundSection";
|
import { routes } from "./route-config";
|
||||||
import { RealmContextProvider } from "./components/realm-context/RealmContext";
|
import { PageBreadCrumbs } from "./components/bread-crumb/PageBreadCrumbs";
|
||||||
import { ClientSettings } from "./clients/ClientSettings";
|
|
||||||
|
|
||||||
export const App = () => {
|
export const App = () => {
|
||||||
return (
|
return (
|
||||||
<Router>
|
<Router>
|
||||||
|
<WhoAmIContextProvider>
|
||||||
<RealmContextProvider>
|
<RealmContextProvider>
|
||||||
<Help>
|
<Help>
|
||||||
<Page header={<Header />} isManagedSidebar sidebar={<PageNav />}>
|
<Page
|
||||||
|
header={<Header />}
|
||||||
|
isManagedSidebar
|
||||||
|
sidebar={<PageNav />}
|
||||||
|
breadcrumb={<PageBreadCrumbs />}
|
||||||
|
>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route exact path="/add-realm" component={NewRealmForm}></Route>
|
{routes(() => {}).map((route, i) => (
|
||||||
|
<Route key={i} {...route} exact />
|
||||||
<Route exact path="/clients" component={ClientsSection}></Route>
|
))}
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path="/client-settings/:id"
|
|
||||||
component={ClientSettings}
|
|
||||||
></Route>
|
|
||||||
<Route exact path="/add-client" component={NewClientForm}></Route>
|
|
||||||
<Route exact path="/import-client" component={ImportForm}></Route>
|
|
||||||
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path="/client-scopes"
|
|
||||||
component={ClientScopesSection}
|
|
||||||
></Route>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path="/add-client-scopes"
|
|
||||||
component={NewClientScopeForm}
|
|
||||||
></Route>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path="/realm-roles"
|
|
||||||
component={RealmRolesSection}
|
|
||||||
></Route>
|
|
||||||
<Route exact path="/users" component={UsersSection}></Route>
|
|
||||||
<Route exact path="/groups" component={GroupsSection}></Route>
|
|
||||||
<Route exact path="/sessions" component={SessionsSection}></Route>
|
|
||||||
<Route exact path="/events" component={EventsSection}></Route>
|
|
||||||
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path="/realm-settings"
|
|
||||||
component={RealmSettingsSection}
|
|
||||||
></Route>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path="/authentication"
|
|
||||||
component={AuthenticationSection}
|
|
||||||
></Route>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path="/identity-providers"
|
|
||||||
component={IdentityProvidersSection}
|
|
||||||
></Route>
|
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path="/user-federation"
|
|
||||||
component={UserFederationSection}
|
|
||||||
></Route>
|
|
||||||
|
|
||||||
<Route exact path="/" component={ClientsSection} />
|
|
||||||
<Route component={PageNotFoundSection} />
|
|
||||||
</Switch>
|
</Switch>
|
||||||
</Page>
|
</Page>
|
||||||
</Help>
|
</Help>
|
||||||
</RealmContextProvider>
|
</RealmContextProvider>
|
||||||
|
</WhoAmIContextProvider>
|
||||||
</Router>
|
</Router>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,7 +14,8 @@ import {
|
||||||
PageHeaderToolsGroup,
|
PageHeaderToolsGroup,
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
import { HelpIcon } from "@patternfly/react-icons";
|
import { HelpIcon } from "@patternfly/react-icons";
|
||||||
import { KeycloakContext } from "./auth/KeycloakContext";
|
import { KeycloakContext } from "./context/auth/KeycloakContext";
|
||||||
|
import { WhoAmIContext } from "./context/whoami/WhoAmI";
|
||||||
import { HelpHeader } from "./components/help-enabler/HelpHeader";
|
import { HelpHeader } from "./components/help-enabler/HelpHeader";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
|
@ -134,6 +135,7 @@ const KebabDropdown = () => {
|
||||||
|
|
||||||
const UserDropdown = () => {
|
const UserDropdown = () => {
|
||||||
const keycloak = useContext(KeycloakContext);
|
const keycloak = useContext(KeycloakContext);
|
||||||
|
const whoami = useContext(WhoAmIContext);
|
||||||
const [isDropdownOpen, setDropdownOpen] = useState(false);
|
const [isDropdownOpen, setDropdownOpen] = useState(false);
|
||||||
|
|
||||||
const onDropdownToggle = () => {
|
const onDropdownToggle = () => {
|
||||||
|
@ -147,7 +149,7 @@ const UserDropdown = () => {
|
||||||
isOpen={isDropdownOpen}
|
isOpen={isDropdownOpen}
|
||||||
toggle={
|
toggle={
|
||||||
<DropdownToggle onToggle={onDropdownToggle}>
|
<DropdownToggle onToggle={onDropdownToggle}>
|
||||||
{keycloak?.loggedInUser}
|
{whoami.getDisplayName()}
|
||||||
</DropdownToggle>
|
</DropdownToggle>
|
||||||
}
|
}
|
||||||
dropdownItems={userDropdownItems}
|
dropdownItems={userDropdownItems}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
import { RealmSelector } from "./components/realm-selector/RealmSelector";
|
import { RealmSelector } from "./components/realm-selector/RealmSelector";
|
||||||
import { DataLoader } from "./components/data-loader/DataLoader";
|
import { DataLoader } from "./components/data-loader/DataLoader";
|
||||||
import { HttpClientContext } from "./http-service/HttpClientContext";
|
import { HttpClientContext } from "./context/http-service/HttpClientContext";
|
||||||
import { RealmRepresentation } from "./realm/models/Realm";
|
import { RealmRepresentation } from "./realm/models/Realm";
|
||||||
|
|
||||||
export const PageNav: React.FunctionComponent = () => {
|
export const PageNav: React.FunctionComponent = () => {
|
||||||
|
@ -63,7 +63,7 @@ export const PageNav: React.FunctionComponent = () => {
|
||||||
<Nav onSelect={onSelect}>
|
<Nav onSelect={onSelect}>
|
||||||
<NavList>
|
<NavList>
|
||||||
<NavItem className="keycloak__page_nav__nav_item__realm-selector">
|
<NavItem className="keycloak__page_nav__nav_item__realm-selector">
|
||||||
<RealmSelector realmList={realmList || []} />
|
<RealmSelector realmList={realmList.data || []} />
|
||||||
</NavItem>
|
</NavItem>
|
||||||
</NavList>
|
</NavList>
|
||||||
<NavGroup title={t("manage")}>
|
<NavGroup title={t("manage")}>
|
||||||
|
|
|
@ -3,12 +3,13 @@ import { Button, PageSection } from "@patternfly/react-core";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
|
|
||||||
import { RealmContext } from "../components/realm-context/RealmContext";
|
import { RealmContext } from "../context/realm-context/RealmContext";
|
||||||
import { HttpClientContext } from "../http-service/HttpClientContext";
|
import { HttpClientContext } from "../context/http-service/HttpClientContext";
|
||||||
import { ClientRepresentation } from "../realm/models/Realm";
|
import { ClientRepresentation } from "../realm/models/Realm";
|
||||||
import { DataLoader } from "../components/data-loader/DataLoader";
|
import { DataLoader } from "../components/data-loader/DataLoader";
|
||||||
import { TableToolbar } from "../components/table-toolbar/TableToolbar";
|
import { TableToolbar } from "../components/table-toolbar/TableToolbar";
|
||||||
import { ClientScopeList } from "./ClientScopesList";
|
import { ClientScopeList } from "./ClientScopesList";
|
||||||
|
import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||||
|
|
||||||
export const ClientScopesSection = () => {
|
export const ClientScopesSection = () => {
|
||||||
const { t } = useTranslation("client-scopes");
|
const { t } = useTranslation("client-scopes");
|
||||||
|
@ -25,11 +26,16 @@ export const ClientScopesSection = () => {
|
||||||
.then((r) => r.data as ClientRepresentation[]);
|
.then((r) => r.data as ClientRepresentation[]);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<ViewHeader
|
||||||
|
titleKey="clientScopes"
|
||||||
|
subKey="client-scopes:clientScopeExplain"
|
||||||
|
/>
|
||||||
<PageSection variant="light">
|
<PageSection variant="light">
|
||||||
<DataLoader loader={loader}>
|
<DataLoader loader={loader}>
|
||||||
{(scopes) => (
|
{(scopes) => (
|
||||||
<TableToolbar
|
<TableToolbar
|
||||||
count={scopes!.length}
|
count={scopes.data.length}
|
||||||
first={first}
|
first={first}
|
||||||
max={max}
|
max={max}
|
||||||
onNextClick={setFirst}
|
onNextClick={setFirst}
|
||||||
|
@ -44,10 +50,11 @@ export const ClientScopesSection = () => {
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ClientScopeList clientScopes={scopes} />
|
<ClientScopeList clientScopes={scopes.data} />
|
||||||
</TableToolbar>
|
</TableToolbar>
|
||||||
)}
|
)}
|
||||||
</DataLoader>
|
</DataLoader>
|
||||||
</PageSection>
|
</PageSection>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,8 +16,8 @@ import { Controller, useForm } from "react-hook-form";
|
||||||
|
|
||||||
import { ClientScopeRepresentation } from "../models/client-scope";
|
import { ClientScopeRepresentation } from "../models/client-scope";
|
||||||
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
||||||
import { HttpClientContext } from "../../http-service/HttpClientContext";
|
import { HttpClientContext } from "../../context/http-service/HttpClientContext";
|
||||||
import { RealmContext } from "../../components/realm-context/RealmContext";
|
import { RealmContext } from "../../context/realm-context/RealmContext";
|
||||||
import { useAlerts } from "../../components/alert/Alerts";
|
import { useAlerts } from "../../components/alert/Alerts";
|
||||||
|
|
||||||
export const NewClientScopeForm = () => {
|
export const NewClientScopeForm = () => {
|
||||||
|
@ -115,6 +115,7 @@ export const NewClientScopeForm = () => {
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
|
hasNoPaddingTop
|
||||||
label={
|
label={
|
||||||
<>
|
<>
|
||||||
{t("displayOnConsentScreen")}{" "}
|
{t("displayOnConsentScreen")}{" "}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
"client-scopes": {
|
"client-scopes": {
|
||||||
"createClientScope": "Create client scope",
|
"createClientScope": "Create client scope",
|
||||||
"clientScopeList": "List of client scopes",
|
"clientScopeList": "List of client scopes",
|
||||||
|
"clientScopeExplain": "Client scopes allow you to define a common set of protocol mappers and roles, which are shared between multiple clients",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
"protocol": "Protocol",
|
"protocol": "Protocol",
|
||||||
|
|
|
@ -10,16 +10,17 @@ import {
|
||||||
IFormatterValueType,
|
IFormatterValueType,
|
||||||
} from "@patternfly/react-table";
|
} from "@patternfly/react-table";
|
||||||
import { Badge, AlertVariant } from "@patternfly/react-core";
|
import { Badge, AlertVariant } from "@patternfly/react-core";
|
||||||
import FileSaver from "file-saver";
|
|
||||||
|
|
||||||
import { ExternalLink } from "../components/external-link/ExternalLink";
|
import { ExternalLink } from "../components/external-link/ExternalLink";
|
||||||
import { HttpClientContext } from "../http-service/HttpClientContext";
|
import { HttpClientContext } from "../context/http-service/HttpClientContext";
|
||||||
import { useAlerts } from "../components/alert/Alerts";
|
import { useAlerts } from "../components/alert/Alerts";
|
||||||
import { ClientRepresentation } from "./models/client-model";
|
import { ClientRepresentation } from "./models/client-model";
|
||||||
import { RealmContext } from "../components/realm-context/RealmContext";
|
import { RealmContext } from "../context/realm-context/RealmContext";
|
||||||
|
import { exportClient } from "../util";
|
||||||
|
|
||||||
type ClientListProps = {
|
type ClientListProps = {
|
||||||
clients?: ClientRepresentation[];
|
clients?: ClientRepresentation[];
|
||||||
|
refresh: () => void;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -30,23 +31,12 @@ const columns: (keyof ClientRepresentation)[] = [
|
||||||
"baseUrl",
|
"baseUrl",
|
||||||
];
|
];
|
||||||
|
|
||||||
export const ClientList = ({ baseUrl, clients }: ClientListProps) => {
|
export const ClientList = ({ baseUrl, clients, refresh }: ClientListProps) => {
|
||||||
const { t } = useTranslation("clients");
|
const { t } = useTranslation("clients");
|
||||||
const httpClient = useContext(HttpClientContext)!;
|
const httpClient = useContext(HttpClientContext)!;
|
||||||
const { realm } = useContext(RealmContext);
|
const { realm } = useContext(RealmContext);
|
||||||
const [add, Alerts] = useAlerts();
|
const [add, Alerts] = useAlerts();
|
||||||
|
|
||||||
const enabled = (): IFormatter => (data?: IFormatterValueType) => {
|
|
||||||
const field = data!.toString();
|
|
||||||
const [id, clientId, disabled] = field.split("#");
|
|
||||||
return (
|
|
||||||
<Link to={`client-settings/${id}`}>
|
|
||||||
{clientId}
|
|
||||||
{disabled !== "true" && <Badge isRead>Disabled</Badge>}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const emptyFormatter = (): IFormatter => (data?: IFormatterValueType) => {
|
const emptyFormatter = (): IFormatter => (data?: IFormatterValueType) => {
|
||||||
return data ? data : "—";
|
return data ? data : "—";
|
||||||
};
|
};
|
||||||
|
@ -73,12 +63,25 @@ export const ClientList = ({ baseUrl, clients }: ClientListProps) => {
|
||||||
|
|
||||||
const data = clients!
|
const data = clients!
|
||||||
.map((client) => {
|
.map((client) => {
|
||||||
client.clientId = `${client.id}#${client.clientId}#${client.enabled}`;
|
|
||||||
client.baseUrl = replaceBaseUrl(client);
|
client.baseUrl = replaceBaseUrl(client);
|
||||||
return client;
|
return client;
|
||||||
})
|
})
|
||||||
.map((column) => {
|
.map((client) => {
|
||||||
return { cells: columns.map((col) => column[col]), client: column };
|
return {
|
||||||
|
cells: columns.map((col) =>
|
||||||
|
col === "clientId" ? (
|
||||||
|
<>
|
||||||
|
<Link key={client.id} to={`/clients/${client.id}`}>
|
||||||
|
{client.clientId}
|
||||||
|
{!client.enabled && <Badge isRead>Disabled</Badge>}
|
||||||
|
</Link>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
client[col]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
client,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -86,7 +89,7 @@ export const ClientList = ({ baseUrl, clients }: ClientListProps) => {
|
||||||
<Table
|
<Table
|
||||||
variant={TableVariant.compact}
|
variant={TableVariant.compact}
|
||||||
cells={[
|
cells={[
|
||||||
{ title: t("clientID"), cellFormatters: [enabled()] },
|
t("clientID"),
|
||||||
t("type"),
|
t("type"),
|
||||||
{ title: t("description"), cellFormatters: [emptyFormatter()] },
|
{ title: t("description"), cellFormatters: [emptyFormatter()] },
|
||||||
{
|
{
|
||||||
|
@ -99,32 +102,17 @@ export const ClientList = ({ baseUrl, clients }: ClientListProps) => {
|
||||||
{
|
{
|
||||||
title: t("common:export"),
|
title: t("common:export"),
|
||||||
onClick: (_, rowId) => {
|
onClick: (_, rowId) => {
|
||||||
const clientCopy = JSON.parse(JSON.stringify(data[rowId].client));
|
exportClient(data[rowId].client);
|
||||||
const [, orgClientId] = clientCopy.clientId.split("#");
|
|
||||||
clientCopy.clientId = orgClientId;
|
|
||||||
delete clientCopy.id;
|
|
||||||
|
|
||||||
if (clientCopy.protocolMappers) {
|
|
||||||
for (let i = 0; i < clientCopy.protocolMappers.length; i++) {
|
|
||||||
delete clientCopy.protocolMappers[i].id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FileSaver.saveAs(
|
|
||||||
new Blob([JSON.stringify(clientCopy, null, 2)], {
|
|
||||||
type: "application/json",
|
|
||||||
}),
|
|
||||||
clientCopy.clientId + ".json"
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("common:delete"),
|
title: t("common:delete"),
|
||||||
onClick: (_, rowId) => {
|
onClick: async (_, rowId) => {
|
||||||
try {
|
try {
|
||||||
httpClient.doDelete(
|
await httpClient.doDelete(
|
||||||
`/admin/realms/${realm}/clients/${data[rowId].client.id}`
|
`/admin/realms/${realm}/clients/${data[rowId].client.id}`
|
||||||
);
|
);
|
||||||
|
refresh();
|
||||||
add(t("clientDeletedSuccess"), AlertVariant.success);
|
add(t("clientDeletedSuccess"), AlertVariant.success);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
add(`${t("clientDeleteError")} ${error}`, AlertVariant.danger);
|
add(`${t("clientDeleteError")} ${error}`, AlertVariant.danger);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useContext, useEffect } from "react";
|
import React, { useContext, useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
FormGroup,
|
FormGroup,
|
||||||
|
@ -10,6 +10,8 @@ import {
|
||||||
ActionGroup,
|
ActionGroup,
|
||||||
Button,
|
Button,
|
||||||
AlertVariant,
|
AlertVariant,
|
||||||
|
SelectOption,
|
||||||
|
ButtonVariant,
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
@ -17,8 +19,8 @@ import { Controller, useForm } from "react-hook-form";
|
||||||
import { ScrollForm } from "../components/scroll-form/ScrollForm";
|
import { ScrollForm } from "../components/scroll-form/ScrollForm";
|
||||||
import { ClientDescription } from "./ClientDescription";
|
import { ClientDescription } from "./ClientDescription";
|
||||||
import { CapabilityConfig } from "./add/CapabilityConfig";
|
import { CapabilityConfig } from "./add/CapabilityConfig";
|
||||||
import { RealmContext } from "../components/realm-context/RealmContext";
|
import { RealmContext } from "../context/realm-context/RealmContext";
|
||||||
import { HttpClientContext } from "../http-service/HttpClientContext";
|
import { HttpClientContext } from "../context/http-service/HttpClientContext";
|
||||||
import { ClientRepresentation } from "../realm/models/Realm";
|
import { ClientRepresentation } from "../realm/models/Realm";
|
||||||
import {
|
import {
|
||||||
convertToMultiline,
|
convertToMultiline,
|
||||||
|
@ -26,6 +28,9 @@ import {
|
||||||
toValue,
|
toValue,
|
||||||
} from "../components/multi-line-input/MultiLineInput";
|
} from "../components/multi-line-input/MultiLineInput";
|
||||||
import { useAlerts } from "../components/alert/Alerts";
|
import { useAlerts } from "../components/alert/Alerts";
|
||||||
|
import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||||
|
import { exportClient } from "../util";
|
||||||
|
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
||||||
|
|
||||||
export const ClientSettings = () => {
|
export const ClientSettings = () => {
|
||||||
const { t } = useTranslation("clients");
|
const { t } = useTranslation("clients");
|
||||||
|
@ -34,6 +39,7 @@ export const ClientSettings = () => {
|
||||||
const [addAlert, Alerts] = useAlerts();
|
const [addAlert, Alerts] = useAlerts();
|
||||||
|
|
||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>();
|
||||||
|
const [name, setName] = useState("");
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
const url = `/admin/realms/${realm}/clients/${id}`;
|
const url = `/admin/realms/${realm}/clients/${id}`;
|
||||||
|
|
||||||
|
@ -41,6 +47,7 @@ export const ClientSettings = () => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const fetchedClient = await httpClient.doGet<ClientRepresentation>(url);
|
const fetchedClient = await httpClient.doGet<ClientRepresentation>(url);
|
||||||
if (fetchedClient.data) {
|
if (fetchedClient.data) {
|
||||||
|
setName(fetchedClient.data.clientId);
|
||||||
Object.entries(fetchedClient.data).map((entry) => {
|
Object.entries(fetchedClient.data).map((entry) => {
|
||||||
if (entry[0] !== "redirectUris") {
|
if (entry[0] !== "redirectUris") {
|
||||||
form.setValue(entry[0], entry[1]);
|
form.setValue(entry[0], entry[1]);
|
||||||
|
@ -52,6 +59,21 @@ export const ClientSettings = () => {
|
||||||
})();
|
})();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
||||||
|
titleKey: "clients:clientDeleteConfirmTitle",
|
||||||
|
messageKey: "clients:clientDeleteConfirm",
|
||||||
|
continueButtonLabel: "common:delete",
|
||||||
|
continueButtonVariant: ButtonVariant.danger,
|
||||||
|
onConfirm: () => {
|
||||||
|
try {
|
||||||
|
httpClient.doDelete(`/admin/realms/${realm}/clients/${id}`);
|
||||||
|
addAlert(t("clientDeletedSuccess"), AlertVariant.success);
|
||||||
|
} catch (error) {
|
||||||
|
addAlert(`${t("clientDeleteError")} ${error}`, AlertVariant.danger);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const save = async () => {
|
const save = async () => {
|
||||||
if (await form.trigger()) {
|
if (await form.trigger()) {
|
||||||
const redirectUris = toValue(form.getValues()["redirectUris"]);
|
const redirectUris = toValue(form.getValues()["redirectUris"]);
|
||||||
|
@ -65,7 +87,58 @@ export const ClientSettings = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageSection>
|
<>
|
||||||
|
<DeleteConfirm />
|
||||||
|
<Controller
|
||||||
|
name="enabled"
|
||||||
|
control={form.control}
|
||||||
|
defaultValue={true}
|
||||||
|
render={({ onChange, value }) => {
|
||||||
|
const [toggleDisableDialog, DisableConfirm] = useConfirmDialog({
|
||||||
|
titleKey: "clients:disableConfirmTitle",
|
||||||
|
messageKey: "clients:disableConfirm",
|
||||||
|
continueButtonLabel: "common:disable",
|
||||||
|
onConfirm: () => {
|
||||||
|
onChange(!value);
|
||||||
|
save();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DisableConfirm />
|
||||||
|
<ViewHeader
|
||||||
|
titleKey={name}
|
||||||
|
subKey="clients:clientsExplain"
|
||||||
|
selectItems={[
|
||||||
|
<SelectOption key="export" value="export">
|
||||||
|
{t("common:export")}
|
||||||
|
</SelectOption>,
|
||||||
|
<SelectOption key="delete" value="delete">
|
||||||
|
{t("common:delete")}
|
||||||
|
</SelectOption>,
|
||||||
|
]}
|
||||||
|
isEnabled={value}
|
||||||
|
onToggle={(value) => {
|
||||||
|
if (!value) {
|
||||||
|
toggleDisableDialog();
|
||||||
|
} else {
|
||||||
|
onChange(value);
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onSelect={(value) => {
|
||||||
|
if (value === "export") {
|
||||||
|
exportClient(form.getValues());
|
||||||
|
} else if (value === "delete") {
|
||||||
|
toggleDeleteDialog();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<PageSection variant="light">
|
||||||
<Alerts />
|
<Alerts />
|
||||||
<ScrollForm
|
<ScrollForm
|
||||||
sections={[
|
sections={[
|
||||||
|
@ -101,7 +174,11 @@ export const ClientSettings = () => {
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</Form>
|
</Form>
|
||||||
<Form isHorizontal>
|
<Form isHorizontal>
|
||||||
<FormGroup label={t("consentRequired")} fieldId="kc-consent">
|
<FormGroup
|
||||||
|
label={t("consentRequired")}
|
||||||
|
fieldId="kc-consent"
|
||||||
|
hasNoPaddingTop
|
||||||
|
>
|
||||||
<Controller
|
<Controller
|
||||||
name="consentRequired"
|
name="consentRequired"
|
||||||
defaultValue={false}
|
defaultValue={false}
|
||||||
|
@ -120,6 +197,7 @@ export const ClientSettings = () => {
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={t("displayOnClient")}
|
label={t("displayOnClient")}
|
||||||
fieldId="kc-display-on-client"
|
fieldId="kc-display-on-client"
|
||||||
|
hasNoPaddingTop
|
||||||
>
|
>
|
||||||
<Controller
|
<Controller
|
||||||
name="alwaysDisplayInConsole"
|
name="alwaysDisplayInConsole"
|
||||||
|
@ -155,5 +233,6 @@ export const ClientSettings = () => {
|
||||||
</Form>
|
</Form>
|
||||||
</ScrollForm>
|
</ScrollForm>
|
||||||
</PageSection>
|
</PageSection>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
import React, { useState, useContext } from "react";
|
import React, { useState, useContext, useEffect } from "react";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Button, PageSection } from "@patternfly/react-core";
|
import { Button, PageSection, Spinner } from "@patternfly/react-core";
|
||||||
|
|
||||||
import { DataLoader } from "../components/data-loader/DataLoader";
|
|
||||||
import { TableToolbar } from "../components/table-toolbar/TableToolbar";
|
import { TableToolbar } from "../components/table-toolbar/TableToolbar";
|
||||||
import { ClientList } from "./ClientList";
|
import { ClientList } from "./ClientList";
|
||||||
import { HttpClientContext } from "../http-service/HttpClientContext";
|
import { HttpClientContext } from "../context/http-service/HttpClientContext";
|
||||||
import { KeycloakContext } from "../auth/KeycloakContext";
|
import { KeycloakContext } from "../context/auth/KeycloakContext";
|
||||||
import { ClientRepresentation } from "./models/client-model";
|
import { ClientRepresentation } from "./models/client-model";
|
||||||
import { RealmContext } from "../components/realm-context/RealmContext";
|
import { RealmContext } from "../context/realm-context/RealmContext";
|
||||||
import { ViewHeader } from "../components/view-header/ViewHeader";
|
import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||||
|
|
||||||
export const ClientsSection = () => {
|
export const ClientsSection = () => {
|
||||||
|
@ -18,16 +17,29 @@ export const ClientsSection = () => {
|
||||||
|
|
||||||
const [max, setMax] = useState(10);
|
const [max, setMax] = useState(10);
|
||||||
const [first, setFirst] = useState(0);
|
const [first, setFirst] = useState(0);
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
const [clients, setClients] = useState<ClientRepresentation[]>();
|
||||||
const httpClient = useContext(HttpClientContext)!;
|
const httpClient = useContext(HttpClientContext)!;
|
||||||
const keycloak = useContext(KeycloakContext);
|
const keycloak = useContext(KeycloakContext);
|
||||||
const { realm } = useContext(RealmContext);
|
const { realm } = useContext(RealmContext);
|
||||||
|
|
||||||
const loader = async () => {
|
const loader = async () => {
|
||||||
return await httpClient
|
const params: { [name: string]: string | number } = { first, max };
|
||||||
.doGet(`/admin/realms/${realm}/clients`, { params: { first, max } })
|
if (search) {
|
||||||
.then((r) => r.data as ClientRepresentation[]);
|
params.clientId = search;
|
||||||
|
params.search = "true";
|
||||||
|
}
|
||||||
|
const result = await httpClient.doGet<ClientRepresentation[]>(
|
||||||
|
`/admin/realms/${realm}/clients`,
|
||||||
|
{ params: params }
|
||||||
|
);
|
||||||
|
setClients(result.data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loader();
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewHeader
|
<ViewHeader
|
||||||
|
@ -35,8 +47,12 @@ export const ClientsSection = () => {
|
||||||
subKey="clients:clientsExplain"
|
subKey="clients:clientsExplain"
|
||||||
/>
|
/>
|
||||||
<PageSection variant="light">
|
<PageSection variant="light">
|
||||||
<DataLoader loader={loader}>
|
{!clients && (
|
||||||
{(clients) => (
|
<div style={{ textAlign: "center" }}>
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{clients && (
|
||||||
<TableToolbar
|
<TableToolbar
|
||||||
count={clients!.length}
|
count={clients!.length}
|
||||||
first={first}
|
first={first}
|
||||||
|
@ -48,6 +64,8 @@ export const ClientsSection = () => {
|
||||||
setMax(max);
|
setMax(max);
|
||||||
}}
|
}}
|
||||||
inputGroupName="clientsToolbarTextInput"
|
inputGroupName="clientsToolbarTextInput"
|
||||||
|
inputGroupOnChange={setSearch}
|
||||||
|
inputGroupOnClick={() => loader()}
|
||||||
inputGroupPlaceholder={t("Search for client")}
|
inputGroupPlaceholder={t("Search for client")}
|
||||||
toolbarItem={
|
toolbarItem={
|
||||||
<>
|
<>
|
||||||
|
@ -65,11 +83,11 @@ export const ClientsSection = () => {
|
||||||
>
|
>
|
||||||
<ClientList
|
<ClientList
|
||||||
clients={clients}
|
clients={clients}
|
||||||
|
refresh={loader}
|
||||||
baseUrl={keycloak!.authServerUrl()!}
|
baseUrl={keycloak!.authServerUrl()!}
|
||||||
/>
|
/>
|
||||||
</TableToolbar>
|
</TableToolbar>
|
||||||
)}
|
)}
|
||||||
</DataLoader>
|
|
||||||
</PageSection>
|
</PageSection>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,7 +8,11 @@ import { ClientList } from "../ClientList";
|
||||||
test("renders ClientList", () => {
|
test("renders ClientList", () => {
|
||||||
const container = render(
|
const container = render(
|
||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
<ClientList clients={clientMock} baseUrl="http://blog.nerdin.ch" />
|
<ClientList
|
||||||
|
clients={clientMock}
|
||||||
|
baseUrl="http://blog.nerdin.ch"
|
||||||
|
refresh={() => {}}
|
||||||
|
/>
|
||||||
</MemoryRouter>
|
</MemoryRouter>
|
||||||
);
|
);
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
|
|
|
@ -6,7 +6,7 @@ Object {
|
||||||
"baseElement": <body>
|
"baseElement": <body>
|
||||||
<div>
|
<div>
|
||||||
<table
|
<table
|
||||||
aria-label="clientList"
|
aria-label="Client list"
|
||||||
class="pf-c-table pf-m-grid-md pf-m-compact"
|
class="pf-c-table pf-m-grid-md pf-m-compact"
|
||||||
data-ouia-component-id="OUIA-Generated-Table-1"
|
data-ouia-component-id="OUIA-Generated-Table-1"
|
||||||
data-ouia-component-type="PF4/Table"
|
data-ouia-component-type="PF4/Table"
|
||||||
|
@ -20,34 +20,34 @@ Object {
|
||||||
<th
|
<th
|
||||||
class=""
|
class=""
|
||||||
data-key="0"
|
data-key="0"
|
||||||
data-label="clientID"
|
data-label="Client ID"
|
||||||
scope="col"
|
scope="col"
|
||||||
>
|
>
|
||||||
clientID
|
Client ID
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
class=""
|
class=""
|
||||||
data-key="1"
|
data-key="1"
|
||||||
data-label="type"
|
data-label="Type"
|
||||||
scope="col"
|
scope="col"
|
||||||
>
|
>
|
||||||
type
|
Type
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
class=""
|
class=""
|
||||||
data-key="2"
|
data-key="2"
|
||||||
data-label="description"
|
data-label="Description"
|
||||||
scope="col"
|
scope="col"
|
||||||
>
|
>
|
||||||
description
|
Description
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
class=""
|
class=""
|
||||||
data-key="3"
|
data-key="3"
|
||||||
data-label="homeURL"
|
data-label="Home URL"
|
||||||
scope="col"
|
scope="col"
|
||||||
>
|
>
|
||||||
homeURL
|
Home URL
|
||||||
</th>
|
</th>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
|
@ -68,10 +68,10 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="0"
|
data-key="0"
|
||||||
data-label="clientID"
|
data-label="Client ID"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="/client-settings/767756c2-21f8-431c-9f4b-edf30654d653"
|
href="/clients/767756c2-21f8-431c-9f4b-edf30654d653"
|
||||||
>
|
>
|
||||||
account
|
account
|
||||||
</a>
|
</a>
|
||||||
|
@ -79,21 +79,21 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="1"
|
data-key="1"
|
||||||
data-label="type"
|
data-label="Type"
|
||||||
>
|
>
|
||||||
openid-connect
|
openid-connect
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="2"
|
data-key="2"
|
||||||
data-label="description"
|
data-label="Description"
|
||||||
>
|
>
|
||||||
—
|
—
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="3"
|
data-key="3"
|
||||||
data-label="homeURL"
|
data-label="Home URL"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
|
@ -167,10 +167,10 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="0"
|
data-key="0"
|
||||||
data-label="clientID"
|
data-label="Client ID"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="/client-settings/337dc87b-e08d-409e-aaac-6ab7df4b925b"
|
href="/clients/337dc87b-e08d-409e-aaac-6ab7df4b925b"
|
||||||
>
|
>
|
||||||
account-console
|
account-console
|
||||||
</a>
|
</a>
|
||||||
|
@ -178,21 +178,21 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="1"
|
data-key="1"
|
||||||
data-label="type"
|
data-label="Type"
|
||||||
>
|
>
|
||||||
openid-connect
|
openid-connect
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="2"
|
data-key="2"
|
||||||
data-label="description"
|
data-label="Description"
|
||||||
>
|
>
|
||||||
—
|
—
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="3"
|
data-key="3"
|
||||||
data-label="homeURL"
|
data-label="Home URL"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
|
@ -266,10 +266,10 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="0"
|
data-key="0"
|
||||||
data-label="clientID"
|
data-label="Client ID"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="/client-settings/60d59afe-7926-4c22-b829-798125793ef5"
|
href="/clients/60d59afe-7926-4c22-b829-798125793ef5"
|
||||||
>
|
>
|
||||||
admin-cli
|
admin-cli
|
||||||
</a>
|
</a>
|
||||||
|
@ -277,21 +277,21 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="1"
|
data-key="1"
|
||||||
data-label="type"
|
data-label="Type"
|
||||||
>
|
>
|
||||||
openid-connect
|
openid-connect
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="2"
|
data-key="2"
|
||||||
data-label="description"
|
data-label="Description"
|
||||||
>
|
>
|
||||||
—
|
—
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="3"
|
data-key="3"
|
||||||
data-label="homeURL"
|
data-label="Home URL"
|
||||||
>
|
>
|
||||||
—
|
—
|
||||||
</td>
|
</td>
|
||||||
|
@ -339,10 +339,10 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="0"
|
data-key="0"
|
||||||
data-label="clientID"
|
data-label="Client ID"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="/client-settings/c2d74093-2b8c-4ecb-870f-c7358ff48237"
|
href="/clients/c2d74093-2b8c-4ecb-870f-c7358ff48237"
|
||||||
>
|
>
|
||||||
broker
|
broker
|
||||||
</a>
|
</a>
|
||||||
|
@ -350,21 +350,21 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="1"
|
data-key="1"
|
||||||
data-label="type"
|
data-label="Type"
|
||||||
>
|
>
|
||||||
openid-connect
|
openid-connect
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="2"
|
data-key="2"
|
||||||
data-label="description"
|
data-label="Description"
|
||||||
>
|
>
|
||||||
—
|
—
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="3"
|
data-key="3"
|
||||||
data-label="homeURL"
|
data-label="Home URL"
|
||||||
>
|
>
|
||||||
—
|
—
|
||||||
</td>
|
</td>
|
||||||
|
@ -412,10 +412,10 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="0"
|
data-key="0"
|
||||||
data-label="clientID"
|
data-label="Client ID"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="/client-settings/66135023-e667-4864-b1f3-f87e805fabc2"
|
href="/clients/66135023-e667-4864-b1f3-f87e805fabc2"
|
||||||
>
|
>
|
||||||
master-realm
|
master-realm
|
||||||
</a>
|
</a>
|
||||||
|
@ -423,19 +423,19 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="1"
|
data-key="1"
|
||||||
data-label="type"
|
data-label="Type"
|
||||||
/>
|
/>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="2"
|
data-key="2"
|
||||||
data-label="description"
|
data-label="Description"
|
||||||
>
|
>
|
||||||
—
|
—
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="3"
|
data-key="3"
|
||||||
data-label="homeURL"
|
data-label="Home URL"
|
||||||
>
|
>
|
||||||
—
|
—
|
||||||
</td>
|
</td>
|
||||||
|
@ -483,10 +483,10 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="0"
|
data-key="0"
|
||||||
data-label="clientID"
|
data-label="Client ID"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="/client-settings/324f4182-d302-44f8-ac8a-149eaa29dc90"
|
href="/clients/324f4182-d302-44f8-ac8a-149eaa29dc90"
|
||||||
>
|
>
|
||||||
new
|
new
|
||||||
</a>
|
</a>
|
||||||
|
@ -494,21 +494,21 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="1"
|
data-key="1"
|
||||||
data-label="type"
|
data-label="Type"
|
||||||
>
|
>
|
||||||
openid-connect
|
openid-connect
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="2"
|
data-key="2"
|
||||||
data-label="description"
|
data-label="Description"
|
||||||
>
|
>
|
||||||
—
|
—
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="3"
|
data-key="3"
|
||||||
data-label="homeURL"
|
data-label="Home URL"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
|
@ -582,10 +582,10 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="0"
|
data-key="0"
|
||||||
data-label="clientID"
|
data-label="Client ID"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="/client-settings/fb45882b-4d85-4f40-920e-6a68298d36d0"
|
href="/clients/fb45882b-4d85-4f40-920e-6a68298d36d0"
|
||||||
>
|
>
|
||||||
photoz-realm
|
photoz-realm
|
||||||
</a>
|
</a>
|
||||||
|
@ -593,19 +593,19 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="1"
|
data-key="1"
|
||||||
data-label="type"
|
data-label="Type"
|
||||||
/>
|
/>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="2"
|
data-key="2"
|
||||||
data-label="description"
|
data-label="Description"
|
||||||
>
|
>
|
||||||
—
|
—
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="3"
|
data-key="3"
|
||||||
data-label="homeURL"
|
data-label="Home URL"
|
||||||
>
|
>
|
||||||
—
|
—
|
||||||
</td>
|
</td>
|
||||||
|
@ -653,10 +653,10 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="0"
|
data-key="0"
|
||||||
data-label="clientID"
|
data-label="Client ID"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="/client-settings/9ed60e41-d794-4046-842f-3247bf32f5ce"
|
href="/clients/9ed60e41-d794-4046-842f-3247bf32f5ce"
|
||||||
>
|
>
|
||||||
security-admin-console
|
security-admin-console
|
||||||
</a>
|
</a>
|
||||||
|
@ -664,21 +664,21 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="1"
|
data-key="1"
|
||||||
data-label="type"
|
data-label="Type"
|
||||||
>
|
>
|
||||||
openid-connect
|
openid-connect
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="2"
|
data-key="2"
|
||||||
data-label="description"
|
data-label="Description"
|
||||||
>
|
>
|
||||||
—
|
—
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="3"
|
data-key="3"
|
||||||
data-label="homeURL"
|
data-label="Home URL"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
|
@ -754,7 +754,7 @@ Object {
|
||||||
</body>,
|
</body>,
|
||||||
"container": <div>
|
"container": <div>
|
||||||
<table
|
<table
|
||||||
aria-label="clientList"
|
aria-label="Client list"
|
||||||
class="pf-c-table pf-m-grid-md pf-m-compact"
|
class="pf-c-table pf-m-grid-md pf-m-compact"
|
||||||
data-ouia-component-id="OUIA-Generated-Table-1"
|
data-ouia-component-id="OUIA-Generated-Table-1"
|
||||||
data-ouia-component-type="PF4/Table"
|
data-ouia-component-type="PF4/Table"
|
||||||
|
@ -768,34 +768,34 @@ Object {
|
||||||
<th
|
<th
|
||||||
class=""
|
class=""
|
||||||
data-key="0"
|
data-key="0"
|
||||||
data-label="clientID"
|
data-label="Client ID"
|
||||||
scope="col"
|
scope="col"
|
||||||
>
|
>
|
||||||
clientID
|
Client ID
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
class=""
|
class=""
|
||||||
data-key="1"
|
data-key="1"
|
||||||
data-label="type"
|
data-label="Type"
|
||||||
scope="col"
|
scope="col"
|
||||||
>
|
>
|
||||||
type
|
Type
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
class=""
|
class=""
|
||||||
data-key="2"
|
data-key="2"
|
||||||
data-label="description"
|
data-label="Description"
|
||||||
scope="col"
|
scope="col"
|
||||||
>
|
>
|
||||||
description
|
Description
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
class=""
|
class=""
|
||||||
data-key="3"
|
data-key="3"
|
||||||
data-label="homeURL"
|
data-label="Home URL"
|
||||||
scope="col"
|
scope="col"
|
||||||
>
|
>
|
||||||
homeURL
|
Home URL
|
||||||
</th>
|
</th>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
|
@ -816,10 +816,10 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="0"
|
data-key="0"
|
||||||
data-label="clientID"
|
data-label="Client ID"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="/client-settings/767756c2-21f8-431c-9f4b-edf30654d653"
|
href="/clients/767756c2-21f8-431c-9f4b-edf30654d653"
|
||||||
>
|
>
|
||||||
account
|
account
|
||||||
</a>
|
</a>
|
||||||
|
@ -827,21 +827,21 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="1"
|
data-key="1"
|
||||||
data-label="type"
|
data-label="Type"
|
||||||
>
|
>
|
||||||
openid-connect
|
openid-connect
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="2"
|
data-key="2"
|
||||||
data-label="description"
|
data-label="Description"
|
||||||
>
|
>
|
||||||
—
|
—
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="3"
|
data-key="3"
|
||||||
data-label="homeURL"
|
data-label="Home URL"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
|
@ -915,10 +915,10 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="0"
|
data-key="0"
|
||||||
data-label="clientID"
|
data-label="Client ID"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="/client-settings/337dc87b-e08d-409e-aaac-6ab7df4b925b"
|
href="/clients/337dc87b-e08d-409e-aaac-6ab7df4b925b"
|
||||||
>
|
>
|
||||||
account-console
|
account-console
|
||||||
</a>
|
</a>
|
||||||
|
@ -926,21 +926,21 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="1"
|
data-key="1"
|
||||||
data-label="type"
|
data-label="Type"
|
||||||
>
|
>
|
||||||
openid-connect
|
openid-connect
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="2"
|
data-key="2"
|
||||||
data-label="description"
|
data-label="Description"
|
||||||
>
|
>
|
||||||
—
|
—
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="3"
|
data-key="3"
|
||||||
data-label="homeURL"
|
data-label="Home URL"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
|
@ -1014,10 +1014,10 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="0"
|
data-key="0"
|
||||||
data-label="clientID"
|
data-label="Client ID"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="/client-settings/60d59afe-7926-4c22-b829-798125793ef5"
|
href="/clients/60d59afe-7926-4c22-b829-798125793ef5"
|
||||||
>
|
>
|
||||||
admin-cli
|
admin-cli
|
||||||
</a>
|
</a>
|
||||||
|
@ -1025,21 +1025,21 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="1"
|
data-key="1"
|
||||||
data-label="type"
|
data-label="Type"
|
||||||
>
|
>
|
||||||
openid-connect
|
openid-connect
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="2"
|
data-key="2"
|
||||||
data-label="description"
|
data-label="Description"
|
||||||
>
|
>
|
||||||
—
|
—
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="3"
|
data-key="3"
|
||||||
data-label="homeURL"
|
data-label="Home URL"
|
||||||
>
|
>
|
||||||
—
|
—
|
||||||
</td>
|
</td>
|
||||||
|
@ -1087,10 +1087,10 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="0"
|
data-key="0"
|
||||||
data-label="clientID"
|
data-label="Client ID"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="/client-settings/c2d74093-2b8c-4ecb-870f-c7358ff48237"
|
href="/clients/c2d74093-2b8c-4ecb-870f-c7358ff48237"
|
||||||
>
|
>
|
||||||
broker
|
broker
|
||||||
</a>
|
</a>
|
||||||
|
@ -1098,21 +1098,21 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="1"
|
data-key="1"
|
||||||
data-label="type"
|
data-label="Type"
|
||||||
>
|
>
|
||||||
openid-connect
|
openid-connect
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="2"
|
data-key="2"
|
||||||
data-label="description"
|
data-label="Description"
|
||||||
>
|
>
|
||||||
—
|
—
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="3"
|
data-key="3"
|
||||||
data-label="homeURL"
|
data-label="Home URL"
|
||||||
>
|
>
|
||||||
—
|
—
|
||||||
</td>
|
</td>
|
||||||
|
@ -1160,10 +1160,10 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="0"
|
data-key="0"
|
||||||
data-label="clientID"
|
data-label="Client ID"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="/client-settings/66135023-e667-4864-b1f3-f87e805fabc2"
|
href="/clients/66135023-e667-4864-b1f3-f87e805fabc2"
|
||||||
>
|
>
|
||||||
master-realm
|
master-realm
|
||||||
</a>
|
</a>
|
||||||
|
@ -1171,19 +1171,19 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="1"
|
data-key="1"
|
||||||
data-label="type"
|
data-label="Type"
|
||||||
/>
|
/>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="2"
|
data-key="2"
|
||||||
data-label="description"
|
data-label="Description"
|
||||||
>
|
>
|
||||||
—
|
—
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="3"
|
data-key="3"
|
||||||
data-label="homeURL"
|
data-label="Home URL"
|
||||||
>
|
>
|
||||||
—
|
—
|
||||||
</td>
|
</td>
|
||||||
|
@ -1231,10 +1231,10 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="0"
|
data-key="0"
|
||||||
data-label="clientID"
|
data-label="Client ID"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="/client-settings/324f4182-d302-44f8-ac8a-149eaa29dc90"
|
href="/clients/324f4182-d302-44f8-ac8a-149eaa29dc90"
|
||||||
>
|
>
|
||||||
new
|
new
|
||||||
</a>
|
</a>
|
||||||
|
@ -1242,21 +1242,21 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="1"
|
data-key="1"
|
||||||
data-label="type"
|
data-label="Type"
|
||||||
>
|
>
|
||||||
openid-connect
|
openid-connect
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="2"
|
data-key="2"
|
||||||
data-label="description"
|
data-label="Description"
|
||||||
>
|
>
|
||||||
—
|
—
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="3"
|
data-key="3"
|
||||||
data-label="homeURL"
|
data-label="Home URL"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
|
@ -1330,10 +1330,10 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="0"
|
data-key="0"
|
||||||
data-label="clientID"
|
data-label="Client ID"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="/client-settings/fb45882b-4d85-4f40-920e-6a68298d36d0"
|
href="/clients/fb45882b-4d85-4f40-920e-6a68298d36d0"
|
||||||
>
|
>
|
||||||
photoz-realm
|
photoz-realm
|
||||||
</a>
|
</a>
|
||||||
|
@ -1341,19 +1341,19 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="1"
|
data-key="1"
|
||||||
data-label="type"
|
data-label="Type"
|
||||||
/>
|
/>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="2"
|
data-key="2"
|
||||||
data-label="description"
|
data-label="Description"
|
||||||
>
|
>
|
||||||
—
|
—
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="3"
|
data-key="3"
|
||||||
data-label="homeURL"
|
data-label="Home URL"
|
||||||
>
|
>
|
||||||
—
|
—
|
||||||
</td>
|
</td>
|
||||||
|
@ -1401,10 +1401,10 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="0"
|
data-key="0"
|
||||||
data-label="clientID"
|
data-label="Client ID"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="/client-settings/9ed60e41-d794-4046-842f-3247bf32f5ce"
|
href="/clients/9ed60e41-d794-4046-842f-3247bf32f5ce"
|
||||||
>
|
>
|
||||||
security-admin-console
|
security-admin-console
|
||||||
</a>
|
</a>
|
||||||
|
@ -1412,21 +1412,21 @@ Object {
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="1"
|
data-key="1"
|
||||||
data-label="type"
|
data-label="Type"
|
||||||
>
|
>
|
||||||
openid-connect
|
openid-connect
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="2"
|
data-key="2"
|
||||||
data-label="description"
|
data-label="Description"
|
||||||
>
|
>
|
||||||
—
|
—
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class=""
|
class=""
|
||||||
data-key="3"
|
data-key="3"
|
||||||
data-label="homeURL"
|
data-label="Home URL"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
aria-disabled="false"
|
aria-disabled="false"
|
||||||
|
|
|
@ -18,7 +18,11 @@ export const CapabilityConfig = ({ form }: CapabilityConfigProps) => {
|
||||||
const { t } = useTranslation("clients");
|
const { t } = useTranslation("clients");
|
||||||
return (
|
return (
|
||||||
<Form isHorizontal>
|
<Form isHorizontal>
|
||||||
<FormGroup label={t("clientAuthentication")} fieldId="kc-authentication">
|
<FormGroup
|
||||||
|
hasNoPaddingTop
|
||||||
|
label={t("clientAuthentication")}
|
||||||
|
fieldId="kc-authentication"
|
||||||
|
>
|
||||||
<Controller
|
<Controller
|
||||||
name="publicClient"
|
name="publicClient"
|
||||||
defaultValue={false}
|
defaultValue={false}
|
||||||
|
@ -35,7 +39,11 @@ export const CapabilityConfig = ({ form }: CapabilityConfigProps) => {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup label={t("clientAuthorization")} fieldId="kc-authorization">
|
<FormGroup
|
||||||
|
hasNoPaddingTop
|
||||||
|
label={t("clientAuthorization")}
|
||||||
|
fieldId="kc-authorization"
|
||||||
|
>
|
||||||
<Controller
|
<Controller
|
||||||
name="authorizationServicesEnabled"
|
name="authorizationServicesEnabled"
|
||||||
defaultValue={false}
|
defaultValue={false}
|
||||||
|
@ -52,7 +60,11 @@ export const CapabilityConfig = ({ form }: CapabilityConfigProps) => {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup label={t("authenticationFlow")} fieldId="kc-flow">
|
<FormGroup
|
||||||
|
hasNoPaddingTop
|
||||||
|
label={t("authenticationFlow")}
|
||||||
|
fieldId="kc-flow"
|
||||||
|
>
|
||||||
<Grid>
|
<Grid>
|
||||||
<GridItem lg={4} sm={6}>
|
<GridItem lg={4} sm={6}>
|
||||||
<Controller
|
<Controller
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Controller, UseFormMethods } from "react-hook-form";
|
import { Controller, UseFormMethods } from "react-hook-form";
|
||||||
|
|
||||||
import { HttpClientContext } from "../../http-service/HttpClientContext";
|
import { HttpClientContext } from "../../context/http-service/HttpClientContext";
|
||||||
import { sortProvider } from "../../util";
|
import { sortProvider } from "../../util";
|
||||||
import { ServerInfoRepresentation } from "../models/server-info";
|
import { ServerInfoRepresentation } from "../models/server-info";
|
||||||
import { ClientDescription } from "../ClientDescription";
|
import { ClientDescription } from "../ClientDescription";
|
||||||
|
|
|
@ -11,12 +11,12 @@ import {
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
|
||||||
import { HttpClientContext } from "../../http-service/HttpClientContext";
|
import { HttpClientContext } from "../../context/http-service/HttpClientContext";
|
||||||
import { GeneralSettings } from "./GeneralSettings";
|
import { GeneralSettings } from "./GeneralSettings";
|
||||||
import { CapabilityConfig } from "./CapabilityConfig";
|
import { CapabilityConfig } from "./CapabilityConfig";
|
||||||
import { ClientRepresentation } from "../models/client-model";
|
import { ClientRepresentation } from "../models/client-model";
|
||||||
import { useAlerts } from "../../components/alert/Alerts";
|
import { useAlerts } from "../../components/alert/Alerts";
|
||||||
import { RealmContext } from "../../components/realm-context/RealmContext";
|
import { RealmContext } from "../../context/realm-context/RealmContext";
|
||||||
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
||||||
|
|
||||||
export const NewClientForm = () => {
|
export const NewClientForm = () => {
|
||||||
|
|
|
@ -13,10 +13,10 @@ import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { ClientRepresentation } from "../models/client-model";
|
import { ClientRepresentation } from "../models/client-model";
|
||||||
import { ClientDescription } from "../ClientDescription";
|
import { ClientDescription } from "../ClientDescription";
|
||||||
import { HttpClientContext } from "../../http-service/HttpClientContext";
|
import { HttpClientContext } from "../../context/http-service/HttpClientContext";
|
||||||
import { JsonFileUpload } from "../../components/json-file-upload/JsonFileUpload";
|
import { JsonFileUpload } from "../../components/json-file-upload/JsonFileUpload";
|
||||||
import { useAlerts } from "../../components/alert/Alerts";
|
import { useAlerts } from "../../components/alert/Alerts";
|
||||||
import { RealmContext } from "../../components/realm-context/RealmContext";
|
import { RealmContext } from "../../context/realm-context/RealmContext";
|
||||||
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
||||||
|
|
||||||
export const ImportForm = () => {
|
export const ImportForm = () => {
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"clientList": "Client list",
|
"clientList": "Client list",
|
||||||
|
"clientSettings": "Client details",
|
||||||
"generalSettings": "General Settings",
|
"generalSettings": "General Settings",
|
||||||
"capabilityConfig": "Capability config",
|
"capabilityConfig": "Capability config",
|
||||||
"clientsExplain": "Clients are applications and services that can request authentication of a user",
|
"clientsExplain": "Clients are applications and services that can request authentication of a user",
|
||||||
|
@ -19,6 +20,10 @@
|
||||||
"clientImportSuccess": "Client imported successfully",
|
"clientImportSuccess": "Client imported successfully",
|
||||||
"clientDeletedSuccess": "The client has been deleted",
|
"clientDeletedSuccess": "The client has been deleted",
|
||||||
"clientDeleteError": "Could not delete client:",
|
"clientDeleteError": "Could not delete client:",
|
||||||
|
"clientDeleteConfirmTitle": "Delete client?",
|
||||||
|
"disableConfirmTitle": "Disable client?",
|
||||||
|
"disableConfirm": "If you disable this client, you cannot initiate a login or obtain access tokens.",
|
||||||
|
"clientDeleteConfirm": "If you delete this client, all associated data will be removed.",
|
||||||
"clientAuthentication": "Client authentication",
|
"clientAuthentication": "Client authentication",
|
||||||
"authentication": "Authentication",
|
"authentication": "Authentication",
|
||||||
"authenticationFlow": "Authentication flow",
|
"authenticationFlow": "Authentication flow",
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
"fullName": "{{givenName}} {{familyName}}",
|
"fullName": "{{givenName}} {{familyName}}",
|
||||||
"unknownUser": "Anonymous",
|
"unknownUser": "Anonymous",
|
||||||
|
|
||||||
|
"create": "Create",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"continue": "Continue",
|
"continue": "Continue",
|
||||||
|
@ -10,12 +11,14 @@
|
||||||
"next": "Next",
|
"next": "Next",
|
||||||
"back": "Back",
|
"back": "Back",
|
||||||
"export": "Export",
|
"export": "Export",
|
||||||
|
"action": "Action",
|
||||||
"resourceFile": "Resource file",
|
"resourceFile": "Resource file",
|
||||||
"clearFile": "Clear this file",
|
"clearFile": "Clear this file",
|
||||||
"on": "On",
|
"on": "On",
|
||||||
"off":"Off",
|
"off":"Off",
|
||||||
"enabled": "Enabled",
|
"enabled": "Enabled",
|
||||||
"disabled": "Disabled",
|
"disabled": "Disabled",
|
||||||
|
"disable": "Disable",
|
||||||
|
|
||||||
"signOut": "Sign out",
|
"signOut": "Sign out",
|
||||||
"manageAccount": "Manage account",
|
"manageAccount": "Manage account",
|
||||||
|
@ -24,6 +27,7 @@
|
||||||
"documentation": "Documentation",
|
"documentation": "Documentation",
|
||||||
"enableHelpMode": "Enable help mode",
|
"enableHelpMode": "Enable help mode",
|
||||||
|
|
||||||
|
"home": "Home",
|
||||||
"manage": "Manage",
|
"manage": "Manage",
|
||||||
"clients": "Clients",
|
"clients": "Clients",
|
||||||
"clientScopes": "Client scopes",
|
"clientScopes": "Client scopes",
|
||||||
|
|
22
src/components/bread-crumb/PageBreadCrumbs.tsx
Normal file
22
src/components/bread-crumb/PageBreadCrumbs.tsx
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import { Breadcrumb, BreadcrumbItem } from "@patternfly/react-core";
|
||||||
|
import useBreadcrumbs from "use-react-router-breadcrumbs";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import { routes } from "../../route-config";
|
||||||
|
|
||||||
|
export const PageBreadCrumbs = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const crumbs = useBreadcrumbs(routes(t));
|
||||||
|
return (
|
||||||
|
<Breadcrumb>
|
||||||
|
{crumbs.map(({ match, breadcrumb: crumb }, i) => (
|
||||||
|
<BreadcrumbItem key={i} isActive={crumbs.length - 1 === i}>
|
||||||
|
{crumbs.length - 1 !== i && <Link to={match.url}>{crumb}</Link>}
|
||||||
|
{crumbs.length - 1 === i && <>{crumb}</>}
|
||||||
|
</BreadcrumbItem>
|
||||||
|
))}
|
||||||
|
</Breadcrumb>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,15 @@
|
||||||
|
import React from "react";
|
||||||
|
import { mount } from "enzyme";
|
||||||
|
import { PageBreadCrumbs } from "../PageBreadCrumbs";
|
||||||
|
import { MemoryRouter } from "react-router-dom";
|
||||||
|
|
||||||
|
describe("BreadCrumbs tests", () => {
|
||||||
|
it("couple of crumbs", () => {
|
||||||
|
const crumbs = mount(
|
||||||
|
<MemoryRouter initialEntries={["/add-client"]}>
|
||||||
|
<PageBreadCrumbs />
|
||||||
|
</MemoryRouter>
|
||||||
|
);
|
||||||
|
expect(crumbs.find(PageBreadCrumbs)).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,92 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`BreadCrumbs tests couple of crumbs 1`] = `
|
||||||
|
<PageBreadCrumbs>
|
||||||
|
<Breadcrumb>
|
||||||
|
<nav
|
||||||
|
aria-label="Breadcrumb"
|
||||||
|
className="pf-c-breadcrumb"
|
||||||
|
data-ouia-component-id="OUIA-Generated-Breadcrumb-1"
|
||||||
|
data-ouia-component-type="PF4/Breadcrumb"
|
||||||
|
data-ouia-safe={true}
|
||||||
|
>
|
||||||
|
<ol
|
||||||
|
className="pf-c-breadcrumb__list"
|
||||||
|
>
|
||||||
|
<BreadcrumbItem
|
||||||
|
isActive={false}
|
||||||
|
key=".$0"
|
||||||
|
showDivider={false}
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
className="pf-c-breadcrumb__item"
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
to="/"
|
||||||
|
>
|
||||||
|
<LinkAnchor
|
||||||
|
href="/"
|
||||||
|
navigate={[Function]}
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href="/"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
key="/"
|
||||||
|
>
|
||||||
|
Home
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</LinkAnchor>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
<BreadcrumbItem
|
||||||
|
isActive={true}
|
||||||
|
key=".$1"
|
||||||
|
showDivider={true}
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
className="pf-c-breadcrumb__item"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className="pf-c-breadcrumb__item-divider"
|
||||||
|
>
|
||||||
|
<AngleRightIcon
|
||||||
|
color="currentColor"
|
||||||
|
noVerticalAlign={false}
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden={true}
|
||||||
|
aria-labelledby={null}
|
||||||
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
role="img"
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"verticalAlign": "-0.125em",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
viewBox="0 0 256 512"
|
||||||
|
width="1em"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M224.3 273l-136 136c-9.4 9.4-24.6 9.4-33.9 0l-22.6-22.6c-9.4-9.4-9.4-24.6 0-33.9l96.4-96.4-96.4-96.4c-9.4-9.4-9.4-24.6 0-33.9L54.3 103c9.4-9.4 24.6-9.4 33.9 0l136 136c9.5 9.4 9.5 24.6.1 34z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</AngleRightIcon>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
key="/add-client"
|
||||||
|
>
|
||||||
|
Create client
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
</Breadcrumb>
|
||||||
|
</PageBreadCrumbs>
|
||||||
|
`;
|
|
@ -4,24 +4,31 @@ import { Spinner } from "@patternfly/react-core";
|
||||||
type DataLoaderProps<T> = {
|
type DataLoaderProps<T> = {
|
||||||
loader: () => Promise<T>;
|
loader: () => Promise<T>;
|
||||||
deps?: any[];
|
deps?: any[];
|
||||||
children: ((arg: T) => any) | React.ReactNode;
|
children: ((arg: Result<T>) => any) | React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Result<T> = {
|
||||||
|
data: T;
|
||||||
|
refresh: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function DataLoader<T>(props: DataLoaderProps<T>) {
|
export function DataLoader<T>(props: DataLoaderProps<T>) {
|
||||||
const [data, setData] = useState<{ result: T } | undefined>(undefined);
|
const [data, setData] = useState<{ result: T } | undefined>(undefined);
|
||||||
useEffect(() => {
|
|
||||||
setData(undefined);
|
|
||||||
const loadData = async () => {
|
const loadData = async () => {
|
||||||
const result = await props.loader();
|
const result = await props.loader();
|
||||||
setData({ result });
|
setData({ result });
|
||||||
};
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
setData(undefined);
|
||||||
loadData();
|
loadData();
|
||||||
}, [props]);
|
}, [props]);
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
if (props.children instanceof Function) {
|
if (props.children instanceof Function) {
|
||||||
return props.children(data.result);
|
return props.children({
|
||||||
|
data: data.result,
|
||||||
|
refresh: () => loadData(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return props.children;
|
return props.children;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,9 @@ describe("<DataLoader />", () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(
|
render(
|
||||||
<DataLoader loader={loader}>
|
<DataLoader loader={loader}>
|
||||||
{(data) => (
|
{(result) => (
|
||||||
<div>
|
<div>
|
||||||
{data.map((d, i) => (
|
{result.data.map((d, i) => (
|
||||||
<i key={i}>{d}</i>
|
<i key={i}>{d}</i>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -15,7 +15,8 @@ import {
|
||||||
import { CheckIcon } from "@patternfly/react-icons";
|
import { CheckIcon } from "@patternfly/react-icons";
|
||||||
|
|
||||||
import { RealmRepresentation } from "../../realm/models/Realm";
|
import { RealmRepresentation } from "../../realm/models/Realm";
|
||||||
import { RealmContext } from "../realm-context/RealmContext";
|
import { RealmContext } from "../../context/realm-context/RealmContext";
|
||||||
|
import { WhoAmIContext } from "../../context/whoami/WhoAmI";
|
||||||
|
|
||||||
import "./realm-selector.css";
|
import "./realm-selector.css";
|
||||||
|
|
||||||
|
@ -25,6 +26,7 @@ type RealmSelectorProps = {
|
||||||
|
|
||||||
export const RealmSelector = ({ realmList }: RealmSelectorProps) => {
|
export const RealmSelector = ({ realmList }: RealmSelectorProps) => {
|
||||||
const { realm, setRealm } = useContext(RealmContext);
|
const { realm, setRealm } = useContext(RealmContext);
|
||||||
|
const whoami = useContext(WhoAmIContext);
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
const [filteredItems, setFilteredItems] = useState(realmList);
|
const [filteredItems, setFilteredItems] = useState(realmList);
|
||||||
|
@ -77,6 +79,19 @@ export const RealmSelector = ({ realmList }: RealmSelectorProps) => {
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
));
|
));
|
||||||
|
|
||||||
|
const addRealmComponent = (
|
||||||
|
<React.Fragment key="Add Realm">
|
||||||
|
{whoami.canCreateRealm() && (
|
||||||
|
<>
|
||||||
|
<Divider key="divider" />
|
||||||
|
<DropdownItem key="add">
|
||||||
|
<AddRealm />
|
||||||
|
</DropdownItem>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{realmList.length > 5 && (
|
{realmList.length > 5 && (
|
||||||
|
@ -119,13 +134,7 @@ export const RealmSelector = ({ realmList }: RealmSelectorProps) => {
|
||||||
{toUpperCase(realm)}
|
{toUpperCase(realm)}
|
||||||
</DropdownToggle>
|
</DropdownToggle>
|
||||||
}
|
}
|
||||||
dropdownItems={[
|
dropdownItems={[...dropdownItems, addRealmComponent]}
|
||||||
...dropdownItems,
|
|
||||||
<Divider key="divider" />,
|
|
||||||
<DropdownItem key="add">
|
|
||||||
<AddRealm />
|
|
||||||
</DropdownItem>,
|
|
||||||
]}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { mount } from "enzyme";
|
||||||
import { act } from "@testing-library/react";
|
import { act } from "@testing-library/react";
|
||||||
|
|
||||||
import { RealmSelector } from "../RealmSelector";
|
import { RealmSelector } from "../RealmSelector";
|
||||||
import { RealmContextProvider } from "../../realm-context/RealmContext";
|
import { RealmContextProvider } from "../../../context/realm-context/RealmContext";
|
||||||
|
|
||||||
it("renders realm selector", async () => {
|
it("renders realm selector", async () => {
|
||||||
const wrapper = mount(
|
const wrapper = mount(
|
||||||
|
|
|
@ -23,10 +23,7 @@ exports[`renders realm selector 1`] = `
|
||||||
value="another"
|
value="another"
|
||||||
/>
|
/>
|
||||||
</DropdownItem>,
|
</DropdownItem>,
|
||||||
<Divider />,
|
<React.Fragment />,
|
||||||
<DropdownItem>
|
|
||||||
<AddRealm />
|
|
||||||
</DropdownItem>,
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
id="realm-select"
|
id="realm-select"
|
||||||
|
@ -54,10 +51,7 @@ exports[`renders realm selector 1`] = `
|
||||||
value="another"
|
value="another"
|
||||||
/>
|
/>
|
||||||
</DropdownItem>,
|
</DropdownItem>,
|
||||||
<Divider />,
|
<React.Fragment />,
|
||||||
<DropdownItem>
|
|
||||||
<AddRealm />
|
|
||||||
</DropdownItem>,
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
id="realm-select"
|
id="realm-select"
|
||||||
|
@ -160,29 +154,6 @@ exports[`renders realm selector 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<hr
|
|
||||||
class="pf-c-divider"
|
|
||||||
index="1"
|
|
||||||
/>
|
|
||||||
<li
|
|
||||||
role="menuitem"
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
aria-disabled="false"
|
|
||||||
class="pf-c-dropdown__menu-item"
|
|
||||||
tabindex="-1"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
aria-disabled="false"
|
|
||||||
class="pf-c-button pf-m-primary pf-m-block"
|
|
||||||
data-ouia-component-id="OUIA-Generated-Button-primary-1"
|
|
||||||
data-ouia-component-type="PF4/Button"
|
|
||||||
data-ouia-safe="true"
|
|
||||||
>
|
|
||||||
Create Realm
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>,
|
</div>,
|
||||||
}
|
}
|
||||||
|
@ -268,29 +239,6 @@ exports[`renders realm selector 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<hr
|
|
||||||
class="pf-c-divider"
|
|
||||||
index="1"
|
|
||||||
/>
|
|
||||||
<li
|
|
||||||
role="menuitem"
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
aria-disabled="false"
|
|
||||||
class="pf-c-dropdown__menu-item"
|
|
||||||
tabindex="-1"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
aria-disabled="false"
|
|
||||||
class="pf-c-button pf-m-primary pf-m-block"
|
|
||||||
data-ouia-component-id="OUIA-Generated-Button-primary-1"
|
|
||||||
data-ouia-component-type="PF4/Button"
|
|
||||||
data-ouia-safe="true"
|
|
||||||
>
|
|
||||||
Create Realm
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>,
|
</div>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
.sticky {
|
.sticky {
|
||||||
position: fixed;
|
position: sticky;
|
||||||
top: 100px;
|
top: 100px;
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import React from "react";
|
import React, { MouseEventHandler } from "react";
|
||||||
import {
|
import {
|
||||||
ToggleTemplateProps,
|
ToggleTemplateProps,
|
||||||
Toolbar,
|
Toolbar,
|
||||||
|
@ -28,6 +28,7 @@ type TableToolbarProps = {
|
||||||
newInput: string,
|
newInput: string,
|
||||||
event: React.FormEvent<HTMLInputElement>
|
event: React.FormEvent<HTMLInputElement>
|
||||||
) => void;
|
) => void;
|
||||||
|
inputGroupOnClick?: MouseEventHandler;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TableToolbar = ({
|
export const TableToolbar = ({
|
||||||
|
@ -42,6 +43,7 @@ export const TableToolbar = ({
|
||||||
inputGroupName,
|
inputGroupName,
|
||||||
inputGroupPlaceholder,
|
inputGroupPlaceholder,
|
||||||
inputGroupOnChange,
|
inputGroupOnChange,
|
||||||
|
inputGroupOnClick,
|
||||||
}: TableToolbarProps) => {
|
}: TableToolbarProps) => {
|
||||||
const { t } = useTranslation("groups");
|
const { t } = useTranslation("groups");
|
||||||
const page = first / max;
|
const page = first / max;
|
||||||
|
@ -82,6 +84,7 @@ export const TableToolbar = ({
|
||||||
<Button
|
<Button
|
||||||
variant={ButtonVariant.control}
|
variant={ButtonVariant.control}
|
||||||
aria-label={t("Search")}
|
aria-label={t("Search")}
|
||||||
|
onClick={inputGroupOnClick}
|
||||||
>
|
>
|
||||||
<SearchIcon />
|
<SearchIcon />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -15,6 +15,7 @@ import {
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
import { HelpContext } from "../help-enabler/HelpHeader";
|
import { HelpContext } from "../help-enabler/HelpHeader";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { PageBreadCrumbs } from "../bread-crumb/PageBreadCrumbs";
|
||||||
|
|
||||||
export type ViewHeaderProps = {
|
export type ViewHeaderProps = {
|
||||||
titleKey: string;
|
titleKey: string;
|
||||||
|
@ -31,14 +32,13 @@ export const ViewHeader = ({
|
||||||
badge,
|
badge,
|
||||||
subKey,
|
subKey,
|
||||||
selectItems,
|
selectItems,
|
||||||
isEnabled,
|
isEnabled = true,
|
||||||
onSelect,
|
onSelect,
|
||||||
onToggle,
|
onToggle,
|
||||||
}: ViewHeaderProps) => {
|
}: ViewHeaderProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { enabled } = useContext(HelpContext);
|
const { enabled } = useContext(HelpContext);
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [checked, setChecked] = useState(isEnabled);
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageSection variant="light">
|
<PageSection variant="light">
|
||||||
|
@ -68,17 +68,17 @@ export const ViewHeader = ({
|
||||||
label={t("common:enabled")}
|
label={t("common:enabled")}
|
||||||
labelOff={t("common:disabled")}
|
labelOff={t("common:disabled")}
|
||||||
className="pf-u-mr-lg"
|
className="pf-u-mr-lg"
|
||||||
isChecked={checked}
|
isChecked={isEnabled}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
if (onToggle) {
|
if (onToggle) {
|
||||||
onToggle(value);
|
onToggle(value);
|
||||||
}
|
}
|
||||||
setChecked(value);
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ToolbarItem>
|
</ToolbarItem>
|
||||||
<ToolbarItem>
|
<ToolbarItem>
|
||||||
<Select
|
<Select
|
||||||
|
placeholderText={t("common:action")}
|
||||||
isOpen={open}
|
isOpen={open}
|
||||||
onToggle={() => setOpen(!open)}
|
onToggle={() => setOpen(!open)}
|
||||||
onSelect={(_, value) => {
|
onSelect={(_, value) => {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState, useContext } from "react";
|
||||||
|
import { WhoAmIContext } from "../../context/whoami/WhoAmI";
|
||||||
|
|
||||||
export const RealmContext = React.createContext({
|
export const RealmContext = React.createContext({
|
||||||
realm: "master",
|
realm: "",
|
||||||
setRealm: (realm: string) => {},
|
setRealm: (realm: string) => {},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -10,7 +11,8 @@ type RealmContextProviderProps = { children: React.ReactNode };
|
||||||
export const RealmContextProvider = ({
|
export const RealmContextProvider = ({
|
||||||
children,
|
children,
|
||||||
}: RealmContextProviderProps) => {
|
}: RealmContextProviderProps) => {
|
||||||
const [realm, setRealm] = useState("master");
|
const homeRealm = useContext(WhoAmIContext).getHomeRealm();
|
||||||
|
const [realm, setRealm] = useState(homeRealm);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RealmContext.Provider value={{ realm, setRealm }}>
|
<RealmContext.Provider value={{ realm, setRealm }}>
|
78
src/context/whoami/WhoAmI.tsx
Normal file
78
src/context/whoami/WhoAmI.tsx
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import React, { useContext } from "react";
|
||||||
|
import i18n from "../../i18n";
|
||||||
|
|
||||||
|
import WhoAmIRepresentation from "./who-am-i-model";
|
||||||
|
|
||||||
|
import { HttpClientContext } from "../http-service/HttpClientContext";
|
||||||
|
import { KeycloakContext } from "../auth/KeycloakContext";
|
||||||
|
import { DataLoader } from "../../components/data-loader/DataLoader";
|
||||||
|
|
||||||
|
export class WhoAmI {
|
||||||
|
constructor(
|
||||||
|
private homeRealm?: string | undefined,
|
||||||
|
private me?: WhoAmIRepresentation | undefined
|
||||||
|
) {
|
||||||
|
if (this.me !== undefined && this.me.locale) {
|
||||||
|
i18n.changeLanguage(this.me.locale, (error) => {
|
||||||
|
if (error) console.log("Unable to set locale to " + this.me?.locale);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getDisplayName(): string {
|
||||||
|
if (this.me === undefined) return "";
|
||||||
|
|
||||||
|
return this.me.displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the realm I am signed in to.
|
||||||
|
*/
|
||||||
|
public getHomeRealm(): string {
|
||||||
|
let realm: string | undefined = this.homeRealm;
|
||||||
|
if (realm === undefined) realm = this.me?.realm;
|
||||||
|
if (realm === undefined) realm = "master"; // this really can't happen in the real world
|
||||||
|
|
||||||
|
return realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public canCreateRealm(): boolean {
|
||||||
|
return this.me !== undefined && this.me.createRealm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getRealmAccess(): Readonly<{ [key: string]: ReadonlyArray<string> }> {
|
||||||
|
if (this.me === undefined) return {};
|
||||||
|
|
||||||
|
return this.me.realm_access;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WhoAmIContext = React.createContext(new WhoAmI());
|
||||||
|
|
||||||
|
type WhoAmIProviderProps = { children: React.ReactNode };
|
||||||
|
export const WhoAmIContextProvider = ({ children }: WhoAmIProviderProps) => {
|
||||||
|
const httpClient = useContext(HttpClientContext)!;
|
||||||
|
const keycloak = useContext(KeycloakContext);
|
||||||
|
|
||||||
|
const whoAmILoader = async () => {
|
||||||
|
if (keycloak === undefined) return undefined;
|
||||||
|
|
||||||
|
const realm = keycloak.realm();
|
||||||
|
|
||||||
|
return await httpClient
|
||||||
|
.doGet(`/admin/${realm}/console/whoami/`)
|
||||||
|
.then((r) => r.data as WhoAmIRepresentation);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DataLoader loader={whoAmILoader}>
|
||||||
|
{(whoamirep) => (
|
||||||
|
<WhoAmIContext.Provider
|
||||||
|
value={new WhoAmI(keycloak?.realm(), whoamirep.data)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</WhoAmIContext.Provider>
|
||||||
|
)}
|
||||||
|
</DataLoader>
|
||||||
|
);
|
||||||
|
};
|
28
src/context/whoami/__tests__/WhoAmI.test.tsx
Normal file
28
src/context/whoami/__tests__/WhoAmI.test.tsx
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import whoamiMock from "./mock-whoami.json";
|
||||||
|
import { WhoAmI } from "../WhoAmI";
|
||||||
|
|
||||||
|
test("returns display name", () => {
|
||||||
|
const whoami = new WhoAmI("master", whoamiMock);
|
||||||
|
expect(whoami.getDisplayName()).toEqual("Stan Silvert");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns correct home realm", () => {
|
||||||
|
let whoami = new WhoAmI("myrealm", whoamiMock);
|
||||||
|
expect(whoami.getHomeRealm()).toEqual("myrealm");
|
||||||
|
whoami = new WhoAmI(undefined, whoamiMock);
|
||||||
|
expect(whoami.getHomeRealm()).toEqual("master");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("can not create realm", () => {
|
||||||
|
const whoami = new WhoAmI("master", whoamiMock);
|
||||||
|
expect(whoami.canCreateRealm()).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getRealmAccess", () => {
|
||||||
|
const whoami = new WhoAmI("master", whoamiMock);
|
||||||
|
expect(Object.keys(whoami.getRealmAccess()).length).toEqual(2);
|
||||||
|
expect(whoami.getRealmAccess()["master"].length).toEqual(18);
|
||||||
|
});
|
||||||
|
|
||||||
|
//TODO: When we have easy access to i18n, create test for setting locale.
|
||||||
|
// Tested manually and it does work.
|
49
src/context/whoami/__tests__/mock-whoami.json
Normal file
49
src/context/whoami/__tests__/mock-whoami.json
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"userId": "1b635073-7ac8-49db-8eb9-f9fa9cd15bf5",
|
||||||
|
"realm": "master",
|
||||||
|
"displayName": "Stan Silvert",
|
||||||
|
"locale": "en",
|
||||||
|
"createRealm": false,
|
||||||
|
"realm_access": {
|
||||||
|
"aaa": [
|
||||||
|
"view-identity-providers",
|
||||||
|
"view-realm",
|
||||||
|
"manage-identity-providers",
|
||||||
|
"impersonation",
|
||||||
|
"create-client",
|
||||||
|
"manage-users",
|
||||||
|
"query-realms",
|
||||||
|
"view-authorization",
|
||||||
|
"query-clients",
|
||||||
|
"query-users",
|
||||||
|
"manage-events",
|
||||||
|
"manage-realm",
|
||||||
|
"view-events",
|
||||||
|
"view-users",
|
||||||
|
"view-clients",
|
||||||
|
"manage-authorization",
|
||||||
|
"manage-clients",
|
||||||
|
"query-groups"
|
||||||
|
],
|
||||||
|
"master": [
|
||||||
|
"view-realm",
|
||||||
|
"view-identity-providers",
|
||||||
|
"manage-identity-providers",
|
||||||
|
"impersonation",
|
||||||
|
"create-client",
|
||||||
|
"manage-users",
|
||||||
|
"query-realms",
|
||||||
|
"view-authorization",
|
||||||
|
"query-clients",
|
||||||
|
"query-users",
|
||||||
|
"manage-events",
|
||||||
|
"manage-realm",
|
||||||
|
"view-events",
|
||||||
|
"view-users",
|
||||||
|
"view-clients",
|
||||||
|
"manage-authorization",
|
||||||
|
"manage-clients",
|
||||||
|
"query-groups"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
8
src/context/whoami/who-am-i-model.ts
Normal file
8
src/context/whoami/who-am-i-model.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
export default interface WhoAmIRepresentation {
|
||||||
|
userId: string;
|
||||||
|
realm: string;
|
||||||
|
displayName: string;
|
||||||
|
locale: string;
|
||||||
|
createRealm: boolean;
|
||||||
|
realm_access: { [key: string]: string[] };
|
||||||
|
}
|
5
src/events/messages.json
Normal file
5
src/events/messages.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"events": {
|
||||||
|
"title": "Events"
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,8 +9,8 @@ import {
|
||||||
TextInput,
|
TextInput,
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { HttpClientContext } from "../http-service/HttpClientContext";
|
import { HttpClientContext } from "../context/http-service/HttpClientContext";
|
||||||
import { RealmContext } from "../components/realm-context/RealmContext";
|
import { RealmContext } from "../context/realm-context/RealmContext";
|
||||||
import { useAlerts } from "../components/alert/Alerts";
|
import { useAlerts } from "../components/alert/Alerts";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,8 @@ import { Button, AlertVariant } from "@patternfly/react-core";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { GroupRepresentation } from "./models/groups";
|
import { GroupRepresentation } from "./models/groups";
|
||||||
import { UsersIcon } from "@patternfly/react-icons";
|
import { UsersIcon } from "@patternfly/react-icons";
|
||||||
import { HttpClientContext } from "../http-service/HttpClientContext";
|
import { HttpClientContext } from "../context/http-service/HttpClientContext";
|
||||||
import { RealmContext } from "../components/realm-context/RealmContext";
|
import { RealmContext } from "../context/realm-context/RealmContext";
|
||||||
import { useAlerts } from "../components/alert/Alerts";
|
import { useAlerts } from "../components/alert/Alerts";
|
||||||
|
|
||||||
type GroupsListProps = {
|
type GroupsListProps = {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useContext, useState, useEffect } from "react";
|
import React, { useContext, useState, useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { HttpClientContext } from "../http-service/HttpClientContext";
|
import { HttpClientContext } from "../context/http-service/HttpClientContext";
|
||||||
import { GroupsList } from "./GroupsList";
|
import { GroupsList } from "./GroupsList";
|
||||||
import { GroupsCreateModal } from "./GroupsCreateModal";
|
import { GroupsCreateModal } from "./GroupsCreateModal";
|
||||||
import { GroupRepresentation } from "./models/groups";
|
import { GroupRepresentation } from "./models/groups";
|
||||||
|
|
|
@ -8,6 +8,9 @@ import clientScopes from "./client-scopes/messages.json";
|
||||||
import groups from "./groups/messages.json";
|
import groups from "./groups/messages.json";
|
||||||
import realm from "./realm/messages.json";
|
import realm from "./realm/messages.json";
|
||||||
import roles from "./realm-roles/messages.json";
|
import roles from "./realm-roles/messages.json";
|
||||||
|
import users from "./user/messages.json";
|
||||||
|
import sessions from "./sessions/messages.json";
|
||||||
|
import events from "./events/messages.json";
|
||||||
import help from "./help.json";
|
import help from "./help.json";
|
||||||
|
|
||||||
const initOptions = {
|
const initOptions = {
|
||||||
|
@ -21,6 +24,10 @@ const initOptions = {
|
||||||
...groups,
|
...groups,
|
||||||
...realm,
|
...realm,
|
||||||
...roles,
|
...roles,
|
||||||
|
...groups,
|
||||||
|
...users,
|
||||||
|
...sessions,
|
||||||
|
...events,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
lng: "en",
|
lng: "en",
|
||||||
|
|
|
@ -3,11 +3,11 @@ import ReactDom from "react-dom";
|
||||||
import i18n from "./i18n";
|
import i18n from "./i18n";
|
||||||
|
|
||||||
import { App } from "./App";
|
import { App } from "./App";
|
||||||
import init from "./auth/keycloak";
|
import init from "./context/auth/keycloak";
|
||||||
import { KeycloakContext } from "./auth/KeycloakContext";
|
import { KeycloakContext } from "./context/auth/KeycloakContext";
|
||||||
import { KeycloakService } from "./auth/keycloak.service";
|
import { KeycloakService } from "./context/auth/keycloak.service";
|
||||||
import { HttpClientContext } from "./http-service/HttpClientContext";
|
import { HttpClientContext } from "./context/http-service/HttpClientContext";
|
||||||
import { HttpClient } from "./http-service/http-client";
|
import { HttpClient } from "./context/http-service/http-client";
|
||||||
|
|
||||||
console.info("supported languages", ...i18n.languages);
|
console.info("supported languages", ...i18n.languages);
|
||||||
init().then((keycloak) => {
|
init().then((keycloak) => {
|
||||||
|
|
|
@ -14,10 +14,10 @@ import {
|
||||||
|
|
||||||
import { DataLoader } from "../components/data-loader/DataLoader";
|
import { DataLoader } from "../components/data-loader/DataLoader";
|
||||||
import { TableToolbar } from "../components/table-toolbar/TableToolbar";
|
import { TableToolbar } from "../components/table-toolbar/TableToolbar";
|
||||||
import { HttpClientContext } from "../http-service/HttpClientContext";
|
import { HttpClientContext } from "../context/http-service/HttpClientContext";
|
||||||
import { RoleRepresentation } from "../model/role-model";
|
import { RoleRepresentation } from "../model/role-model";
|
||||||
import { RolesList } from "./RoleList";
|
import { RolesList } from "./RoleList";
|
||||||
import { RealmContext } from "../components/realm-context/RealmContext";
|
import { RealmContext } from "../context/realm-context/RealmContext";
|
||||||
|
|
||||||
export const RealmRolesSection = () => {
|
export const RealmRolesSection = () => {
|
||||||
const { t } = useTranslation("roles");
|
const { t } = useTranslation("roles");
|
||||||
|
@ -51,7 +51,7 @@ export const RealmRolesSection = () => {
|
||||||
<Divider component="li" key={1} />
|
<Divider component="li" key={1} />
|
||||||
<PageSection padding={{ default: "noPadding" }}>
|
<PageSection padding={{ default: "noPadding" }}>
|
||||||
<TableToolbar
|
<TableToolbar
|
||||||
count={roles!.length}
|
count={roles.data.length}
|
||||||
first={first}
|
first={first}
|
||||||
max={max}
|
max={max}
|
||||||
onNextClick={setFirst}
|
onNextClick={setFirst}
|
||||||
|
@ -68,7 +68,7 @@ export const RealmRolesSection = () => {
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<RolesList roles={roles} />
|
<RolesList roles={roles.data} />
|
||||||
</TableToolbar>
|
</TableToolbar>
|
||||||
</PageSection>
|
</PageSection>
|
||||||
</>
|
</>
|
||||||
|
|
96
src/realm-roles/add/NewRoleForm.tsx
Normal file
96
src/realm-roles/add/NewRoleForm.tsx
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
import React, { useContext } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import {
|
||||||
|
Text,
|
||||||
|
PageSection,
|
||||||
|
TextContent,
|
||||||
|
FormGroup,
|
||||||
|
Form,
|
||||||
|
TextInput,
|
||||||
|
ActionGroup,
|
||||||
|
Button,
|
||||||
|
Divider,
|
||||||
|
AlertVariant,
|
||||||
|
TextArea,
|
||||||
|
ValidatedOptions,
|
||||||
|
} from "@patternfly/react-core";
|
||||||
|
|
||||||
|
import { RoleRepresentation } from "../../model/role-model";
|
||||||
|
import { HttpClientContext } from "../../context/http-service/HttpClientContext";
|
||||||
|
import { useAlerts } from "../../components/alert/Alerts";
|
||||||
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
import { RealmContext } from "../../context/realm-context/RealmContext";
|
||||||
|
|
||||||
|
export const NewRoleForm = () => {
|
||||||
|
const { t } = useTranslation("roles");
|
||||||
|
const httpClient = useContext(HttpClientContext)!;
|
||||||
|
const [addAlert, Alerts] = useAlerts();
|
||||||
|
const { realm } = useContext(RealmContext);
|
||||||
|
|
||||||
|
const { register, control, errors, handleSubmit } = useForm<
|
||||||
|
RoleRepresentation
|
||||||
|
>();
|
||||||
|
|
||||||
|
const save = async (role: RoleRepresentation) => {
|
||||||
|
try {
|
||||||
|
await httpClient.doPost(`admin/realms/${realm}/roles`, role);
|
||||||
|
addAlert(t("roleCreated"), AlertVariant.success);
|
||||||
|
} catch (error) {
|
||||||
|
addAlert(`${t("roleCreateError")} '${error}'`, AlertVariant.danger);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Alerts />
|
||||||
|
<PageSection variant="light">
|
||||||
|
<TextContent>
|
||||||
|
<Text component="h1">{t("createRole")}</Text>
|
||||||
|
</TextContent>
|
||||||
|
</PageSection>
|
||||||
|
<Divider />
|
||||||
|
<PageSection variant="light">
|
||||||
|
<Form isHorizontal onSubmit={handleSubmit(save)}>
|
||||||
|
<FormGroup label={t("roleName")} isRequired fieldId="kc-role-name">
|
||||||
|
<TextInput
|
||||||
|
isRequired
|
||||||
|
type="text"
|
||||||
|
id="kc-role-name"
|
||||||
|
name="name"
|
||||||
|
ref={register()}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
label={t("description")}
|
||||||
|
fieldId="kc-role-description"
|
||||||
|
helperTextInvalid="Max length 255"
|
||||||
|
validated={
|
||||||
|
errors ? ValidatedOptions.error : ValidatedOptions.default
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Controller
|
||||||
|
name="description"
|
||||||
|
defaultValue=""
|
||||||
|
control={control}
|
||||||
|
rules={{ maxLength: 255 }}
|
||||||
|
render={({ onChange, value }) => (
|
||||||
|
<TextArea
|
||||||
|
type="text"
|
||||||
|
id="kc-role-description"
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<ActionGroup>
|
||||||
|
<Button variant="primary" type="submit">
|
||||||
|
{t("common:create")}
|
||||||
|
</Button>
|
||||||
|
<Button variant="link">{t("common:cancel")}</Button>
|
||||||
|
</ActionGroup>
|
||||||
|
</Form>
|
||||||
|
</PageSection>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
|
@ -13,7 +13,9 @@
|
||||||
"generalSettings": "General Settings",
|
"generalSettings": "General Settings",
|
||||||
"capabilityConfig": "Capability config",
|
"capabilityConfig": "Capability config",
|
||||||
"roleImportError": "Could not import role",
|
"roleImportError": "Could not import role",
|
||||||
"roleImportSuccess": "Role imported succeful",
|
"roleCreated": "Role created",
|
||||||
|
"roleCreateError": "Could not create role:",
|
||||||
|
"roleImportSuccess": "Role import successful",
|
||||||
"roleDeletedSucess": "The role has been deleted",
|
"roleDeletedSucess": "The role has been deleted",
|
||||||
"roleDeleteError": "Could not delete role:",
|
"roleDeleteError": "Could not delete role:",
|
||||||
"roleAuthentication": "Role authentication"
|
"roleAuthentication": "Role authentication"
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {
|
||||||
|
|
||||||
import { JsonFileUpload } from "../../components/json-file-upload/JsonFileUpload";
|
import { JsonFileUpload } from "../../components/json-file-upload/JsonFileUpload";
|
||||||
import { RealmRepresentation } from "../models/Realm";
|
import { RealmRepresentation } from "../models/Realm";
|
||||||
import { HttpClientContext } from "../../http-service/HttpClientContext";
|
import { HttpClientContext } from "../../context/http-service/HttpClientContext";
|
||||||
import { useAlerts } from "../../components/alert/Alerts";
|
import { useAlerts } from "../../components/alert/Alerts";
|
||||||
import { useForm, Controller } from "react-hook-form";
|
import { useForm, Controller } from "react-hook-form";
|
||||||
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
"uploadFile":"Upload JSON file",
|
"uploadFile":"Upload JSON file",
|
||||||
"realmName":"Realm name",
|
"realmName":"Realm name",
|
||||||
"enabled":"Enabled",
|
"enabled":"Enabled",
|
||||||
"create":"Create",
|
|
||||||
"createRealm": "Create realm",
|
"createRealm": "Create realm",
|
||||||
"realmExplain": "A realm manages a set of users, credentials, roles, and groups. A user belongs to and logs into a realm. Realms are isolated from one another and can only manage and authenticate the users that they control.",
|
"realmExplain": "A realm manages a set of users, credentials, roles, and groups. A user belongs to and logs into a realm. Realms are isolated from one another and can only manage and authenticate the users that they control.",
|
||||||
"noRealmRoles": "No realm roles",
|
"noRealmRoles": "No realm roles",
|
||||||
|
|
117
src/route-config.ts
Normal file
117
src/route-config.ts
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
import { TFunction } from "i18next";
|
||||||
|
import { AuthenticationSection } from "./authentication/AuthenticationSection";
|
||||||
|
import { NewClientScopeForm } from "./client-scopes/add/NewClientScopeForm";
|
||||||
|
import { ClientScopesSection } from "./client-scopes/ClientScopesSection";
|
||||||
|
import { NewClientForm } from "./clients/add/NewClientForm";
|
||||||
|
import { ClientSettings } from "./clients/ClientSettings";
|
||||||
|
import { ClientsSection } from "./clients/ClientsSection";
|
||||||
|
import { ImportForm } from "./clients/import/ImportForm";
|
||||||
|
import { EventsSection } from "./events/EventsSection";
|
||||||
|
import { GroupsSection } from "./groups/GroupsSection";
|
||||||
|
import { IdentityProvidersSection } from "./identity-providers/IdentityProvidersSection";
|
||||||
|
import { PageNotFoundSection } from "./PageNotFoundSection";
|
||||||
|
import { NewRoleForm } from "./realm-roles/add/NewRoleForm";
|
||||||
|
import { RealmRolesSection } from "./realm-roles/RealmRolesSection";
|
||||||
|
import { RealmSettingsSection } from "./realm-settings/RealmSettingsSection";
|
||||||
|
import { NewRealmForm } from "./realm/add/NewRealmForm";
|
||||||
|
import { SessionsSection } from "./sessions/SessionsSection";
|
||||||
|
import { UserFederationSection } from "./user-federation/UserFederationSection";
|
||||||
|
import { UsersSection } from "./user/UsersSection";
|
||||||
|
|
||||||
|
export const routes = (t: TFunction) => [
|
||||||
|
{
|
||||||
|
path: "/add-realm",
|
||||||
|
component: NewRealmForm,
|
||||||
|
breadcrumb: t("realm:createRealm"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/clients",
|
||||||
|
component: ClientsSection,
|
||||||
|
breadcrumb: t("clients:clientList"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/clients/:id",
|
||||||
|
component: ClientSettings,
|
||||||
|
breadcrumb: t("clients:clientSettings"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/add-client",
|
||||||
|
component: NewClientForm,
|
||||||
|
breadcrumb: t("clients:createClient"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/import-client",
|
||||||
|
component: ImportForm,
|
||||||
|
breadcrumb: t("clients:importClient"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/client-scopes",
|
||||||
|
component: ClientScopesSection,
|
||||||
|
breadcrumb: t("clientScopeList"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/add-client-scopes",
|
||||||
|
component: NewClientScopeForm,
|
||||||
|
breadcrumb: t("client-scopes:createClientScope"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/realm-roles",
|
||||||
|
component: RealmRolesSection,
|
||||||
|
breadcrumb: t("roles:roleList"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/add-role",
|
||||||
|
component: NewRoleForm,
|
||||||
|
breadcrumb: t("roles:createRole"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/users",
|
||||||
|
component: UsersSection,
|
||||||
|
breadcrumb: t("users:title"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/groups",
|
||||||
|
component: GroupsSection,
|
||||||
|
breadcrumb: t("groups"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/sessions",
|
||||||
|
component: SessionsSection,
|
||||||
|
breadcrumb: t("sessions:title"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/events",
|
||||||
|
component: EventsSection,
|
||||||
|
breadcrumb: t("events:title"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/realm-settings",
|
||||||
|
component: RealmSettingsSection,
|
||||||
|
breadcrumb: t("realmSettings"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/authentication",
|
||||||
|
component: AuthenticationSection,
|
||||||
|
breadcrumb: t("authentication"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/identity-providers",
|
||||||
|
component: IdentityProvidersSection,
|
||||||
|
breadcrumb: t("identityProviders"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/user-federation",
|
||||||
|
component: UserFederationSection,
|
||||||
|
breadcrumb: t("userFederation"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
component: ClientsSection,
|
||||||
|
breadcrumb: t("common:home"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
component: PageNotFoundSection,
|
||||||
|
breadcrumb: "",
|
||||||
|
},
|
||||||
|
];
|
5
src/sessions/messages.json
Normal file
5
src/sessions/messages.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"sessions": {
|
||||||
|
"title": "Sessions"
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,5 +10,9 @@ export default {
|
||||||
} as Meta;
|
} as Meta;
|
||||||
|
|
||||||
export const ClientListExample = () => (
|
export const ClientListExample = () => (
|
||||||
<ClientList clients={clientMock} baseUrl="http://test.nl/" />
|
<ClientList
|
||||||
|
clients={clientMock}
|
||||||
|
baseUrl="http://test.nl/"
|
||||||
|
refresh={() => {}}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
import { Meta } from "@storybook/react";
|
import { Meta } from "@storybook/react";
|
||||||
|
|
||||||
import { RealmSelector } from "../components/realm-selector/RealmSelector";
|
import { RealmSelector } from "../components/realm-selector/RealmSelector";
|
||||||
import { RealmContextProvider } from "../components/realm-context/RealmContext";
|
import { RealmContextProvider } from "../context/realm-context/RealmContext";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "Header",
|
title: "Header",
|
5
src/user/messages.json
Normal file
5
src/user/messages.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"users": {
|
||||||
|
"title": "Users"
|
||||||
|
}
|
||||||
|
}
|
21
src/util.ts
21
src/util.ts
|
@ -1,3 +1,6 @@
|
||||||
|
import FileSaver from "file-saver";
|
||||||
|
|
||||||
|
import { ClientRepresentation } from "./clients/models/client-model";
|
||||||
import { ProviderRepresentation } from "./clients/models/server-info";
|
import { ProviderRepresentation } from "./clients/models/server-info";
|
||||||
|
|
||||||
export const sortProvider = (
|
export const sortProvider = (
|
||||||
|
@ -20,3 +23,21 @@ export const sortProvider = (
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const exportClient = (client: ClientRepresentation): void => {
|
||||||
|
const clientCopy = JSON.parse(JSON.stringify(client));
|
||||||
|
delete clientCopy.id;
|
||||||
|
|
||||||
|
if (clientCopy.protocolMappers) {
|
||||||
|
for (let i = 0; i < clientCopy.protocolMappers.length; i++) {
|
||||||
|
delete clientCopy.protocolMappers[i].id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSaver.saveAs(
|
||||||
|
new Blob([JSON.stringify(clientCopy, null, 2)], {
|
||||||
|
type: "application/json",
|
||||||
|
}),
|
||||||
|
clientCopy.clientId + ".json"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
|
@ -18484,6 +18484,11 @@ use-latest@^1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
use-isomorphic-layout-effect "^1.0.0"
|
use-isomorphic-layout-effect "^1.0.0"
|
||||||
|
|
||||||
|
use-react-router-breadcrumbs@^1.0.4:
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/use-react-router-breadcrumbs/-/use-react-router-breadcrumbs-1.0.4.tgz#12b67ba27ac7e6a00e6ae10896ea91e178e87ee2"
|
||||||
|
integrity sha512-SskKm+wFYPD7eiYrg89y1Wn8vMlY+DiZXNFuP4Wt5gMP2aolcahHGR6pRTWsfMW93CEQxdVkXv/ceHL7nfz2Fw==
|
||||||
|
|
||||||
use-sidecar@^1.0.1:
|
use-sidecar@^1.0.1:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.0.3.tgz#17a4e567d4830c0c0ee100040e85a7fe68611e0f"
|
resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.0.3.tgz#17a4e567d4830c0c0ee100040e85a7fe68611e0f"
|
||||||
|
|
Loading…
Reference in a new issue