mirror of
https://github.com/grafana/grafana.git
synced 2025-08-01 17:23:49 +08:00

* i18n: removes useTranslate hook * chore: fix duplicate imports * chore: fix import sorting and hook dependencies
482 lines
20 KiB
TypeScript
482 lines
20 KiB
TypeScript
import { css } from '@emotion/css';
|
|
import { SyntheticEvent } from 'react';
|
|
|
|
import {
|
|
DataSourcePluginOptionsEditorProps,
|
|
GrafanaTheme2,
|
|
onUpdateDatasourceJsonDataOption,
|
|
onUpdateDatasourceSecureJsonDataOption,
|
|
SelectableValue,
|
|
updateDatasourcePluginJsonDataOption,
|
|
updateDatasourcePluginResetOption,
|
|
} from '@grafana/data';
|
|
import { Trans, t } from '@grafana/i18n';
|
|
import { ConfigSection, ConfigSubSection, DataSourceDescription } from '@grafana/plugin-ui';
|
|
import { config } from '@grafana/runtime';
|
|
import { ConnectionLimits, useMigrateDatabaseFields } from '@grafana/sql';
|
|
import { NumberInput } from '@grafana/sql/src/components/configuration/NumberInput';
|
|
import {
|
|
Alert,
|
|
FieldSet,
|
|
Input,
|
|
TextLink,
|
|
SecretInput,
|
|
Select,
|
|
useStyles2,
|
|
SecureSocksProxySettings,
|
|
Divider,
|
|
Field,
|
|
Switch,
|
|
} from '@grafana/ui';
|
|
|
|
import { AzureAuthSettings } from '../azureauth/AzureAuthSettings';
|
|
import {
|
|
MSSQLAuthenticationType,
|
|
MSSQLEncryptOptions,
|
|
MssqlOptions,
|
|
AzureAuthConfigType,
|
|
MssqlSecureOptions,
|
|
} from '../types';
|
|
|
|
import { KerberosConfig, KerberosAdvancedSettings, UsernameMessage } from './Kerberos';
|
|
|
|
const LONG_WIDTH = 40;
|
|
|
|
export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps<MssqlOptions, MssqlSecureOptions>) => {
|
|
useMigrateDatabaseFields(props);
|
|
|
|
const { options: dsSettings, onOptionsChange } = props;
|
|
|
|
const styles = useStyles2(getStyles);
|
|
const jsonData = dsSettings.jsonData;
|
|
const azureAuthIsSupported = config.azureAuthEnabled;
|
|
|
|
const azureAuthSettings: AzureAuthConfigType = {
|
|
azureAuthIsSupported,
|
|
azureAuthSettingsUI: AzureAuthSettings,
|
|
};
|
|
|
|
const onResetPassword = () => {
|
|
updateDatasourcePluginResetOption(props, 'password');
|
|
};
|
|
|
|
const onDSOptionChanged = (property: keyof MssqlOptions) => {
|
|
return (event: SyntheticEvent<HTMLInputElement>) => {
|
|
onOptionsChange({ ...dsSettings, ...{ [property]: event.currentTarget.value } });
|
|
};
|
|
};
|
|
|
|
const onSkipTLSVerifyChanged = (event: SyntheticEvent<HTMLInputElement>) => {
|
|
updateDatasourcePluginJsonDataOption(props, 'tlsSkipVerify', event.currentTarget.checked);
|
|
};
|
|
|
|
const onEncryptChanged = (value: SelectableValue) => {
|
|
updateDatasourcePluginJsonDataOption(props, 'encrypt', value.value);
|
|
};
|
|
|
|
const onAuthenticationMethodChanged = (value: SelectableValue) => {
|
|
onOptionsChange({
|
|
...dsSettings,
|
|
...{
|
|
jsonData: {
|
|
...jsonData,
|
|
...{ authenticationType: value.value },
|
|
azureCredentials: undefined,
|
|
keytabFilePath: undefined,
|
|
credentialCache: undefined,
|
|
credentialCacheLookupFile: undefined,
|
|
},
|
|
secureJsonData: { ...dsSettings.secureJsonData, ...{ password: '' } },
|
|
secureJsonFields: { ...dsSettings.secureJsonFields, ...{ password: false } },
|
|
user: '',
|
|
},
|
|
});
|
|
};
|
|
|
|
const onConnectionTimeoutChanged = (connectionTimeout?: number) => {
|
|
if (connectionTimeout && connectionTimeout < 0) {
|
|
connectionTimeout = 0;
|
|
}
|
|
updateDatasourcePluginJsonDataOption(props, 'connectionTimeout', connectionTimeout);
|
|
};
|
|
|
|
const buildAuthenticationOptions = (): Array<SelectableValue<MSSQLAuthenticationType>> => {
|
|
const basicAuthenticationOptions: Array<SelectableValue<MSSQLAuthenticationType>> = [
|
|
{ value: MSSQLAuthenticationType.sqlAuth, label: 'SQL Server Authentication' },
|
|
{ value: MSSQLAuthenticationType.windowsAuth, label: 'Windows Authentication' },
|
|
{ value: MSSQLAuthenticationType.kerberosRaw, label: 'Windows AD: Username + password' },
|
|
{ value: MSSQLAuthenticationType.kerberosKeytab, label: 'Windows AD: Keytab file' },
|
|
{ value: MSSQLAuthenticationType.kerberosCredentialCache, label: 'Windows AD: Credential cache' },
|
|
{ value: MSSQLAuthenticationType.kerberosCredentialCacheLookupFile, label: 'Windows AD: Credential cache file' },
|
|
];
|
|
|
|
if (azureAuthIsSupported) {
|
|
return [
|
|
...basicAuthenticationOptions,
|
|
{ value: MSSQLAuthenticationType.azureAuth, label: MSSQLAuthenticationType.azureAuth },
|
|
];
|
|
}
|
|
|
|
return basicAuthenticationOptions;
|
|
};
|
|
|
|
const encryptOptions: Array<SelectableValue<string>> = [
|
|
{ value: MSSQLEncryptOptions.disable, label: 'disable' },
|
|
{ value: MSSQLEncryptOptions.false, label: 'false' },
|
|
{ value: MSSQLEncryptOptions.true, label: 'true' },
|
|
];
|
|
|
|
return (
|
|
<>
|
|
<DataSourceDescription
|
|
dataSourceName="Microsoft SQL Server"
|
|
docsLink="https://grafana.com/docs/grafana/latest/datasources/mssql/"
|
|
hasRequiredFields
|
|
/>
|
|
<Alert title={t('configuration.configuration-editor.title-user-permission', 'User Permission')} severity="info">
|
|
<Trans
|
|
i18nKey="configuration.configuration-editor.body-user-permission"
|
|
values={{ permissionType: 'SELECT', example1: 'USE otherdb;', example2: 'DROP TABLE user;' }}
|
|
>
|
|
The database user should only be granted {'{{permissionType}}'} permissions on the specified database and
|
|
tables you want to query. Grafana does not validate that queries are safe so queries can contain any SQL
|
|
statement. For example, statements like <code>{'{{example1}}'}</code> and <code>{'{{example2}}'}</code> would
|
|
be executed. To protect against this we <em>highly</em> recommend you create a specific MS SQL user with
|
|
restricted permissions. Check out the{' '}
|
|
<TextLink external href="http://docs.grafana.org/features/datasources/mssql/">
|
|
Microsoft SQL Server Data Source Docs
|
|
</TextLink>{' '}
|
|
for more information.
|
|
</Trans>
|
|
</Alert>
|
|
<Divider />
|
|
<ConfigSection title={t('configuration.configuration-editor.title-connection', 'Connection')}>
|
|
<Field
|
|
label={t('configuration.configuration-editor.title-host', 'Host')}
|
|
required
|
|
invalid={!dsSettings.url}
|
|
error={t('configuration.configuration-editor.required-host', 'Host is required')}
|
|
>
|
|
<Input
|
|
width={LONG_WIDTH}
|
|
name="host"
|
|
type="text"
|
|
value={dsSettings.url || ''}
|
|
// eslint-disable-next-line @grafana/i18n/no-untranslated-strings
|
|
placeholder="localhost:1433"
|
|
onChange={onDSOptionChanged('url')}
|
|
/>
|
|
</Field>
|
|
<Field
|
|
label={t('configuration.configuration-editor.title-database', 'Database')}
|
|
required
|
|
invalid={!jsonData.database}
|
|
error={t('configuration.configuration-editor.required-database', 'Database is required')}
|
|
>
|
|
<Input
|
|
width={LONG_WIDTH}
|
|
name="database"
|
|
value={jsonData.database || ''}
|
|
placeholder={t('configuration.configuration-editor.placeholder-database', 'database name')}
|
|
onChange={onUpdateDatasourceJsonDataOption(props, 'database')}
|
|
/>
|
|
</Field>
|
|
</ConfigSection>
|
|
|
|
<ConfigSection title={t('configuration.configuration-editor.title-tls-auth', 'TLS/SSL Auth')}>
|
|
<Field
|
|
htmlFor="encrypt"
|
|
description={
|
|
<>
|
|
<Trans i18nKey="configuration.configuration-editor.description-encrypt">
|
|
Determines whether or to which extent a secure SSL TCP/IP connection will be negotiated with the server.
|
|
</Trans>
|
|
<ul className={styles.ulPadding}>
|
|
<li>
|
|
<Trans
|
|
i18nKey="configuration.configuration-editor.description-encrypt-disable"
|
|
values={{ encryptionValue: 'disable' }}
|
|
>
|
|
<i>{'{{encryptionValue}}'}</i> - Data sent between client and server is not encrypted.
|
|
</Trans>
|
|
</li>
|
|
<li>
|
|
<Trans
|
|
i18nKey="configuration.configuration-editor.description-encrypt-false"
|
|
values={{ encryptionValue: 'false' }}
|
|
>
|
|
<i>{'{{encryptionValue}}'}</i> - Data sent between client and server is not encrypted beyond the
|
|
login packet. (default)
|
|
</Trans>
|
|
</li>
|
|
<li>
|
|
<Trans
|
|
i18nKey="configuration.configuration-editor.description-encrypt-true"
|
|
values={{ encryptionValue: 'true' }}
|
|
>
|
|
<i>{'{{encryptionValue}}'}</i> - Data sent between client and server is encrypted.
|
|
</Trans>
|
|
</li>
|
|
</ul>
|
|
<Trans i18nKey="configuration.configuration-editor.description-encrypt-older-version">
|
|
If you're using an older version of Microsoft SQL Server like 2008 and 2008R2 you may need to
|
|
disable encryption to be able to connect.
|
|
</Trans>
|
|
</>
|
|
}
|
|
label={t('configuration.configuration-editor.label-encrypt', 'Encrypt')}
|
|
>
|
|
<Select
|
|
options={encryptOptions}
|
|
value={jsonData.encrypt || MSSQLEncryptOptions.false}
|
|
inputId="encrypt"
|
|
onChange={onEncryptChanged}
|
|
width={LONG_WIDTH}
|
|
/>
|
|
</Field>
|
|
|
|
{jsonData.encrypt === MSSQLEncryptOptions.true ? (
|
|
<>
|
|
<Field
|
|
htmlFor="skipTlsVerify"
|
|
label={t('configuration.configuration-editor.label-skip-tls', 'Skip TLS Verify')}
|
|
>
|
|
<Switch id="skipTlsVerify" onChange={onSkipTLSVerifyChanged} value={jsonData.tlsSkipVerify || false} />
|
|
</Field>
|
|
{jsonData.tlsSkipVerify ? null : (
|
|
<>
|
|
<Field
|
|
description={
|
|
<span>
|
|
<Trans i18nKey="configuration.configuration-editor.description-tls-cert">
|
|
Path to file containing the public key certificate of the CA that signed the SQL Server
|
|
certificate. Needed when the server certificate is self signed.
|
|
</Trans>
|
|
</span>
|
|
}
|
|
label={t('configuration.configuration-editor.label-tls-cert', 'TLS/SSL Root Certificate')}
|
|
>
|
|
<Input
|
|
value={jsonData.sslRootCertFile || ''}
|
|
onChange={onUpdateDatasourceJsonDataOption(props, 'sslRootCertFile')}
|
|
placeholder={t(
|
|
'configuration.configuration-editor.placeholder-tls-cert',
|
|
'TLS/SSL root certificate file path'
|
|
)}
|
|
width={LONG_WIDTH}
|
|
/>
|
|
</Field>
|
|
<Field
|
|
label={t('configuration.configuration-editor.label-common-name', 'Hostname in server certificate')}
|
|
>
|
|
<Input
|
|
placeholder={t(
|
|
'configuration.configuration-editor.placeholder-common-name',
|
|
'Common Name (CN) in server certificate'
|
|
)}
|
|
value={jsonData.serverName || ''}
|
|
onChange={onUpdateDatasourceJsonDataOption(props, 'serverName')}
|
|
width={LONG_WIDTH}
|
|
/>
|
|
</Field>
|
|
</>
|
|
)}
|
|
</>
|
|
) : null}
|
|
</ConfigSection>
|
|
|
|
<ConfigSection title={t('configuration.configuration-editor.title-authentication', 'Authentication')}>
|
|
<Field
|
|
label={t('configuration.configuration-editor.label-auth-type', 'Authentication Type')}
|
|
htmlFor="authenticationType"
|
|
description={
|
|
<ul className={styles.ulPadding}>
|
|
<li>
|
|
<Trans i18nKey="configuration.configuration-editor.description-auth-type-sql-server">
|
|
<i>SQL Server Authentication</i> This is the default mechanism to connect to MS SQL Server. Enter the
|
|
SQL Server Authentication login or the Windows Authentication login in the DOMAIN\User format.
|
|
</Trans>
|
|
</li>
|
|
<li>
|
|
<Trans i18nKey="configuration.configuration-editor.description-auth-type-windows-auth">
|
|
<i>Windows Authentication</i> Windows Integrated Security - single sign on for users who are already
|
|
logged onto Windows and have enabled this option for MS SQL Server.
|
|
</Trans>
|
|
</li>
|
|
{azureAuthIsSupported && (
|
|
<li>
|
|
<Trans i18nKey="configuration.configuration-editor.description-auth-type-azure-auth">
|
|
<i>Azure Authentication</i> Securely authenticate and access Azure resources and applications using
|
|
Azure AD credentials - Managed Service Identity and Client Secret Credentials are supported.
|
|
</Trans>
|
|
</li>
|
|
)}
|
|
<li>
|
|
<Trans i18nKey="configuration.configuration-editor.description-auth-type-username-password">
|
|
<i>Windows AD: Username + password</i> Windows Active Directory - Sign on for domain user via
|
|
username/password.
|
|
</Trans>
|
|
</li>
|
|
<li>
|
|
<Trans i18nKey="configuration.configuration-editor.description-auth-type-keytab">
|
|
<i>Windows AD: Keytab</i> Windows Active Directory - Sign on for domain user via keytab file.
|
|
</Trans>
|
|
</li>
|
|
<li>
|
|
<Trans i18nKey="configuration.configuration-editor.description-auth-type-credential-cache">
|
|
<i>Windows AD: Credential cache</i> Windows Active Directory - Sign on for domain user via credential
|
|
cache.
|
|
</Trans>
|
|
</li>
|
|
<li>
|
|
<Trans i18nKey="configuration.configuration-editor.description-auth-type-credential-cache-file">
|
|
<i>Windows AD: Credential cache file</i> Windows Active Directory - Sign on for domain user via
|
|
credential cache file.
|
|
</Trans>
|
|
</li>
|
|
</ul>
|
|
}
|
|
>
|
|
<Select
|
|
// Default to basic authentication of none is set
|
|
value={jsonData.authenticationType || MSSQLAuthenticationType.sqlAuth}
|
|
inputId="authenticationType"
|
|
options={buildAuthenticationOptions()}
|
|
onChange={onAuthenticationMethodChanged}
|
|
width={LONG_WIDTH}
|
|
/>
|
|
</Field>
|
|
|
|
<KerberosConfig {...props} />
|
|
|
|
{/* Basic SQL auth. Render if authType === MSSQLAuthenticationType.sqlAuth OR
|
|
authType === MSSQLAuthenticationType.kerberosRaw OR
|
|
if no authType exists, which will be the case when creating a new data source */}
|
|
{(jsonData.authenticationType === MSSQLAuthenticationType.sqlAuth ||
|
|
jsonData.authenticationType === MSSQLAuthenticationType.kerberosRaw ||
|
|
!jsonData.authenticationType) && (
|
|
<>
|
|
<Field
|
|
label={t('configuration.configuration-editor.label-username', 'Username')}
|
|
required
|
|
invalid={!dsSettings.user}
|
|
error={t('configuration.configuration-editor.required-username', 'Username is required')}
|
|
description={jsonData.authenticationType === MSSQLAuthenticationType.kerberosRaw ? UsernameMessage : ''}
|
|
>
|
|
<Input
|
|
value={dsSettings.user || ''}
|
|
placeholder={
|
|
jsonData.authenticationType === MSSQLAuthenticationType.kerberosRaw
|
|
? // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
|
|
'name@EXAMPLE.COM'
|
|
: t('configuration.configuration-editor.placeholder-user', 'user')
|
|
}
|
|
onChange={onDSOptionChanged('user')}
|
|
width={LONG_WIDTH}
|
|
/>
|
|
</Field>
|
|
<Field
|
|
label={t('configuration.configuration-editor.label-password', 'Password')}
|
|
required
|
|
invalid={!dsSettings.secureJsonFields.password && !dsSettings.secureJsonData?.password}
|
|
error={t('configuration.configuration-editor.required-password', 'Password is required')}
|
|
>
|
|
<SecretInput
|
|
width={LONG_WIDTH}
|
|
placeholder={t('configuration.configuration-editor.placeholder-password', 'Password')}
|
|
isConfigured={dsSettings.secureJsonFields && dsSettings.secureJsonFields.password}
|
|
onReset={onResetPassword}
|
|
onChange={onUpdateDatasourceSecureJsonDataOption(props, 'password')}
|
|
required
|
|
/>
|
|
</Field>
|
|
</>
|
|
)}
|
|
|
|
{azureAuthIsSupported && jsonData.authenticationType === MSSQLAuthenticationType.azureAuth && (
|
|
<FieldSet
|
|
label={t('configuration.configuration-editor.label-auth-settings', 'Azure Authentication Settings')}
|
|
>
|
|
<azureAuthSettings.azureAuthSettingsUI dataSourceConfig={dsSettings} onChange={onOptionsChange} />
|
|
</FieldSet>
|
|
)}
|
|
</ConfigSection>
|
|
|
|
<Divider />
|
|
<ConfigSection
|
|
title={t('configuration.configuration-editor.title-additional-settings', 'Additional settings')}
|
|
description={t(
|
|
'configuration.configuration-editor.description-additional-settings',
|
|
'Additional settings are optional settings that can be configured for more control over your data source. This includes connection limits, connection timeout, group-by time interval, and Secure Socks Proxy.'
|
|
)}
|
|
isCollapsible={true}
|
|
isInitiallyOpen={true}
|
|
>
|
|
<ConnectionLimits options={dsSettings} onOptionsChange={onOptionsChange} />
|
|
|
|
<ConfigSubSection
|
|
title={t('configuration.configuration-editor.title-connection-details', 'Connection details')}
|
|
>
|
|
<Field
|
|
description={
|
|
<span>
|
|
<Trans
|
|
i18nKey="configuration.configuration-editor.description-min-interval"
|
|
values={{ exampleInterval: '1m' }}
|
|
>
|
|
A lower limit for the auto group by time interval. Recommended to be set to write frequency, for
|
|
example
|
|
<code>{'{{exampleInterval}}'}</code> if your data is written every minute.
|
|
</Trans>
|
|
</span>
|
|
}
|
|
label={t('configuration.configuration-editor.label-min-interval', 'Min time interval')}
|
|
>
|
|
<Input
|
|
width={LONG_WIDTH}
|
|
// eslint-disable-next-line @grafana/i18n/no-untranslated-strings
|
|
placeholder="1m"
|
|
value={jsonData.timeInterval || ''}
|
|
onChange={onUpdateDatasourceJsonDataOption(props, 'timeInterval')}
|
|
/>
|
|
</Field>
|
|
<Field
|
|
description={
|
|
<span>
|
|
<Trans
|
|
i18nKey="configuration.configuration-editor.description-connection-timeout"
|
|
values={{ defaultTimeout: '0' }}
|
|
>
|
|
The number of seconds to wait before canceling the request when connecting to the database. The
|
|
default is <code>{'{{defaultTimeout}}'}</code>, meaning no timeout.
|
|
</Trans>
|
|
</span>
|
|
}
|
|
label={t('configuration.configuration-editor.label-connection-timeout', 'Connection timeout')}
|
|
>
|
|
<NumberInput
|
|
width={LONG_WIDTH}
|
|
defaultValue={60}
|
|
value={jsonData.connectionTimeout || 0}
|
|
onChange={onConnectionTimeoutChanged}
|
|
/>
|
|
</Field>
|
|
</ConfigSubSection>
|
|
{config.secureSocksDSProxyEnabled && (
|
|
<SecureSocksProxySettings options={dsSettings} onOptionsChange={onOptionsChange} />
|
|
)}
|
|
<KerberosAdvancedSettings {...props} />
|
|
</ConfigSection>
|
|
</>
|
|
);
|
|
};
|
|
|
|
function getStyles(theme: GrafanaTheme2) {
|
|
return {
|
|
ulPadding: css({
|
|
margin: theme.spacing(1, 0),
|
|
paddingLeft: theme.spacing(5),
|
|
}),
|
|
};
|
|
}
|