mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
SSO LDAP: ironing details (#93584)
* Rework description tooltip * Null protect empty * Add missing ref properties for form controllers * Rework LDAP provider label * Add missing `*` for required input * Hide multiselect visual queues * Add modal * Adjust translated text hierarchies * Add redirect confirmation modal
This commit is contained in:
parent
267f417924
commit
b6906cc866
@ -60,7 +60,7 @@ export const LdapDrawerComponent = ({
|
||||
useEffect(() => {
|
||||
const { client_cert, client_key, root_ca_cert } = getValues(serverConfig);
|
||||
setEncryptionProvider(
|
||||
!client_cert.length && !client_key.length && !root_ca_cert?.length
|
||||
!client_cert?.length && !client_key?.length && !root_ca_cert?.length
|
||||
? EncryptionProvider.Base64
|
||||
: EncryptionProvider.FilePath
|
||||
);
|
||||
@ -83,19 +83,21 @@ export const LdapDrawerComponent = ({
|
||||
);
|
||||
|
||||
const useTlsDescription = (
|
||||
<Trans i18nKey="ldap-drawer.extra-security-section.use-ssl-tooltip">
|
||||
For a complete list of supported ciphers and TLS versions, refer to:{' '}
|
||||
{
|
||||
<TextLink style={{ fontSize: 'inherit' }} href="https://go.dev/src/crypto/tls/cipher_suites.go" external>
|
||||
https://go.dev/src/crypto/tls/cipher_suites.go
|
||||
</TextLink>
|
||||
}
|
||||
</Trans>
|
||||
<>
|
||||
<Trans i18nKey="ldap-drawer.extra-security-section.use-ssl-tooltip">
|
||||
For a complete list of supported ciphers and TLS versions, refer to:
|
||||
</Trans>{' '}
|
||||
{/* eslint-disable-next-line @grafana/no-untranslated-strings */}
|
||||
<TextLink style={{ fontSize: 'inherit' }} href="https://go.dev/src/crypto/tls/cipher_suites.go" external>
|
||||
https://go.dev/src/crypto/tls/cipher_suites.go
|
||||
</TextLink>
|
||||
</>
|
||||
);
|
||||
|
||||
const onAddGroupMapping = () => {
|
||||
const groupMappings = getValues(`${serverConfig}.group_mappings`) || [];
|
||||
setValue(`${serverConfig}.group_mappings`, [
|
||||
...getValues(`${serverConfig}.group_mappings`),
|
||||
...groupMappings,
|
||||
{
|
||||
group_dn: '',
|
||||
org_id: 1,
|
||||
@ -338,10 +340,12 @@ export const LdapDrawerComponent = ({
|
||||
<Controller
|
||||
name={`${serverConfig}.root_ca_cert_value`}
|
||||
control={control}
|
||||
render={({ field: { onChange, value, ...field } }) => (
|
||||
render={({ field: { onChange, ref, value, ...field } }) => (
|
||||
<MultiSelect
|
||||
{...field}
|
||||
allowCustomValue
|
||||
className={styles.multiSelect}
|
||||
noOptionsMessage={''}
|
||||
onChange={(v) => onChange(v.map(({ value }) => String(value)))}
|
||||
value={value?.map((v) => ({ label: renderMultiSelectLabel(v), value: v }))}
|
||||
/>
|
||||
@ -447,5 +451,10 @@ function getStyles(theme: GrafanaTheme2) {
|
||||
button: css({
|
||||
marginBottom: theme.spacing(4),
|
||||
}),
|
||||
multiSelect: css({
|
||||
svg: {
|
||||
display: 'none',
|
||||
},
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import { Controller, FormProvider, useForm } from 'react-hook-form';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { AppEvents, GrafanaTheme2, NavModelItem } from '@grafana/data';
|
||||
import { getBackendSrv, getAppEvents } from '@grafana/runtime';
|
||||
import { getBackendSrv, getAppEvents, reportInteraction } from '@grafana/runtime';
|
||||
import {
|
||||
useStyles2,
|
||||
Alert,
|
||||
@ -15,12 +15,14 @@ import {
|
||||
Input,
|
||||
LinkButton,
|
||||
Menu,
|
||||
Modal,
|
||||
Stack,
|
||||
Text,
|
||||
TextLink,
|
||||
Dropdown,
|
||||
MultiSelect,
|
||||
} from '@grafana/ui';
|
||||
import { FormPrompt } from 'app/core/components/FormPrompt/FormPrompt';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
import config from 'app/core/config';
|
||||
import { t, Trans } from 'app/core/internationalization';
|
||||
@ -94,6 +96,7 @@ const emptySettings: LdapPayload = {
|
||||
export const LdapSettingsPage = () => {
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
|
||||
const [mapKeyCertConfigured, setMapKeyCertConfigured] = useState<MapKeyCertConfigured>({
|
||||
// values
|
||||
@ -107,14 +110,25 @@ export const LdapSettingsPage = () => {
|
||||
});
|
||||
|
||||
const methods = useForm<LdapPayload>({ defaultValues: emptySettings });
|
||||
const { control, getValues, handleSubmit, register, reset, watch } = methods;
|
||||
const {
|
||||
control,
|
||||
formState: { isDirty },
|
||||
getValues,
|
||||
handleSubmit,
|
||||
register,
|
||||
reset,
|
||||
watch,
|
||||
} = methods;
|
||||
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
useEffect(() => {
|
||||
async function init() {
|
||||
const payload = await getSettings();
|
||||
const serverConfig = payload.settings.config.servers[0];
|
||||
let serverConfig = emptySettings.settings.config.servers[0];
|
||||
if (payload.settings.config.servers?.length > 0) {
|
||||
serverConfig = payload.settings.config.servers[0];
|
||||
}
|
||||
setMapKeyCertConfigured({
|
||||
rootCaCertValue: serverConfig.root_ca_cert_value?.length > 0,
|
||||
clientCertValue: serverConfig.client_cert_value !== '',
|
||||
@ -203,12 +217,14 @@ export const LdapSettingsPage = () => {
|
||||
/**
|
||||
* Button's Actions
|
||||
*/
|
||||
const submitAndEnableLdapSettings = (payload: LdapPayload) => {
|
||||
const submitAndEnableLdapSettings = async (payload: LdapPayload) => {
|
||||
payload.settings.enabled = true;
|
||||
putPayload(payload);
|
||||
await putPayload(payload);
|
||||
reportInteraction('authentication_ldap_enabled');
|
||||
};
|
||||
const saveForm = () => {
|
||||
putPayload(getValues());
|
||||
const saveForm = async () => {
|
||||
await putPayload(getValues());
|
||||
reportInteraction('authentication_ldap_saved');
|
||||
};
|
||||
const deleteLDAPConfig = async () => {
|
||||
try {
|
||||
@ -220,6 +236,7 @@ export const LdapSettingsPage = () => {
|
||||
payload: [t('ldap-settings-page.alert.discard-success', 'LDAP settings discarded')],
|
||||
});
|
||||
reset(payload);
|
||||
reportInteraction('authentication_ldap_deleted');
|
||||
} catch (error) {
|
||||
appEvents.publish({
|
||||
type: AppEvents.alertError.name,
|
||||
@ -230,6 +247,10 @@ export const LdapSettingsPage = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const onDiscard = () => {
|
||||
reportInteraction('authentication_ldap_abandoned');
|
||||
};
|
||||
|
||||
const subTitle = (
|
||||
<Trans i18nKey="ldap-settings-page.subtitle">
|
||||
The LDAP integration in Grafana allows your Grafana users to log in with their LDAP credentials. Find out more in
|
||||
@ -259,6 +280,7 @@ export const LdapSettingsPage = () => {
|
||||
{config.disableLoginForm && disabledFormAlert}
|
||||
<FormProvider {...methods}>
|
||||
<form onSubmit={handleSubmit(submitAndEnableLdapSettings, onErrors)}>
|
||||
<FormPrompt confirmRedirect={isDirty} onDiscard={onDiscard} />
|
||||
{isLoading && <Loader />}
|
||||
{!isLoading && (
|
||||
<section className={styles.form}>
|
||||
@ -266,7 +288,7 @@ export const LdapSettingsPage = () => {
|
||||
<Trans i18nKey="ldap-settings-page.title">Basic Settings</Trans>
|
||||
</h3>
|
||||
<Field
|
||||
label={t('ldap-settings-page.host.label', 'Server host')}
|
||||
label={t('ldap-settings-page.host.label', 'Server host *')}
|
||||
description={t(
|
||||
'ldap-settings-page.host.description',
|
||||
'Hostname or IP address of the LDAP server you wish to connect to.'
|
||||
@ -301,7 +323,7 @@ export const LdapSettingsPage = () => {
|
||||
/>
|
||||
</Field>
|
||||
<Field
|
||||
label={t('ldap-settings-page.search_filter.label', 'Search filter*')}
|
||||
label={t('ldap-settings-page.search_filter.label', 'Search filter *')}
|
||||
description={t(
|
||||
'ldap-settings-page.search_filter.description',
|
||||
'LDAP search filter used to locate specific entries within the directory.'
|
||||
@ -324,10 +346,12 @@ export const LdapSettingsPage = () => {
|
||||
<Controller
|
||||
name={`${serverConfig}.search_base_dns`}
|
||||
control={control}
|
||||
render={({ field: { onChange, ...field } }) => (
|
||||
render={({ field: { onChange, ref, ...field } }) => (
|
||||
<MultiSelect
|
||||
{...field}
|
||||
allowCustomValue
|
||||
className={styles.multiSelect}
|
||||
noOptionsMessage={''}
|
||||
onChange={(v) => onChange(v.map(({ value }) => String(value)))}
|
||||
/>
|
||||
)}
|
||||
@ -346,20 +370,20 @@ export const LdapSettingsPage = () => {
|
||||
</Text>
|
||||
</Stack>
|
||||
<Button variant="secondary" onClick={() => setIsDrawerOpen(true)}>
|
||||
<Trans i18nKey="ldap-settings-page.advanced-settings-section.edit.button">Edit</Trans>
|
||||
<Trans i18nKey="ldap-settings-page.advanced-settings-section.edit-button">Edit</Trans>
|
||||
</Button>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Box display="flex" gap={2} marginTop={5}>
|
||||
<Stack alignItems="center" gap={2}>
|
||||
<Button type="submit">
|
||||
<Trans i18nKey="ldap-settings-page.buttons-section.save-and-enable.button">Save and enable</Trans>
|
||||
<Trans i18nKey="ldap-settings-page.buttons-section.save-and-enable-button">Save and enable</Trans>
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={saveForm}>
|
||||
<Trans i18nKey="ldap-settings-page.buttons-section.save.button">Save</Trans>
|
||||
<Trans i18nKey="ldap-settings-page.buttons-section.save-button">Save</Trans>
|
||||
</Button>
|
||||
<LinkButton href="/admin/authentication" variant="secondary">
|
||||
<Trans i18nKey="ldap-settings-page.buttons-section.discard.button">Discard</Trans>
|
||||
<Trans i18nKey="ldap-settings-page.buttons-section.discard-button">Discard</Trans>
|
||||
</LinkButton>
|
||||
<Dropdown
|
||||
overlay={
|
||||
@ -392,6 +416,26 @@ export const LdapSettingsPage = () => {
|
||||
</form>
|
||||
</FormProvider>
|
||||
</Page.Contents>
|
||||
<Modal
|
||||
title={t('ldap-settings-page.discard-modal.title', 'Leave LDAP configuration?')}
|
||||
isOpen={isModalOpen}
|
||||
onDismiss={() => setIsModalOpen(false)}
|
||||
>
|
||||
<Stack direction={'column'} gap={2}>
|
||||
<Trans i18nKey="ldap-settings-page.discard-modal.description">
|
||||
Are you sure you want to abandon the changes you‘ve made to the LDAP configuration? All changes will
|
||||
be lost.
|
||||
</Trans>
|
||||
<Stack direction="row" gap={2} justifyContent="flex-end">
|
||||
<Button variant="secondary">
|
||||
<Trans i18nKey="ldap-settings-page.discard-modal.cancel-button">Back to editing</Trans>
|
||||
</Button>
|
||||
<Button variant="destructive">
|
||||
<Trans i18nKey="ldap-settings-page.discard-modal.discard-button">Abandon LDAP</Trans>
|
||||
</Button>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Modal>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
@ -401,6 +445,11 @@ function getStyles(theme: GrafanaTheme2) {
|
||||
form: css({
|
||||
width: theme.spacing(68),
|
||||
}),
|
||||
multiSelect: css({
|
||||
svg: {
|
||||
display: 'none',
|
||||
},
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,13 @@ export const AuthConfigPageUnconnected = ({
|
||||
|
||||
providers = providers.map((p) => {
|
||||
if (p.provider === 'ldap') {
|
||||
p.settings.type = p.provider;
|
||||
return {
|
||||
...p,
|
||||
settings: {
|
||||
...p.settings,
|
||||
type: 'LDAP',
|
||||
},
|
||||
};
|
||||
}
|
||||
return p;
|
||||
});
|
||||
|
@ -1091,7 +1091,7 @@
|
||||
"tls-ciphers-placeholder": "e.g. [\"TLS_AES_256_GCM_SHA384\"]",
|
||||
"use-ssl-description": "Set to true if LDAP server should use TLS connection (either with STARTTLS or LDAPS)",
|
||||
"use-ssl-label": "Use SSL",
|
||||
"use-ssl-tooltip": "For a complete list of supported ciphers and TLS versions, refer to: {\n <TextLink style={{ fontSize: 'inherit' }} href=\"https://go.dev/src/crypto/tls/cipher_suites.go\" external>\n https://go.dev/src/crypto/tls/cipher_suites.go\n </TextLink>}"
|
||||
"use-ssl-tooltip": "For a complete list of supported ciphers and TLS versions, refer to:"
|
||||
},
|
||||
"group-mapping": {
|
||||
"grafana-admin": {
|
||||
@ -1141,9 +1141,7 @@
|
||||
},
|
||||
"ldap-settings-page": {
|
||||
"advanced-settings-section": {
|
||||
"edit": {
|
||||
"button": "Edit"
|
||||
},
|
||||
"edit-button": "Edit",
|
||||
"subtitle": "Mappings, extra security measures, and more.",
|
||||
"title": "Advanced Settings"
|
||||
},
|
||||
@ -1164,20 +1162,20 @@
|
||||
"label": "Bind password"
|
||||
},
|
||||
"buttons-section": {
|
||||
"discard": {
|
||||
"button": "Discard"
|
||||
},
|
||||
"save": {
|
||||
"button": "Save"
|
||||
},
|
||||
"save-and-enable": {
|
||||
"button": "Save and enable"
|
||||
}
|
||||
"discard-button": "Discard",
|
||||
"save-and-enable-button": "Save and enable",
|
||||
"save-button": "Save"
|
||||
},
|
||||
"discard-modal": {
|
||||
"cancel-button": "Back to editing",
|
||||
"description": "Are you sure you want to abandon the changes you‘ve made to the LDAP configuration? All changes will be lost.",
|
||||
"discard-button": "Abandon LDAP",
|
||||
"title": "Leave LDAP configuration?"
|
||||
},
|
||||
"documentation": "documentation",
|
||||
"host": {
|
||||
"description": "Hostname or IP address of the LDAP server you wish to connect to.",
|
||||
"label": "Server host",
|
||||
"label": "Server host *",
|
||||
"placeholder": "example: 127.0.0.1"
|
||||
},
|
||||
"login-form-alert": {
|
||||
@ -1186,7 +1184,7 @@
|
||||
},
|
||||
"search_filter": {
|
||||
"description": "LDAP search filter used to locate specific entries within the directory.",
|
||||
"label": "Search filter*",
|
||||
"label": "Search filter *",
|
||||
"placeholder": "example: cn=%s"
|
||||
},
|
||||
"search-base-dns": {
|
||||
|
@ -1091,7 +1091,7 @@
|
||||
"tls-ciphers-placeholder": "ę.ģ. [\"ŦĿŜ_ÅĒŜ_256_ĞCM_ŜĦÅ384\"]",
|
||||
"use-ssl-description": "Ŝęŧ ŧő ŧřūę įƒ ĿĐÅP şęřvęř şĥőūľđ ūşę ŦĿŜ čőʼnʼnęčŧįőʼn (ęįŧĥęř ŵįŧĥ ŜŦÅŖŦŦĿŜ őř ĿĐÅPŜ)",
|
||||
"use-ssl-label": "Ůşę ŜŜĿ",
|
||||
"use-ssl-tooltip": "Főř ä čőmpľęŧę ľįşŧ őƒ şūppőřŧęđ čįpĥęřş äʼnđ ŦĿŜ vęřşįőʼnş, řęƒęř ŧő: {\n <ŦęχŧĿįʼnĸ şŧyľę={{ fontSize: 'inherit' }} ĥřęƒ=\"ĥŧŧpş://ģő.đęv/şřč/čřypŧő/ŧľş/čįpĥęř_şūįŧęş.ģő\" ęχŧęřʼnäľ>\n ĥŧŧpş://ģő.đęv/şřč/čřypŧő/ŧľş/čįpĥęř_şūįŧęş.ģő\n </ŦęχŧĿįʼnĸ>}"
|
||||
"use-ssl-tooltip": "Főř ä čőmpľęŧę ľįşŧ őƒ şūppőřŧęđ čįpĥęřş äʼnđ ŦĿŜ vęřşįőʼnş, řęƒęř ŧő:"
|
||||
},
|
||||
"group-mapping": {
|
||||
"grafana-admin": {
|
||||
@ -1141,9 +1141,7 @@
|
||||
},
|
||||
"ldap-settings-page": {
|
||||
"advanced-settings-section": {
|
||||
"edit": {
|
||||
"button": "Ēđįŧ"
|
||||
},
|
||||
"edit-button": "Ēđįŧ",
|
||||
"subtitle": "Mäppįʼnģş, ęχŧřä şęčūřįŧy męäşūřęş, äʼnđ mőřę.",
|
||||
"title": "Åđväʼnčęđ Ŝęŧŧįʼnģş"
|
||||
},
|
||||
@ -1164,20 +1162,20 @@
|
||||
"label": "ßįʼnđ päşşŵőřđ"
|
||||
},
|
||||
"buttons-section": {
|
||||
"discard": {
|
||||
"button": "Đįşčäřđ"
|
||||
},
|
||||
"save": {
|
||||
"button": "Ŝävę"
|
||||
},
|
||||
"save-and-enable": {
|
||||
"button": "Ŝävę äʼnđ ęʼnäþľę"
|
||||
}
|
||||
"discard-button": "Đįşčäřđ",
|
||||
"save-and-enable-button": "Ŝävę äʼnđ ęʼnäþľę",
|
||||
"save-button": "Ŝävę"
|
||||
},
|
||||
"discard-modal": {
|
||||
"cancel-button": "ßäčĸ ŧő ęđįŧįʼnģ",
|
||||
"description": "Åřę yőū şūřę yőū ŵäʼnŧ ŧő äþäʼnđőʼn ŧĥę čĥäʼnģęş yőū&ľşqūő;vę mäđę ŧő ŧĥę ĿĐÅP čőʼnƒįģūřäŧįőʼn? Åľľ čĥäʼnģęş ŵįľľ þę ľőşŧ.",
|
||||
"discard-button": "Åþäʼnđőʼn ĿĐÅP",
|
||||
"title": "Ŀęävę ĿĐÅP čőʼnƒįģūřäŧįőʼn?"
|
||||
},
|
||||
"documentation": "đőčūmęʼnŧäŧįőʼn",
|
||||
"host": {
|
||||
"description": "Ħőşŧʼnämę őř ĨP äđđřęşş őƒ ŧĥę ĿĐÅP şęřvęř yőū ŵįşĥ ŧő čőʼnʼnęčŧ ŧő.",
|
||||
"label": "Ŝęřvęř ĥőşŧ",
|
||||
"label": "Ŝęřvęř ĥőşŧ *",
|
||||
"placeholder": "ęχämpľę: 127.0.0.1"
|
||||
},
|
||||
"login-form-alert": {
|
||||
@ -1186,7 +1184,7 @@
|
||||
},
|
||||
"search_filter": {
|
||||
"description": "ĿĐÅP şęäřčĥ ƒįľŧęř ūşęđ ŧő ľőčäŧę şpęčįƒįč ęʼnŧřįęş ŵįŧĥįʼn ŧĥę đįřęčŧőřy.",
|
||||
"label": "Ŝęäřčĥ ƒįľŧęř*",
|
||||
"label": "Ŝęäřčĥ ƒįľŧęř *",
|
||||
"placeholder": "ęχämpľę: čʼn=%ş"
|
||||
},
|
||||
"search-base-dns": {
|
||||
|
Loading…
Reference in New Issue
Block a user