SSO LDAP: Add configuration for root ca, client key and client certificate (#92866)

* Add missing properties with `_value` prefix

* Add RadioButtonGrpoup

* Add file/value section for certs

* Add mapping for keys and certificates

* Rename base and file types

* Add styles

* Add base64 values configuration flags

* Add function to render root ca certificate contents

* generate i18n files
This commit is contained in:
linoman 2024-09-04 12:19:45 +02:00 committed by GitHub
parent 13c8f4d212
commit b213ecc2dd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 352 additions and 178 deletions

View File

@ -1,5 +1,5 @@
import { css } from '@emotion/css';
import { useId } from 'react';
import { Dispatch, SetStateAction, useEffect, useId, useState } from 'react';
import { useFormContext } from 'react-hook-form';
import { GrafanaTheme2, SelectableValue } from '@grafana/data';
@ -12,25 +12,41 @@ import {
Icon,
Input,
Label,
MultiSelect,
Select,
Stack,
Switch,
Text,
TextLink,
Tooltip,
RadioButtonGroup,
SecretInput,
} from '@grafana/ui';
import { t, Trans } from 'app/core/internationalization';
import { LdapPayload } from 'app/types';
import { LdapPayload, MapKeyCertConfigured } from 'app/types';
interface Props {
onClose: () => void;
mapKeyCertConfigured: MapKeyCertConfigured;
setMapKeyCertConfigured: Dispatch<SetStateAction<MapKeyCertConfigured>>;
}
const serverConfig = 'settings.config.servers.0';
const tlsOptions: Array<SelectableValue<string>> = ['TLS1.2', 'TLS1.3'].map((v) => ({ label: v, value: v }));
enum EncryptionProvider {
Base64 = 'base64',
FilePath = 'path',
}
export const LdapDrawerComponent = ({
onClose,
mapKeyCertConfigured: mapCertConfigured,
setMapKeyCertConfigured: setMapCertConfigured,
}: Props) => {
const [encryptionProvider, setEncryptionProvider] = useState(EncryptionProvider.Base64);
export const LdapDrawerComponent = ({ onClose }: Props) => {
const styles = useStyles2(getStyles);
const { register, setValue, watch } = useFormContext<LdapPayload>();
const { getValues, register, setValue, watch } = useFormContext<LdapPayload>();
const nameId = useId();
const surnameId = useId();
@ -38,6 +54,22 @@ export const LdapDrawerComponent = ({ onClose }: Props) => {
const memberOfId = useId();
const emailId = useId();
useEffect(() => {
const { client_cert, client_key, root_ca_cert } = getValues(serverConfig);
setEncryptionProvider(
!client_cert.length && !client_key.length && !root_ca_cert?.length
? EncryptionProvider.Base64
: EncryptionProvider.FilePath
);
}, [getValues]);
const renderMultiSelectLabel = (value: string) => {
if (value.length >= 5) {
return `${value.slice(0, 2)}...${value.slice(-2)}`;
}
return value;
};
const groupMappingsLabel = (
<Label
className={styles.sectionLabel}
@ -48,7 +80,7 @@ export const LdapDrawerComponent = ({ onClose }: Props) => {
);
const useTlsDescription = (
<Trans i18nKey="ldap-drawer.extra-security-section.use-ssl.tooltip">
<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>
@ -62,18 +94,18 @@ export const LdapDrawerComponent = ({ onClose }: Props) => {
<Drawer title={t('ldap-drawer.title', 'Advanced settings')} onClose={onClose}>
<CollapsableSection label={t('ldap-drawer.misc-section.label', 'Misc')} isOpen={true}>
<Field
label={t('ldap-drawer.misc-section.allow-sign-up.label', 'Allow sign up')}
label={t('ldap-drawer.misc-section.allow-sign-up-label', 'Allow sign up')}
description={t(
'ldap-drawer.misc-section.allow-sign-up.descrition',
'ldap-drawer.misc-section.allow-sign-up-descrition',
'If not enabled, only existing Grafana users can log in using LDAP'
)}
>
<Switch id="allow-sign-up" {...register('settings.allowSignUp')} />
</Field>
<Field
label={t('ldap-drawer.misc-section.port.label', 'Port')}
label={t('ldap-drawer.misc-section.port-label', 'Port')}
description={t(
'ldap-drawer.misc-section.port.description',
'ldap-drawer.misc-section.port-description',
'Default port is 389 without SSL or 636 with SSL'
)}
>
@ -81,13 +113,13 @@ export const LdapDrawerComponent = ({ onClose }: Props) => {
id="port"
placeholder="389"
type="number"
{...register('settings.config.servers.0.port', { valueAsNumber: true })}
{...register(`${serverConfig}.port`, { valueAsNumber: true })}
/>
</Field>
<Field
label={t('ldap-drawer.misc-section.timeout.label', 'Timeout')}
label={t('ldap-drawer.misc-section.timeout-label', 'Timeout')}
description={t(
'ldap-drawer.misc-section.timeout.description',
'ldap-drawer.misc-section.timeout-description',
'Timeout in seconds for the connection to the LDAP server'
)}
>
@ -95,7 +127,7 @@ export const LdapDrawerComponent = ({ onClose }: Props) => {
id="timeout"
placeholder="10"
type="number"
{...register('settings.config.servers.0.timeout', { valueAsNumber: true })}
{...register(`${serverConfig}.timeout`, { valueAsNumber: true })}
/>
</Field>
</CollapsableSection>
@ -106,72 +138,70 @@ export const LdapDrawerComponent = ({ onClose }: Props) => {
the application correctly retrieves and displays user information.
</Trans>
</Text>
<Field label={t('ldap-drawer.attributes-section.name.label', 'Name')}>
<Input id={nameId} {...register('settings.config.servers.0.attributes.name')} />
<Field label={t('ldap-drawer.attributes-section.name-label', 'Name')}>
<Input id={nameId} {...register(`${serverConfig}.attributes.name`)} />
</Field>
<Field label={t('ldap-drawer.attributes-section.surname.label', 'Surname')}>
<Input id={surnameId} {...register('settings.config.servers.0.attributes.surname')} />
<Field label={t('ldap-drawer.attributes-section.surname-label', 'Surname')}>
<Input id={surnameId} {...register(`${serverConfig}.attributes.surname`)} />
</Field>
<Field label={t('ldap-drawer.attributes-section.username.label', 'Username')}>
<Input id={usernameId} {...register('settings.config.servers.0.attributes.username')} />
<Field label={t('ldap-drawer.attributes-section.username-label', 'Username')}>
<Input id={usernameId} {...register(`${serverConfig}.attributes.username`)} />
</Field>
<Field label={t('ldap-drawer.attributes-section.member-of.label', 'Member Of')}>
<Input id={memberOfId} {...register('settings.config.servers.0.attributes.member_of')} />
<Field label={t('ldap-drawer.attributes-section.member-of-label', 'Member Of')}>
<Input id={memberOfId} {...register(`${serverConfig}.attributes.member_of`)} />
</Field>
<Field label={t('ldap-drawer.attributes-section.email.label', 'Email')}>
<Input id={emailId} {...register('settings.config.servers.0.attributes.email')} />
<Field label={t('ldap-drawer.attributes-section.email-label', 'Email')}>
<Input id={emailId} {...register(`${serverConfig}.attributes.email`)} />
</Field>
</CollapsableSection>
<CollapsableSection label={groupMappingsLabel} isOpen={true}>
<Field
htmlFor="skip-org-role-sync"
label={t('ldap-drawer.group-mapping-section.skip-org-role-sync.label', 'Skip organization role sync')}
label={t('ldap-drawer.group-mapping-section.skip-org-role-sync-label', 'Skip organization role sync')}
description={t(
'ldap-drawer.group-mapping-section.skip-org-role-sync.description',
'ldap-drawer.group-mapping-section.skip-org-role-sync-description',
'Prevent synchronizing users organization roles from your IdP'
)}
>
<Switch id="skip-org-role-sync" {...register('settings.config.servers.0.skip_org_role_sync')} />
<Switch id="skip-org-role-sync" {...register(`${serverConfig}.skip_org_role_sync`)} />
</Field>
<Field
htmlFor="group-search-filter"
label={t('ldap-drawer.group-mapping-section.group-search-filter.label', 'Group search filter')}
label={t('ldap-drawer.group-mapping-section.group-search-filter-label', 'Group search filter')}
description={t(
'ldap-drawer.group-mapping-section.group-search-filter.description',
'ldap-drawer.group-mapping-section.group-search-filter-description',
'Used to filter and identify group entries within the directory'
)}
>
<Input id="group-search-filter" {...register('settings.config.servers.0.group_search_filter')} />
<Input id="group-search-filter" {...register(`${serverConfig}.group_search_filter`)} />
</Field>
<Field
htmlFor="group-search-base-dns"
label={t('ldap-drawer.group-mapping-section.group-search-base-dns.label', 'Group search base DNS')}
label={t('ldap-drawer.group-mapping-section.group-search-base-dns-label', 'Group search base DNS')}
description={t(
'ldap-drawer.group-mapping-section.group-search-base-dns.description',
'ldap-drawer.group-mapping-section.group-search-base-dns-description',
'Separate by commas or spaces'
)}
>
<Input
id="group-search-base-dns"
onChange={({ currentTarget: { value } }) =>
setValue('settings.config.servers.0.group_search_base_dns', [value])
}
onChange={({ currentTarget: { value } }) => setValue(`${serverConfig}.group_search_base_dns`, [value])}
/>
</Field>
<Field
htmlFor="group-search-filter-user-attribute"
label={t(
'ldap-drawer.group-mapping-section.group-search-filter-user-attribute.label',
'ldap-drawer.group-mapping-section.group-search-filter-user-attribute-label',
'Group name attribute'
)}
description={t(
'ldap-drawer.group-mapping-section.group-search-filter-user-attribute.description',
'ldap-drawer.group-mapping-section.group-search-filter-user-attribute-description',
'Identifies users within group entries for filtering purposes'
)}
>
<Input
id="group-search-filter-user-attribute"
{...register('settings.config.servers.0.group_search_filter_user_attribute')}
{...register(`${serverConfig}.group_search_filter_user_attribute`)}
/>
</Field>
<Divider />
@ -181,67 +211,203 @@ export const LdapDrawerComponent = ({ onClose }: Props) => {
isOpen={true}
>
<Field
label={t('ldap-drawer.extra-security-section.use-ssl.label', 'Use SSL')}
label={t('ldap-drawer.extra-security-section.use-ssl-label', 'Use SSL')}
description={t(
'ldap-drawer.extra-security-section.use-ssl.description',
'ldap-drawer.extra-security-section.use-ssl-description',
'Set to true if LDAP server should use TLS connection (either with STARTTLS or LDAPS)'
)}
>
<Stack>
<Switch id="use-ssl" {...register('settings.config.servers.0.use_ssl')} />
<Switch id="use-ssl" {...register(`${serverConfig}.use_ssl`)} />
<Tooltip content={useTlsDescription} interactive>
<Icon name="info-circle" />
</Tooltip>
</Stack>
</Field>
{watch('settings.config.servers.0.use_ssl') && (
{watch(`${serverConfig}.use_ssl`) && (
<>
<Field
label={t('ldap-drawer.extra-security-section.start-tls.label', 'Start TLS')}
label={t('ldap-drawer.extra-security-section.start-tls-label', 'Start TLS')}
description={t(
'ldap-drawer.extra-security-section.start-tls.description',
'ldap-drawer.extra-security-section.start-tls-description',
'If set to true, use LDAP with STARTTLS instead of LDAPS'
)}
>
<Switch id="start-tls" {...register('settings.config.servers.0.start_tls')} />
<Switch id="start-tls" {...register(`${serverConfig}.start_tls`)} />
</Field>
<Field
htmlFor="min-tls-version"
label={t('ldap-drawer.extra-security-section.min-tls-version.label', 'Min TLS version')}
label={t('ldap-drawer.extra-security-section.min-tls-version-label', 'Min TLS version')}
description={t(
'ldap-drawer.extra-security-section.min-tls-version.description',
'ldap-drawer.extra-security-section.min-tls-version-description',
'This is the minimum TLS version allowed. Accepted values are: TLS1.2, TLS1.3.'
)}
>
<Select
id="min-tls-version"
options={tlsOptions}
value={watch('settings.config.servers.0.min_tls_version')}
onChange={({ value }) => setValue('settings.config.servers.0.min_tls_version', value)}
value={watch(`${serverConfig}.min_tls_version`)}
onChange={({ value }) => setValue(`${serverConfig}.min_tls_version`, value)}
/>
</Field>
<Field
label={t('ldap-drawer.extra-security-section.tls-ciphers.label', 'TLS ciphers')}
label={t('ldap-drawer.extra-security-section.tls-ciphers-label', 'TLS ciphers')}
description={t(
'ldap-drawer.extra-security-section.tls-ciphers.description',
'ldap-drawer.extra-security-section.tls-ciphers-description',
'List of comma- or space-separated ciphers'
)}
>
<Input
id="tls-ciphers"
placeholder={t(
'ldap-drawer.extra-security-section.tls-ciphers.placeholder',
'ldap-drawer.extra-security-section.tls-ciphers-placeholder',
'e.g. ["TLS_AES_256_GCM_SHA384"]'
)}
value={watch('settings.config.servers.0.tls_ciphers')}
value={watch(`${serverConfig}.tls_ciphers`) || ''}
onChange={({ currentTarget: { value } }) =>
setValue(
'settings.config.servers.0.tls_ciphers',
value?.split(/,|\s/).map((v: string) => v.trim())
`${serverConfig}.tls_ciphers`,
value?.split(/,|\s/).map((v) => v.trim())
)
}
/>
</Field>
<Field
label={t(
'ldap-drawer.extra-security-section.encryption-provider-label',
'Encryption key and certificate provision specification.'
)}
description={t(
'ldap-drawer.extra-security-section.encryption-provider-description',
'X.509 certificate provides the public part, while the private key issued in a PKCS#8 format provides the private part of the asymmetric encryption.'
)}
>
<RadioButtonGroup
id="encryption-provider"
options={[
{
label: t(
'ldap-drawer.extra-security-section.encryption-provider-base-64',
'Base64-encoded content'
),
value: EncryptionProvider.Base64,
},
{
label: t('ldap-drawer.extra-security-section.encryption-provider-file-path', 'Path to files'),
value: EncryptionProvider.FilePath,
},
]}
value={encryptionProvider}
onChange={setEncryptionProvider}
/>
</Field>
{encryptionProvider === EncryptionProvider.Base64 && (
<>
<Field
label={t(
'ldap-drawer.extra-security-section.root-ca-cert-value-label',
'Root CA certificate content'
)}
>
<MultiSelect
id="root-ca-cert"
allowCustomValue
onChange={(v) => {
setValue(
`${serverConfig}.root_ca_cert_value`,
v.filter(({ v }) => typeof v === 'string')?.map(({ v }) => v)
);
}}
value={watch(`${serverConfig}.root_ca_cert_value`)?.map((v) => ({
label: renderMultiSelectLabel(v),
value: v,
}))}
/>
</Field>
<Field
label={t('ldap-drawer.extra-security-section.client-cert-value-label', 'Client certificate content')}
>
<SecretInput
id="client-cert"
placeholder={t(
'ldap-drawer.extra-security-section.client-cert-value-placeholder',
'Client certificate content in base64'
)}
isConfigured={mapCertConfigured.clientCertValue}
onReset={() => {
setValue(`${serverConfig}.client_cert_value`, '');
setMapCertConfigured({ ...mapCertConfigured, clientCertValue: false });
}}
/>
</Field>
<Field label={t('ldap-drawer.extra-security-section.client-key-value-label', 'Client key content')}>
<SecretInput
id="client-key"
placeholder={t(
'ldap-drawer.extra-security-section.client-key-value-placeholder',
'Client key content in base64'
)}
isConfigured={mapCertConfigured.clientKeyCertValue}
onReset={() => {
setValue(`${serverConfig}.client_key_value`, '');
setMapCertConfigured({ ...mapCertConfigured, clientKeyCertValue: false });
}}
/>
</Field>
</>
)}
{encryptionProvider === EncryptionProvider.FilePath && (
<>
<Field label={t('ldap-drawer.extra-security-section.root-ca-cert-label', 'Root CA certificate path')}>
<SecretInput
id="root-ca-cert"
placeholder={t(
'ldap-drawer.extra-security-section.root-ca-cert-placeholder',
'/path/to/root_ca_cert.pem'
)}
isConfigured={mapCertConfigured.rootCaCertPath}
onReset={() => {
setValue(`${serverConfig}.root_ca_cert`, '');
setMapCertConfigured({ ...mapCertConfigured, rootCaCertPath: false });
}}
value={watch(`${serverConfig}.root_ca_cert`)}
onChange={({ currentTarget: { value } }) => setValue(`${serverConfig}.root_ca_cert`, value)}
/>
</Field>
<Field label={t('ldap-drawer.extra-security-section.client-cert-label', 'Client certificate path')}>
<SecretInput
id="client-cert"
placeholder={t(
'ldap-drawer.extra-security-section.client-cert-placeholder',
'/path/to/client_cert.pem'
)}
isConfigured={mapCertConfigured.clientCertPath}
onReset={() => {
setValue(`${serverConfig}.client_cert`, '');
setMapCertConfigured({ ...mapCertConfigured, clientCertPath: false });
}}
value={watch(`${serverConfig}.client_cert`)}
onChange={({ currentTarget: { value } }) => setValue(`${serverConfig}.client_cert`, value)}
/>
</Field>
<Field label={t('ldap-drawer.extra-security-section.client-key-label', 'Client key path')}>
<SecretInput
id="client-key"
placeholder={t(
'ldap-drawer.extra-security-section.client-key-placeholder',
'/path/to/client_key.pem'
)}
isConfigured={mapCertConfigured.clientKeyCertPath}
onReset={() => {
setValue(`${serverConfig}.client_key`, '');
setMapCertConfigured({ ...mapCertConfigured, clientKeyCertPath: false });
}}
value={watch(`${serverConfig}.client_key`)}
onChange={({ currentTarget: { value } }) => setValue(`${serverConfig}.client_key`, value)}
/>
</Field>
</>
)}
</>
)}
</CollapsableSection>

View File

@ -10,7 +10,7 @@ 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';
import { LdapPayload, MapKeyCertConfigured, StoreState } from 'app/types';
import { LdapDrawerComponent } from './LdapDrawer';
@ -44,7 +44,9 @@ const emptySettings: LdapPayload = {
bind_dn: '',
bind_password: '',
client_cert: '',
client_cert_value: '',
client_key: '',
client_key_value: '',
group_mappings: [],
group_search_base_dns: [],
group_search_filter: '',
@ -53,6 +55,7 @@ const emptySettings: LdapPayload = {
min_tls_version: '',
port: 389,
root_ca_cert: '',
root_ca_cert_value: [],
search_base_dns: [],
search_filter: '',
skip_org_role_sync: false,
@ -75,6 +78,17 @@ export const LdapSettingsPage = () => {
const [isLoading, setIsLoading] = useState(true);
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const [mapKeyCertConfigured, setMapKeyCertConfigured] = useState<MapKeyCertConfigured>({
// values
rootCaCertValue: false,
clientCertValue: false,
clientKeyCertValue: false,
// paths
rootCaCertPath: false,
clientCertPath: false,
clientKeyCertPath: false,
});
const methods = useForm<LdapPayload>({ defaultValues: emptySettings });
const { getValues, handleSubmit, register, reset } = methods;
@ -91,6 +105,16 @@ export const LdapSettingsPage = () => {
return;
}
const serverConfig = payload.settings.config.servers[0];
setMapKeyCertConfigured({
rootCaCertValue: serverConfig.root_ca_cert_value?.length > 0,
clientCertValue: serverConfig.client_cert_value !== '',
clientKeyCertValue: serverConfig.client_key_value !== '',
rootCaCertPath: serverConfig.root_ca_cert !== '',
clientCertPath: serverConfig.client_cert !== '',
clientKeyCertPath: serverConfig.client_key !== '',
});
reset(payload);
setIsLoading(false);
}
@ -300,7 +324,13 @@ export const LdapSettingsPage = () => {
</Box>
</section>
)}
{isDrawerOpen && <LdapDrawerComponent onClose={() => setIsDrawerOpen(false)} />}
{isDrawerOpen && (
<LdapDrawerComponent
onClose={() => setIsDrawerOpen(false)}
mapKeyCertConfigured={mapKeyCertConfigured}
setMapKeyCertConfigured={setMapKeyCertConfigured}
/>
)}
</form>
</FormProvider>
</Page.Contents>

View File

@ -84,7 +84,9 @@ export interface LdapServerConfig {
bind_dn: string;
bind_password?: string;
client_cert: string;
client_cert_value: string;
client_key: string;
client_key_value: string;
group_mappings: GroupMapping[];
group_search_base_dns: string[];
group_search_filter: string;
@ -93,6 +95,7 @@ export interface LdapServerConfig {
min_tls_version?: string;
port: number;
root_ca_cert: string;
root_ca_cert_value: string[];
search_base_dns: string[];
search_filter: string;
skip_org_role_sync: boolean;
@ -135,3 +138,12 @@ export interface LdapPayload {
settings: LdapSettings;
source: string;
}
export interface MapKeyCertConfigured {
rootCaCertValue: boolean;
clientCertValue: boolean;
clientKeyCertValue: boolean;
rootCaCertPath: boolean;
clientCertPath: boolean;
clientKeyCertPath: boolean;
}

View File

@ -1005,78 +1005,61 @@
"ldap-drawer": {
"attributes-section": {
"description": "Specify the LDAP attributes that map to the user&lsquo;s given name, surname, and email address, ensuring the application correctly retrieves and displays user information.",
"email": {
"label": "Email"
},
"email-label": "Email",
"label": "Attributes",
"member-of": {
"label": "Member Of"
},
"name": {
"label": "Name"
},
"surname": {
"label": "Surname"
},
"username": {
"label": "Username"
}
"member-of-label": "Member Of",
"name-label": "Name",
"surname-label": "Surname",
"username-label": "Username"
},
"extra-security-section": {
"client-cert-label": "Client certificate path",
"client-cert-placeholder": "/path/to/client_cert.pem",
"client-cert-value-label": "Client certificate content",
"client-cert-value-placeholder": "Client certificate content in base64",
"client-key-label": "Client key path",
"client-key-placeholder": "/path/to/client_key.pem",
"client-key-value-label": "Client key content",
"client-key-value-placeholder": "Client key content in base64",
"encryption-provider-base-64": "Base64-encoded content",
"encryption-provider-description": "X.509 certificate provides the public part, while the private key issued in a PKCS#8 format provides the private part of the asymmetric encryption.",
"encryption-provider-file-path": "Path to files",
"encryption-provider-label": "Encryption key and certificate provision specification.",
"label": "Extra security measures",
"min-tls-version": {
"description": "This is the minimum TLS version allowed. Accepted values are: TLS1.2, TLS1.3.",
"label": "Min TLS version"
},
"start-tls": {
"description": "If set to true, use LDAP with STARTTLS instead of LDAPS",
"label": "Start TLS"
},
"tls-ciphers": {
"description": "List of comma- or space-separated ciphers",
"label": "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)",
"label": "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>}"
}
"min-tls-version-description": "This is the minimum TLS version allowed. Accepted values are: TLS1.2, TLS1.3.",
"min-tls-version-label": "Min TLS version",
"root-ca-cert-label": "Root CA certificate path",
"root-ca-cert-placeholder": "/path/to/root_ca_cert.pem",
"root-ca-cert-value-label": "Root CA certificate content",
"start-tls-description": "If set to true, use LDAP with STARTTLS instead of LDAPS",
"start-tls-label": "Start TLS",
"tls-ciphers-description": "List of comma- or space-separated ciphers",
"tls-ciphers-label": "TLS ciphers",
"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>}"
},
"group-mapping-section": {
"description": "Map LDAP groups to Grafana org roles",
"group-search-base-dns": {
"description": "Separate by commas or spaces",
"label": "Group search base DNS"
},
"group-search-filter": {
"description": "Used to filter and identify group entries within the directory",
"label": "Group search filter"
},
"group-search-filter-user-attribute": {
"description": "Identifies users within group entries for filtering purposes",
"label": "Group name attribute"
},
"group-search-base-dns-description": "Separate by commas or spaces",
"group-search-base-dns-label": "Group search base DNS",
"group-search-filter-description": "Used to filter and identify group entries within the directory",
"group-search-filter-label": "Group search filter",
"group-search-filter-user-attribute-description": "Identifies users within group entries for filtering purposes",
"group-search-filter-user-attribute-label": "Group name attribute",
"label": "Group mapping",
"skip-org-role-sync": {
"description": "Prevent synchronizing users organization roles from your IdP",
"label": "Skip organization role sync"
}
"skip-org-role-sync-description": "Prevent synchronizing users organization roles from your IdP",
"skip-org-role-sync-label": "Skip organization role sync"
},
"misc-section": {
"allow-sign-up": {
"descrition": "If not enabled, only existing Grafana users can log in using LDAP",
"label": "Allow sign up"
},
"allow-sign-up-descrition": "If not enabled, only existing Grafana users can log in using LDAP",
"allow-sign-up-label": "Allow sign up",
"label": "Misc",
"port": {
"description": "Default port is 389 without SSL or 636 with SSL",
"label": "Port"
},
"timeout": {
"description": "Timeout in seconds for the connection to the LDAP server",
"label": "Timeout"
}
"port-description": "Default port is 389 without SSL or 636 with SSL",
"port-label": "Port",
"timeout-description": "Timeout in seconds for the connection to the LDAP server",
"timeout-label": "Timeout"
},
"title": "Advanced settings"
},

View File

@ -1005,78 +1005,61 @@
"ldap-drawer": {
"attributes-section": {
"description": "Ŝpęčįƒy ŧĥę ĿĐÅP äŧŧřįþūŧęş ŧĥäŧ mäp ŧő ŧĥę ūşęř&ľşqūő;ş ģįvęʼn ʼnämę, şūřʼnämę, äʼnđ ęmäįľ äđđřęşş, ęʼnşūřįʼnģ ŧĥę äppľįčäŧįőʼn čőřřęčŧľy řęŧřįęvęş äʼnđ đįşpľäyş ūşęř įʼnƒőřmäŧįőʼn.",
"email": {
"label": "Ēmäįľ"
},
"email-label": "Ēmäįľ",
"label": "Åŧŧřįþūŧęş",
"member-of": {
"label": "Męmþęř ؃"
},
"name": {
"label": "Ńämę"
},
"surname": {
"label": "Ŝūřʼnämę"
},
"username": {
"label": "Ůşęřʼnämę"
}
"member-of-label": "Męmþęř ؃",
"name-label": "Ńämę",
"surname-label": "Ŝūřʼnämę",
"username-label": "Ůşęřʼnämę"
},
"extra-security-section": {
"client-cert-label": "Cľįęʼnŧ čęřŧįƒįčäŧę päŧĥ",
"client-cert-placeholder": "/päŧĥ/ŧő/čľįęʼnŧ_čęřŧ.pęm",
"client-cert-value-label": "Cľįęʼnŧ čęřŧįƒįčäŧę čőʼnŧęʼnŧ",
"client-cert-value-placeholder": "Cľįęʼnŧ čęřŧįƒįčäŧę čőʼnŧęʼnŧ įʼn þäşę64",
"client-key-label": "Cľįęʼnŧ ĸęy päŧĥ",
"client-key-placeholder": "/päŧĥ/ŧő/čľįęʼnŧ_ĸęy.pęm",
"client-key-value-label": "Cľįęʼnŧ ĸęy čőʼnŧęʼnŧ",
"client-key-value-placeholder": "Cľįęʼnŧ ĸęy čőʼnŧęʼnŧ įʼn þäşę64",
"encryption-provider-base-64": "ßäşę64-ęʼnčőđęđ čőʼnŧęʼnŧ",
"encryption-provider-description": "X.509 čęřŧįƒįčäŧę přővįđęş ŧĥę pūþľįč päřŧ, ŵĥįľę ŧĥę přįväŧę ĸęy įşşūęđ įʼn ä PĶCŜ#8 ƒőřmäŧ přővįđęş ŧĥę přįväŧę päřŧ őƒ ŧĥę äşymmęŧřįč ęʼnčřypŧįőʼn.",
"encryption-provider-file-path": "Päŧĥ ŧő ƒįľęş",
"encryption-provider-label": "Ēʼnčřypŧįőʼn ĸęy äʼnđ čęřŧįƒįčäŧę přővįşįőʼn şpęčįƒįčäŧįőʼn.",
"label": "Ēχŧřä şęčūřįŧy męäşūřęş",
"min-tls-version": {
"description": "Ŧĥįş įş ŧĥę mįʼnįmūm ŦĿŜ vęřşįőʼn äľľőŵęđ. Åččępŧęđ väľūęş äřę: ŦĿŜ1.2, ŦĿŜ1.3.",
"label": "Mįʼn ŦĿŜ vęřşįőʼn"
},
"start-tls": {
"description": "Ĩƒ şęŧ ŧő ŧřūę, ūşę ĿĐÅP ŵįŧĥ ŜŦÅŖŦŦĿŜ įʼnşŧęäđ őƒ ĿĐÅPŜ",
"label": "Ŝŧäřŧ ŦĿŜ"
},
"tls-ciphers": {
"description": "Ŀįşŧ őƒ čőmmä- őř şpäčę-şępäřäŧęđ čįpĥęřş",
"label": "ŦĿŜ čįpĥęřş",
"placeholder": "ę.ģ. [\"ŦĿŜ_ÅĒŜ_256_ĞCM_ŜĦÅ384\"]"
},
"use-ssl": {
"description": "Ŝęŧ ŧő ŧřūę įƒ ĿĐÅP şęřvęř şĥőūľđ ūşę ŦĿŜ čőʼnʼnęčŧįőʼn (ęįŧĥęř ŵįŧĥ ŜŦÅŖŦŦĿŜ őř ĿĐÅPŜ)",
"label": "Ůşę ŜŜĿ",
"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ĸ>}"
}
"min-tls-version-description": "Ŧĥįş įş ŧĥę mįʼnįmūm ŦĿŜ vęřşįőʼn äľľőŵęđ. Åččępŧęđ väľūęş äřę: ŦĿŜ1.2, ŦĿŜ1.3.",
"min-tls-version-label": "Mįʼn ŦĿŜ vęřşįőʼn",
"root-ca-cert-label": "Ŗőőŧ CÅ čęřŧįƒįčäŧę päŧĥ",
"root-ca-cert-placeholder": "/päŧĥ/ŧő/řőőŧ_čä_čęřŧ.pęm",
"root-ca-cert-value-label": "Ŗőőŧ CÅ čęřŧįƒįčäŧę čőʼnŧęʼnŧ",
"start-tls-description": "Ĩƒ şęŧ ŧő ŧřūę, ūşę ĿĐÅP ŵįŧĥ ŜŦÅŖŦŦĿŜ įʼnşŧęäđ őƒ ĿĐÅPŜ",
"start-tls-label": "Ŝŧäřŧ ŦĿŜ",
"tls-ciphers-description": "Ŀįşŧ őƒ čőmmä- őř şpäčę-şępäřäŧęđ čįpĥęřş",
"tls-ciphers-label": "ŦĿŜ čįpĥęřş",
"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ĸ>}"
},
"group-mapping-section": {
"description": "Mäp ĿĐÅP ģřőūpş ŧő Ğřäƒäʼnä őřģ řőľęş",
"group-search-base-dns": {
"description": "Ŝępäřäŧę þy čőmmäş őř şpäčęş",
"label": "Ğřőūp şęäřčĥ þäşę ĐŃŜ"
},
"group-search-filter": {
"description": "Ůşęđ ŧő ƒįľŧęř äʼnđ įđęʼnŧįƒy ģřőūp ęʼnŧřįęş ŵįŧĥįʼn ŧĥę đįřęčŧőřy",
"label": "Ğřőūp şęäřčĥ ƒįľŧęř"
},
"group-search-filter-user-attribute": {
"description": "Ĩđęʼnŧįƒįęş ūşęřş ŵįŧĥįʼn ģřőūp ęʼnŧřįęş ƒőř ƒįľŧęřįʼnģ pūřpőşęş",
"label": "Ğřőūp ʼnämę äŧŧřįþūŧę"
},
"group-search-base-dns-description": "Ŝępäřäŧę þy čőmmäş őř şpäčęş",
"group-search-base-dns-label": "Ğřőūp şęäřčĥ þäşę ĐŃŜ",
"group-search-filter-description": "Ůşęđ ŧő ƒįľŧęř äʼnđ įđęʼnŧįƒy ģřőūp ęʼnŧřįęş ŵįŧĥįʼn ŧĥę đįřęčŧőřy",
"group-search-filter-label": "Ğřőūp şęäřčĥ ƒįľŧęř",
"group-search-filter-user-attribute-description": "Ĩđęʼnŧįƒįęş ūşęřş ŵįŧĥįʼn ģřőūp ęʼnŧřįęş ƒőř ƒįľŧęřįʼnģ pūřpőşęş",
"group-search-filter-user-attribute-label": "Ğřőūp ʼnämę äŧŧřįþūŧę",
"label": "Ğřőūp mäppįʼnģ",
"skip-org-role-sync": {
"description": "Přęvęʼnŧ şyʼnčĥřőʼnįžįʼnģ ūşęřş’ őřģäʼnįžäŧįőʼn řőľęş ƒřőm yőūř ĨđP",
"label": "Ŝĸįp őřģäʼnįžäŧįőʼn řőľę şyʼnč"
}
"skip-org-role-sync-description": "Přęvęʼnŧ şyʼnčĥřőʼnįžįʼnģ ūşęřş’ őřģäʼnįžäŧįőʼn řőľęş ƒřőm yőūř ĨđP",
"skip-org-role-sync-label": "Ŝĸįp őřģäʼnįžäŧįőʼn řőľę şyʼnč"
},
"misc-section": {
"allow-sign-up": {
"descrition": "Ĩƒ ʼnőŧ ęʼnäþľęđ, őʼnľy ęχįşŧįʼnģ Ğřäƒäʼnä ūşęřş čäʼn ľőģ įʼn ūşįʼnģ ĿĐÅP",
"label": "Åľľőŵ şįģʼn ūp"
},
"allow-sign-up-descrition": "Ĩƒ ʼnőŧ ęʼnäþľęđ, őʼnľy ęχįşŧįʼnģ Ğřäƒäʼnä ūşęřş čäʼn ľőģ įʼn ūşįʼnģ ĿĐÅP",
"allow-sign-up-label": "Åľľőŵ şįģʼn ūp",
"label": "Mįşč",
"port": {
"description": "Đęƒäūľŧ pőřŧ įş 389 ŵįŧĥőūŧ ŜŜĿ őř 636 ŵįŧĥ ŜŜĿ",
"label": "Pőřŧ"
},
"timeout": {
"description": "Ŧįmęőūŧ įʼn şęčőʼnđş ƒőř ŧĥę čőʼnʼnęčŧįőʼn ŧő ŧĥę ĿĐÅP şęřvęř",
"label": "Ŧįmęőūŧ"
}
"port-description": "Đęƒäūľŧ pőřŧ įş 389 ŵįŧĥőūŧ ŜŜĿ őř 636 ŵįŧĥ ŜŜĿ",
"port-label": "Pőřŧ",
"timeout-description": "Ŧįmęőūŧ įʼn şęčőʼnđş ƒőř ŧĥę čőʼnʼnęčŧįőʼn ŧő ŧĥę ĿĐÅP şęřvęř",
"timeout-label": "Ŧįmęőūŧ"
},
"title": "Åđväʼnčęđ şęŧŧįʼnģş"
},