/usr/share/grafana/public/app/plugins/datasource/mssql/configuration
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), }), }; }
.
Edit
..
Edit
ConfigurationEditor.tsx
Edit
Kerberos.tsx
Edit