Use new admin client with seperated Keycloak JS (#2964)

This commit is contained in:
Jon Koops 2022-07-14 15:02:28 +02:00 committed by GitHub
parent b2e78b3476
commit aeda5f3e4c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
167 changed files with 275 additions and 270 deletions

17
package-lock.json generated
View file

@ -7,7 +7,7 @@
"name": "keycloak-admin-ui",
"license": "Apache",
"dependencies": {
"@keycloak/keycloak-admin-client": "^19.0.0-dev.17",
"@keycloak/keycloak-admin-client": "^19.0.0-dev.18",
"@patternfly/patternfly": "^4.202.1",
"@patternfly/react-code-editor": "^4.65.1",
"@patternfly/react-core": "^4.224.1",
@ -18,6 +18,7 @@
"flat": "^5.0.2",
"i18next": "^21.8.13",
"i18next-http-backend": "^1.4.1",
"keycloak-js": "^18.0.1",
"lodash-es": "^4.17.21",
"react": "^17.0.2",
"react-dom": "^17.0.2",
@ -2205,13 +2206,12 @@
}
},
"node_modules/@keycloak/keycloak-admin-client": {
"version": "19.0.0-dev.17",
"resolved": "https://registry.npmjs.org/@keycloak/keycloak-admin-client/-/keycloak-admin-client-19.0.0-dev.17.tgz",
"integrity": "sha512-tzhTd0Hlu79ZfU7Cz4POlBkl+kcMEccOuOXiXx7Lh1rkM0nNnfsik9R83yJxcfi3nJK/aOnYHmdP+5sQLjBVXw==",
"version": "19.0.0-dev.18",
"resolved": "https://registry.npmjs.org/@keycloak/keycloak-admin-client/-/keycloak-admin-client-19.0.0-dev.18.tgz",
"integrity": "sha512-qsBnkII1T9CGKXs989CSjmhZRvyW9rcEv6gQ832ys4o0oazmqU9Rond+i/JWk2DVNIY9KNqK8VXTqq/e968fyw==",
"dependencies": {
"axios": "^0.27.2",
"camelize-ts": "^2.1.1",
"keycloak-js": "^18.0.0",
"lodash-es": "^4.17.21",
"query-string": "^7.0.1",
"url-join": "^5.0.0",
@ -20977,13 +20977,12 @@
}
},
"@keycloak/keycloak-admin-client": {
"version": "19.0.0-dev.17",
"resolved": "https://registry.npmjs.org/@keycloak/keycloak-admin-client/-/keycloak-admin-client-19.0.0-dev.17.tgz",
"integrity": "sha512-tzhTd0Hlu79ZfU7Cz4POlBkl+kcMEccOuOXiXx7Lh1rkM0nNnfsik9R83yJxcfi3nJK/aOnYHmdP+5sQLjBVXw==",
"version": "19.0.0-dev.18",
"resolved": "https://registry.npmjs.org/@keycloak/keycloak-admin-client/-/keycloak-admin-client-19.0.0-dev.18.tgz",
"integrity": "sha512-qsBnkII1T9CGKXs989CSjmhZRvyW9rcEv6gQ832ys4o0oazmqU9Rond+i/JWk2DVNIY9KNqK8VXTqq/e968fyw==",
"requires": {
"axios": "^0.27.2",
"camelize-ts": "^2.1.1",
"keycloak-js": "^18.0.0",
"lodash-es": "^4.17.21",
"query-string": "^7.0.1",
"url-join": "^5.0.0",

View file

@ -26,7 +26,7 @@
"server:import-client": "./scripts/import-client.mjs"
},
"dependencies": {
"@keycloak/keycloak-admin-client": "^19.0.0-dev.17",
"@keycloak/keycloak-admin-client": "^19.0.0-dev.18",
"@patternfly/patternfly": "^4.202.1",
"@patternfly/react-code-editor": "^4.65.1",
"@patternfly/react-core": "^4.224.1",
@ -37,6 +37,7 @@
"flat": "^5.0.2",
"i18next": "^21.8.13",
"i18next-http-backend": "^1.4.1",
"keycloak-js": "^18.0.1",
"lodash-es": "^4.17.21",
"react": "^17.0.2",
"react-dom": "^17.0.2",

View file

@ -2,6 +2,8 @@ import React, { FunctionComponent, Suspense } from "react";
import { Page } from "@patternfly/react-core";
import { HashRouter as Router, Route, Switch } from "react-router-dom";
import { ErrorBoundary } from "react-error-boundary";
import type Keycloak from "keycloak-js";
import type KeycloakAdminClient from "@keycloak/keycloak-admin-client";
import { Header } from "./PageHeader";
import { PageNav } from "./PageNav";
@ -21,20 +23,21 @@ import { RealmContextProvider } from "./context/realm-context/RealmContext";
import { ErrorRenderer } from "./components/error/ErrorRenderer";
import { AdminClient } from "./context/auth/AdminClient";
import { WhoAmIContextProvider } from "./context/whoami/WhoAmI";
import type KeycloakAdminClient from "@keycloak/keycloak-admin-client";
export const mainPageContentId = "kc-main-content-page-container";
export type AdminClientProps = {
keycloak: Keycloak;
adminClient: KeycloakAdminClient;
};
const AppContexts: FunctionComponent<AdminClientProps> = ({
children,
keycloak,
adminClient,
}) => (
<Router>
<AdminClient.Provider value={adminClient}>
<AdminClient.Provider value={{ keycloak, adminClient }}>
<WhoAmIContextProvider>
<RealmsProvider>
<RealmContextProvider>
@ -72,9 +75,9 @@ const SecuredRoute = ({ route }: SecuredRouteProps) => {
return <ForbiddenSection permissionNeeded={route.access} />;
};
export const App = ({ adminClient }: AdminClientProps) => {
export const App = ({ keycloak, adminClient }: AdminClientProps) => {
return (
<AppContexts adminClient={adminClient}>
<AppContexts keycloak={keycloak} adminClient={adminClient}>
<Page
header={<Header />}
isManagedSidebar

View file

@ -24,30 +24,28 @@ import environment from "./environment";
export const Header = () => {
const { realm } = useRealm();
const adminClient = useAdminClient();
const { keycloak } = useAdminClient();
const { t } = useTranslation();
const ManageAccountDropdownItem = () =>
adminClient.keycloak ? (
<DropdownItem
key="manage account"
id="manage-account"
onClick={() => adminClient.keycloak?.accountManagement()}
>
{t("manageAccount")}
</DropdownItem>
) : null;
const ManageAccountDropdownItem = () => (
<DropdownItem
key="manage account"
id="manage-account"
onClick={() => keycloak.accountManagement()}
>
{t("manageAccount")}
</DropdownItem>
);
const SignOutDropdownItem = () =>
adminClient.keycloak ? (
<DropdownItem
id="sign-out"
key="sign out"
onClick={() => adminClient.keycloak?.logout({ redirectUri: "" })}
>
{t("signOut")}
</DropdownItem>
) : null;
const SignOutDropdownItem = () => (
<DropdownItem
id="sign-out"
key="sign out"
onClick={() => keycloak.logout({ redirectUri: "" })}
>
{t("signOut")}
</DropdownItem>
);
const ServerInfoDropdownItem = () => {
const { realm } = useRealm();

View file

@ -56,7 +56,7 @@ export const REALM_FLOWS = [
export default function AuthenticationSection() {
const { t } = useTranslation("authentication");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { realm } = useRealm();
const history = useHistory();
const [key, setKey] = useState(0);

View file

@ -33,7 +33,7 @@ export const BindFlowDialog = ({ flowAlias, onClose }: BindFlowDialogProps) => {
const { t } = useTranslation("authentication");
const { control, handleSubmit } = useForm<BindingForm>();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const { realm } = useRealm();
const [open, toggle] = useToggle();

View file

@ -32,7 +32,7 @@ export const DuplicateFlowModal = ({
shouldUnregister: false,
});
const { setValue, trigger, getValues } = form;
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
useEffect(() => {

View file

@ -31,7 +31,7 @@ export const EditFlowModal = ({ flow, toggleDialog }: EditFlowModalProps) => {
shouldUnregister: false,
});
const { reset, handleSubmit } = form;
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
useEffect(() => {

View file

@ -51,7 +51,7 @@ export const providerConditionFilter = (
export default function FlowDetails() {
const { t } = useTranslation("authentication");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { realm } = useRealm();
const { addAlert, addError } = useAlerts();
const { id, usedBy, builtIn } = useParams<FlowParams>();

View file

@ -22,7 +22,7 @@ type Row = {
export const RequiredActions = () => {
const { t } = useTranslation("authentication");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const [actions, setActions] = useState<Row[]>();

View file

@ -29,7 +29,7 @@ export const AddFlowDropdown = ({
onAddFlow,
}: AddFlowDropdownProps) => {
const { t } = useTranslation("authentication");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const [open, setOpen] = useState(false);
const [type, setType] = useState<FlowType>();

View file

@ -38,7 +38,7 @@ export const ExecutionConfigModal = ({
execution,
}: ExecutionConfigModalProps) => {
const { t } = useTranslation("authentication");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const [show, setShow] = useState(false);

View file

@ -58,7 +58,7 @@ type AddStepModalProps = {
export const AddStepModal = ({ name, type, onSelect }: AddStepModalProps) => {
const { t } = useTranslation("authentication");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const [value, setValue] = useState<AuthenticationProviderRepresentation>();
const [providers, setProviders] =

View file

@ -50,7 +50,7 @@ export const AddSubFlowModal = ({
const [openProvider, setOpenProvider] = useState(false);
const [formProviders, setFormProviders] =
useState<AuthenticationProviderRepresentation[]>();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
useFetch(
() => adminClient.authenticationManagement.getFormProviders(),

View file

@ -29,7 +29,7 @@ export default function CreateFlow() {
});
const { handleSubmit, register } = form;
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert } = useAlerts();
const save = async (flow: AuthenticationFlowRepresentation) => {

View file

@ -45,7 +45,7 @@ export const OtpPolicy = ({ realm, realmUpdated }: OtpPolicyProps) => {
handleSubmit,
formState: { isDirty, errors },
} = useForm({ mode: "onChange" });
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { realm: realmName } = useRealm();
const { addAlert, addError } = useAlerts();

View file

@ -83,7 +83,7 @@ export const PasswordPolicy = ({
const { t } = useTranslation("authentication");
const { passwordPolicies } = useServerInfo();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const { realm: realmName } = useRealm();

View file

@ -13,7 +13,7 @@ import { KeycloakSpinner } from "../../components/keycloak-spinner/KeycloakSpinn
export const Policies = () => {
const { t } = useTranslation("authentication");
const [subTab, setSubTab] = useState(1);
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { realm: realmName } = useRealm();
const [realm, setRealm] = useState<RealmRepresentation>();

View file

@ -157,7 +157,7 @@ export const WebauthnPolicy = ({
isPasswordLess = false,
}: WebauthnPolicyProps) => {
const { t } = useTranslation("authentication");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const { realm: realmName } = useRealm();
const { enabled } = useHelp();

View file

@ -27,7 +27,7 @@ export const ChangeTypeDropdown = ({
const { t } = useTranslation("client-scopes");
const [open, setOpen] = useState(false);
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
return (

View file

@ -53,7 +53,7 @@ export default function ClientScopesSection() {
const { realm } = useRealm();
const { t } = useTranslation("client-scopes");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const [kebabOpen, setKebabOpen] = useState(false);

View file

@ -33,7 +33,7 @@ import "./mapping-details.css";
export default function MappingDetails() {
const { t } = useTranslation("client-scopes");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const { id, mapperId, type } = useParams<MapperParams>();

View file

@ -48,7 +48,7 @@ export default function ClientScopeForm() {
const { realm } = useRealm();
const [hide, toggleHide] = useToggle();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { id, type } = useParams<{ id: string; type: AllClientScopes }>();
const { addAlert, addError } = useAlerts();

View file

@ -183,7 +183,7 @@ export type SaveOptions = {
export default function ClientDetails() {
const { t } = useTranslation("clients");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const { realm } = useRealm();
const { profileInfo } = useServerInfo();

View file

@ -13,7 +13,7 @@ type ClientSessionsProps = {
};
export const ClientSessions = ({ client }: ClientSessionsProps) => {
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { t } = useTranslation("sessions");
const loader: LoaderFunction<UserSessionRepresentation> = (first, max) =>

View file

@ -24,7 +24,7 @@ import {
import { ViewHeader } from "../components/view-header/ViewHeader";
import { useAdminClient } from "../context/auth/AdminClient";
import { useRealm } from "../context/realm-context/RealmContext";
import { emptyFormatter, exportClient, getBaseUrl } from "../util";
import { addTrailingSlash, emptyFormatter, exportClient } from "../util";
import { InitialAccessTokenList } from "./initial-access/InitialAccessTokenList";
import { toAddClient } from "./routes/AddClient";
import { toClient } from "./routes/Client";
@ -42,9 +42,8 @@ export default function ClientsSection() {
const { t } = useTranslation("clients");
const { addAlert, addError } = useAlerts();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { realm } = useRealm();
const baseUrl = getBaseUrl(adminClient);
const history = useHistory();
const [key, setKey] = useState(0);
@ -224,8 +223,14 @@ export default function ClientsSection() {
) {
return (
client.rootUrl
.replace("${authBaseUrl}", baseUrl)
.replace("${authAdminUrl}", baseUrl) +
.replace(
"${authBaseUrl}",
addTrailingSlash(adminClient.baseUrl)
)
.replace(
"${authAdminUrl}",
addTrailingSlash(adminClient.baseUrl)
) +
(client.baseUrl ? client.baseUrl.substring(1) : "")
);
}

View file

@ -24,7 +24,7 @@ import { GeneralSettings } from "./GeneralSettings";
export default function NewClientForm() {
const { t } = useTranslation("clients");
const { realm } = useRealm();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const history = useHistory();
const [showCapabilityConfig, setShowCapabilityConfig] = useState(false);

View file

@ -32,7 +32,7 @@ export const AddHostDialog = ({
}: AddHostDialogProps) => {
const { t } = useTranslation("clients");
const { register, getValues } = useForm<Host>();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
return (

View file

@ -30,7 +30,7 @@ export const AuthenticationOverrides = ({
reset,
hasConfigureAccess,
}: AuthenticationOverridesProps) => {
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { t } = useTranslation("clients");
const [flows, setFlows] = useState<JSX.Element[]>([]);
const [browserFlowOpen, setBrowserFlowOpen] = useState(false);

View file

@ -30,7 +30,7 @@ export const ClusteringPanel = ({
}: AdvancedProps) => {
const { t } = useTranslation("clients");
const { control } = useFormContext();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const formatDate = useFormatDate();

View file

@ -29,7 +29,7 @@ export const RevocationPanel = ({
const pushRevocationButtonRef = useRef<HTMLElement>();
const { t } = useTranslation("clients");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { realm } = useRealm();
const { addAlert } = useAlerts();
const formatDate = useFormatDate();

View file

@ -87,7 +87,7 @@ export const AuthorizationEvaluate = ({ client }: Props) => {
formState: { isValid },
} = form;
const { t } = useTranslation("clients");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const realm = useRealm();
const [scopesDropdownOpen, setScopesDropdownOpen] = useState(false);

View file

@ -24,7 +24,7 @@ import "./authorization-details.css";
export const AuthorizationExport = () => {
const { t } = useTranslation("clients");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { clientId } = useParams<ClientParams>();
const { addAlert, addError } = useAlerts();

View file

@ -27,7 +27,7 @@ export const DeleteScopeDialog = ({
toggleDialog,
}: DeleteScopeDialogProps) => {
const { t } = useTranslation("clients");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
return (

View file

@ -20,7 +20,7 @@ type DetailCellProps = {
};
export const DetailCell = ({ id, clientId, uris }: DetailCellProps) => {
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { realm } = useRealm();
const [scope, setScope] = useState<Scope>();
const [permissions, setPermissions] =

View file

@ -51,7 +51,7 @@ export default function PermissionDetails() {
NewPermissionParams & PermissionDetailsParams
>();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const [permission, setPermission] = useState<PolicyRepresentation>();
const [applyToResourceTypeFlag, setApplyToResourceTypeFlag] = useState(false);

View file

@ -53,7 +53,7 @@ type ExpandablePolicyRepresentation = PolicyRepresentation & {
export const AuthorizationPermissions = ({ clientId }: PermissionsProps) => {
const { t } = useTranslation("clients");
const history = useHistory();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const { realm } = useRealm();

View file

@ -49,7 +49,7 @@ type ExpandablePolicyRepresentation = PolicyRepresentation & {
export const AuthorizationPolicies = ({ clientId }: PoliciesProps) => {
const { t } = useTranslation("clients");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const { realm } = useRealm();
const history = useHistory();

View file

@ -48,7 +48,7 @@ export default function ResourceDetails() {
const [permissions, setPermission] =
useState<ResourceServerRepresentation[]>();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const form = useForm<SubmittedResource>({
shouldUnregister: false,

View file

@ -44,7 +44,7 @@ type ExpandableResourceRepresentation = ResourceRepresentation & {
export const AuthorizationResources = ({ clientId }: ResourcesProps) => {
const { t } = useTranslation("clients");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const { realm } = useRealm();

View file

@ -57,7 +57,7 @@ export const ResourcesPolicySelect = ({
isRequired = false,
}: ResourcesPolicySelectProps) => {
const { t } = useTranslation("clients");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const {
control,

View file

@ -30,7 +30,7 @@ export default function ScopeDetails() {
const { id, scopeId, realm } = useParams<ScopeDetailsParams>();
const history = useHistory();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const [deleteDialog, toggleDeleteDialog] = useToggle();

View file

@ -24,7 +24,7 @@ export const ScopePicker = ({ clientId }: { clientId: string }) => {
const [scopes, setScopes] = useState<JSX.Element[]>();
const [search, setSearch] = useState("");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
useFetch(
() => {

View file

@ -18,7 +18,7 @@ export const ScopeSelect = ({
preSelected,
}: ScopeSelectProps) => {
const { t } = useTranslation("clients");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const {
control,

View file

@ -47,7 +47,7 @@ export type ExpandableScopeRepresentation = ScopeRepresentation & {
export const AuthorizationScopes = ({ clientId }: ScopesProps) => {
const { t } = useTranslation("clients");
const history = useHistory();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { realm } = useRealm();
const [deleteDialog, toggleDeleteDialog] = useToggle();

View file

@ -36,7 +36,7 @@ export const AuthorizationSettings = ({ clientId }: { clientId: string }) => {
const form = useForm<ResourceServerRepresentation>({});
const { control, reset, handleSubmit } = form;
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
useFetch(

View file

@ -26,7 +26,7 @@ export const Client = () => {
const [clients, setClients] = useState<ClientRepresentation[]>([]);
const [search, setSearch] = useState("");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
useFetch(
async () => {

View file

@ -40,7 +40,7 @@ export const ClientScope = () => {
ClientScopeRepresentation[]
>([]);
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const localeSort = useLocaleSort();
useFetch(

View file

@ -41,7 +41,7 @@ export const Group = () => {
[]
);
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
useFetch(
() => {

View file

@ -66,7 +66,7 @@ export default function PolicyDetails() {
const form = useForm({ shouldUnregister: false });
const { reset, handleSubmit } = form;
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const [policy, setPolicy] = useState<PolicyRepresentation>();

View file

@ -33,7 +33,7 @@ export const Role = () => {
const [open, setOpen] = useState(false);
const [selectedRoles, setSelectedRoles] = useState<Row[]>([]);
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
useFetch(
async () => {

View file

@ -77,7 +77,7 @@ const ExpireDateFormatter = ({ time }: { time: number }) => {
export const ClientSecret = ({ client, secret, toggle }: ClientSecretProps) => {
const { t } = useTranslation("clients");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const [secretRotated, setSecretRotated] = useState<string | undefined>(

View file

@ -43,7 +43,7 @@ export type CredentialsProps = {
export const Credentials = ({ client, save, refresh }: CredentialsProps) => {
const { t } = useTranslation("clients");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const clientId = client.id!;

View file

@ -26,7 +26,7 @@ import { toClients } from "../routes/Clients";
export default function ImportForm() {
const { t } = useTranslation("clients");
const history = useHistory();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { realm } = useRealm();
const form = useForm<ClientRepresentation>({ shouldUnregister: false });
const { register, handleSubmit, setValue } = form;

View file

@ -30,7 +30,7 @@ export default function CreateInitialAccessToken() {
formState: { isValid, errors },
} = useForm({ mode: "onChange" });
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { realm } = useRealm();
const { addAlert, addError } = useAlerts();

View file

@ -16,7 +16,7 @@ import useFormatDate, { FORMAT_DATE_AND_TIME } from "../../utils/useFormatDate";
export const InitialAccessTokenList = () => {
const { t } = useTranslation("clients");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const { realm } = useRealm();
const formatDate = useFormatDate();

View file

@ -46,7 +46,7 @@ export const Keys = ({ clientId, save, hasConfigureAccess }: KeysProps) => {
getValues,
formState: { isDirty },
} = useFormContext<ClientRepresentation>();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const [keyInfo, setKeyInfo] = useState<CertificateRepresentation>();

View file

@ -25,7 +25,7 @@ export const SamlImportKeyDialog = ({
const form = useFormContext();
const { handleSubmit } = form;
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const submit = (form: SamlKeysDialogForm) => {

View file

@ -150,7 +150,7 @@ export const SamlKeys = ({ clientId, save }: SamlKeysProps) => {
const { setValue } = useFormContext();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
useFetch(

View file

@ -81,7 +81,7 @@ export const SamlKeysDialog = ({
formState: { isDirty },
} = form;
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const submit = (form: SamlKeysDialogForm) => {

View file

@ -63,7 +63,7 @@ export const ClientScopes = ({
fineGrainedAccess,
}: ClientScopesProps) => {
const { t } = useTranslation("clients");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const { realm } = useRealm();
const localeSort = useLocaleSort();

View file

@ -30,7 +30,7 @@ export const DedicatedScope = ({
client: initialClient,
}: DedicatedScopeProps) => {
const { t } = useTranslation("clients");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const [client, setClient] = useState<ClientRepresentation>(initialClient);

View file

@ -33,7 +33,7 @@ export default function DedicatedScopes() {
const history = useHistory();
const { realm, clientId } = useParams<DedicatedScopeDetailsParams>();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const [client, setClient] = useState<ClientRepresentation>();

View file

@ -112,7 +112,7 @@ export const EvaluateScopes = ({ clientId, protocol }: EvaluateScopesProps) => {
const prefix = "openid";
const { t } = useTranslation("clients");
const { enabled } = useHelp();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { realm } = useRealm();
const mapperTypes = useServerInfo().protocolMapperTypes![protocol];

View file

@ -27,7 +27,7 @@ type ServiceAccountProps = {
export const ServiceAccount = ({ client }: ServiceAccountProps) => {
const { t } = useTranslation("clients");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const { realm } = useRealm();

View file

@ -35,7 +35,7 @@ export const ClientSelect = ({
const [clients, setClients] = useState<ClientRepresentation[]>([]);
const [search, setSearch] = useState("");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
useFetch(
() => {

View file

@ -1,10 +1,11 @@
/**
* @vitest-environment jsdom
*/
import React, { FunctionComponent } from "react";
import type KeycloakAdminClient from "@keycloak/keycloak-admin-client";
import type { ServerInfoRepresentation } from "@keycloak/keycloak-admin-client/lib/defs/serverInfoRepesentation";
import { render, waitFor } from "@testing-library/react";
import type Keycloak from "keycloak-js";
import React, { FunctionComponent } from "react";
import { HashRouter } from "react-router-dom";
import { describe, expect, it } from "vitest";
import { AccessContextProvider } from "../../context/access/Access";
@ -16,40 +17,24 @@ import { WhoAmIContextProvider } from "../../context/whoami/WhoAmI";
import whoamiMock from "../../context/whoami/__tests__/mock-whoami.json";
import { DataLoader } from "./DataLoader";
/**
* This component provides some mocked default react context so that other components can work in a storybook.
* In it's simplest form wrap your component like so:
* @example
* <MockAdminClient>
* <SomeComponent />
* </MockAdminClient>
* @example <caption>With an endpoint, roles => findOneById</caption>
* <MockAdminClient mock={{ roles: { findOneById: () => mockJson } }}>
* <<SomeComponent />
* </MockAdminClient>
* @param props mock endpoints to be mocked
*/
export const MockAdminClient: FunctionComponent<{ mock?: object }> = (
props
) => {
const MockAdminClient: FunctionComponent = ({ children }) => {
const keycloak = {
init: () => Promise.resolve(),
} as unknown as Keycloak;
const adminClient = {
whoAmI: { find: () => Promise.resolve(whoamiMock) },
setConfig: () => {},
} as unknown as KeycloakAdminClient;
return (
<HashRouter>
<ServerInfoContext.Provider
value={serverInfo as unknown as ServerInfoRepresentation}
>
<AdminClient.Provider
value={
{
...props.mock,
keycloak: {},
whoAmI: { find: () => Promise.resolve(whoamiMock) },
setConfig: () => {},
} as unknown as KeycloakAdminClient
}
>
<AdminClient.Provider value={{ keycloak, adminClient }}>
<WhoAmIContextProvider>
<RealmContext.Provider value={{ realm: "master" }}>
<AccessContextProvider>{props.children}</AccessContextProvider>
<AccessContextProvider>{children}</AccessContextProvider>
</RealmContext.Provider>
</WhoAmIContextProvider>
</AdminClient.Provider>

View file

@ -34,7 +34,7 @@ export const DownloadDialog = ({
toggleDialog,
protocol = "openid-connect",
}: DownloadDialogProps) => {
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { t } = useTranslation("common");
const { enabled } = useHelp();
const serverInfo = useServerInfo();

View file

@ -21,7 +21,7 @@ export const MultivaluedScopesComponent = ({
const { t } = useTranslation("dynamic");
const { control } = useFormContext();
const { conditionName } = useParams<EditClientPolicyConditionParams>();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const [open, setOpen] = useState(false);
const localeSort = useLocaleSort();
const [clientScopes, setClientScopes] = useState<ClientScopeRepresentation[]>(

View file

@ -32,7 +32,7 @@ export const RoleComponent = ({
}: ComponentProps) => {
const { t } = useTranslation("dynamic");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { realm } = useRealm();
const {
control,

View file

@ -44,7 +44,7 @@ export const GroupPickerDialog = ({
onConfirm,
}: GroupPickerDialogProps) => {
const { t } = useTranslation();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const [selectedRows, setSelectedRows] = useState<SelectableGroup[]>([]);
const [navigation, setNavigation] = useState<SelectableGroup[]>([]);

View file

@ -46,7 +46,7 @@ type PermissionsTabProps = {
export const PermissionsTab = ({ id, type }: PermissionsTabProps) => {
const { t } = useTranslation("common");
const history = useHistory();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { realm } = useRealm();
const [realmId, setRealmId] = useState("");
const [permission, setPermission] = useState<ManagementPermissionReference>();

View file

@ -56,7 +56,7 @@ export const AddRoleMappingModal = ({
onClose,
}: AddRoleMappingModalProps) => {
const { t } = useTranslation("common");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const [clients, setClients] = useState<ClientRole[]>([]);
const [searchToggle, setSearchToggle] = useState(false);

View file

@ -167,7 +167,7 @@ export const RoleMapping = ({
onHideRolesToggle,
}: RoleMappingProps) => {
const { t } = useTranslation(type);
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const [key, setKey] = useState(0);

View file

@ -41,7 +41,7 @@ export const UserSelect = ({
const [users, setUsers] = useState<(UserRepresentation | undefined)[]>([]);
const [search, setSearch] = useState("");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
useFetch(
() => {

View file

@ -25,7 +25,7 @@ export const RealmsContext = createContext<RealmsContextProps | undefined>(
);
export const RealmsProvider: FunctionComponent = ({ children }) => {
const adminClient = useAdminClient();
const { keycloak, adminClient } = useAdminClient();
const [realms, setRealms] = useState<RealmRepresentation[]>([]);
const recentUsed = useMemo(() => new RecentUsed(), []);
@ -57,7 +57,7 @@ export const RealmsProvider: FunctionComponent = ({ children }) => {
const refresh = useCallback(async () => {
//this is needed otherwise the realm find function will not return
//new or renamed realms because of the cached realms in the token (perhaps?)
await adminClient.keycloak?.updateToken(Number.MAX_VALUE);
await keycloak.updateToken(Number.MAX_VALUE);
updateRealms(await adminClient.realms.find({ briefRepresentation: true }));
}, []);

View file

@ -1,12 +1,18 @@
import KeycloakAdminClient from "@keycloak/keycloak-admin-client";
import axios from "axios";
import Keycloak from "keycloak-js";
import { createContext, DependencyList, useEffect } from "react";
import { useErrorHandler } from "react-error-boundary";
import environment from "../../environment";
import useRequiredContext from "../../utils/useRequiredContext";
export const AdminClient = createContext<KeycloakAdminClient | undefined>(
export type AdminClientProps = {
keycloak: Keycloak;
adminClient: KeycloakAdminClient;
};
export const AdminClient = createContext<AdminClientProps | undefined>(
undefined
);
@ -32,7 +38,7 @@ export function useFetch<T>(
callback: (param: T) => void,
deps?: DependencyList
) {
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const onError = useErrorHandler();
useEffect(() => {
@ -65,21 +71,31 @@ export function useFetch<T>(
}
export async function initAdminClient() {
const kcAdminClient = new KeycloakAdminClient();
const keycloak = new Keycloak({
url: environment.authServerUrl,
realm: environment.loginRealm,
clientId: environment.isRunningAsTheme
? "security-admin-console"
: "security-admin-console-v2",
});
await kcAdminClient.init(
{ onLoad: "check-sso", pkceMethod: "S256" },
{
url: environment.authServerUrl,
realm: environment.loginRealm,
clientId: environment.isRunningAsTheme
? "security-admin-console"
: "security-admin-console-v2",
}
);
await keycloak.init({ onLoad: "check-sso", pkceMethod: "S256" });
kcAdminClient.setConfig({ realmName: environment.loginRealm });
kcAdminClient.baseUrl = environment.authServerUrl;
const adminClient = new KeycloakAdminClient();
return kcAdminClient;
adminClient.setConfig({ realmName: environment.loginRealm });
adminClient.baseUrl = keycloak.authServerUrl ?? environment.authServerUrl;
adminClient.registerTokenProvider({
async getAccessToken() {
try {
await keycloak.updateToken(5);
} catch (error) {
keycloak.login();
}
return keycloak.token;
},
});
return { keycloak, adminClient };
}

View file

@ -18,7 +18,7 @@ export const RealmContext = React.createContext<RealmContextType | undefined>(
);
export const RealmContextProvider: FunctionComponent = ({ children }) => {
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const recentUsed = useMemo(() => new RecentUsed(), []);
const routeMatch = useRouteMatch<DashboardParams>(DashboardRoute.path);
const realmParam = routeMatch?.params.realm;

View file

@ -15,7 +15,7 @@ export const useLoginProviders = () =>
sortProviders(useServerInfo().providers!["login-protocol"].providers);
export const ServerInfoProvider: FunctionComponent = ({ children }) => {
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const [serverInfo, setServerInfo] = useState<ServerInfoRepresentation>({});
useFetch(adminClient.serverInfo.find, setServerInfo, []);

View file

@ -60,7 +60,7 @@ export const WhoAmIContext = React.createContext<WhoAmIProps | undefined>(
export const useWhoAmI = () => useRequiredContext(WhoAmIContext);
export const WhoAmIContextProvider: FunctionComponent = ({ children }) => {
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const [whoAmI, setWhoAmI] = useState<WhoAmI>(new WhoAmI());
const [key, setKey] = useState(0);

View file

@ -89,7 +89,7 @@ const DisplayDialog: FunctionComponent<DisplayDialogProps> = ({
export const AdminEvents = () => {
const { t } = useTranslation("events");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { realm } = useRealm();
const serverInfo = useServerInfo();
const formatDate = useFormatDate();

View file

@ -92,7 +92,7 @@ const DetailCell = (event: EventRepresentation) => (
export default function EventsSection() {
const { t } = useTranslation("events");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { realm } = useRealm();
const formatDate = useFormatDate();
const [key, setKey] = useState(0);

View file

@ -24,7 +24,7 @@ import { useLocation } from "react-router-dom";
export const GroupAttributes = () => {
const { t } = useTranslation("groups");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const form = useForm<AttributeForm>({
mode: "onChange",

View file

@ -18,7 +18,7 @@ type GroupRoleMappingProps = {
export const GroupRoleMapping = ({ id, name }: GroupRoleMappingProps) => {
const { t } = useTranslation("clients");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const [hide, setHide] = useState(false);

View file

@ -29,7 +29,7 @@ import { useAccess } from "../context/access/Access";
export const GroupTable = () => {
const { t } = useTranslation("groups");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const { realm } = useRealm();
const [isKebabOpen, setIsKebabOpen] = useState(false);

View file

@ -31,7 +31,7 @@ export const GroupsModal = ({
refresh,
}: GroupsModalProps) => {
const { t } = useTranslation("groups");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const {
register,

View file

@ -36,7 +36,7 @@ export default function GroupsSection() {
const { t } = useTranslation("groups");
const [activeTab, setActiveTab] = useState(0);
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { subGroups, setSubGroups, currentGroup } = useSubGroups();
const { addAlert, addError } = useAlerts();
const { realm } = useRealm();

View file

@ -34,7 +34,7 @@ type MembersOf = UserRepresentation & {
export const Members = () => {
const { t } = useTranslation("groups");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { realm } = useRealm();
const { addAlert, addError } = useAlerts();
const location = useLocation();

View file

@ -22,7 +22,7 @@ type MemberModalProps = {
export const MemberModal = ({ groupId, onClose }: MemberModalProps) => {
const { t } = useTranslation("groups");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const [selectedRows, setSelectedRows] = useState<UserRepresentation[]>([]);

View file

@ -31,7 +31,7 @@ type SearchGroup = GroupRepresentation & {
export default function SearchGroups() {
const { t } = useTranslation("groups");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { realm } = useRealm();
const [searchTerm, setSearchTerm] = useState("");

View file

@ -57,7 +57,7 @@ export default function IdentityProvidersSection() {
const [selectedProvider, setSelectedProvider] =
useState<IdentityProviderRepresentation>();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
useFetch(

View file

@ -30,7 +30,7 @@ export const ManageOrderDialog = ({
onClose,
}: ManageOrderDialogProps) => {
const { t } = useTranslation("identity-providers");
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const [alias, setAlias] = useState("");

View file

@ -30,7 +30,7 @@ export default function AddIdentityProvider() {
formState: { isDirty },
} = form;
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const history = useHistory();
const { realm } = useRealm();

View file

@ -56,7 +56,7 @@ export default function AddMapper() {
const localeSort = useLocaleSort();
const { realm } = useRealm();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { providerId, alias } = useParams<IdentityProviderAddMapperParams>();
const { id } = useParams<IdentityProviderEditMapperParams>();

View file

@ -40,7 +40,7 @@ export default function AddOpenIdConnect() {
formState: { isDirty },
} = form;
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const { realm } = useRealm();

View file

@ -37,7 +37,7 @@ export default function AddSamlConnect() {
formState: { isDirty },
} = form;
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert } = useAlerts();
const { realm } = useRealm();

View file

@ -23,7 +23,7 @@ const LoginFlow = ({
const { t } = useTranslation("identity-providers");
const { control } = useFormContext();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const [flows, setFlows] = useState<AuthenticationFlowRepresentation[]>();
const [open, setOpen] = useState(false);

View file

@ -126,7 +126,7 @@ export default function DetailSettings() {
const [selectedMapper, setSelectedMapper] =
useState<IdPWithMapperAttributes>();
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const history = useHistory();
const { realm } = useRealm();

View file

@ -8,14 +8,14 @@ import { useAdminClient } from "../../context/auth/AdminClient";
import { JsonFileUpload } from "../../components/json-file-upload/JsonFileUpload";
import { useRealm } from "../../context/realm-context/RealmContext";
import { DiscoverySettings } from "./DiscoverySettings";
import { getBaseUrl } from "../../util";
import { DiscoveryEndpointField } from "../component/DiscoveryEndpointField";
import { addTrailingSlash } from "../../util";
export const OpenIdConnectSettings = () => {
const { t } = useTranslation("identity-providers");
const id = "oidc";
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { realm } = useRealm();
const {
setValue,
@ -40,8 +40,8 @@ export const OpenIdConnectSettings = () => {
try {
const response = await fetch(
`${getBaseUrl(
adminClient
`${addTrailingSlash(
adminClient.baseUrl
)}admin/realms/${realm}/identity-provider/import-config`,
{
method: "POST",

View file

@ -10,16 +10,16 @@ import type IdentityProviderRepresentation from "@keycloak/keycloak-admin-client
import { FileUploadForm } from "../../components/json-file-upload/FileUploadForm";
import { useRealm } from "../../context/realm-context/RealmContext";
import { DescriptorSettings } from "./DescriptorSettings";
import { getBaseUrl } from "../../util";
import { DiscoveryEndpointField } from "../component/DiscoveryEndpointField";
import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput";
import environment from "../../environment";
import { addTrailingSlash } from "../../util";
export const SamlConnectSettings = () => {
const { t } = useTranslation("identity-providers");
const id = "saml";
const adminClient = useAdminClient();
const { adminClient } = useAdminClient();
const { realm } = useRealm();
const {
setValue,
@ -46,8 +46,8 @@ export const SamlConnectSettings = () => {
try {
const response = await fetch(
`${getBaseUrl(
adminClient
`${addTrailingSlash(
adminClient.baseUrl
)}admin/realms/${realm}/identity-provider/import-config`,
{
method: "POST",

Some files were not shown because too many files have changed in this diff Show more