diff --git a/src/clients/ClientDescription.tsx b/src/clients/ClientDescription.tsx
index acda755a53..3f5afb30f5 100644
--- a/src/clients/ClientDescription.tsx
+++ b/src/clients/ClientDescription.tsx
@@ -1,7 +1,14 @@
import React from "react";
-import { FormGroup, TextInput, ValidatedOptions } from "@patternfly/react-core";
import { useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
+import {
+ FormGroup,
+ TextArea,
+ TextInput,
+ ValidatedOptions,
+} from "@patternfly/react-core";
+
+import { HelpItem } from "../components/help-enabler/HelpItem";
import { FormAccess } from "../components/form-access/FormAccess";
import { ClientForm } from "./ClientDetails";
@@ -12,6 +19,13 @@ export const ClientDescription = () => {
return (
+ }
label={t("clientID")}
fieldId="kc-client-id"
helperTextInvalid={t("common:required")}
@@ -30,10 +44,27 @@ export const ClientDescription = () => {
}
/>
-
+
+ }
+ label={t("common:name")}
+ fieldId="kc-name"
+ >
+ }
label={t("common:description")}
fieldId="kc-description"
validated={
@@ -41,7 +72,7 @@ export const ClientDescription = () => {
}
helperTextInvalid={errors.description?.message}
>
- {
{client.clientId}
{!client.enabled && (
-
+
Disabled
)}
diff --git a/src/clients/add/CapabilityConfig.tsx b/src/clients/add/CapabilityConfig.tsx
index c52178ce47..b8101ea729 100644
--- a/src/clients/add/CapabilityConfig.tsx
+++ b/src/clients/add/CapabilityConfig.tsx
@@ -8,129 +8,203 @@ import {
GridItem,
} from "@patternfly/react-core";
import { Controller, useFormContext } from "react-hook-form";
-
import { FormAccess } from "../../components/form-access/FormAccess";
import { ClientForm } from "../ClientDetails";
+import { HelpItem } from "../../components/help-enabler/HelpItem";
-export const CapabilityConfig = () => {
+type CapabilityConfigProps = {
+ unWrap?: boolean;
+ protocol?: string;
+};
+
+export const CapabilityConfig = ({
+ unWrap,
+ protocol: type,
+}: CapabilityConfigProps) => {
const { t } = useTranslation("clients");
- const { control } = useFormContext();
+ const { control, watch } = useFormContext();
+ const protocol = type || watch("protocol");
+
return (
-
-
- (
-
- )}
- />
-
-
- (
-
- )}
- />
-
-
-
-
- (
-
+ <>
+ {protocol === "openid-connect" && (
+ <>
+
+ (
+
+ )}
+ />
+
+
+ (
+
+ )}
+ />
+
+
+
+
+ (
+
+ )}
+ />
+
+
+ (
+
+ )}
+ />
+
+
+ (
+
+ )}
+ />
+
+
+ (
+
+ )}
+ />
+
+
+
+ >
+ )}
+ >
+ <>
+ {protocol === "saml" && (
+ <>
+
- )}
- />
-
-
- (
-
+ (
+ onChange("" + value)}
+ />
+ )}
+ />
+
+
- )}
- />
-
-
- (
-
- )}
- />
-
-
- (
-
- )}
- />
-
-
-
+ }
+ label={t("clientSignature")}
+ fieldId="kc-client-signature"
+ >
+ (
+ onChange("" + value)}
+ />
+ )}
+ />
+
+ >
+ )}
+ >
);
};
diff --git a/src/clients/add/NewClientForm.tsx b/src/clients/add/NewClientForm.tsx
index 49d51c8d41..55364e7f04 100644
--- a/src/clients/add/NewClientForm.tsx
+++ b/src/clients/add/NewClientForm.tsx
@@ -111,11 +111,11 @@ export const NewClientForm = () => {
},
{
name: t("capabilityConfig"),
- component: ,
+ component: ,
},
]}
footer={}
- onSave={() => save()}
+ onSave={save}
/>
diff --git a/src/clients/help.json b/src/clients/help.json
index 85fe0f6dcd..bfc67b9026 100644
--- a/src/clients/help.json
+++ b/src/clients/help.json
@@ -2,6 +2,11 @@
"clients-help": {
"webOrigins": "Allowed CORS origins. To permit all origins of Valid Redirect URIs, add '+'. This does not include the '*' wildcard though. To permit all origins, explicitly add '*'.",
"adminURL": "URL to the admin interface of the client. Set this if the client supports the adapter REST API. This REST API allows the auth server to push revocation policies and other administrative tasks. Usually this is set to the base URL of the client.",
+ "clientID": "Specifies ID referenced in URI and tokens. For example 'my-client'. For SAML this is also the expected issuer value from authn requests",
+ "clientName": "Specifies display name of the client. For example 'My Client'. Supports keys for localized values as well. For example: ${my_client}",
+ "description": "Specifies description of the client. For example 'My Client for TimeSheets'. Supports keys for localized values as well. For example: ${my_client_description}",
+ "encryptAssertions": "Should SAML assertions be encrypted with client's public key using AES?",
+ "clientSignature": "Will the client sign their saml requests and responses? And should they be validated?",
"downloadType": "this is information about the download type",
"details": "this is information about the details",
"createToken": "An initial access token can only be used to create clients",
diff --git a/src/clients/import/ImportForm.tsx b/src/clients/import/ImportForm.tsx
index 3659c45a32..ad25687e4a 100644
--- a/src/clients/import/ImportForm.tsx
+++ b/src/clients/import/ImportForm.tsx
@@ -1,4 +1,6 @@
import React from "react";
+import { useHistory } from "react-router-dom";
+import { useTranslation } from "react-i18next";
import {
PageSection,
FormGroup,
@@ -8,7 +10,6 @@ import {
AlertVariant,
} from "@patternfly/react-core";
import { FormProvider, useForm } from "react-hook-form";
-import { useTranslation } from "react-i18next";
import { ClientDescription } from "../ClientDescription";
import { JsonFileUpload } from "../../components/json-file-upload/JsonFileUpload";
@@ -17,10 +18,15 @@ import { ViewHeader } from "../../components/view-header/ViewHeader";
import ClientRepresentation from "keycloak-admin/lib/defs/clientRepresentation";
import { useAdminClient } from "../../context/auth/AdminClient";
import { FormAccess } from "../../components/form-access/FormAccess";
+import { useRealm } from "../../context/realm-context/RealmContext";
+import { convertFormValuesToObject, convertToFormValues } from "../../util";
+import { CapabilityConfig } from "../add/CapabilityConfig";
export const ImportForm = () => {
const { t } = useTranslation("clients");
+ const history = useHistory();
const adminClient = useAdminClient();
+ const { realm } = useRealm();
const form = useForm();
const { register, handleSubmit, setValue } = form;
@@ -36,18 +42,27 @@ export const ImportForm = () => {
const obj = value ? JSON.parse(value as string) : defaultClient;
Object.keys(obj).forEach((k) => {
- setValue(k, obj[k]);
+ if (k === "attributes") {
+ convertToFormValues(obj[k], "attributes", form.setValue);
+ } else {
+ setValue(k, obj[k]);
+ }
});
};
const save = async (client: ClientRepresentation) => {
try {
- await adminClient.clients.create({ ...client });
+ const newClient = await adminClient.clients.create({
+ ...client,
+ attributes: convertFormValuesToObject(client.attributes || {}),
+ });
addAlert(t("clientImportSuccess"), AlertVariant.success);
+ history.push(`/${realm}/clients/${newClient.id}`);
} catch (error) {
- addAlert(`${t("clientImportError")} '${error}'`, AlertVariant.danger);
+ addAlert(t("clientImportError", { error }), AlertVariant.danger);
}
};
+
return (
<>
{
ref={register()}
/>
+
-
+
diff --git a/src/clients/messages.json b/src/clients/messages.json
index 31a5a4b838..7fea965c01 100644
--- a/src/clients/messages.json
+++ b/src/clients/messages.json
@@ -9,6 +9,8 @@
"webOrigins": "Web origins",
"adminURL": "Admin URL",
"formatOption": "Format option",
+ "encryptAssertions": "Encrypt assertions",
+ "clientSignature": "Client signature required",
"downloadAdaptorTitle": "Download adaptor configs",
"credentials": "Credentials",
"roles": "Roles",
@@ -61,7 +63,7 @@
"clientsExplain": "Clients are applications and services that can request authentication of a user",
"createSuccess": "Client created successfully",
"createError": "Could not create client: '{{error}}'",
- "clientImportError": "Could not import client",
+ "clientImportError": "Could not import client: {{error}}",
"clientSaveSuccess": "Client successfully updated",
"clientSaveError": "Client could not be updated:",
"clientImportSuccess": "Client imported successfully",
diff --git a/src/components/alert/AlertPanel.tsx b/src/components/alert/AlertPanel.tsx
index 2caf3c788c..8d527b3409 100644
--- a/src/components/alert/AlertPanel.tsx
+++ b/src/components/alert/AlertPanel.tsx
@@ -22,23 +22,22 @@ export function AlertPanel({ alerts, onCloseAlert }: AlertPanelProps) {
return (
{alerts.map(({ key, variant, message, description }) => (
- <>
- onCloseAlert(key)}
- />
- }
- >
- {description && {description}
}
-
- >
+ onCloseAlert(key)}
+ />
+ }
+ >
+ {description && {description}
}
+
))}
);
diff --git a/src/components/alert/Alerts.tsx b/src/components/alert/Alerts.tsx
index 7c96723860..aae5531451 100644
--- a/src/components/alert/Alerts.tsx
+++ b/src/components/alert/Alerts.tsx
@@ -16,11 +16,6 @@ export const AlertContext = createContext({
export const useAlerts = () => useContext(AlertContext);
-type TimeOut = {
- key: number;
- timeOut: NodeJS.Timeout;
-};
-
export const AlertProvider = ({ children }: { children: ReactNode }) => {
const [alerts, setAlerts] = useState([]);
diff --git a/src/components/json-file-upload/JsonFileUpload.tsx b/src/components/json-file-upload/JsonFileUpload.tsx
index d20a41ac74..d269a89a3a 100644
--- a/src/components/json-file-upload/JsonFileUpload.tsx
+++ b/src/components/json-file-upload/JsonFileUpload.tsx
@@ -49,7 +49,10 @@ export const JsonFileUpload = ({
| React.ChangeEvent
| React.MouseEvent
): void => {
- if (event.nativeEvent instanceof MouseEvent) {
+ if (
+ event.nativeEvent instanceof MouseEvent &&
+ !(event.nativeEvent instanceof DragEvent)
+ ) {
setFileUpload({ ...fileUpload, modal: true });
} else {
setFileUpload({
@@ -100,7 +103,6 @@ export const JsonFileUpload = ({
value={fileUpload.value}
filename={fileUpload.filename}
onChange={handleChange}
- allowEditingUploadedText
onReadStarted={() =>
setFileUpload({ ...fileUpload, isLoading: true })
}
diff --git a/src/components/json-file-upload/__tests__/__snapshots__/JsonFileUpload.test.tsx.snap b/src/components/json-file-upload/__tests__/__snapshots__/JsonFileUpload.test.tsx.snap
index efd84ff8c7..04d782fd08 100644
--- a/src/components/json-file-upload/__tests__/__snapshots__/JsonFileUpload.test.tsx.snap
+++ b/src/components/json-file-upload/__tests__/__snapshots__/JsonFileUpload.test.tsx.snap
@@ -32,7 +32,6 @@ exports[` render 1`] = `
className="pf-c-form__group-control"
>
render 1`] = `
preventDropOnDocument={true}
>
upload file 1`] = `
className="pf-c-form__group-control"
>
upload file 1`] = `
preventDropOnDocument={true}
>