grafana/public/app/features/admin/ldap/LdapSettingsPage.tsx

295 lines
9.9 KiB
TypeScript
Raw Normal View History

import { css } from '@emotion/css';
import { useEffect, useState } from 'react';
import { 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 { useStyles2, Alert, Box, Button, Field, Input, Stack, TextLink } from '@grafana/ui';
import { Page } from 'app/core/components/Page/Page';
import config from 'app/core/config';
import { t, Trans } from 'app/core/internationalization';
import { Loader } from 'app/features/plugins/admin/components/Loader';
import { LdapPayload, StoreState } from 'app/types';
const appEvents = getAppEvents();
const mapStateToProps = (state: StoreState) => ({
ldapSsoSettings: state.ldap.ldapSsoSettings,
});
const mapDispatchToProps = {};
const connector = connect(mapStateToProps, mapDispatchToProps);
const pageNav: NavModelItem = {
text: 'LDAP',
icon: 'shield',
id: 'LDAP',
};
const emptySettings: LdapPayload = {
id: '',
provider: '',
source: '',
settings: {
activeSyncEnabled: false,
allowSignUp: false,
config: {
servers: [
{
attributes: {},
bind_dn: '',
bind_password: '',
client_cert: '',
client_key: '',
group_mappings: [],
group_search_base_dns: [],
group_search_filter: '',
group_search_filter_user_attribute: '',
host: '',
min_tls_version: '',
port: 389,
root_ca_cert: '',
search_base_dns: [],
search_filter: '',
skip_org_role_sync: false,
ssl_skip_verify: false,
start_tls: false,
timeout: 10,
tls_ciphers: [],
tls_skip_verify: false,
use_ssl: false,
},
],
},
enabled: false,
skipOrgRoleSync: false,
syncCron: '',
},
};
export const LdapSettingsPage = () => {
const [isLoading, setIsLoading] = useState(true);
const methods = useForm<LdapPayload>({ defaultValues: emptySettings });
const { getValues, handleSubmit, register, reset } = methods;
const styles = useStyles2(getStyles);
useEffect(() => {
async function init() {
const payload = await getBackendSrv().get<LdapPayload>('/api/v1/sso-settings/ldap');
if (!payload || !payload.settings || !payload.settings.config) {
appEvents.publish({
type: AppEvents.alertError.name,
payload: [t('ldap-settings-page.alert.error-fetching', 'Error fetching LDAP settings')],
});
return;
}
reset(payload);
setIsLoading(false);
}
init();
}, [reset]);
/**
* Display warning if the feature flag is disabled
*/
if (!config.featureToggles.ssoSettingsLDAP) {
return (
<Alert title="invalid configuration">
<Trans i18nKey="ldap-settings-page.alert.feature-flag-disabled">
This page is only accessible by enabling the <strong>ssoSettingsLDAP</strong> feature flag.
</Trans>
</Alert>
);
}
/**
* Save payload to the backend
* @param payload LdapPayload
*/
const putPayload = async (payload: LdapPayload) => {
try {
const result = await getBackendSrv().put('/api/v1/sso-settings/ldap', payload);
if (result) {
appEvents.publish({
type: AppEvents.alertError.name,
payload: [t('ldap-settings-page.alert.error-saving', 'Error saving LDAP settings')],
});
}
appEvents.publish({
type: AppEvents.alertSuccess.name,
payload: [t('ldap-settings-page.alert.saved', 'LDAP settings saved')],
});
} catch (error) {
appEvents.publish({
type: AppEvents.alertError.name,
payload: [t('ldap-settings-page.alert.error-saving', 'Error saving LDAP settings')],
});
}
};
const onErrors = () => {
appEvents.publish({
type: AppEvents.alertError.name,
payload: [t('ldap-settings-page.alert.error-validate-form', 'Error validating LDAP settings')],
});
};
/**
* Button's Actions
*/
const submitAndEnableLdapSettings = (payload: LdapPayload) => {
payload.settings.enabled = true;
putPayload(payload);
};
const saveForm = () => {
putPayload(getValues());
};
const discardForm = async () => {
try {
setIsLoading(true);
await getBackendSrv().delete('/api/v1/sso-settings/ldap');
const payload = await getBackendSrv().get<LdapPayload>('/api/v1/sso-settings/ldap');
if (!payload || !payload.settings || !payload.settings.config) {
appEvents.publish({
type: AppEvents.alertError.name,
payload: [t('ldap-settings-page.alert.error-update', 'Error updating LDAP settings')],
});
return;
}
reset(payload);
} catch (error) {
appEvents.publish({
type: AppEvents.alertError.name,
payload: [t('ldap-settings-page.alert.error-saving', 'Error saving LDAP settings')],
});
} finally {
setIsLoading(false);
}
};
const documentation = (
<TextLink
href="https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication/ldap/"
external
>
<Trans i18nKey="ldap-settings-page.documentation">documentation</Trans>
</TextLink>
);
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
our {documentation}.
</Trans>
);
return (
<Page navId="authentication" pageNav={pageNav} subTitle={subTitle}>
<Page.Contents>
<FormProvider {...methods}>
<form onSubmit={handleSubmit(submitAndEnableLdapSettings, onErrors)}>
{isLoading && <Loader />}
{!isLoading && (
<section className={styles.form}>
<h3>
<Trans i18nKey="ldap-settings-page.title">Basic Settings</Trans>
</h3>
<Field
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.'
)}
>
<Input
id="host"
placeholder={t('ldap-settings-page.host.placeholder', 'example: 127.0.0.1')}
type="text"
{...register('settings.config.servers.0.host', { required: true })}
/>
</Field>
<Field
label={t('ldap-settings-page.bind-dn.label', 'Bind DN')}
description={t(
'ldap-settings-page.bind-dn.description',
'Distinguished name of the account used to bind and authenticate to the LDAP server.'
)}
>
<Input
id="bind-dn"
placeholder={t('ldap-settings-page.bind-dn.placeholder', 'example: cn=admin,dc=grafana,dc=org')}
type="text"
{...register('settings.config.servers.0.bind_dn')}
/>
</Field>
<Field label={t('ldap-settings-page.bind-password.label', 'Bind password')}>
<Input
id="bind-password"
type="text"
{...register('settings.config.servers.0.bind_password', { required: false })}
/>
</Field>
<Field
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.'
)}
>
<Input
id="search_filter"
placeholder={t('ldap-settings-page.search_filter.placeholder', 'example: cn=%s')}
type="text"
{...register('settings.config.servers.0.search_filter', { required: true })}
/>
</Field>
<Field
label={t('ldap-settings-page.search-base-dns.label', 'Search base DNS *')}
description={t(
'ldap-settings-page.search-base-dns.description',
'An array of base dns to search through; separate by commas or spaces.'
)}
>
<Input
id="search-base-dns"
placeholder={t('ldap-settings-page.search-base-dns.placeholder', 'example: "dc=grafana.dc=org"')}
type="text"
{...register('settings.config.servers.0.search_base_dns', { required: true })}
/>
</Field>
<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>
</Button>
<Button variant="secondary" onClick={saveForm}>
<Trans i18nKey="ldap-settings-page.buttons-section.save.button">Save</Trans>
</Button>
<Button variant="secondary" onClick={discardForm}>
<Trans i18nKey="ldap-settings-page.buttons-section.discard.button">Discard</Trans>
</Button>
</Stack>
</Box>
</section>
)}
</form>
</FormProvider>
</Page.Contents>
</Page>
);
};
function getStyles(theme: GrafanaTheme2) {
return {
form: css({
width: theme.spacing(68),
}),
};
}
export default connector(LdapSettingsPage);