Merge pull request #556 from edewit/change-asyncFetch-to-it's-own-hook
changed `asyncStateFetch` to be it's own hook
This commit is contained in:
commit
c0c3fd6692
33 changed files with 569 additions and 728 deletions
|
@ -25,6 +25,7 @@
|
||||||
"@patternfly/react-core": "4.115.1",
|
"@patternfly/react-core": "4.115.1",
|
||||||
"@patternfly/react-icons": "4.10.1",
|
"@patternfly/react-icons": "4.10.1",
|
||||||
"@patternfly/react-table": "4.26.7",
|
"@patternfly/react-table": "4.26.7",
|
||||||
|
"axios": "^0.21.1",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"i18next": "^19.6.2",
|
"i18next": "^19.6.2",
|
||||||
"keycloak-admin": "1.14.16",
|
"keycloak-admin": "1.14.16",
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import React, { useContext, useEffect, useState } from "react";
|
import React, { useContext, useState } from "react";
|
||||||
import { useHistory, useParams } from "react-router-dom";
|
import { useHistory, useParams } from "react-router-dom";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
import {
|
import {
|
||||||
FormGroup,
|
FormGroup,
|
||||||
PageSection,
|
PageSection,
|
||||||
|
@ -24,10 +23,7 @@ import ClientRepresentation from "keycloak-admin/lib/defs/clientRepresentation";
|
||||||
import ProtocolMapperRepresentation from "keycloak-admin/lib/defs/protocolMapperRepresentation";
|
import ProtocolMapperRepresentation from "keycloak-admin/lib/defs/protocolMapperRepresentation";
|
||||||
import { useAlerts } from "../../components/alert/Alerts";
|
import { useAlerts } from "../../components/alert/Alerts";
|
||||||
import { RealmContext } from "../../context/realm-context/RealmContext";
|
import { RealmContext } from "../../context/realm-context/RealmContext";
|
||||||
import {
|
import { useAdminClient, useFetch } from "../../context/auth/AdminClient";
|
||||||
useAdminClient,
|
|
||||||
asyncStateFetch,
|
|
||||||
} from "../../context/auth/AdminClient";
|
|
||||||
|
|
||||||
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
||||||
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
||||||
|
@ -36,7 +32,6 @@ import { FormAccess } from "../../components/form-access/FormAccess";
|
||||||
export const RoleMappingForm = () => {
|
export const RoleMappingForm = () => {
|
||||||
const { realm } = useContext(RealmContext);
|
const { realm } = useContext(RealmContext);
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const handleError = useErrorHandler();
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { addAlert } = useAlerts();
|
const { addAlert } = useAlerts();
|
||||||
|
|
||||||
|
@ -51,53 +46,49 @@ export const RoleMappingForm = () => {
|
||||||
const [selectedClient, setSelectedClient] = useState<ClientRepresentation>();
|
const [selectedClient, setSelectedClient] = useState<ClientRepresentation>();
|
||||||
const [clientRoles, setClientRoles] = useState<RoleRepresentation[]>([]);
|
const [clientRoles, setClientRoles] = useState<RoleRepresentation[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useFetch(
|
||||||
return asyncStateFetch(
|
async () => {
|
||||||
async () => {
|
const clients = await adminClient.clients.find();
|
||||||
const clients = await adminClient.clients.find();
|
|
||||||
|
|
||||||
const asyncFilter = async (
|
const asyncFilter = async (
|
||||||
predicate: (client: ClientRepresentation) => Promise<boolean>
|
predicate: (client: ClientRepresentation) => Promise<boolean>
|
||||||
) => {
|
) => {
|
||||||
const results = await Promise.all(clients.map(predicate));
|
const results = await Promise.all(clients.map(predicate));
|
||||||
return clients.filter((_, index) => results[index]);
|
return clients.filter((_, index) => results[index]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const filteredClients = await asyncFilter(
|
const filteredClients = await asyncFilter(
|
||||||
async (client) =>
|
async (client) =>
|
||||||
(await adminClient.clients.listRoles({ id: client.id! })).length > 0
|
(await adminClient.clients.listRoles({ id: client.id! })).length > 0
|
||||||
);
|
);
|
||||||
|
|
||||||
filteredClients.map(
|
filteredClients.map(
|
||||||
(client) =>
|
(client) =>
|
||||||
(client.toString = function () {
|
(client.toString = function () {
|
||||||
return this.clientId!;
|
return this.clientId!;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
return filteredClients;
|
return filteredClients;
|
||||||
},
|
},
|
||||||
(filteredClients) => setClients(filteredClients),
|
(filteredClients) => setClients(filteredClients),
|
||||||
handleError
|
[]
|
||||||
);
|
);
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useFetch(
|
||||||
return asyncStateFetch(
|
async () => {
|
||||||
async () => {
|
const client = selectedClient as ClientRepresentation;
|
||||||
const client = selectedClient as ClientRepresentation;
|
if (client && client.name !== "realmRoles") {
|
||||||
if (client && client.name !== "realmRoles") {
|
const clientRoles = await adminClient.clients.listRoles({
|
||||||
const clientRoles = await adminClient.clients.listRoles({
|
id: client.id!,
|
||||||
id: client.id!,
|
});
|
||||||
});
|
return clientRoles;
|
||||||
return clientRoles;
|
} else {
|
||||||
} else {
|
return await adminClient.roles.find();
|
||||||
return await adminClient.roles.find();
|
}
|
||||||
}
|
},
|
||||||
},
|
(clientRoles) => setClientRoles(clientRoles),
|
||||||
(clientRoles) => setClientRoles(clientRoles),
|
[selectedClient]
|
||||||
handleError
|
);
|
||||||
);
|
|
||||||
}, [selectedClient]);
|
|
||||||
|
|
||||||
const save = async (mapping: ProtocolMapperRepresentation) => {
|
const save = async (mapping: ProtocolMapperRepresentation) => {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useHistory, useParams } from "react-router-dom";
|
import { useHistory, useParams } from "react-router-dom";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
import {
|
import {
|
||||||
ActionGroup,
|
ActionGroup,
|
||||||
AlertVariant,
|
AlertVariant,
|
||||||
|
@ -24,10 +23,7 @@ import { ConfigPropertyRepresentation } from "keycloak-admin/lib/defs/configProp
|
||||||
import ProtocolMapperRepresentation from "keycloak-admin/lib/defs/protocolMapperRepresentation";
|
import ProtocolMapperRepresentation from "keycloak-admin/lib/defs/protocolMapperRepresentation";
|
||||||
|
|
||||||
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
||||||
import {
|
import { useAdminClient, useFetch } from "../../context/auth/AdminClient";
|
||||||
useAdminClient,
|
|
||||||
asyncStateFetch,
|
|
||||||
} from "../../context/auth/AdminClient";
|
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog";
|
import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog";
|
||||||
import { useAlerts } from "../../components/alert/Alerts";
|
import { useAlerts } from "../../components/alert/Alerts";
|
||||||
|
@ -45,7 +41,6 @@ type Params = {
|
||||||
export const MappingDetails = () => {
|
export const MappingDetails = () => {
|
||||||
const { t } = useTranslation("client-scopes");
|
const { t } = useTranslation("client-scopes");
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const handleError = useErrorHandler();
|
|
||||||
const { addAlert } = useAlerts();
|
const { addAlert } = useAlerts();
|
||||||
|
|
||||||
const { id, mapperId } = useParams<Params>();
|
const { id, mapperId } = useParams<Params>();
|
||||||
|
@ -61,52 +56,50 @@ export const MappingDetails = () => {
|
||||||
const serverInfo = useServerInfo();
|
const serverInfo = useServerInfo();
|
||||||
const isGuid = /^[{]?[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}[}]?$/;
|
const isGuid = /^[{]?[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}[}]?$/;
|
||||||
|
|
||||||
useEffect(() => {
|
useFetch(
|
||||||
return asyncStateFetch(
|
async () => {
|
||||||
async () => {
|
if (mapperId.match(isGuid)) {
|
||||||
if (mapperId.match(isGuid)) {
|
const data = await adminClient.clientScopes.findProtocolMapper({
|
||||||
const data = await adminClient.clientScopes.findProtocolMapper({
|
id,
|
||||||
id,
|
mapperId,
|
||||||
mapperId,
|
});
|
||||||
|
if (data) {
|
||||||
|
Object.entries(data).map((entry) => {
|
||||||
|
convertToFormValues(entry[1], "config", setValue);
|
||||||
});
|
});
|
||||||
if (data) {
|
|
||||||
Object.entries(data).map((entry) => {
|
|
||||||
convertToFormValues(entry[1], "config", setValue);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const mapperTypes = serverInfo.protocolMapperTypes![data!.protocol!];
|
|
||||||
const properties = mapperTypes.find(
|
|
||||||
(type) => type.id === data!.protocolMapper
|
|
||||||
)?.properties!;
|
|
||||||
|
|
||||||
return {
|
|
||||||
configProperties: properties,
|
|
||||||
mapping: data,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
const scope = await adminClient.clientScopes.findOne({ id });
|
|
||||||
const protocolMappers = serverInfo.protocolMapperTypes![
|
|
||||||
scope.protocol!
|
|
||||||
];
|
|
||||||
const mapping = protocolMappers.find(
|
|
||||||
(mapper) => mapper.id === mapperId
|
|
||||||
)!;
|
|
||||||
return {
|
|
||||||
mapping: {
|
|
||||||
name: mapping.name,
|
|
||||||
protocol: scope.protocol,
|
|
||||||
protocolMapper: mapperId,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
},
|
const mapperTypes = serverInfo.protocolMapperTypes![data!.protocol!];
|
||||||
(result) => {
|
const properties = mapperTypes.find(
|
||||||
setConfigProperties(result.configProperties);
|
(type) => type.id === data!.protocolMapper
|
||||||
setMapping(result.mapping);
|
)?.properties!;
|
||||||
},
|
|
||||||
handleError
|
return {
|
||||||
);
|
configProperties: properties,
|
||||||
}, []);
|
mapping: data,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const scope = await adminClient.clientScopes.findOne({ id });
|
||||||
|
const protocolMappers = serverInfo.protocolMapperTypes![
|
||||||
|
scope.protocol!
|
||||||
|
];
|
||||||
|
const mapping = protocolMappers.find(
|
||||||
|
(mapper) => mapper.id === mapperId
|
||||||
|
)!;
|
||||||
|
return {
|
||||||
|
mapping: {
|
||||||
|
name: mapping.name,
|
||||||
|
protocol: scope.protocol,
|
||||||
|
protocolMapper: mapperId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(result) => {
|
||||||
|
setConfigProperties(result.configProperties);
|
||||||
|
setMapping(result.mapping);
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
||||||
titleKey: "common:deleteMappingTitle",
|
titleKey: "common:deleteMappingTitle",
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
AlertVariant,
|
AlertVariant,
|
||||||
|
@ -11,10 +10,7 @@ import {
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
|
|
||||||
import ClientScopeRepresentation from "keycloak-admin/lib/defs/clientScopeRepresentation";
|
import ClientScopeRepresentation from "keycloak-admin/lib/defs/clientScopeRepresentation";
|
||||||
import {
|
import { useAdminClient, useFetch } from "../../context/auth/AdminClient";
|
||||||
useAdminClient,
|
|
||||||
asyncStateFetch,
|
|
||||||
} from "../../context/auth/AdminClient";
|
|
||||||
import { KeycloakTabs } from "../../components/keycloak-tabs/KeycloakTabs";
|
import { KeycloakTabs } from "../../components/keycloak-tabs/KeycloakTabs";
|
||||||
import { useAlerts } from "../../components/alert/Alerts";
|
import { useAlerts } from "../../components/alert/Alerts";
|
||||||
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
||||||
|
@ -30,7 +26,6 @@ export const ClientScopeForm = () => {
|
||||||
const [hide, setHide] = useState(false);
|
const [hide, setHide] = useState(false);
|
||||||
|
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const handleError = useErrorHandler();
|
|
||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>();
|
||||||
|
|
||||||
const { addAlert } = useAlerts();
|
const { addAlert } = useAlerts();
|
||||||
|
@ -38,19 +33,17 @@ export const ClientScopeForm = () => {
|
||||||
const [key, setKey] = useState(0);
|
const [key, setKey] = useState(0);
|
||||||
const refresh = () => setKey(new Date().getTime());
|
const refresh = () => setKey(new Date().getTime());
|
||||||
|
|
||||||
useEffect(() => {
|
useFetch(
|
||||||
return asyncStateFetch(
|
async () => {
|
||||||
async () => {
|
if (id) {
|
||||||
if (id) {
|
return await adminClient.clientScopes.findOne({ id });
|
||||||
return await adminClient.clientScopes.findOne({ id });
|
}
|
||||||
}
|
},
|
||||||
},
|
(clientScope) => {
|
||||||
(clientScope) => {
|
setClientScope(clientScope);
|
||||||
setClientScope(clientScope);
|
},
|
||||||
},
|
[key, id]
|
||||||
handleError
|
);
|
||||||
);
|
|
||||||
}, [key, id]);
|
|
||||||
|
|
||||||
const loader = async () => {
|
const loader = async () => {
|
||||||
const assignedRoles = hide
|
const assignedRoles = hide
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useState } from "react";
|
||||||
import {
|
import {
|
||||||
Alert,
|
Alert,
|
||||||
AlertVariant,
|
AlertVariant,
|
||||||
|
@ -12,9 +12,8 @@ import {
|
||||||
TabTitleText,
|
TabTitleText,
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
import { useHistory, useParams } from "react-router-dom";
|
import { useHistory, useParams } from "react-router-dom";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Controller, FormProvider, useForm } from "react-hook-form";
|
import { Controller, FormProvider, useForm, useWatch } from "react-hook-form";
|
||||||
import ClientRepresentation from "keycloak-admin/lib/defs/clientRepresentation";
|
import ClientRepresentation from "keycloak-admin/lib/defs/clientRepresentation";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
|
||||||
|
@ -26,7 +25,7 @@ import {
|
||||||
} from "../components/confirm-dialog/ConfirmDialog";
|
} from "../components/confirm-dialog/ConfirmDialog";
|
||||||
import { DownloadDialog } from "../components/download-dialog/DownloadDialog";
|
import { DownloadDialog } from "../components/download-dialog/DownloadDialog";
|
||||||
import { ViewHeader } from "../components/view-header/ViewHeader";
|
import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||||
import { useAdminClient, asyncStateFetch } from "../context/auth/AdminClient";
|
import { useAdminClient, useFetch } from "../context/auth/AdminClient";
|
||||||
import { Credentials } from "./credentials/Credentials";
|
import { Credentials } from "./credentials/Credentials";
|
||||||
import {
|
import {
|
||||||
convertFormValuesToObject,
|
convertFormValuesToObject,
|
||||||
|
@ -125,7 +124,6 @@ export const ClientDetails = () => {
|
||||||
const { t } = useTranslation("clients");
|
const { t } = useTranslation("clients");
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const { addAlert } = useAlerts();
|
const { addAlert } = useAlerts();
|
||||||
const handleError = useErrorHandler();
|
|
||||||
const { realm } = useRealm();
|
const { realm } = useRealm();
|
||||||
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
@ -140,6 +138,12 @@ export const ClientDetails = () => {
|
||||||
const form = useForm<ClientForm>();
|
const form = useForm<ClientForm>();
|
||||||
const { clientId } = useParams<{ clientId: string }>();
|
const { clientId } = useParams<{ clientId: string }>();
|
||||||
|
|
||||||
|
const clientAuthenticatorType = useWatch({
|
||||||
|
control: form.control,
|
||||||
|
name: "clientAuthenticatorType",
|
||||||
|
defaultValue: "client-secret",
|
||||||
|
});
|
||||||
|
|
||||||
const [client, setClient] = useState<ClientRepresentation>();
|
const [client, setClient] = useState<ClientRepresentation>();
|
||||||
|
|
||||||
const loader = async () => {
|
const loader = async () => {
|
||||||
|
@ -178,16 +182,14 @@ export const ClientDetails = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useFetch(
|
||||||
return asyncStateFetch(
|
() => adminClient.clients.findOne({ id: clientId }),
|
||||||
() => adminClient.clients.findOne({ id: clientId }),
|
(fetchedClient) => {
|
||||||
(fetchedClient) => {
|
setClient(fetchedClient);
|
||||||
setClient(fetchedClient);
|
setupForm(fetchedClient);
|
||||||
setupForm(fetchedClient);
|
},
|
||||||
},
|
[clientId]
|
||||||
handleError
|
);
|
||||||
);
|
|
||||||
}, [clientId]);
|
|
||||||
|
|
||||||
const save = async (
|
const save = async (
|
||||||
{ confirmed = false, messageKey = "clientSaveSuccess" }: SaveOptions = {
|
{ confirmed = false, messageKey = "clientSaveSuccess" }: SaveOptions = {
|
||||||
|
@ -198,8 +200,7 @@ export const ClientDetails = () => {
|
||||||
if (await form.trigger()) {
|
if (await form.trigger()) {
|
||||||
if (
|
if (
|
||||||
!client?.publicClient &&
|
!client?.publicClient &&
|
||||||
client?.clientAuthenticatorType !==
|
client?.clientAuthenticatorType !== clientAuthenticatorType &&
|
||||||
form.getValues("clientAuthenticatorType") &&
|
|
||||||
!confirmed
|
!confirmed
|
||||||
) {
|
) {
|
||||||
toggleChangeAuthenticator();
|
toggleChangeAuthenticator();
|
||||||
|
@ -241,7 +242,7 @@ export const ClientDetails = () => {
|
||||||
<ConfirmDialogModal
|
<ConfirmDialogModal
|
||||||
continueButtonLabel="common:yes"
|
continueButtonLabel="common:yes"
|
||||||
titleKey={t("changeAuthenticatorConfirmTitle", {
|
titleKey={t("changeAuthenticatorConfirmTitle", {
|
||||||
clientAuthenticatorType: form.getValues("clientAuthenticatorType"),
|
clientAuthenticatorType: clientAuthenticatorType,
|
||||||
})}
|
})}
|
||||||
open={changeAuthenticatorOpen}
|
open={changeAuthenticatorOpen}
|
||||||
toggleDialog={toggleChangeAuthenticator}
|
toggleDialog={toggleChangeAuthenticator}
|
||||||
|
@ -249,9 +250,9 @@ export const ClientDetails = () => {
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
{t("changeAuthenticatorConfirm", {
|
{t("changeAuthenticatorConfirm", {
|
||||||
clientAuthenticatorType: form.getValues("clientAuthenticatorType"),
|
clientAuthenticatorType: clientAuthenticatorType,
|
||||||
})}
|
})}
|
||||||
{form.getValues("clientAuthenticatorType") === "client-jwt" && (
|
{clientAuthenticatorType === "client-jwt" && (
|
||||||
<Alert variant="info" isInline title={t("signedJWTConfirm")} />
|
<Alert variant="info" isInline title={t("signedJWTConfirm")} />
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Link, useHistory } from "react-router-dom";
|
import { Link, useHistory } from "react-router-dom";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
|
@ -49,8 +49,6 @@ export const ClientsSection = () => {
|
||||||
return await adminClient.clients.find({ ...params });
|
return await adminClient.clients.find({ ...params });
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(refresh, [selectedClient]);
|
|
||||||
|
|
||||||
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
||||||
titleKey: t("clientDelete", { clientId: selectedClient?.clientId }),
|
titleKey: t("clientDelete", { clientId: selectedClient?.clientId }),
|
||||||
messageKey: "clients:clientDeleteConfirm",
|
messageKey: "clients:clientDeleteConfirm",
|
||||||
|
@ -62,7 +60,7 @@ export const ClientsSection = () => {
|
||||||
id: selectedClient!.id!,
|
id: selectedClient!.id!,
|
||||||
});
|
});
|
||||||
addAlert(t("clientDeletedSuccess"), AlertVariant.success);
|
addAlert(t("clientDeletedSuccess"), AlertVariant.success);
|
||||||
setSelectedClient(undefined);
|
refresh();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
addAlert(t("clientDeleteError", { error }), AlertVariant.danger);
|
addAlert(t("clientDeleteError", { error }), AlertVariant.danger);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Control, Controller } from "react-hook-form";
|
import { Control, Controller } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
@ -11,12 +11,8 @@ import {
|
||||||
|
|
||||||
import { FormAccess } from "../../components/form-access/FormAccess";
|
import { FormAccess } from "../../components/form-access/FormAccess";
|
||||||
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
||||||
import {
|
import { useFetch, useAdminClient } from "../../context/auth/AdminClient";
|
||||||
asyncStateFetch,
|
|
||||||
useAdminClient,
|
|
||||||
} from "../../context/auth/AdminClient";
|
|
||||||
import { SaveReset } from "./SaveReset";
|
import { SaveReset } from "./SaveReset";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
|
|
||||||
type AuthenticationOverridesProps = {
|
type AuthenticationOverridesProps = {
|
||||||
control: Control<Record<string, any>>;
|
control: Control<Record<string, any>>;
|
||||||
|
@ -34,32 +30,27 @@ export const AuthenticationOverrides = ({
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const { t } = useTranslation("clients");
|
const { t } = useTranslation("clients");
|
||||||
const [flows, setFlows] = useState<JSX.Element[]>([]);
|
const [flows, setFlows] = useState<JSX.Element[]>([]);
|
||||||
const handleError = useErrorHandler();
|
|
||||||
const [browserFlowOpen, setBrowserFlowOpen] = useState(false);
|
const [browserFlowOpen, setBrowserFlowOpen] = useState(false);
|
||||||
const [directGrantOpen, setDirectGrantOpen] = useState(false);
|
const [directGrantOpen, setDirectGrantOpen] = useState(false);
|
||||||
|
|
||||||
useEffect(
|
useFetch(
|
||||||
() =>
|
() => adminClient.authenticationManagement.getFlows(),
|
||||||
asyncStateFetch(
|
(flows) => {
|
||||||
() => adminClient.authenticationManagement.getFlows(),
|
let filteredFlows = [
|
||||||
(flows) => {
|
...flows.filter((flow) => flow.providerId !== "client-flow"),
|
||||||
let filteredFlows = [
|
];
|
||||||
...flows.filter((flow) => flow.providerId !== "client-flow"),
|
filteredFlows = _.sortBy(filteredFlows, [(f) => f.alias]);
|
||||||
];
|
setFlows([
|
||||||
filteredFlows = _.sortBy(filteredFlows, [(f) => f.alias]);
|
<SelectOption key="empty" value="">
|
||||||
setFlows([
|
{t("common:choose")}
|
||||||
<SelectOption key="empty" value="">
|
</SelectOption>,
|
||||||
{t("common:choose")}
|
...filteredFlows.map((flow) => (
|
||||||
</SelectOption>,
|
<SelectOption key={flow.id} value={flow.id}>
|
||||||
...filteredFlows.map((flow) => (
|
{flow.alias}
|
||||||
<SelectOption key={flow.id} value={flow.id}>
|
</SelectOption>
|
||||||
{flow.alias}
|
)),
|
||||||
</SelectOption>
|
]);
|
||||||
)),
|
},
|
||||||
]);
|
|
||||||
},
|
|
||||||
handleError
|
|
||||||
),
|
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -24,10 +24,7 @@ import { useAlerts } from "../../components/alert/Alerts";
|
||||||
import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog";
|
import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog";
|
||||||
import { FormAccess } from "../../components/form-access/FormAccess";
|
import { FormAccess } from "../../components/form-access/FormAccess";
|
||||||
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
||||||
import {
|
import { useAdminClient, useFetch } from "../../context/auth/AdminClient";
|
||||||
useAdminClient,
|
|
||||||
asyncStateFetch,
|
|
||||||
} from "../../context/auth/AdminClient";
|
|
||||||
|
|
||||||
import { ClientSecret } from "./ClientSecret";
|
import { ClientSecret } from "./ClientSecret";
|
||||||
import { SignedJWT } from "./SignedJWT";
|
import { SignedJWT } from "./SignedJWT";
|
||||||
|
@ -55,8 +52,12 @@ export type CredentialsProps = {
|
||||||
export const Credentials = ({ clientId, save }: CredentialsProps) => {
|
export const Credentials = ({ clientId, save }: CredentialsProps) => {
|
||||||
const { t } = useTranslation("clients");
|
const { t } = useTranslation("clients");
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const handleError = useErrorHandler();
|
|
||||||
const { addAlert } = useAlerts();
|
const { addAlert } = useAlerts();
|
||||||
|
|
||||||
|
const [providers, setProviders] = useState<ClientAuthenticatorProviders[]>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
formState: { isDirty },
|
formState: { isDirty },
|
||||||
|
@ -64,37 +65,33 @@ export const Credentials = ({ clientId, save }: CredentialsProps) => {
|
||||||
const clientAuthenticatorType = useWatch({
|
const clientAuthenticatorType = useWatch({
|
||||||
control: control,
|
control: control,
|
||||||
name: "clientAuthenticatorType",
|
name: "clientAuthenticatorType",
|
||||||
|
defaultValue: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
const [providers, setProviders] = useState<ClientAuthenticatorProviders[]>(
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
const [secret, setSecret] = useState("");
|
const [secret, setSecret] = useState("");
|
||||||
const [accessToken, setAccessToken] = useState("");
|
const [accessToken, setAccessToken] = useState("");
|
||||||
const [open, isOpen] = useState(false);
|
const [open, isOpen] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useFetch(
|
||||||
return asyncStateFetch(
|
async () => {
|
||||||
async () => {
|
const providers = await adminClient.authenticationManagement.getClientAuthenticatorProviders(
|
||||||
const providers = await adminClient.authenticationManagement.getClientAuthenticatorProviders(
|
{ id: clientId }
|
||||||
{ id: clientId }
|
);
|
||||||
);
|
|
||||||
|
|
||||||
const secret = await adminClient.clients.getClientSecret({
|
const secret = await adminClient.clients.getClientSecret({
|
||||||
id: clientId,
|
id: clientId,
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
providers,
|
providers,
|
||||||
secret: secret.value!,
|
secret: secret.value!,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
({ providers, secret }) => {
|
({ providers, secret }) => {
|
||||||
setProviders(providers);
|
setProviders(providers);
|
||||||
setSecret(secret);
|
setSecret(secret);
|
||||||
},
|
},
|
||||||
handleError
|
[]
|
||||||
);
|
);
|
||||||
}, []);
|
|
||||||
|
|
||||||
async function regenerate<T>(
|
async function regenerate<T>(
|
||||||
call: (clientId: string) => Promise<T>,
|
call: (clientId: string) => Promise<T>,
|
||||||
|
@ -164,6 +161,7 @@ export const Credentials = ({ clientId, save }: CredentialsProps) => {
|
||||||
<Controller
|
<Controller
|
||||||
name="clientAuthenticatorType"
|
name="clientAuthenticatorType"
|
||||||
control={control}
|
control={control}
|
||||||
|
defaultValue=""
|
||||||
render={({ onChange, value }) => (
|
render={({ onChange, value }) => (
|
||||||
<Select
|
<Select
|
||||||
toggleId="kc-client-authenticator-type"
|
toggleId="kc-client-authenticator-type"
|
||||||
|
|
|
@ -25,13 +25,9 @@ import { FormAccess } from "../../components/form-access/FormAccess";
|
||||||
import { Controller, useFormContext, useWatch } from "react-hook-form";
|
import { Controller, useFormContext, useWatch } from "react-hook-form";
|
||||||
import { ClientForm } from "../ClientDetails";
|
import { ClientForm } from "../ClientDetails";
|
||||||
import { GenerateKeyDialog } from "./GenerateKeyDialog";
|
import { GenerateKeyDialog } from "./GenerateKeyDialog";
|
||||||
import {
|
import { useFetch, useAdminClient } from "../../context/auth/AdminClient";
|
||||||
asyncStateFetch,
|
|
||||||
useAdminClient,
|
|
||||||
} from "../../context/auth/AdminClient";
|
|
||||||
import { useAlerts } from "../../components/alert/Alerts";
|
import { useAlerts } from "../../components/alert/Alerts";
|
||||||
import { ImportKeyDialog, ImportFile } from "./ImportKeyDialog";
|
import { ImportKeyDialog, ImportFile } from "./ImportKeyDialog";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
|
|
||||||
type KeysProps = {
|
type KeysProps = {
|
||||||
save: () => void;
|
save: () => void;
|
||||||
|
@ -48,7 +44,6 @@ export const Keys = ({ clientId, save }: KeysProps) => {
|
||||||
formState: { isDirty },
|
formState: { isDirty },
|
||||||
} = useFormContext<ClientForm>();
|
} = useFormContext<ClientForm>();
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const errorHandler = useErrorHandler();
|
|
||||||
const { addAlert } = useAlerts();
|
const { addAlert } = useAlerts();
|
||||||
|
|
||||||
const [keyInfo, setKeyInfo] = useState<CertificateRepresentation>();
|
const [keyInfo, setKeyInfo] = useState<CertificateRepresentation>();
|
||||||
|
@ -60,13 +55,10 @@ export const Keys = ({ clientId, save }: KeysProps) => {
|
||||||
name: "attributes.use-jwks-url",
|
name: "attributes.use-jwks-url",
|
||||||
defaultValue: "false",
|
defaultValue: "false",
|
||||||
});
|
});
|
||||||
useEffect(
|
|
||||||
() =>
|
useFetch(
|
||||||
asyncStateFetch(
|
() => adminClient.clients.getKeyInfo({ id: clientId, attr }),
|
||||||
() => adminClient.clients.getKeyInfo({ id: clientId, attr }),
|
(info) => setKeyInfo(info),
|
||||||
(info) => setKeyInfo(info),
|
|
||||||
errorHandler
|
|
||||||
),
|
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import React, { useContext, useEffect, useRef, useState } from "react";
|
import React, { useContext, useEffect, useRef, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
import {
|
import {
|
||||||
ClipboardCopy,
|
ClipboardCopy,
|
||||||
EmptyState,
|
EmptyState,
|
||||||
|
@ -31,10 +30,7 @@ import RoleRepresentation from "keycloak-admin/lib/defs/roleRepresentation";
|
||||||
import ProtocolMapperRepresentation from "keycloak-admin/lib/defs/protocolMapperRepresentation";
|
import ProtocolMapperRepresentation from "keycloak-admin/lib/defs/protocolMapperRepresentation";
|
||||||
import { ProtocolMapperTypeRepresentation } from "keycloak-admin/lib/defs/serverInfoRepesentation";
|
import { ProtocolMapperTypeRepresentation } from "keycloak-admin/lib/defs/serverInfoRepesentation";
|
||||||
|
|
||||||
import {
|
import { useAdminClient, useFetch } from "../../context/auth/AdminClient";
|
||||||
useAdminClient,
|
|
||||||
asyncStateFetch,
|
|
||||||
} from "../../context/auth/AdminClient";
|
|
||||||
import { useServerInfo } from "../../context/server-info/ServerInfoProvider";
|
import { useServerInfo } from "../../context/server-info/ServerInfoProvider";
|
||||||
import { RealmContext } from "../../context/realm-context/RealmContext";
|
import { RealmContext } from "../../context/realm-context/RealmContext";
|
||||||
import { KeycloakDataTable } from "../../components/table-toolbar/KeycloakDataTable";
|
import { KeycloakDataTable } from "../../components/table-toolbar/KeycloakDataTable";
|
||||||
|
@ -149,14 +145,11 @@ export const EvaluateScopes = ({ clientId, protocol }: EvaluateScopesProps) => {
|
||||||
const tabContent2 = useRef(null);
|
const tabContent2 = useRef(null);
|
||||||
const tabContent3 = useRef(null);
|
const tabContent3 = useRef(null);
|
||||||
|
|
||||||
const handleError = useErrorHandler();
|
useFetch(
|
||||||
useEffect(() => {
|
() => adminClient.clients.listOptionalClientScopes({ id: clientId }),
|
||||||
return asyncStateFetch(
|
(optionalClientScopes) => setSelectableScopes(optionalClientScopes),
|
||||||
() => adminClient.clients.listOptionalClientScopes({ id: clientId }),
|
[]
|
||||||
(optionalClientScopes) => setSelectableScopes(optionalClientScopes),
|
);
|
||||||
handleError
|
|
||||||
);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const toString = (user: UserRepresentation) => {
|
const toString = (user: UserRepresentation) => {
|
||||||
return (
|
return (
|
||||||
|
@ -169,90 +162,82 @@ export const EvaluateScopes = ({ clientId, protocol }: EvaluateScopesProps) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useFetch(
|
||||||
return asyncStateFetch(
|
() => {
|
||||||
() => {
|
if (userSearch.length > 2) {
|
||||||
if (userSearch.length > 2) {
|
return adminClient.users.find({ search: userSearch });
|
||||||
return adminClient.users.find({ search: userSearch });
|
} else {
|
||||||
} else {
|
return Promise.resolve<UserRepresentation[]>([]);
|
||||||
return Promise.resolve<UserRepresentation[]>([]);
|
}
|
||||||
}
|
},
|
||||||
},
|
(users) =>
|
||||||
(users) =>
|
setUserItems(
|
||||||
setUserItems(
|
users
|
||||||
users
|
.map((user) => {
|
||||||
.map((user) => {
|
user.toString = function () {
|
||||||
user.toString = function () {
|
return toString(this);
|
||||||
return toString(this);
|
};
|
||||||
};
|
return user;
|
||||||
return user;
|
})
|
||||||
})
|
.map((user) => <SelectOption key={user.id} value={user} />)
|
||||||
.map((user) => <SelectOption key={user.id} value={user} />)
|
),
|
||||||
),
|
[userSearch]
|
||||||
handleError
|
);
|
||||||
);
|
|
||||||
}, [userSearch]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useFetch(
|
||||||
return asyncStateFetch(
|
async () => {
|
||||||
async () => {
|
const scope = selected.join(" ");
|
||||||
const scope = selected.join(" ");
|
const effectiveRoles = await adminClient.clients.evaluatePermission({
|
||||||
const effectiveRoles = await adminClient.clients.evaluatePermission({
|
id: clientId,
|
||||||
|
roleContainer: realm,
|
||||||
|
scope,
|
||||||
|
type: "granted",
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapperList = (await adminClient.clients.evaluateListProtocolMapper({
|
||||||
|
id: clientId,
|
||||||
|
scope,
|
||||||
|
})) as ({
|
||||||
|
type: ProtocolMapperTypeRepresentation;
|
||||||
|
} & ProtocolMapperRepresentation)[];
|
||||||
|
|
||||||
|
return {
|
||||||
|
mapperList,
|
||||||
|
effectiveRoles,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
({ mapperList, effectiveRoles }) => {
|
||||||
|
setEffectiveRoles(effectiveRoles);
|
||||||
|
mapperList.map((mapper) => {
|
||||||
|
mapper.type = mapperTypes.filter(
|
||||||
|
(type) => type.id === mapper.protocolMapper
|
||||||
|
)[0];
|
||||||
|
});
|
||||||
|
|
||||||
|
setProtocolMappers(mapperList);
|
||||||
|
refresh();
|
||||||
|
},
|
||||||
|
[selected]
|
||||||
|
);
|
||||||
|
|
||||||
|
useFetch(
|
||||||
|
() => {
|
||||||
|
const scope = selected.join(" ");
|
||||||
|
if (user) {
|
||||||
|
return adminClient.clients.evaluateGenerateAccessToken({
|
||||||
id: clientId,
|
id: clientId,
|
||||||
roleContainer: realm,
|
userId: user.id!,
|
||||||
scope,
|
scope,
|
||||||
type: "granted",
|
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
const mapperList = (await adminClient.clients.evaluateListProtocolMapper(
|
return Promise.resolve({});
|
||||||
{
|
}
|
||||||
id: clientId,
|
},
|
||||||
scope,
|
(accessToken) => {
|
||||||
}
|
setAccessToken(JSON.stringify(accessToken, undefined, 3));
|
||||||
)) as ({
|
},
|
||||||
type: ProtocolMapperTypeRepresentation;
|
[user, selected]
|
||||||
} & ProtocolMapperRepresentation)[];
|
);
|
||||||
|
|
||||||
return {
|
|
||||||
mapperList,
|
|
||||||
effectiveRoles,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
({ mapperList, effectiveRoles }) => {
|
|
||||||
setEffectiveRoles(effectiveRoles);
|
|
||||||
mapperList.map((mapper) => {
|
|
||||||
mapper.type = mapperTypes.filter(
|
|
||||||
(type) => type.id === mapper.protocolMapper
|
|
||||||
)[0];
|
|
||||||
});
|
|
||||||
|
|
||||||
setProtocolMappers(mapperList);
|
|
||||||
refresh();
|
|
||||||
},
|
|
||||||
handleError
|
|
||||||
);
|
|
||||||
}, [selected]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return asyncStateFetch(
|
|
||||||
() => {
|
|
||||||
const scope = selected.join(" ");
|
|
||||||
if (user) {
|
|
||||||
return adminClient.clients.evaluateGenerateAccessToken({
|
|
||||||
id: clientId,
|
|
||||||
userId: user.id!,
|
|
||||||
scope,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return Promise.resolve({});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(accessToken) => {
|
|
||||||
setAccessToken(JSON.stringify(accessToken, undefined, 3));
|
|
||||||
},
|
|
||||||
handleError
|
|
||||||
);
|
|
||||||
}, [user, selected]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import React, { DependencyList, useEffect, useState } from "react";
|
import React, { DependencyList, useState } from "react";
|
||||||
import { Spinner } from "@patternfly/react-core";
|
import { Spinner } from "@patternfly/react-core";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
|
|
||||||
import { asyncStateFetch } from "../../context/auth/AdminClient";
|
import { useFetch } from "../../context/auth/AdminClient";
|
||||||
|
|
||||||
type DataLoaderProps<T> = {
|
type DataLoaderProps<T> = {
|
||||||
loader: () => Promise<T>;
|
loader: () => Promise<T>;
|
||||||
|
@ -12,15 +11,12 @@ type DataLoaderProps<T> = {
|
||||||
|
|
||||||
export function DataLoader<T>(props: DataLoaderProps<T>) {
|
export function DataLoader<T>(props: DataLoaderProps<T>) {
|
||||||
const [data, setData] = useState<T | undefined>();
|
const [data, setData] = useState<T | undefined>();
|
||||||
const handleError = useErrorHandler();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useFetch(
|
||||||
return asyncStateFetch(
|
() => props.loader(),
|
||||||
() => props.loader(),
|
(result) => setData(result),
|
||||||
(result) => setData(result),
|
props.deps || []
|
||||||
handleError
|
);
|
||||||
);
|
|
||||||
}, props.deps || []);
|
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
if (props.children instanceof Function) {
|
if (props.children instanceof Function) {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import React from "react";
|
||||||
import { DataLoader } from "../DataLoader";
|
import { DataLoader } from "../DataLoader";
|
||||||
import { act } from "@testing-library/react";
|
import { act } from "@testing-library/react";
|
||||||
import { render, unmountComponentAtNode } from "react-dom";
|
import { render, unmountComponentAtNode } from "react-dom";
|
||||||
|
import { MockAdminClient } from "../../../stories/MockAdminClient";
|
||||||
|
|
||||||
let container: HTMLDivElement;
|
let container: HTMLDivElement;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -19,15 +20,17 @@ describe("<DataLoader />", () => {
|
||||||
const loader = () => Promise.resolve(["a", "b"]);
|
const loader = () => Promise.resolve(["a", "b"]);
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(
|
render(
|
||||||
<DataLoader loader={loader}>
|
<MockAdminClient>
|
||||||
{(result) => (
|
<DataLoader loader={loader}>
|
||||||
<div>
|
{(result) => (
|
||||||
{result.map((d, i) => (
|
<div>
|
||||||
<i key={i}>{d}</i>
|
{result.map((d, i) => (
|
||||||
))}
|
<i key={i}>{d}</i>
|
||||||
</div>
|
))}
|
||||||
)}
|
</div>
|
||||||
</DataLoader>,
|
)}
|
||||||
|
</DataLoader>
|
||||||
|
</MockAdminClient>,
|
||||||
container
|
container
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import React, { useState, useEffect, useContext } from "react";
|
import React, { useState, useContext } from "react";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
import {
|
import {
|
||||||
Alert,
|
Alert,
|
||||||
AlertVariant,
|
AlertVariant,
|
||||||
|
@ -19,10 +18,7 @@ import { ConfirmDialogModal } from "../confirm-dialog/ConfirmDialog";
|
||||||
import { HelpItem } from "../help-enabler/HelpItem";
|
import { HelpItem } from "../help-enabler/HelpItem";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useServerInfo } from "../../context/server-info/ServerInfoProvider";
|
import { useServerInfo } from "../../context/server-info/ServerInfoProvider";
|
||||||
import {
|
import { useAdminClient, useFetch } from "../../context/auth/AdminClient";
|
||||||
useAdminClient,
|
|
||||||
asyncStateFetch,
|
|
||||||
} from "../../context/auth/AdminClient";
|
|
||||||
import { HelpContext } from "../help-enabler/HelpHeader";
|
import { HelpContext } from "../help-enabler/HelpHeader";
|
||||||
|
|
||||||
type DownloadDialogProps = {
|
type DownloadDialogProps = {
|
||||||
|
@ -39,7 +35,6 @@ export const DownloadDialog = ({
|
||||||
protocol = "openid-connect",
|
protocol = "openid-connect",
|
||||||
}: DownloadDialogProps) => {
|
}: DownloadDialogProps) => {
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const handleError = useErrorHandler();
|
|
||||||
const { t } = useTranslation("common");
|
const { t } = useTranslation("common");
|
||||||
const { enabled } = useContext(HelpContext);
|
const { enabled } = useContext(HelpContext);
|
||||||
const serverInfo = useServerInfo();
|
const serverInfo = useServerInfo();
|
||||||
|
@ -51,23 +46,21 @@ export const DownloadDialog = ({
|
||||||
const [snippet, setSnippet] = useState("");
|
const [snippet, setSnippet] = useState("");
|
||||||
const [openType, setOpenType] = useState(false);
|
const [openType, setOpenType] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useFetch(
|
||||||
return asyncStateFetch(
|
async () => {
|
||||||
async () => {
|
const snippet = await adminClient.clients.getInstallationProviders({
|
||||||
const snippet = await adminClient.clients.getInstallationProviders({
|
id,
|
||||||
id,
|
providerId: selected,
|
||||||
providerId: selected,
|
});
|
||||||
});
|
if (typeof snippet === "string") {
|
||||||
if (typeof snippet === "string") {
|
return snippet;
|
||||||
return snippet;
|
} else {
|
||||||
} else {
|
return JSON.stringify(snippet, undefined, 3);
|
||||||
return JSON.stringify(snippet, undefined, 3);
|
}
|
||||||
}
|
},
|
||||||
},
|
(snippet) => setSnippet(snippet),
|
||||||
(snippet) => setSnippet(snippet),
|
[id, selected]
|
||||||
handleError
|
);
|
||||||
);
|
|
||||||
}, [id, selected]);
|
|
||||||
return (
|
return (
|
||||||
<ConfirmDialogModal
|
<ConfirmDialogModal
|
||||||
titleKey={t("clients:downloadAdaptorTitle")}
|
titleKey={t("clients:downloadAdaptorTitle")}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import {
|
import {
|
||||||
Badge,
|
Badge,
|
||||||
|
@ -18,10 +17,7 @@ import {
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
|
|
||||||
import { KeycloakDataTable } from "../table-toolbar/KeycloakDataTable";
|
import { KeycloakDataTable } from "../table-toolbar/KeycloakDataTable";
|
||||||
import {
|
import { useFetch, useAdminClient } from "../../context/auth/AdminClient";
|
||||||
asyncStateFetch,
|
|
||||||
useAdminClient,
|
|
||||||
} from "../../context/auth/AdminClient";
|
|
||||||
import ClientRepresentation from "keycloak-admin/lib/defs/clientRepresentation";
|
import ClientRepresentation from "keycloak-admin/lib/defs/clientRepresentation";
|
||||||
import { FilterIcon } from "@patternfly/react-icons";
|
import { FilterIcon } from "@patternfly/react-icons";
|
||||||
import { Row, ServiceRole } from "./RoleMapping";
|
import { Row, ServiceRole } from "./RoleMapping";
|
||||||
|
@ -54,7 +50,6 @@ export const AddRoleMappingModal = ({
|
||||||
}: AddRoleMappingModalProps) => {
|
}: AddRoleMappingModalProps) => {
|
||||||
const { t } = useTranslation("clients");
|
const { t } = useTranslation("clients");
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const errorHandler = useErrorHandler();
|
|
||||||
|
|
||||||
const [clients, setClients] = useState<ClientRole[]>([]);
|
const [clients, setClients] = useState<ClientRole[]>([]);
|
||||||
const [searchToggle, setSearchToggle] = useState(false);
|
const [searchToggle, setSearchToggle] = useState(false);
|
||||||
|
@ -65,48 +60,42 @@ export const AddRoleMappingModal = ({
|
||||||
const [selectedClients, setSelectedClients] = useState<ClientRole[]>([]);
|
const [selectedClients, setSelectedClients] = useState<ClientRole[]>([]);
|
||||||
const [selectedRows, setSelectedRows] = useState<Row[]>([]);
|
const [selectedRows, setSelectedRows] = useState<Row[]>([]);
|
||||||
|
|
||||||
useEffect(
|
useFetch(
|
||||||
() =>
|
async () => {
|
||||||
asyncStateFetch(
|
const clients = await adminClient.clients.find();
|
||||||
async () => {
|
return (
|
||||||
const clients = await adminClient.clients.find();
|
await Promise.all(
|
||||||
return (
|
clients.map(async (client) => {
|
||||||
await Promise.all(
|
let roles: RoleRepresentation[] = [];
|
||||||
clients.map(async (client) => {
|
if (type === "service-account") {
|
||||||
let roles: RoleRepresentation[] = [];
|
roles = await adminClient.users.listAvailableClientRoleMappings({
|
||||||
if (type === "service-account") {
|
id: id,
|
||||||
roles = await adminClient.users.listAvailableClientRoleMappings(
|
clientUniqueId: client.id!,
|
||||||
{
|
});
|
||||||
id: id,
|
} else if (type === "client-scope") {
|
||||||
clientUniqueId: client.id!,
|
roles = await adminClient.clientScopes.listAvailableClientScopeMappings(
|
||||||
}
|
{
|
||||||
);
|
id,
|
||||||
} else if (type === "client-scope") {
|
client: client.id!,
|
||||||
roles = await adminClient.clientScopes.listAvailableClientScopeMappings(
|
|
||||||
{
|
|
||||||
id,
|
|
||||||
client: client.id!,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return {
|
);
|
||||||
roles,
|
}
|
||||||
client,
|
return {
|
||||||
};
|
roles,
|
||||||
})
|
client,
|
||||||
)
|
};
|
||||||
)
|
})
|
||||||
.flat()
|
)
|
||||||
.filter((row) => row.roles.length !== 0)
|
)
|
||||||
.map((row) => {
|
.flat()
|
||||||
return { ...row.client, numberOfRoles: row.roles.length };
|
.filter((row) => row.roles.length !== 0)
|
||||||
});
|
.map((row) => {
|
||||||
},
|
return { ...row.client, numberOfRoles: row.roles.length };
|
||||||
(clients) => {
|
});
|
||||||
setClients(clients);
|
},
|
||||||
},
|
(clients) => {
|
||||||
errorHandler
|
setClients(clients);
|
||||||
),
|
},
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { Children } from "react";
|
import React, { Children, Fragment } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
Grid,
|
Grid,
|
||||||
|
@ -38,22 +38,18 @@ export const ScrollForm = ({
|
||||||
<Grid hasGutter {...rest}>
|
<Grid hasGutter {...rest}>
|
||||||
<GridItem span={8}>
|
<GridItem span={8}>
|
||||||
{sections.map((cat, index) => (
|
{sections.map((cat, index) => (
|
||||||
<>
|
<Fragment key={cat}>
|
||||||
{!borders && (
|
{!borders && (
|
||||||
<ScrollPanel
|
<ScrollPanel scrollId={spacesToHyphens(cat)} title={cat}>
|
||||||
scrollId={spacesToHyphens(cat)}
|
|
||||||
key={cat}
|
|
||||||
title={cat}
|
|
||||||
>
|
|
||||||
{nodes[index]}
|
{nodes[index]}
|
||||||
</ScrollPanel>
|
</ScrollPanel>
|
||||||
)}
|
)}
|
||||||
{borders && (
|
{borders && (
|
||||||
<FormPanel scrollId={spacesToHyphens(cat)} key={cat} title={cat}>
|
<FormPanel scrollId={spacesToHyphens(cat)} title={cat}>
|
||||||
{nodes[index]}
|
{nodes[index]}
|
||||||
</FormPanel>
|
</FormPanel>
|
||||||
)}
|
)}
|
||||||
</>
|
</Fragment>
|
||||||
))}
|
))}
|
||||||
</GridItem>
|
</GridItem>
|
||||||
<GridItem span={4}>
|
<GridItem span={4}>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import React, { isValidElement, ReactNode, useEffect, useState } from "react";
|
import React, { isValidElement, ReactNode, useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
import {
|
import {
|
||||||
IAction,
|
IAction,
|
||||||
IActions,
|
IActions,
|
||||||
|
@ -16,7 +15,7 @@ import { Spinner } from "@patternfly/react-core";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
|
||||||
import { PaginatingTableToolbar } from "./PaginatingTableToolbar";
|
import { PaginatingTableToolbar } from "./PaginatingTableToolbar";
|
||||||
import { asyncStateFetch } from "../../context/auth/AdminClient";
|
import { useFetch } from "../../context/auth/AdminClient";
|
||||||
import { ListEmptyState } from "../list-empty-state/ListEmptyState";
|
import { ListEmptyState } from "../list-empty-state/ListEmptyState";
|
||||||
import { SVGIconProps } from "@patternfly/react-icons/dist/js/createIcon";
|
import { SVGIconProps } from "@patternfly/react-icons/dist/js/createIcon";
|
||||||
|
|
||||||
|
@ -181,7 +180,6 @@ export function KeycloakDataTable<T>({
|
||||||
|
|
||||||
const [key, setKey] = useState(0);
|
const [key, setKey] = useState(0);
|
||||||
const refresh = () => setKey(new Date().getTime());
|
const refresh = () => setKey(new Date().getTime());
|
||||||
const handleError = useErrorHandler();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (canSelectAll) {
|
if (canSelectAll) {
|
||||||
|
@ -199,27 +197,24 @@ export function KeycloakDataTable<T>({
|
||||||
}
|
}
|
||||||
}, [selected]);
|
}, [selected]);
|
||||||
|
|
||||||
useEffect(() => {
|
useFetch(
|
||||||
setLoading(true);
|
async () => {
|
||||||
return asyncStateFetch(
|
setLoading(true);
|
||||||
async () => {
|
return unPaginatedData || (await loader(first, max, search));
|
||||||
let data = unPaginatedData || (await loader(first, max, search));
|
},
|
||||||
|
(data) => {
|
||||||
|
if (!isPaginated) {
|
||||||
|
setUnPaginatedData(data);
|
||||||
|
data = data.slice(first, first + max);
|
||||||
|
}
|
||||||
|
|
||||||
if (!isPaginated) {
|
const result = convertToColumns(data);
|
||||||
setUnPaginatedData(data);
|
setRows(result);
|
||||||
data = data.slice(first, first + max);
|
setFilteredData(result);
|
||||||
}
|
setLoading(false);
|
||||||
|
},
|
||||||
return convertToColumns(data);
|
[key, first, max, search]
|
||||||
},
|
);
|
||||||
(result) => {
|
|
||||||
setRows(result);
|
|
||||||
setFilteredData(result);
|
|
||||||
setLoading(false);
|
|
||||||
},
|
|
||||||
handleError
|
|
||||||
);
|
|
||||||
}, [key, first, max, search]);
|
|
||||||
|
|
||||||
const getNodeText = (node: Cell<T>): string => {
|
const getNodeText = (node: Cell<T>): string => {
|
||||||
if (["string", "number"].includes(typeof node)) {
|
if (["string", "number"].includes(typeof node)) {
|
||||||
|
@ -356,6 +351,7 @@ export function KeycloakDataTable<T>({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{!rows && loading && <Loading />}
|
||||||
{rows && (
|
{rows && (
|
||||||
<PaginatingTableToolbar
|
<PaginatingTableToolbar
|
||||||
count={rows.length}
|
count={rows.length}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
import { createContext, useContext } from "react";
|
import { createContext, DependencyList, useContext, useEffect } from "react";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
import KeycloakAdminClient from "keycloak-admin";
|
import KeycloakAdminClient from "keycloak-admin";
|
||||||
|
import { useErrorHandler } from "react-error-boundary";
|
||||||
|
|
||||||
export const AdminClient = createContext<KeycloakAdminClient | undefined>(
|
export const AdminClient = createContext<KeycloakAdminClient | undefined>(
|
||||||
undefined
|
undefined
|
||||||
|
@ -15,37 +18,39 @@ export const useAdminClient = () => {
|
||||||
* It takes 2 functions one you do your adminClient call in and the other to set your state
|
* It takes 2 functions one you do your adminClient call in and the other to set your state
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* useEffect(() => {
|
* useFetch(
|
||||||
* return asyncStateFetch(
|
* () => adminClient.components.findOne({ id }),
|
||||||
* () => adminClient.components.findOne({ id }),
|
* (component) => setupForm(component),
|
||||||
* (component) => setupForm(component)
|
* []
|
||||||
* );
|
* );
|
||||||
* }, []);
|
|
||||||
*
|
*
|
||||||
* @param adminClientCall use this to do your adminClient call
|
* @param adminClientCall use this to do your adminClient call
|
||||||
* @param callback when the data is fetched this is where you set your state
|
* @param callback when the data is fetched this is where you set your state
|
||||||
* @param onError custom error handler
|
|
||||||
*/
|
*/
|
||||||
export function asyncStateFetch<T>(
|
export function useFetch<T>(
|
||||||
adminClientCall: () => Promise<T>,
|
adminClientCall: () => Promise<T>,
|
||||||
callback: (param: T) => void,
|
callback: (param: T) => void,
|
||||||
onError: (error: Error) => void
|
deps?: DependencyList
|
||||||
) {
|
) {
|
||||||
let canceled = false;
|
const adminClient = useAdminClient();
|
||||||
|
const onError = useErrorHandler();
|
||||||
|
|
||||||
adminClientCall()
|
const source = axios.CancelToken.source();
|
||||||
.then((result) => {
|
adminClient.setConfig({ requestConfig: { cancelToken: source.token } });
|
||||||
try {
|
|
||||||
if (!canceled) {
|
useEffect(() => {
|
||||||
callback(result);
|
adminClientCall()
|
||||||
|
.then((result) => {
|
||||||
|
callback(result);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
if (!axios.isCancel(error)) {
|
||||||
|
onError(error);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
});
|
||||||
if (onError) onError(error);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(onError);
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
canceled = true;
|
source.cancel();
|
||||||
};
|
};
|
||||||
|
}, deps);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import React, { useContext, useEffect, useState } from "react";
|
import React, { useContext, useState } from "react";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
|
||||||
import RealmRepresentation from "keycloak-admin/lib/defs/realmRepresentation";
|
import RealmRepresentation from "keycloak-admin/lib/defs/realmRepresentation";
|
||||||
import { RecentUsed } from "../../components/realm-selector/recent-used";
|
import { RecentUsed } from "../../components/realm-selector/recent-used";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
import { useAdminClient, useFetch } from "../auth/AdminClient";
|
||||||
import { asyncStateFetch, useAdminClient } from "../auth/AdminClient";
|
|
||||||
import { WhoAmIContext } from "../whoami/WhoAmI";
|
import { WhoAmIContext } from "../whoami/WhoAmI";
|
||||||
|
|
||||||
type RealmContextType = {
|
type RealmContextType = {
|
||||||
|
@ -30,7 +29,6 @@ export const RealmContextProvider = ({
|
||||||
const [realm, setRealm] = useState(whoAmI.getHomeRealm());
|
const [realm, setRealm] = useState(whoAmI.getHomeRealm());
|
||||||
const [realms, setRealms] = useState<RealmRepresentation[]>([]);
|
const [realms, setRealms] = useState<RealmRepresentation[]>([]);
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const errorHandler = useErrorHandler();
|
|
||||||
const recentUsed = new RecentUsed();
|
const recentUsed = new RecentUsed();
|
||||||
|
|
||||||
const updateRealmsList = (realms: RealmRepresentation[]) => {
|
const updateRealmsList = (realms: RealmRepresentation[]) => {
|
||||||
|
@ -38,13 +36,9 @@ export const RealmContextProvider = ({
|
||||||
recentUsed.clean(realms.map((r) => r.realm!));
|
recentUsed.clean(realms.map((r) => r.realm!));
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(
|
useFetch(
|
||||||
() =>
|
() => adminClient.realms.find(),
|
||||||
asyncStateFetch(
|
(realms) => updateRealmsList(realms),
|
||||||
() => adminClient.realms.find(),
|
|
||||||
(realms) => updateRealmsList(realms),
|
|
||||||
errorHandler
|
|
||||||
),
|
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
import i18n from "../../i18n";
|
import i18n from "../../i18n";
|
||||||
|
|
||||||
import { asyncStateFetch, useAdminClient } from "../auth/AdminClient";
|
|
||||||
import WhoAmIRepresentation, {
|
import WhoAmIRepresentation, {
|
||||||
AccessType,
|
AccessType,
|
||||||
} from "keycloak-admin/lib/defs/whoAmIRepresentation";
|
} from "keycloak-admin/lib/defs/whoAmIRepresentation";
|
||||||
|
import { useAdminClient, useFetch } from "../auth/AdminClient";
|
||||||
|
|
||||||
export class WhoAmI {
|
export class WhoAmI {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -62,20 +61,17 @@ export const WhoAmIContext = React.createContext<WhoAmIProps>({
|
||||||
type WhoAmIProviderProps = { children: React.ReactNode };
|
type WhoAmIProviderProps = { children: React.ReactNode };
|
||||||
export const WhoAmIContextProvider = ({ children }: WhoAmIProviderProps) => {
|
export const WhoAmIContextProvider = ({ children }: WhoAmIProviderProps) => {
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const handleError = useErrorHandler();
|
|
||||||
const [whoAmI, setWhoAmI] = useState<WhoAmI>(new WhoAmI());
|
const [whoAmI, setWhoAmI] = useState<WhoAmI>(new WhoAmI());
|
||||||
const [key, setKey] = useState(0);
|
const [key, setKey] = useState(0);
|
||||||
|
|
||||||
useEffect(() => {
|
useFetch(
|
||||||
return asyncStateFetch(
|
() => adminClient.whoAmI.find({ realm: "master" }),
|
||||||
() => adminClient.whoAmI.find({ realm: "master" }),
|
(me) => {
|
||||||
(me) => {
|
const whoAmI = new WhoAmI(adminClient.keycloak?.realm, me);
|
||||||
const whoAmI = new WhoAmI(adminClient.keycloak?.realm, me);
|
setWhoAmI(whoAmI);
|
||||||
setWhoAmI(whoAmI);
|
},
|
||||||
},
|
[key]
|
||||||
handleError
|
);
|
||||||
);
|
|
||||||
}, [key]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WhoAmIContext.Provider value={{ refresh: () => setKey(key + 1), whoAmI }}>
|
<WhoAmIContext.Provider value={{ refresh: () => setKey(key + 1), whoAmI }}>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useHistory, useLocation } from "react-router-dom";
|
import { useHistory, useLocation } from "react-router-dom";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
import {
|
import {
|
||||||
DropdownItem,
|
DropdownItem,
|
||||||
PageSection,
|
PageSection,
|
||||||
|
@ -14,7 +13,7 @@ import {
|
||||||
import GroupRepresentation from "keycloak-admin/lib/defs/groupRepresentation";
|
import GroupRepresentation from "keycloak-admin/lib/defs/groupRepresentation";
|
||||||
|
|
||||||
import { ViewHeader } from "../components/view-header/ViewHeader";
|
import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||||
import { asyncStateFetch, useAdminClient } from "../context/auth/AdminClient";
|
import { useFetch, useAdminClient } from "../context/auth/AdminClient";
|
||||||
import { useAlerts } from "../components/alert/Alerts";
|
import { useAlerts } from "../components/alert/Alerts";
|
||||||
import { useRealm } from "../context/realm-context/RealmContext";
|
import { useRealm } from "../context/realm-context/RealmContext";
|
||||||
|
|
||||||
|
@ -35,7 +34,6 @@ export const GroupsSection = () => {
|
||||||
const { subGroups, setSubGroups, currentGroup } = useSubGroups();
|
const { subGroups, setSubGroups, currentGroup } = useSubGroups();
|
||||||
const { addAlert } = useAlerts();
|
const { addAlert } = useAlerts();
|
||||||
const { realm } = useRealm();
|
const { realm } = useRealm();
|
||||||
const errorHandler = useErrorHandler();
|
|
||||||
|
|
||||||
const [rename, setRename] = useState<string>();
|
const [rename, setRename] = useState<string>();
|
||||||
|
|
||||||
|
@ -55,28 +53,24 @@ export const GroupsSection = () => {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(
|
useFetch(
|
||||||
() =>
|
async () => {
|
||||||
asyncStateFetch(
|
const ids = getId(location.pathname);
|
||||||
async () => {
|
const isNavigationStateInValid = ids && ids.length > subGroups.length;
|
||||||
const ids = getId(location.pathname);
|
|
||||||
const isNavigationStateInValid = ids && ids.length > subGroups.length;
|
|
||||||
|
|
||||||
if (isNavigationStateInValid) {
|
if (isNavigationStateInValid) {
|
||||||
const groups: GroupRepresentation[] = [];
|
const groups: GroupRepresentation[] = [];
|
||||||
for (const i of ids!) {
|
for (const i of ids!) {
|
||||||
const group = await adminClient.groups.findOne({ id: i });
|
const group = await adminClient.groups.findOne({ id: i });
|
||||||
if (group) groups.push(group);
|
if (group) groups.push(group);
|
||||||
}
|
}
|
||||||
return groups;
|
return groups;
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
},
|
},
|
||||||
(groups: GroupRepresentation[]) => {
|
(groups: GroupRepresentation[]) => {
|
||||||
if (groups.length) setSubGroups(groups);
|
if (groups.length) setSubGroups(groups);
|
||||||
},
|
},
|
||||||
errorHandler
|
|
||||||
),
|
|
||||||
[id]
|
[id]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
import {
|
import {
|
||||||
Breadcrumb,
|
Breadcrumb,
|
||||||
BreadcrumbItem,
|
BreadcrumbItem,
|
||||||
|
@ -23,7 +22,7 @@ import {
|
||||||
import { AngleRightIcon, SearchIcon } from "@patternfly/react-icons";
|
import { AngleRightIcon, SearchIcon } from "@patternfly/react-icons";
|
||||||
|
|
||||||
import GroupRepresentation from "keycloak-admin/lib/defs/groupRepresentation";
|
import GroupRepresentation from "keycloak-admin/lib/defs/groupRepresentation";
|
||||||
import { asyncStateFetch, useAdminClient } from "../context/auth/AdminClient";
|
import { useFetch, useAdminClient } from "../context/auth/AdminClient";
|
||||||
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
|
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
|
||||||
|
|
||||||
type MoveGroupDialogProps = {
|
type MoveGroupDialogProps = {
|
||||||
|
@ -40,7 +39,6 @@ export const MoveGroupDialog = ({
|
||||||
const { t } = useTranslation("groups");
|
const { t } = useTranslation("groups");
|
||||||
|
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const errorHandler = useErrorHandler();
|
|
||||||
|
|
||||||
const [navigation, setNavigation] = useState<GroupRepresentation[]>([]);
|
const [navigation, setNavigation] = useState<GroupRepresentation[]>([]);
|
||||||
const [groups, setGroups] = useState<GroupRepresentation[]>([]);
|
const [groups, setGroups] = useState<GroupRepresentation[]>([]);
|
||||||
|
@ -50,23 +48,19 @@ export const MoveGroupDialog = ({
|
||||||
const [id, setId] = useState<string>();
|
const [id, setId] = useState<string>();
|
||||||
const currentGroup = () => navigation[navigation.length - 1];
|
const currentGroup = () => navigation[navigation.length - 1];
|
||||||
|
|
||||||
useEffect(
|
useFetch(
|
||||||
() =>
|
async () => {
|
||||||
asyncStateFetch(
|
if (id) {
|
||||||
async () => {
|
const group = await adminClient.groups.findOne({ id });
|
||||||
if (id) {
|
return { group, groups: group.subGroups! };
|
||||||
const group = await adminClient.groups.findOne({ id });
|
} else {
|
||||||
return { group, groups: group.subGroups! };
|
return { groups: await adminClient.groups.find() };
|
||||||
} else {
|
}
|
||||||
return { groups: await adminClient.groups.find() };
|
},
|
||||||
}
|
({ group: selectedGroup, groups }) => {
|
||||||
},
|
if (selectedGroup) setNavigation([...navigation, selectedGroup]);
|
||||||
({ group: selectedGroup, groups }) => {
|
setGroups(groups.filter((g) => g.id !== group.id));
|
||||||
if (selectedGroup) setNavigation([...navigation, selectedGroup]);
|
},
|
||||||
setGroups(groups.filter((g) => g.id !== group.id));
|
|
||||||
},
|
|
||||||
errorHandler
|
|
||||||
),
|
|
||||||
[id]
|
[id]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import React, { Fragment, useEffect, useState } from "react";
|
import React, { Fragment, useState } from "react";
|
||||||
import { Link, useHistory, useRouteMatch } from "react-router-dom";
|
import { Link, useHistory, useRouteMatch } from "react-router-dom";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import {
|
import {
|
||||||
|
@ -26,7 +25,7 @@ import {
|
||||||
|
|
||||||
import IdentityProviderRepresentation from "keycloak-admin/lib/defs/identityProviderRepresentation";
|
import IdentityProviderRepresentation from "keycloak-admin/lib/defs/identityProviderRepresentation";
|
||||||
import { ViewHeader } from "../components/view-header/ViewHeader";
|
import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||||
import { asyncStateFetch, useAdminClient } from "../context/auth/AdminClient";
|
import { useFetch, useAdminClient } from "../context/auth/AdminClient";
|
||||||
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
|
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
|
||||||
import { useRealm } from "../context/realm-context/RealmContext";
|
import { useRealm } from "../context/realm-context/RealmContext";
|
||||||
import { useAlerts } from "../components/alert/Alerts";
|
import { useAlerts } from "../components/alert/Alerts";
|
||||||
|
@ -58,19 +57,14 @@ export const IdentityProvidersSection = () => {
|
||||||
>();
|
>();
|
||||||
|
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const errorHandler = useErrorHandler();
|
|
||||||
const { addAlert } = useAlerts();
|
const { addAlert } = useAlerts();
|
||||||
|
|
||||||
useEffect(
|
useFetch(
|
||||||
() =>
|
async () =>
|
||||||
asyncStateFetch(
|
(await adminClient.realms.findOne({ realm })).identityProviders!,
|
||||||
async () =>
|
(providers) => {
|
||||||
(await adminClient.realms.findOne({ realm })).identityProviders!,
|
setProviders(providers);
|
||||||
(providers) => {
|
},
|
||||||
setProviders(providers);
|
|
||||||
},
|
|
||||||
errorHandler
|
|
||||||
),
|
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Controller, useFormContext } from "react-hook-form";
|
import { Controller, useFormContext } from "react-hook-form";
|
||||||
import {
|
import {
|
||||||
|
@ -12,10 +11,7 @@ import {
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
|
|
||||||
import AuthenticationFlowRepresentation from "keycloak-admin/lib/defs/authenticationFlowRepresentation";
|
import AuthenticationFlowRepresentation from "keycloak-admin/lib/defs/authenticationFlowRepresentation";
|
||||||
import {
|
import { useFetch, useAdminClient } from "../../context/auth/AdminClient";
|
||||||
asyncStateFetch,
|
|
||||||
useAdminClient,
|
|
||||||
} from "../../context/auth/AdminClient";
|
|
||||||
import { SwitchField } from "../component/SwitchField";
|
import { SwitchField } from "../component/SwitchField";
|
||||||
import { TextField } from "../component/TextField";
|
import { TextField } from "../component/TextField";
|
||||||
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
||||||
|
@ -30,19 +26,10 @@ const LoginFlow = ({
|
||||||
const { control } = useFormContext();
|
const { control } = useFormContext();
|
||||||
|
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const errorHandler = useErrorHandler();
|
|
||||||
const [flows, setFlows] = useState<AuthenticationFlowRepresentation[]>();
|
const [flows, setFlows] = useState<AuthenticationFlowRepresentation[]>();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
useEffect(
|
useFetch(() => adminClient.authenticationManagement.getFlows(), setFlows, []);
|
||||||
() =>
|
|
||||||
asyncStateFetch(
|
|
||||||
() => adminClient.authenticationManagement.getFlows(),
|
|
||||||
setFlows,
|
|
||||||
errorHandler
|
|
||||||
),
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import React, { useEffect } from "react";
|
import React from "react";
|
||||||
import { useHistory, useParams } from "react-router-dom";
|
import { useHistory, useParams } from "react-router-dom";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Controller, FormProvider, useForm } from "react-hook-form";
|
import { Controller, FormProvider, useForm } from "react-hook-form";
|
||||||
import {
|
import {
|
||||||
|
@ -18,10 +17,7 @@ import IdentityProviderRepresentation from "keycloak-admin/lib/defs/identityProv
|
||||||
import { FormAccess } from "../../components/form-access/FormAccess";
|
import { FormAccess } from "../../components/form-access/FormAccess";
|
||||||
import { ScrollForm } from "../../components/scroll-form/ScrollForm";
|
import { ScrollForm } from "../../components/scroll-form/ScrollForm";
|
||||||
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
||||||
import {
|
import { useFetch, useAdminClient } from "../../context/auth/AdminClient";
|
||||||
asyncStateFetch,
|
|
||||||
useAdminClient,
|
|
||||||
} from "../../context/auth/AdminClient";
|
|
||||||
import { toUpperCase } from "../../util";
|
import { toUpperCase } from "../../util";
|
||||||
import { GeneralSettings } from "./GeneralSettings";
|
import { GeneralSettings } from "./GeneralSettings";
|
||||||
import { AdvancedSettings } from "./AdvancedSettings";
|
import { AdvancedSettings } from "./AdvancedSettings";
|
||||||
|
@ -91,17 +87,12 @@ export const DetailSettings = () => {
|
||||||
const { addAlert } = useAlerts();
|
const { addAlert } = useAlerts();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { realm } = useRealm();
|
const { realm } = useRealm();
|
||||||
const errorHandler = useErrorHandler();
|
|
||||||
|
|
||||||
useEffect(
|
useFetch(
|
||||||
() =>
|
() => adminClient.identityProviders.findOne({ alias: id }),
|
||||||
asyncStateFetch(
|
(provider) => {
|
||||||
() => adminClient.identityProviders.findOne({ alias: id }),
|
Object.entries(provider).map((entry) => setValue(entry[0], entry[1]));
|
||||||
(provider) => {
|
},
|
||||||
Object.entries(provider).map((entry) => setValue(entry[0], entry[1]));
|
|
||||||
},
|
|
||||||
errorHandler
|
|
||||||
),
|
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -10,13 +10,12 @@ import {
|
||||||
ModalVariant,
|
ModalVariant,
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { asyncStateFetch, useAdminClient } from "../context/auth/AdminClient";
|
import { useFetch, useAdminClient } from "../context/auth/AdminClient";
|
||||||
import RoleRepresentation from "keycloak-admin/lib/defs/roleRepresentation";
|
import RoleRepresentation from "keycloak-admin/lib/defs/roleRepresentation";
|
||||||
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
|
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
|
||||||
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
|
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
|
||||||
import { CaretDownIcon, FilterIcon } from "@patternfly/react-icons";
|
import { CaretDownIcon, FilterIcon } from "@patternfly/react-icons";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
|
|
||||||
type Role = RoleRepresentation & {
|
type Role = RoleRepresentation & {
|
||||||
clientId?: string;
|
clientId?: string;
|
||||||
|
@ -34,7 +33,6 @@ export const AssociatedRolesModal = (props: AssociatedRolesModalProps) => {
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const [selectedRows, setSelectedRows] = useState<RoleRepresentation[]>([]);
|
const [selectedRows, setSelectedRows] = useState<RoleRepresentation[]>([]);
|
||||||
const errorHandler = useErrorHandler();
|
|
||||||
|
|
||||||
const [isFilterDropdownOpen, setIsFilterDropdownOpen] = useState(false);
|
const [isFilterDropdownOpen, setIsFilterDropdownOpen] = useState(false);
|
||||||
const [filterType, setFilterType] = useState("roles");
|
const [filterType, setFilterType] = useState("roles");
|
||||||
|
@ -129,17 +127,19 @@ export const AssociatedRolesModal = (props: AssociatedRolesModalProps) => {
|
||||||
refresh();
|
refresh();
|
||||||
}, [filterType]);
|
}, [filterType]);
|
||||||
|
|
||||||
useEffect(() => {
|
useFetch(
|
||||||
if (id) {
|
async () => {
|
||||||
return asyncStateFetch(
|
if (id) return await adminClient.roles.findOneById({ id });
|
||||||
() => adminClient.roles.findOneById({ id }),
|
},
|
||||||
(fetchedRole) => setName(fetchedRole.name!),
|
(fetchedRole) => {
|
||||||
errorHandler
|
if (fetchedRole) {
|
||||||
);
|
setName(fetchedRole.name!);
|
||||||
} else {
|
} else {
|
||||||
setName(t("createRole"));
|
setName(t("createRole"));
|
||||||
}
|
}
|
||||||
}, []);
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
const onFilterDropdownToggle = () => {
|
const onFilterDropdownToggle = () => {
|
||||||
setIsFilterDropdownOpen(!isFilterDropdownOpen);
|
setIsFilterDropdownOpen(!isFilterDropdownOpen);
|
||||||
|
|
|
@ -2,7 +2,6 @@ import React, { useEffect, useState } 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 { Controller, FormProvider, useForm } from "react-hook-form";
|
import { Controller, FormProvider, useForm } from "react-hook-form";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
import {
|
import {
|
||||||
AlertVariant,
|
AlertVariant,
|
||||||
ButtonVariant,
|
ButtonVariant,
|
||||||
|
@ -17,7 +16,7 @@ import {
|
||||||
import RealmRepresentation from "keycloak-admin/lib/defs/realmRepresentation";
|
import RealmRepresentation from "keycloak-admin/lib/defs/realmRepresentation";
|
||||||
import { toUpperCase } from "../util";
|
import { toUpperCase } from "../util";
|
||||||
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
||||||
import { useAdminClient, asyncStateFetch } from "../context/auth/AdminClient";
|
import { useAdminClient, useFetch } from "../context/auth/AdminClient";
|
||||||
import { useRealm } from "../context/realm-context/RealmContext";
|
import { useRealm } from "../context/realm-context/RealmContext";
|
||||||
import { ViewHeader } from "../components/view-header/ViewHeader";
|
import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||||
import { useAlerts } from "../components/alert/Alerts";
|
import { useAlerts } from "../components/alert/Alerts";
|
||||||
|
@ -124,7 +123,6 @@ const RealmSettingsHeader = ({
|
||||||
export const RealmSettingsSection = () => {
|
export const RealmSettingsSection = () => {
|
||||||
const { t } = useTranslation("realm-settings");
|
const { t } = useTranslation("realm-settings");
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const handleError = useErrorHandler();
|
|
||||||
const { realm: realmName } = useRealm();
|
const { realm: realmName } = useRealm();
|
||||||
const { addAlert } = useAlerts();
|
const { addAlert } = useAlerts();
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
|
@ -136,16 +134,14 @@ export const RealmSettingsSection = () => {
|
||||||
ComponentRepresentation[]
|
ComponentRepresentation[]
|
||||||
>([]);
|
>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useFetch(
|
||||||
return asyncStateFetch(
|
() => adminClient.realms.findOne({ realm: realmName }),
|
||||||
() => adminClient.realms.findOne({ realm: realmName }),
|
(realm) => {
|
||||||
(realm) => {
|
setupForm(realm);
|
||||||
setupForm(realm);
|
setRealm(realm);
|
||||||
setRealm(realm);
|
},
|
||||||
},
|
[]
|
||||||
handleError
|
);
|
||||||
);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const update = async () => {
|
const update = async () => {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useEffect } from "react";
|
import React from "react";
|
||||||
import {
|
import {
|
||||||
ActionGroup,
|
ActionGroup,
|
||||||
AlertVariant,
|
AlertVariant,
|
||||||
|
@ -26,7 +26,7 @@ import ComponentRepresentation from "keycloak-admin/lib/defs/componentRepresenta
|
||||||
|
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
||||||
import { asyncStateFetch, useAdminClient } from "../context/auth/AdminClient";
|
import { useAdminClient, useFetch } from "../context/auth/AdminClient";
|
||||||
import { useAlerts } from "../components/alert/Alerts";
|
import { useAlerts } from "../components/alert/Alerts";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { ViewHeader } from "../components/view-header/ViewHeader";
|
import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||||
|
@ -35,7 +35,6 @@ import { ScrollForm } from "../components/scroll-form/ScrollForm";
|
||||||
|
|
||||||
import { KeycloakTabs } from "../components/keycloak-tabs/KeycloakTabs";
|
import { KeycloakTabs } from "../components/keycloak-tabs/KeycloakTabs";
|
||||||
import { LdapMapperList } from "./ldap/mappers/LdapMapperList";
|
import { LdapMapperList } from "./ldap/mappers/LdapMapperList";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
|
|
||||||
type ldapComponentRepresentation = ComponentRepresentation & {
|
type ldapComponentRepresentation = ComponentRepresentation & {
|
||||||
config?: {
|
config?: {
|
||||||
|
@ -179,24 +178,24 @@ export const UserFederationLdapSettings = () => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const { realm } = useRealm();
|
const { realm } = useRealm();
|
||||||
const errorHandler = useErrorHandler();
|
|
||||||
|
|
||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>();
|
||||||
const { addAlert } = useAlerts();
|
const { addAlert } = useAlerts();
|
||||||
|
|
||||||
useEffect(() => {
|
useFetch(
|
||||||
if (id) {
|
async () => {
|
||||||
return asyncStateFetch(
|
if (id) {
|
||||||
() => adminClient.components.findOne({ id }),
|
return await adminClient.components.findOne({ id });
|
||||||
(fetchedComponent) => {
|
}
|
||||||
if (fetchedComponent) {
|
return undefined;
|
||||||
setupForm(fetchedComponent);
|
},
|
||||||
}
|
(fetchedComponent) => {
|
||||||
},
|
if (fetchedComponent) {
|
||||||
errorHandler
|
setupForm(fetchedComponent);
|
||||||
);
|
}
|
||||||
}
|
},
|
||||||
}, []);
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
const setupForm = (component: ComponentRepresentation) => {
|
const setupForm = (component: ComponentRepresentation) => {
|
||||||
Object.entries(component).map((entry) => {
|
Object.entries(component).map((entry) => {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import React, { useContext, useEffect, useState } from "react";
|
import React, { useContext, useState } from "react";
|
||||||
import { useHistory, useRouteMatch } from "react-router-dom";
|
import { useHistory, useRouteMatch } from "react-router-dom";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
import {
|
import {
|
||||||
AlertVariant,
|
AlertVariant,
|
||||||
ButtonVariant,
|
ButtonVariant,
|
||||||
|
@ -24,7 +23,7 @@ import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||||
import { DatabaseIcon } from "@patternfly/react-icons";
|
import { DatabaseIcon } from "@patternfly/react-icons";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { RealmContext } from "../context/realm-context/RealmContext";
|
import { RealmContext } from "../context/realm-context/RealmContext";
|
||||||
import { useAdminClient, asyncStateFetch } from "../context/auth/AdminClient";
|
import { useAdminClient, useFetch } from "../context/auth/AdminClient";
|
||||||
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
||||||
|
|
||||||
import "./user-federation.css";
|
import "./user-federation.css";
|
||||||
|
@ -40,25 +39,22 @@ export const UserFederationSection = () => {
|
||||||
const [key, setKey] = useState(0);
|
const [key, setKey] = useState(0);
|
||||||
const refresh = () => setKey(new Date().getTime());
|
const refresh = () => setKey(new Date().getTime());
|
||||||
|
|
||||||
const handleError = useErrorHandler();
|
|
||||||
const { url } = useRouteMatch();
|
const { url } = useRouteMatch();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
useEffect(() => {
|
useFetch(
|
||||||
return asyncStateFetch(
|
() => {
|
||||||
() => {
|
const testParams: { [name: string]: string | number } = {
|
||||||
const testParams: { [name: string]: string | number } = {
|
parentId: realm,
|
||||||
parentId: realm,
|
type: "org.keycloak.storage.UserStorageProvider",
|
||||||
type: "org.keycloak.storage.UserStorageProvider",
|
};
|
||||||
};
|
return adminClient.components.find(testParams);
|
||||||
return adminClient.components.find(testParams);
|
},
|
||||||
},
|
(userFederations) => {
|
||||||
(userFederations) => {
|
setUserFederations(userFederations);
|
||||||
setUserFederations(userFederations);
|
},
|
||||||
},
|
[key]
|
||||||
handleError
|
);
|
||||||
);
|
|
||||||
}, [key]);
|
|
||||||
|
|
||||||
const ufAddProviderDropdownItems = [
|
const ufAddProviderDropdownItems = [
|
||||||
<DropdownItem
|
<DropdownItem
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useState } from "react";
|
||||||
import {
|
import {
|
||||||
Breadcrumb,
|
Breadcrumb,
|
||||||
BreadcrumbItem,
|
BreadcrumbItem,
|
||||||
|
@ -20,10 +20,9 @@ import {
|
||||||
ToolbarItem,
|
ToolbarItem,
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { asyncStateFetch, useAdminClient } from "../context/auth/AdminClient";
|
import { useFetch, useAdminClient } from "../context/auth/AdminClient";
|
||||||
import { AngleRightIcon, SearchIcon } from "@patternfly/react-icons";
|
import { AngleRightIcon, SearchIcon } from "@patternfly/react-icons";
|
||||||
import GroupRepresentation from "keycloak-admin/lib/defs/groupRepresentation";
|
import GroupRepresentation from "keycloak-admin/lib/defs/groupRepresentation";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
|
|
||||||
|
@ -52,8 +51,6 @@ export const JoinGroupDialog = ({
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const [selectedRows, setSelectedRows] = useState<Group[]>([]);
|
const [selectedRows, setSelectedRows] = useState<Group[]>([]);
|
||||||
|
|
||||||
const errorHandler = useErrorHandler();
|
|
||||||
|
|
||||||
const [navigation, setNavigation] = useState<Group[]>([]);
|
const [navigation, setNavigation] = useState<Group[]>([]);
|
||||||
const [groups, setGroups] = useState<Group[]>([]);
|
const [groups, setGroups] = useState<Group[]>([]);
|
||||||
const [filtered, setFiltered] = useState<GroupRepresentation[]>();
|
const [filtered, setFiltered] = useState<GroupRepresentation[]>();
|
||||||
|
@ -63,42 +60,38 @@ export const JoinGroupDialog = ({
|
||||||
|
|
||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>();
|
||||||
|
|
||||||
useEffect(
|
useFetch(
|
||||||
() =>
|
async () => {
|
||||||
asyncStateFetch(
|
const allGroups = await adminClient.groups.find();
|
||||||
async () => {
|
|
||||||
const allGroups = await adminClient.groups.find();
|
|
||||||
|
|
||||||
if (groupId) {
|
if (groupId) {
|
||||||
const group = await adminClient.groups.findOne({ id: groupId });
|
const group = await adminClient.groups.findOne({ id: groupId });
|
||||||
return { group, groups: group.subGroups! };
|
return { group, groups: group.subGroups! };
|
||||||
} else if (id) {
|
} else if (id) {
|
||||||
const existingUserGroups = await adminClient.users.listGroups({
|
const existingUserGroups = await adminClient.users.listGroups({
|
||||||
id,
|
id,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
groups: _.differenceBy(allGroups, existingUserGroups, "id"),
|
groups: _.differenceBy(allGroups, existingUserGroups, "id"),
|
||||||
};
|
};
|
||||||
} else
|
} else
|
||||||
return {
|
return {
|
||||||
groups: allGroups,
|
groups: allGroups,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async ({ group: selectedGroup, groups }) => {
|
async ({ group: selectedGroup, groups }) => {
|
||||||
if (selectedGroup) {
|
if (selectedGroup) {
|
||||||
setNavigation([...navigation, selectedGroup]);
|
setNavigation([...navigation, selectedGroup]);
|
||||||
}
|
}
|
||||||
|
|
||||||
groups.forEach((group: Group) => {
|
groups.forEach((group: Group) => {
|
||||||
group.checked = !!selectedRows.find((r) => r.id === group.id);
|
group.checked = !!selectedRows.find((r) => r.id === group.id);
|
||||||
});
|
});
|
||||||
id
|
id
|
||||||
? setGroups(groups)
|
? setGroups(groups)
|
||||||
: setGroups([...groups.filter((row) => !chips.includes(row.name))]);
|
: setGroups([...groups.filter((row) => !chips.includes(row.name))]);
|
||||||
},
|
},
|
||||||
errorHandler
|
|
||||||
),
|
|
||||||
[groupId]
|
[groupId]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useState } from "react";
|
||||||
import {
|
import {
|
||||||
ActionGroup,
|
ActionGroup,
|
||||||
AlertVariant,
|
AlertVariant,
|
||||||
|
@ -19,8 +19,7 @@ import { FormAccess } from "../components/form-access/FormAccess";
|
||||||
import UserRepresentation from "keycloak-admin/lib/defs/userRepresentation";
|
import UserRepresentation from "keycloak-admin/lib/defs/userRepresentation";
|
||||||
import { HelpItem } from "../components/help-enabler/HelpItem";
|
import { HelpItem } from "../components/help-enabler/HelpItem";
|
||||||
import { useRealm } from "../context/realm-context/RealmContext";
|
import { useRealm } from "../context/realm-context/RealmContext";
|
||||||
import { asyncStateFetch, useAdminClient } from "../context/auth/AdminClient";
|
import { useFetch, useAdminClient } from "../context/auth/AdminClient";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { JoinGroupDialog } from "./JoinGroupDialog";
|
import { JoinGroupDialog } from "./JoinGroupDialog";
|
||||||
import GroupRepresentation from "keycloak-admin/lib/defs/groupRepresentation";
|
import GroupRepresentation from "keycloak-admin/lib/defs/groupRepresentation";
|
||||||
|
@ -51,7 +50,6 @@ export const UserForm = ({
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>();
|
||||||
const handleError = useErrorHandler();
|
|
||||||
|
|
||||||
const watchUsernameInput = watch("username");
|
const watchUsernameInput = watch("username");
|
||||||
const [timestamp, setTimestamp] = useState(null);
|
const [timestamp, setTimestamp] = useState(null);
|
||||||
|
@ -64,17 +62,15 @@ export const UserForm = ({
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useFetch(
|
||||||
if (editMode) {
|
async () => {
|
||||||
return asyncStateFetch(
|
if (editMode) return await adminClient.users.findOne({ id: id });
|
||||||
() => adminClient.users.findOne({ id: id }),
|
},
|
||||||
(user) => {
|
(user) => {
|
||||||
setupForm(user);
|
if (user) setupForm(user);
|
||||||
},
|
},
|
||||||
handleError
|
[chips]
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}, [chips]);
|
|
||||||
|
|
||||||
const setupForm = (user: UserRepresentation) => {
|
const setupForm = (user: UserRepresentation) => {
|
||||||
reset();
|
reset();
|
||||||
|
|
|
@ -14,10 +14,9 @@ import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable
|
||||||
import { useAlerts } from "../components/alert/Alerts";
|
import { useAlerts } from "../components/alert/Alerts";
|
||||||
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
||||||
import { emptyFormatter } from "../util";
|
import { emptyFormatter } from "../util";
|
||||||
import { asyncStateFetch, useAdminClient } from "../context/auth/AdminClient";
|
import { useFetch, useAdminClient } from "../context/auth/AdminClient";
|
||||||
import GroupRepresentation from "keycloak-admin/lib/defs/groupRepresentation";
|
import GroupRepresentation from "keycloak-admin/lib/defs/groupRepresentation";
|
||||||
import { cellWidth } from "@patternfly/react-table";
|
import { cellWidth } from "@patternfly/react-table";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import UserRepresentation from "keycloak-admin/lib/defs/userRepresentation";
|
import UserRepresentation from "keycloak-admin/lib/defs/userRepresentation";
|
||||||
import { JoinGroupDialog } from "./JoinGroupDialog";
|
import { JoinGroupDialog } from "./JoinGroupDialog";
|
||||||
|
@ -43,7 +42,6 @@ export const UserGroups = () => {
|
||||||
const { addAlert } = useAlerts();
|
const { addAlert } = useAlerts();
|
||||||
const [key, setKey] = useState(0);
|
const [key, setKey] = useState(0);
|
||||||
const refresh = () => setKey(new Date().getTime());
|
const refresh = () => setKey(new Date().getTime());
|
||||||
const handleError = useErrorHandler();
|
|
||||||
|
|
||||||
const [selectedGroup, setSelectedGroup] = useState<GroupRepresentation>();
|
const [selectedGroup, setSelectedGroup] = useState<GroupRepresentation>();
|
||||||
const [list, setList] = useState(false);
|
const [list, setList] = useState(false);
|
||||||
|
@ -177,17 +175,13 @@ export const UserGroups = () => {
|
||||||
return alphabetize(filterDupesfromGroups);
|
return alphabetize(filterDupesfromGroups);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useFetch(
|
||||||
return asyncStateFetch(
|
() => adminClient.users.listGroups({ id }),
|
||||||
() => {
|
(response) => {
|
||||||
return Promise.resolve(adminClient.users.listGroups({ id }));
|
setListGroups(!!(response && response.length > 0));
|
||||||
},
|
},
|
||||||
(response) => {
|
[]
|
||||||
setListGroups(!!(response && response.length > 0));
|
);
|
||||||
},
|
|
||||||
handleError
|
|
||||||
);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refresh();
|
refresh();
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import React, { useContext, useEffect, useState } from "react";
|
import React, { useContext, useState } from "react";
|
||||||
import { useErrorHandler } from "react-error-boundary";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
AlertVariant,
|
AlertVariant,
|
||||||
|
@ -17,7 +16,7 @@ import {
|
||||||
} from "@patternfly/react-icons";
|
} from "@patternfly/react-icons";
|
||||||
import UserRepresentation from "keycloak-admin/lib/defs/userRepresentation";
|
import UserRepresentation from "keycloak-admin/lib/defs/userRepresentation";
|
||||||
|
|
||||||
import { asyncStateFetch, useAdminClient } from "../context/auth/AdminClient";
|
import { useFetch, useAdminClient } from "../context/auth/AdminClient";
|
||||||
import { ViewHeader } from "../components/view-header/ViewHeader";
|
import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||||
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
|
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
|
||||||
import { useAlerts } from "../components/alert/Alerts";
|
import { useAlerts } from "../components/alert/Alerts";
|
||||||
|
@ -36,7 +35,6 @@ type BruteUser = UserRepresentation & {
|
||||||
|
|
||||||
export const UsersSection = () => {
|
export const UsersSection = () => {
|
||||||
const { t } = useTranslation("users");
|
const { t } = useTranslation("users");
|
||||||
const handleError = useErrorHandler();
|
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const { addAlert } = useAlerts();
|
const { addAlert } = useAlerts();
|
||||||
const { realm: realmName } = useContext(RealmContext);
|
const { realm: realmName } = useContext(RealmContext);
|
||||||
|
@ -50,27 +48,25 @@ export const UsersSection = () => {
|
||||||
const [key, setKey] = useState("");
|
const [key, setKey] = useState("");
|
||||||
const refresh = () => setKey(`${new Date().getTime()}`);
|
const refresh = () => setKey(`${new Date().getTime()}`);
|
||||||
|
|
||||||
useEffect(() => {
|
useFetch(
|
||||||
return asyncStateFetch(
|
() => {
|
||||||
() => {
|
const testParams = {
|
||||||
const testParams = {
|
type: "org.keycloak.storage.UserStorageProvider",
|
||||||
type: "org.keycloak.storage.UserStorageProvider",
|
};
|
||||||
};
|
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
adminClient.components.find(testParams),
|
adminClient.components.find(testParams),
|
||||||
adminClient.users.count(),
|
adminClient.users.count(),
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
(response) => {
|
(response) => {
|
||||||
//should *only* list users when no user federation is configured and uses count > 100
|
//should *only* list users when no user federation is configured and uses count > 100
|
||||||
setListUsers(
|
setListUsers(
|
||||||
!((response[0] && response[0].length > 0) || response[1] > 100)
|
!((response[0] && response[0].length > 0) || response[1] > 100)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
handleError
|
[]
|
||||||
);
|
);
|
||||||
}, []);
|
|
||||||
|
|
||||||
const UserDetailLink = (user: UserRepresentation) => (
|
const UserDetailLink = (user: UserRepresentation) => (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -5974,7 +5974,7 @@ aws4@^1.8.0:
|
||||||
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
|
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
|
||||||
integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==
|
integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==
|
||||||
|
|
||||||
axios@^0.21.0:
|
axios@^0.21.0, axios@^0.21.1:
|
||||||
version "0.21.1"
|
version "0.21.1"
|
||||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
|
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
|
||||||
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
|
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
|
||||||
|
|
Loading…
Reference in a new issue