mirror of
https://github.com/grafana/grafana.git
synced 2024-11-21 16:38:03 -06:00
MSSQL: Password auth for Azure AD (#89746)
* Password auth for Azure AD * rename auth fields * add azure flag for client password cred enabled * prettier * rename flag * Update go.mod * Update public/app/plugins/datasource/mssql/azureauth/AzureCredentialsForm.tsx Co-authored-by: Andreas Christou <andreas.christou@grafana.com> * Apply suggestions from code review Co-authored-by: Andreas Christou <andreas.christou@grafana.com> * update package * go mod * prettier * remove password * gowork * remove unused env test * linter --------- Co-authored-by: Andreas Christou <andreas.christou@grafana.com>
This commit is contained in:
parent
ac21fa8e18
commit
319a874033
@ -997,6 +997,10 @@ username_assertion =
|
||||
# By default this will include all Grafana Labs owned Azure plugins, or those that make use of Azure settings (Azure Monitor, Azure Data Explorer, Prometheus, MSSQL).
|
||||
forward_settings_to_plugins = grafana-azure-monitor-datasource, prometheus, grafana-azure-data-explorer-datasource, mssql
|
||||
|
||||
# Specifies whether Entra password auth can be used for the MSSQL data source
|
||||
# Disabled by default, needs to be explicitly enabled
|
||||
azure_entra_password_credentials_enabled = false
|
||||
|
||||
#################################### Role-based Access Control ###########
|
||||
[rbac]
|
||||
# If enabled, cache permissions in a in memory cache
|
||||
|
@ -984,6 +984,10 @@
|
||||
# By default this will include all Grafana Labs owned Azure plugins, or those that make use of Azure settings (Azure Monitor, Azure Data Explorer, Prometheus, MSSQL).
|
||||
;forward_settings_to_plugins = grafana-azure-monitor-datasource, prometheus, grafana-azure-data-explorer-datasource, mssql
|
||||
|
||||
# Specifies whether Entra password auth can be used for the MSSQL data source
|
||||
# Disabled by default, needs to be explicitly enabled
|
||||
;azure_entra_password_credentials_enabled = false
|
||||
|
||||
#################################### Role-based Access Control ###########
|
||||
[rbac]
|
||||
;permission_cache = true
|
||||
|
@ -1275,6 +1275,12 @@ Set plugins that will receive Azure settings via plugin context.
|
||||
|
||||
By default, this will include all Grafana Labs owned Azure plugins or those that use Azure settings (Azure Monitor, Azure Data Explorer, Prometheus, MSSQL).
|
||||
|
||||
### azure_entra_password_credentials_enabled
|
||||
|
||||
Specifies whether Entra password auth can be used for the MSSQL data source. This authentication is not recommended and consideration should be taken before enabling this.
|
||||
|
||||
Disabled by default, needs to be explicitly enabled.
|
||||
|
||||
## [auth.jwt]
|
||||
|
||||
Refer to [JWT authentication]({{< relref "../configure-security/configure-authentication/jwt" >}}) for more information.
|
||||
|
2
go.mod
2
go.mod
@ -88,7 +88,7 @@ require (
|
||||
github.com/grafana/gofpdf v0.0.0-20231002120153-857cc45be447 // @grafana/sharing-squad
|
||||
github.com/grafana/gomemcache v0.0.0-20240229205252-cd6a66d6fb56 // @grafana/grafana-operator-experience-squad
|
||||
github.com/grafana/grafana-aws-sdk v0.28.0 // @grafana/aws-datasources
|
||||
github.com/grafana/grafana-azure-sdk-go/v2 v2.0.4 // @grafana/partner-datasources
|
||||
github.com/grafana/grafana-azure-sdk-go/v2 v2.1.0 // @grafana/partner-datasources
|
||||
github.com/grafana/grafana-cloud-migration-snapshot v1.1.0 // @grafana/grafana-operator-experience-squad
|
||||
github.com/grafana/grafana-google-sdk-go v0.1.0 // @grafana/partner-datasources
|
||||
github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79 // @grafana/grafana-backend-group
|
||||
|
@ -416,6 +416,8 @@ github.com/grafana/e2e v0.1.1-0.20221018202458-cffd2bb71c7b h1:Ha+kSIoTutf4ytlVw
|
||||
github.com/grafana/e2e v0.1.1-0.20221018202458-cffd2bb71c7b/go.mod h1:3UsooRp7yW5/NJQBlXcTsAHOoykEhNUYXkQ3r6ehEEY=
|
||||
github.com/grafana/e2e v0.1.1 h1:/b6xcv5BtoBnx8cZnCiey9DbjEc8z7gXHO5edoeRYxc=
|
||||
github.com/grafana/e2e v0.1.1/go.mod h1:RpNLgae5VT+BUHvPE+/zSypmOXKwEu4t+tnEMS1ATaE=
|
||||
github.com/grafana/grafana-azure-sdk-go/v2 v2.1.0 h1:lajVqTWaE96MpbjZToj7EshvqgRWOfYNkD4MbIZizaY=
|
||||
github.com/grafana/grafana-azure-sdk-go/v2 v2.1.0/go.mod h1:aKlFPE36IDa8qccRg3KbgZX3MQ5xymS3RelT4j6kkVU=
|
||||
github.com/grafana/prometheus-alertmanager v0.25.1-0.20240422145632-c33c6b5b6e6b h1:HCbWyVL6vi7gxyO76gQksSPH203oBJ1MJ3JcG1OQlsg=
|
||||
github.com/grafana/prometheus-alertmanager v0.25.1-0.20240422145632-c33c6b5b6e6b/go.mod h1:01sXtHoRwI8W324IPAzuxDFOmALqYLCOhvSC2fUHWXc=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
|
||||
|
@ -26,6 +26,7 @@ export interface AzureSettings {
|
||||
workloadIdentityEnabled: boolean;
|
||||
userIdentityEnabled: boolean;
|
||||
userIdentityFallbackCredentialsEnabled: boolean;
|
||||
azureEntraPasswordCredentialsEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface AzureCloudInfo {
|
||||
@ -131,6 +132,7 @@ export class GrafanaBootConfig implements GrafanaConfig {
|
||||
workloadIdentityEnabled: false,
|
||||
userIdentityEnabled: false,
|
||||
userIdentityFallbackCredentialsEnabled: false,
|
||||
azureEntraPasswordCredentialsEnabled: false,
|
||||
};
|
||||
caching = {
|
||||
enabled: false,
|
||||
|
@ -66,12 +66,13 @@ type FrontendSettingsLicenseInfoDTO struct {
|
||||
}
|
||||
|
||||
type FrontendSettingsAzureDTO struct {
|
||||
Cloud string `json:"cloud"`
|
||||
Clouds []azsettings.AzureCloudInfo `json:"clouds"`
|
||||
ManagedIdentityEnabled bool `json:"managedIdentityEnabled"`
|
||||
WorkloadIdentityEnabled bool `json:"workloadIdentityEnabled"`
|
||||
UserIdentityEnabled bool `json:"userIdentityEnabled"`
|
||||
UserIdentityFallbackCredentialsEnabled bool `json:"userIdentityFallbackCredentialsEnabled"`
|
||||
Cloud string `json:"cloud,omitempty"`
|
||||
Clouds []azsettings.AzureCloudInfo `json:"clouds,omitempty"`
|
||||
ManagedIdentityEnabled bool `json:"managedIdentityEnabled,omitempty"`
|
||||
WorkloadIdentityEnabled bool `json:"workloadIdentityEnabled,omitempty"`
|
||||
UserIdentityEnabled bool `json:"userIdentityEnabled,omitempty"`
|
||||
UserIdentityFallbackCredentialsEnabled bool `json:"userIdentityFallbackCredentialsEnabled,omitempty"`
|
||||
AzureEntraPasswordCredentialsEnabled bool `json:"azureEntraPasswordCredentialsEnabled,omitempty"`
|
||||
}
|
||||
|
||||
type FrontendSettingsCachingDTO struct {
|
||||
|
@ -276,6 +276,7 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro
|
||||
WorkloadIdentityEnabled: hs.Cfg.Azure.WorkloadIdentityEnabled,
|
||||
UserIdentityEnabled: hs.Cfg.Azure.UserIdentityEnabled,
|
||||
UserIdentityFallbackCredentialsEnabled: hs.Cfg.Azure.UserIdentityFallbackCredentialsEnabled,
|
||||
AzureEntraPasswordCredentialsEnabled: hs.Cfg.Azure.AzureEntraPasswordCredentialsEnabled,
|
||||
},
|
||||
|
||||
Caching: dtos.FrontendSettingsCachingDTO{
|
||||
|
@ -142,6 +142,8 @@ func (s *RequestConfigProvider) PluginRequestConfig(ctx context.Context, pluginI
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m[azsettings.AzureEntraPasswordCredentialsEnabled] = strconv.FormatBool(azureSettings.AzureEntraPasswordCredentialsEnabled)
|
||||
}
|
||||
|
||||
if s.cfg.UserFacingDefaultError != "" {
|
||||
|
@ -309,6 +309,7 @@ func TestRequestConfigProvider_PluginRequestConfig_azure(t *testing.T) {
|
||||
},
|
||||
UserIdentityFallbackCredentialsEnabled: true,
|
||||
ForwardSettingsPlugins: []string{"grafana-azure-monitor-datasource", "prometheus", "grafana-azure-data-explorer-datasource", "mssql"},
|
||||
AzureEntraPasswordCredentialsEnabled: true,
|
||||
}
|
||||
|
||||
t.Run("uses the azure settings for an Azure plugin", func(t *testing.T) {
|
||||
@ -389,6 +390,7 @@ func TestRequestConfigProvider_PluginRequestConfig_azure(t *testing.T) {
|
||||
require.NotContains(t, m, "GFAZPL_USER_IDENTITY_CLIENT_ID")
|
||||
require.NotContains(t, m, "GFAZPL_USER_IDENTITY_CLIENT_SECRET")
|
||||
require.NotContains(t, m, "GFAZPL_USER_IDENTITY_ASSERTION")
|
||||
require.NotContains(t, m, "GFAZPL_AZURE_ENTRA_PASSWORD_CREDENTIALS_ENABLED")
|
||||
})
|
||||
|
||||
t.Run("uses the azure settings for a non-Azure user-specified plugin", func(t *testing.T) {
|
||||
@ -413,6 +415,7 @@ func TestRequestConfigProvider_PluginRequestConfig_azure(t *testing.T) {
|
||||
"GFAZPL_USER_IDENTITY_CLIENT_ID": "mock_user_identity_client_id",
|
||||
"GFAZPL_USER_IDENTITY_CLIENT_SECRET": "mock_user_identity_client_secret",
|
||||
"GFAZPL_USER_IDENTITY_ASSERTION": "username",
|
||||
"GFAZPL_AZURE_ENTRA_PASSWORD_CREDENTIALS_ENABLED": "true",
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -80,5 +80,7 @@ func (cfg *Cfg) readAzureSettings() {
|
||||
|
||||
azureSettings.ForwardSettingsPlugins = util.SplitString(azureSection.Key("forward_settings_to_plugins").String())
|
||||
|
||||
azureSettings.AzureEntraPasswordCredentialsEnabled = azureSection.Key("azure_entra_password_credentials_enabled").MustBool(false)
|
||||
|
||||
cfg.Azure = azureSettings
|
||||
}
|
||||
|
@ -321,6 +321,17 @@ func getAzureCredentialDSNFragment(azureCredentials azcredentials.AzureCredentia
|
||||
c.ClientSecret,
|
||||
"ActiveDirectoryApplication",
|
||||
)
|
||||
case *azcredentials.AzureEntraPasswordCredentials:
|
||||
if cfg.Azure.AzureEntraPasswordCredentialsEnabled {
|
||||
connStr += fmt.Sprintf("user id=%s;password=%s;applicationclientid=%s;fedauth=%s;",
|
||||
c.UserId,
|
||||
c.Password,
|
||||
c.ClientId,
|
||||
"ActiveDirectoryPassword",
|
||||
)
|
||||
} else {
|
||||
return "", fmt.Errorf("azure entra password authentication is not enabled")
|
||||
}
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported azure authentication type")
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ export const configWithManagedIdentityEnabled: Partial<GrafanaBootConfig> = {
|
||||
workloadIdentityEnabled: false,
|
||||
userIdentityEnabled: false,
|
||||
userIdentityFallbackCredentialsEnabled: false,
|
||||
azureEntraPasswordCredentialsEnabled: false,
|
||||
},
|
||||
};
|
||||
|
||||
@ -19,6 +20,7 @@ export const configWithManagedIdentityDisabled: Partial<GrafanaBootConfig> = {
|
||||
userIdentityEnabled: false,
|
||||
cloud: 'AzureCloud',
|
||||
userIdentityFallbackCredentialsEnabled: false,
|
||||
azureEntraPasswordCredentialsEnabled: false,
|
||||
},
|
||||
};
|
||||
|
||||
@ -48,5 +50,5 @@ export const dataSourceSettingsWithClientSecretInSecureJSONData: Partial<
|
||||
DataSourceSettings<AzureAuthJSONDataType, AzureAuthSecureJSONDataType>
|
||||
> = {
|
||||
...basicJSONData,
|
||||
secureJsonData: { azureClientSecret: 'XXXX-super-secret-secret-XXXX' },
|
||||
secureJsonData: { azureClientSecret: 'XXXX-super-secret-secret-XXXX', password: undefined },
|
||||
};
|
||||
|
@ -13,6 +13,7 @@ import { AzureCredentialsForm } from './AzureCredentialsForm';
|
||||
export const AzureAuthSettings = (props: HttpSettingsBaseProps) => {
|
||||
const { dataSourceConfig: dsSettings, onChange } = props;
|
||||
const managedIdentityEnabled = config.azure.managedIdentityEnabled;
|
||||
const azureEntraPasswordCredentialsEnabled = config.azure.azureEntraPasswordCredentialsEnabled;
|
||||
|
||||
const credentials = useMemo(() => getCredentials(dsSettings, config), [dsSettings]);
|
||||
|
||||
@ -30,6 +31,7 @@ export const AzureAuthSettings = (props: HttpSettingsBaseProps) => {
|
||||
return (
|
||||
<AzureCredentialsForm
|
||||
managedIdentityEnabled={managedIdentityEnabled}
|
||||
azureEntraPasswordCredentialsEnabled={azureEntraPasswordCredentialsEnabled}
|
||||
credentials={credentials}
|
||||
azureCloudOptions={KnownAzureClouds}
|
||||
onCredentialsChange={onCredentialsChange}
|
||||
|
@ -15,5 +15,7 @@ export function isCredentialsComplete(credentials: AzureCredentialsType): boolea
|
||||
return true;
|
||||
case AzureAuthType.CLIENT_SECRET:
|
||||
return !!(credentials.azureCloud && credentials.tenantId && credentials.clientId && credentials.clientSecret);
|
||||
case AzureAuthType.AD_PASSWORD:
|
||||
return !!(credentials.clientId && credentials.password && credentials.userId);
|
||||
}
|
||||
}
|
||||
|
@ -19,15 +19,15 @@ export const getDefaultCredentials = (managedIdentityEnabled: boolean, cloud: st
|
||||
};
|
||||
|
||||
export const getSecret = (
|
||||
clientSecretStoredServerSide: boolean,
|
||||
clientSecret: string | symbol | undefined
|
||||
storedServerSide: boolean,
|
||||
secret: string | symbol | undefined
|
||||
): undefined | string | ConcealedSecretType => {
|
||||
const concealedSecret: ConcealedSecretType = Symbol('Concealed client secret');
|
||||
if (clientSecretStoredServerSide) {
|
||||
if (storedServerSide) {
|
||||
// The secret is concealed server side, so return the symbol
|
||||
return concealedSecret;
|
||||
} else {
|
||||
return typeof clientSecret === 'string' && clientSecret.length > 0 ? clientSecret : undefined;
|
||||
return typeof secret === 'string' && secret.length > 0 ? secret : undefined;
|
||||
}
|
||||
};
|
||||
|
||||
@ -41,6 +41,8 @@ export const getCredentials = (
|
||||
// Secure JSON data/fields
|
||||
const clientSecretStoredServerSide = dsSettings.secureJsonFields?.azureClientSecret;
|
||||
const clientSecret = dsSettings.secureJsonData?.azureClientSecret;
|
||||
const passwordStoredServerSide = dsSettings.secureJsonFields?.password;
|
||||
const password = dsSettings.secureJsonData?.password;
|
||||
|
||||
// BootConfig data
|
||||
const managedIdentityEnabled = !!bootConfig.azure?.managedIdentityEnabled;
|
||||
@ -74,6 +76,13 @@ export const getCredentials = (
|
||||
clientId: credentials.clientId,
|
||||
clientSecret: getSecret(clientSecretStoredServerSide, clientSecret),
|
||||
};
|
||||
case AzureAuthType.AD_PASSWORD:
|
||||
return {
|
||||
authType: AzureAuthType.AD_PASSWORD,
|
||||
userId: credentials.userId,
|
||||
clientId: credentials.clientId,
|
||||
password: getSecret(passwordStoredServerSide, password),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@ -130,5 +139,29 @@ export const updateCredentials = (
|
||||
};
|
||||
|
||||
return dsSettings;
|
||||
|
||||
case AzureAuthType.AD_PASSWORD:
|
||||
return {
|
||||
...dsSettings,
|
||||
jsonData: {
|
||||
...dsSettings.jsonData,
|
||||
azureCredentials: {
|
||||
authType: AzureAuthType.AD_PASSWORD,
|
||||
userId: credentials.userId,
|
||||
clientId: credentials.clientId,
|
||||
},
|
||||
},
|
||||
secureJsonData: {
|
||||
...dsSettings.secureJsonData,
|
||||
password:
|
||||
typeof credentials.password === 'string' && credentials.password.length > 0
|
||||
? credentials.password
|
||||
: undefined,
|
||||
},
|
||||
secureJsonFields: {
|
||||
...dsSettings.secureJsonFields,
|
||||
password: typeof credentials.password === 'symbol',
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -7,25 +7,22 @@ import { AzureCredentialsType, AzureAuthType } from '../types';
|
||||
|
||||
export interface Props {
|
||||
managedIdentityEnabled: boolean;
|
||||
azureEntraPasswordCredentialsEnabled: boolean;
|
||||
credentials: AzureCredentialsType;
|
||||
azureCloudOptions?: SelectableValue[];
|
||||
onCredentialsChange: (updatedCredentials: AzureCredentialsType) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const authTypeOptions: Array<SelectableValue<AzureAuthType>> = [
|
||||
{
|
||||
value: AzureAuthType.MSI,
|
||||
label: 'Managed Identity',
|
||||
},
|
||||
{
|
||||
value: AzureAuthType.CLIENT_SECRET,
|
||||
label: 'App Registration',
|
||||
},
|
||||
];
|
||||
|
||||
export const AzureCredentialsForm = (props: Props) => {
|
||||
const { managedIdentityEnabled, credentials, azureCloudOptions, onCredentialsChange, disabled } = props;
|
||||
const {
|
||||
managedIdentityEnabled,
|
||||
azureEntraPasswordCredentialsEnabled,
|
||||
credentials,
|
||||
azureCloudOptions,
|
||||
onCredentialsChange,
|
||||
disabled,
|
||||
} = props;
|
||||
|
||||
const onAuthTypeChange = (selected: SelectableValue<AzureAuthType>) => {
|
||||
if (onCredentialsChange) {
|
||||
@ -37,8 +34,27 @@ export const AzureCredentialsForm = (props: Props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const authTypeOptions: Array<SelectableValue<AzureAuthType>> = [
|
||||
{
|
||||
value: AzureAuthType.CLIENT_SECRET,
|
||||
label: 'App Registration',
|
||||
},
|
||||
];
|
||||
if (managedIdentityEnabled) {
|
||||
authTypeOptions.push({
|
||||
value: AzureAuthType.MSI,
|
||||
label: 'Managed Identity',
|
||||
});
|
||||
}
|
||||
if (azureEntraPasswordCredentialsEnabled) {
|
||||
authTypeOptions.push({
|
||||
value: AzureAuthType.AD_PASSWORD,
|
||||
label: 'Azure Entra Password',
|
||||
});
|
||||
}
|
||||
|
||||
const onInputChange = ({ property, value }: { property: keyof AzureCredentialsType; value: string }) => {
|
||||
if (onCredentialsChange && credentials.authType === 'clientsecret') {
|
||||
if (onCredentialsChange) {
|
||||
const updated: AzureCredentialsType = {
|
||||
...credentials,
|
||||
[property]: value,
|
||||
@ -49,22 +65,20 @@ export const AzureCredentialsForm = (props: Props) => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
{managedIdentityEnabled && (
|
||||
<Field
|
||||
label="Authentication"
|
||||
description="Choose the type of authentication to Azure services"
|
||||
htmlFor="authentication-type"
|
||||
>
|
||||
<Select
|
||||
width={20}
|
||||
value={authTypeOptions.find((opt) => opt.value === credentials.authType)}
|
||||
options={authTypeOptions}
|
||||
onChange={onAuthTypeChange}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</Field>
|
||||
)}
|
||||
{credentials.authType === 'clientsecret' && (
|
||||
<Field
|
||||
label="Authentication"
|
||||
description="Choose the type of authentication to Azure services"
|
||||
htmlFor="authentication-type"
|
||||
>
|
||||
<Select
|
||||
width={20}
|
||||
value={authTypeOptions.find((opt) => opt.value === credentials.authType)}
|
||||
options={authTypeOptions}
|
||||
onChange={onAuthTypeChange}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</Field>
|
||||
{credentials.authType === AzureAuthType.CLIENT_SECRET && (
|
||||
<>
|
||||
{azureCloudOptions && (
|
||||
<Field label="Azure Cloud" htmlFor="azure-cloud-type" disabled={disabled}>
|
||||
@ -167,6 +181,84 @@ export const AzureCredentialsForm = (props: Props) => {
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
{credentials.authType === AzureAuthType.AD_PASSWORD && azureEntraPasswordCredentialsEnabled && (
|
||||
<>
|
||||
<Field label="User Id" required htmlFor="user-id" invalid={!credentials.userId} error={'User ID is required'}>
|
||||
<Input
|
||||
width={45}
|
||||
value={credentials.userId || ''}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
const value = event.target.value;
|
||||
onInputChange({ property: 'userId', value });
|
||||
}}
|
||||
disabled={disabled}
|
||||
aria-label="User ID"
|
||||
/>
|
||||
</Field>
|
||||
<Field
|
||||
label="Application Client ID"
|
||||
required
|
||||
htmlFor="application-client-id"
|
||||
invalid={!credentials.clientId}
|
||||
error={'Application Client ID is required'}
|
||||
>
|
||||
<Input
|
||||
width={45}
|
||||
value={credentials.clientId || ''}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
const value = event.target.value;
|
||||
onInputChange({ property: 'clientId', value });
|
||||
}}
|
||||
disabled={disabled}
|
||||
aria-label="Application Client ID"
|
||||
/>
|
||||
</Field>
|
||||
{!disabled &&
|
||||
(typeof credentials.password === 'symbol' ? (
|
||||
<Field label="Password" htmlFor="password" required>
|
||||
<div className="width-30" style={{ display: 'flex', gap: '4px' }}>
|
||||
<Input
|
||||
aria-label="Password"
|
||||
placeholder="configured"
|
||||
disabled={true}
|
||||
data-testid={'password'}
|
||||
width={45}
|
||||
/>
|
||||
<Button
|
||||
variant="secondary"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
onInputChange({ property: 'password', value: '' });
|
||||
}}
|
||||
disabled={disabled}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
</div>
|
||||
</Field>
|
||||
) : (
|
||||
<Field
|
||||
label="Password"
|
||||
required
|
||||
htmlFor="password"
|
||||
invalid={!credentials.password}
|
||||
error={'Password is required'}
|
||||
>
|
||||
<Input
|
||||
width={45}
|
||||
aria-label="Password"
|
||||
value={credentials.password || ''}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
const value = event.target.value;
|
||||
onInputChange({ property: 'password', value });
|
||||
}}
|
||||
id="password"
|
||||
disabled={disabled}
|
||||
/>
|
||||
</Field>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -108,7 +108,7 @@ export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps<Ms
|
||||
if (azureAuthIsSupported) {
|
||||
return [
|
||||
...basicAuthenticationOptions,
|
||||
{ value: MSSQLAuthenticationType.azureAuth, label: 'Azure AD Authentication' },
|
||||
{ value: MSSQLAuthenticationType.azureAuth, label: MSSQLAuthenticationType.azureAuth },
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@ export type ConcealedSecretType = symbol;
|
||||
export enum AzureAuthType {
|
||||
MSI = 'msi',
|
||||
CLIENT_SECRET = 'clientsecret',
|
||||
AD_PASSWORD = 'ad-password',
|
||||
}
|
||||
|
||||
export interface AzureCredentialsType {
|
||||
@ -36,6 +37,8 @@ export interface AzureCredentialsType {
|
||||
tenantId?: string;
|
||||
clientId?: string;
|
||||
clientSecret?: string | ConcealedSecretType;
|
||||
userId?: string;
|
||||
password?: string | ConcealedSecretType;
|
||||
}
|
||||
|
||||
export interface MssqlOptions extends SQLOptions {
|
||||
@ -63,6 +66,7 @@ export type AzureAuthJSONDataType = DataSourceJsonData & {
|
||||
|
||||
export type AzureAuthSecureJSONDataType = {
|
||||
azureClientSecret: undefined | string | ConcealedSecretType;
|
||||
password: undefined | string | ConcealedSecretType;
|
||||
};
|
||||
|
||||
export type AzureAuthConfigType = {
|
||||
|
Loading…
Reference in New Issue
Block a user