Added posibilty to import SAML XML (#3049)

This commit is contained in:
Erik Jan de Wit 2022-08-04 13:08:11 +02:00 committed by GitHub
parent 2ecbb60d56
commit c60c83b6cc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 77 additions and 25 deletions

View file

@ -503,6 +503,7 @@
"importFile": "Import file",
"importSuccess": "New certificate imported",
"importError": "Could not import certificate {{error}}",
"importParseError": "Could not parse the file {{error}}",
"tokenLifespan": {
"expires": "Expires in",
"never": "Never expires"

View file

@ -2,5 +2,6 @@
"helpToggleInfo": "This toggle will enable / disable part of the help info in the console. Includes any help text, links and popovers.",
"showPassword": "Show password field in clear text",
"helpFileUpload": "Upload a JSON file",
"helpFileUploadClient": "Upload a JSON or XML file",
"dragHelp": "Press space or enter to begin dragging, and use the arrow keys to navigate up or down. Press enter to confirm the drag, or any other key to cancel the drag operation."
}

View file

@ -1,3 +1,7 @@
import { useState } from "react";
import { Link, useHistory } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { FormProvider, useForm } from "react-hook-form";
import {
ActionGroup,
AlertVariant,
@ -5,23 +9,29 @@ import {
FormGroup,
PageSection,
} from "@patternfly/react-core";
import { Language } from "@patternfly/react-code-editor";
import type ClientRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientRepresentation";
import { useState } from "react";
import { FormProvider, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { Link, useHistory } from "react-router-dom";
import { useAlerts } from "../../components/alert/Alerts";
import { FormAccess } from "../../components/form-access/FormAccess";
import { JsonFileUpload } from "../../components/json-file-upload/JsonFileUpload";
import { ViewHeader } from "../../components/view-header/ViewHeader";
import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput";
import { useAdminClient } from "../../context/auth/AdminClient";
import { useRealm } from "../../context/realm-context/RealmContext";
import { convertFormValuesToObject, convertToFormValues } from "../../util";
import {
addTrailingSlash,
convertFormValuesToObject,
convertToFormValues,
} from "../../util";
import { CapabilityConfig } from "../add/CapabilityConfig";
import { ClientDescription } from "../ClientDescription";
import { toClient } from "../routes/Client";
import { toClients } from "../routes/Clients";
import { FileUploadForm } from "../../components/json-file-upload/FileUploadForm";
import { getAuthorizationHeaders } from "../../utils/getAuthorizationHeaders";
const isXml = (text: string) => text.startsWith("<");
export default function ImportForm() {
const { t } = useTranslation("clients");
@ -34,18 +44,44 @@ export default function ImportForm() {
const { addAlert, addError } = useAlerts();
const handleFileChange = (obj?: object) => {
const defaultClient = {
protocol: "",
clientId: "",
name: "",
description: "",
};
const handleFileChange = async (contents: string) => {
try {
const parsed = await parseFileContents(contents);
convertToFormValues(obj || defaultClient, setValue);
setImported(obj || defaultClient);
convertToFormValues(parsed, setValue);
setImported(parsed);
} catch (error) {
addError("clients:importParseError", error);
}
};
async function parseFileContents(
contents: string
): Promise<ClientRepresentation> {
if (!isXml(contents)) {
return JSON.parse(contents);
}
const response = await fetch(
`${addTrailingSlash(
adminClient.baseUrl
)}admin/realms/${realm}/client-description-converter`,
{
method: "POST",
body: contents,
headers: getAuthorizationHeaders(await adminClient.getAccessToken()),
}
);
if (!response.ok) {
throw new Error(
`Server responded with invalid status: ${response.statusText}`
);
}
return response.json();
}
const save = async (client: ClientRepresentation) => {
try {
const newClient = await adminClient.clients.create({
@ -74,7 +110,13 @@ export default function ImportForm() {
role="manage-clients"
>
<FormProvider {...form}>
<JsonFileUpload id="realm-file" onChange={handleFileChange} />
<FileUploadForm
id="realm-file"
language={Language.json}
extension=".json,.xml"
helpText="common-help:helpFileUploadClient"
onChange={handleFileChange}
/>
<ClientDescription hasConfigureAccess />
<FormGroup label={t("common:type")} fieldId="kc-type">
<KeycloakTextInput

View file

@ -4,6 +4,7 @@ import { initReactI18next } from "react-i18next";
import type KeycloakAdminClient from "@keycloak/keycloak-admin-client";
import environment from "./environment";
import { getAuthorizationHeaders } from "./utils/getAuthorizationHeaders";
export const DEFAULT_LOCALE = "en";
@ -60,9 +61,9 @@ const initOptions = async (
postProcess: ["overrideProcessor"],
backend: {
loadPath: constructLoadPath,
customHeaders: {
Authorization: `Bearer ${await adminClient.getAccessToken()}`,
},
customHeaders: getAuthorizationHeaders(
await adminClient.getAccessToken()
),
},
};
};

View file

@ -9,6 +9,7 @@ import { useRealm } from "../../context/realm-context/RealmContext";
import { DiscoverySettings } from "./DiscoverySettings";
import { DiscoveryEndpointField } from "../component/DiscoveryEndpointField";
import { addTrailingSlash } from "../../util";
import { getAuthorizationHeaders } from "../../utils/getAuthorizationHeaders";
export const OpenIdConnectSettings = () => {
const { t } = useTranslation("identity-providers");
@ -45,9 +46,7 @@ export const OpenIdConnectSettings = () => {
{
method: "POST",
body: formData,
headers: {
Authorization: `Bearer ${await adminClient.getAccessToken()}`,
},
headers: getAuthorizationHeaders(await adminClient.getAccessToken()),
}
);
if (response.ok) {

View file

@ -13,6 +13,7 @@ import { DiscoveryEndpointField } from "../component/DiscoveryEndpointField";
import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput";
import environment from "../../environment";
import { addTrailingSlash } from "../../util";
import { getAuthorizationHeaders } from "../../utils/getAuthorizationHeaders";
export const SamlConnectSettings = () => {
const { t } = useTranslation("identity-providers");
@ -51,9 +52,7 @@ export const SamlConnectSettings = () => {
{
method: "POST",
body: formData,
headers: {
Authorization: `bearer ${await adminClient.getAccessToken()}`,
},
headers: getAuthorizationHeaders(await adminClient.getAccessToken()),
}
);
if (response.ok) {

View file

@ -0,0 +1,9 @@
export function getAuthorizationHeaders(
accessToken?: string
): Record<string, string> {
if (!accessToken) {
return {};
}
return { Authorization: `Bearer ${accessToken}` };
}