Add user profile tab to realm settings (#1509)

This commit is contained in:
Jon Koops 2021-11-12 16:04:05 +01:00 committed by GitHub
parent f57f8c5560
commit c61ba73781
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 164 additions and 1 deletions

View file

@ -51,6 +51,7 @@ import { HelpItem } from "../components/help-enabler/HelpItem";
import { DEFAULT_LOCALE } from "../i18n"; import { DEFAULT_LOCALE } from "../i18n";
import { toDashboard } from "../dashboard/routes/Dashboard"; import { toDashboard } from "../dashboard/routes/Dashboard";
import environment from "../environment"; import environment from "../environment";
import { UserProfileTab } from "./UserProfileTab";
type RealmSettingsHeaderProps = { type RealmSettingsHeaderProps = {
onChange: (value: boolean) => void; onChange: (value: boolean) => void;
@ -438,6 +439,15 @@ export const RealmSettingsTabs = ({
</Tab> </Tab>
</Tabs> </Tabs>
</Tab> </Tab>
<Tab
eventKey="userProfile"
data-testid="rs-user-profile-tab"
title={
<TabTitleText>{t("realm-settings:userProfile")}</TabTitleText>
}
>
<UserProfileTab />
</Tab>
</KeycloakTabs> </KeycloakTabs>
</FormProvider> </FormProvider>
</PageSection> </PageSection>

View file

@ -0,0 +1,144 @@
import type ClientProfilesRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientProfilesRepresentation";
import { CodeEditor, Language } from "@patternfly/react-code-editor";
import {
ActionGroup,
AlertVariant,
Button,
Form,
PageSection,
Tab,
Tabs,
TabTitleText,
} from "@patternfly/react-core";
import type { editor } from "monaco-editor";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { useAlerts } from "../components/alert/Alerts";
import { useAdminClient, useFetch } from "../context/auth/AdminClient";
import { useRealm } from "../context/realm-context/RealmContext";
import { prettyPrintJSON } from "../util";
export const UserProfileTab = () => {
const adminClient = useAdminClient();
const { realm } = useRealm();
const { t } = useTranslation("realm-settings");
const { addAlert, addError } = useAlerts();
const [activeTab, setActiveTab] = useState("attributes");
const [profiles, setProfiles] = useState<ClientProfilesRepresentation>();
const [isSaving, setIsSaving] = useState(false);
const [refreshCount, setRefreshCount] = useState(0);
useFetch(
() =>
adminClient.clientPolicies.listProfiles({
includeGlobalProfiles: true,
realm,
}),
(profiles) => setProfiles(profiles),
[refreshCount]
);
async function onSave(updatedProfiles: ClientProfilesRepresentation) {
setIsSaving(true);
try {
await adminClient.clientPolicies.createProfiles({
...updatedProfiles,
realm,
});
setRefreshCount(refreshCount + 1);
addAlert(t("userProfileSuccess"), AlertVariant.success);
} catch (error) {
addError("realm-settings:userProfileError", error);
}
setIsSaving(false);
}
return (
<Tabs
activeKey={activeTab}
onSelect={(_, key) => setActiveTab(key.toString())}
>
<Tab
eventKey="attributes"
title={<TabTitleText>{t("attributes")}</TabTitleText>}
></Tab>
<Tab
eventKey="attributesGroup"
title={<TabTitleText>{t("attributesGroup")}</TabTitleText>}
></Tab>
<Tab
eventKey="jsonEditor"
title={<TabTitleText>{t("jsonEditor")}</TabTitleText>}
>
{/** The code editor needs to be rendered conditionally to prevent it from being initialized
* while the tab contents are hidden. If the contents of the tab are hidden then it
* might not initialize correctly.
*/}
{activeTab === "jsonEditor" && (
<JsonEditorTab
profiles={profiles}
onSave={onSave}
isSaving={isSaving}
/>
)}
</Tab>
</Tabs>
);
};
type JsonEditorTabProps = {
profiles?: ClientProfilesRepresentation;
onSave: (profiles: ClientProfilesRepresentation) => void;
isSaving: boolean;
};
const JsonEditorTab = ({ profiles, onSave, isSaving }: JsonEditorTabProps) => {
const { t } = useTranslation();
const { addError } = useAlerts();
const [editor, setEditor] = useState<editor.IStandaloneCodeEditor>();
useEffect(() => resetCode(), [profiles, editor]);
function resetCode() {
editor?.setValue(profiles ? prettyPrintJSON(profiles) : "");
}
function save() {
const value = editor?.getValue();
if (!value) {
return;
}
try {
onSave(JSON.parse(value));
} catch (error) {
addError("realm-settings:invalidJsonError", error);
return;
}
}
return (
<PageSection variant="light">
<CodeEditor
language={Language.json}
height="30rem"
onEditorDidMount={(editor) => setEditor(editor)}
isLanguageLabelVisible
/>
<Form>
<ActionGroup>
<Button variant="primary" onClick={save} isDisabled={isSaving}>
{t("common:save")}
</Button>
<Button variant="link" onClick={resetCode} isDisabled={isSaving}>
{t("common:revert")}
</Button>
</ActionGroup>
</Form>
</PageSection>
);
};

View file

@ -338,6 +338,14 @@ export default {
addClientProfile: "Add client profile", addClientProfile: "Add client profile",
emptyProfiles: "No client profiles configured", emptyProfiles: "No client profiles configured",
tokens: "Tokens", tokens: "Tokens",
userProfile: "User profile",
jsonEditor: "JSON editor",
attributes: "Attributes",
attributesGroup: "Attributes group",
invalidJsonError:
"Unable to save user profile, the provided information is not valid JSON.",
userProfileSuccess: "User profile settings successfully updated.",
userProfileError: "Could not update user profile settings: {{error}}",
key: "Key", key: "Key",
value: "Value", value: "Value",
status: "Status", status: "Status",

View file

@ -13,7 +13,8 @@ export type RealmSettingsTab =
| "securityDefences" | "securityDefences"
| "sessions" | "sessions"
| "tokens" | "tokens"
| "clientPolicies"; | "clientPolicies"
| "userProfile";
export type RealmSettingsParams = { export type RealmSettingsParams = {
realm: string; realm: string;