diff --git a/public/app/plugins/datasource/prometheus/configuration/AzureAuthSettings.tsx b/public/app/plugins/datasource/prometheus/configuration/AzureAuthSettings.tsx index f0fd6570dc9..6152b70cb00 100644 --- a/public/app/plugins/datasource/prometheus/configuration/AzureAuthSettings.tsx +++ b/public/app/plugins/datasource/prometheus/configuration/AzureAuthSettings.tsx @@ -2,11 +2,11 @@ import { cx } from '@emotion/css'; import { FormEvent, useMemo, useState } from 'react'; import { useEffectOnce } from 'react-use'; +import { AzureCredentials } from '@grafana/azure-sdk'; import { config } from '@grafana/runtime'; import { InlineField, InlineFieldRow, InlineSwitch, Input } from '@grafana/ui'; import { HttpSettingsBaseProps } from '@grafana/ui/src/components/DataSourceSettings/types'; -import { AzureCredentials } from './AzureCredentials'; import { getAzureCloudOptions, getCredentials, updateCredentials } from './AzureCredentialsConfig'; import { AzureCredentialsForm } from './AzureCredentialsForm'; diff --git a/public/app/plugins/datasource/prometheus/configuration/AzureCredentials.ts b/public/app/plugins/datasource/prometheus/configuration/AzureCredentials.ts deleted file mode 100644 index d3d5e32130a..00000000000 --- a/public/app/plugins/datasource/prometheus/configuration/AzureCredentials.ts +++ /dev/null @@ -1,46 +0,0 @@ -export enum AzureCloud { - Public = 'AzureCloud', - China = 'AzureChinaCloud', - USGovernment = 'AzureUSGovernment', - None = '', -} - -export type AzureAuthType = 'msi' | 'clientsecret' | 'workloadidentity'; - -export type ConcealedSecret = symbol; - -interface AzureCredentialsBase { - authType: AzureAuthType; - defaultSubscriptionId?: string; -} - -export interface AzureManagedIdentityCredentials extends AzureCredentialsBase { - authType: 'msi'; -} - -export interface AzureWorkloadIdentityCredentials extends AzureCredentialsBase { - authType: 'workloadidentity'; -} - -export interface AzureClientSecretCredentials extends AzureCredentialsBase { - authType: 'clientsecret'; - azureCloud?: string; - tenantId?: string; - clientId?: string; - clientSecret?: string | ConcealedSecret; -} - -export type AzureCredentials = - | AzureManagedIdentityCredentials - | AzureClientSecretCredentials - | AzureWorkloadIdentityCredentials; - -export function isCredentialsComplete(credentials: AzureCredentials): boolean { - switch (credentials.authType) { - case 'msi': - case 'workloadidentity': - return true; - case 'clientsecret': - return !!(credentials.azureCloud && credentials.tenantId && credentials.clientId && credentials.clientSecret); - } -} diff --git a/public/app/plugins/datasource/prometheus/configuration/AzureCredentialsConfig.ts b/public/app/plugins/datasource/prometheus/configuration/AzureCredentialsConfig.ts index ca76ccd4e80..e1b88890215 100644 --- a/public/app/plugins/datasource/prometheus/configuration/AzureCredentialsConfig.ts +++ b/public/app/plugins/datasource/prometheus/configuration/AzureCredentialsConfig.ts @@ -1,29 +1,17 @@ -import { getAzureClouds } from '@grafana/azure-sdk'; +import { + AzureCredentials, + AzureDataSourceJsonData, + AzureDataSourceSecureJsonData, + AzureDataSourceSettings, + getAzureClouds, + getDatasourceCredentials, + getDefaultAzureCloud, + updateDatasourceCredentials, +} from '@grafana/azure-sdk'; import { DataSourceSettings, SelectableValue } from '@grafana/data'; +import { PromOptions } from '@grafana/prometheus'; import { config } from '@grafana/runtime'; -import { AzureCloud, AzureCredentials, ConcealedSecret } from './AzureCredentials'; - -const concealed: ConcealedSecret = Symbol('Concealed client secret'); - -function getDefaultAzureCloud(): string { - return config.azure.cloud || AzureCloud.Public; -} - -function getSecret(options: DataSourceSettings): undefined | string | ConcealedSecret { - if (options.secureJsonFields.azureClientSecret) { - // The secret is concealed on server - return concealed; - } else { - const secret = options.secureJsonData?.azureClientSecret; - return typeof secret === 'string' && secret.length > 0 ? secret : undefined; - } -} - -export function hasCredentials(options: DataSourceSettings): boolean { - return !!options.jsonData.azureCredentials; -} - export function getAzureCloudOptions(): Array> { const cloudInfo = getAzureClouds(); @@ -41,101 +29,25 @@ export function getDefaultCredentials(): AzureCredentials { } } -export function getCredentials(options: DataSourceSettings): AzureCredentials { - const credentials = options.jsonData.azureCredentials as AzureCredentials | undefined; +export function getCredentials(options: AzureDataSourceSettings): AzureCredentials { + const credentials = getDatasourceCredentials(options); + if (credentials) { + return credentials; + } // If no credentials saved, then return empty credentials // of type based on whether the managed identity enabled - if (!credentials) { - return getDefaultCredentials(); - } - - switch (credentials.authType) { - case 'msi': - case 'workloadidentity': - if ( - (credentials.authType === 'msi' && config.azure.managedIdentityEnabled) || - (credentials.authType === 'workloadidentity' && config.azure.workloadIdentityEnabled) - ) { - return { - authType: credentials.authType, - }; - } else { - // If authentication type is managed identity or workload identity but either method is disabled in Grafana config, - // then we should fallback to an empty app registration (client secret) configuration - return { - authType: 'clientsecret', - azureCloud: getDefaultAzureCloud(), - }; - } - case 'clientsecret': - return { - authType: 'clientsecret', - azureCloud: credentials.azureCloud || getDefaultAzureCloud(), - tenantId: credentials.tenantId, - clientId: credentials.clientId, - clientSecret: getSecret(options), - }; - } + return getDefaultCredentials(); } export function updateCredentials( - options: DataSourceSettings, + options: AzurePromDataSourceSettings, credentials: AzureCredentials -): DataSourceSettings { - switch (credentials.authType) { - case 'msi': - case 'workloadidentity': - if (credentials.authType === 'msi' && !config.azure.managedIdentityEnabled) { - throw new Error('Managed Identity authentication is not enabled in Grafana config.'); - } - if (credentials.authType === 'workloadidentity' && !config.azure.workloadIdentityEnabled) { - throw new Error('Workload Identity authentication is not enabled in Grafana config.'); - } - - options = { - ...options, - jsonData: { - ...options.jsonData, - azureAuthType: credentials.authType, - azureCredentials: { - authType: credentials.authType, - }, - }, - }; - - return options; - - case 'clientsecret': - options = { - ...options, - jsonData: { - ...options.jsonData, - azureCredentials: { - authType: 'clientsecret', - azureCloud: credentials.azureCloud || getDefaultAzureCloud(), - tenantId: credentials.tenantId, - clientId: credentials.clientId, - }, - }, - secureJsonData: { - ...options.secureJsonData, - azureClientSecret: - typeof credentials.clientSecret === 'string' && credentials.clientSecret.length > 0 - ? credentials.clientSecret - : undefined, - }, - secureJsonFields: { - ...options.secureJsonFields, - azureClientSecret: typeof credentials.clientSecret === 'symbol', - }, - }; - - return options; - } +): AzurePromDataSourceSettings { + return updateDatasourceCredentials(options, credentials); } -export function setDefaultCredentials(options: DataSourceSettings): Partial> { +export function setDefaultCredentials(options: AzurePromDataSourceSettings): Partial { return { jsonData: { ...options.jsonData, @@ -144,13 +56,18 @@ export function setDefaultCredentials(options: DataSourceSettings): Pa }; } -export function resetCredentials(options: DataSourceSettings): Partial> { +export function resetCredentials(options: AzurePromDataSourceSettings): Partial { return { jsonData: { ...options.jsonData, - azureAuth: undefined, azureCredentials: undefined, azureEndpointResourceId: undefined, }, }; } + +export interface AzurePromDataSourceOptions extends PromOptions, AzureDataSourceJsonData { + azureEndpointResourceId?: string; +} + +export type AzurePromDataSourceSettings = DataSourceSettings; diff --git a/public/app/plugins/datasource/prometheus/configuration/AzureCredentialsForm.test.tsx b/public/app/plugins/datasource/prometheus/configuration/AzureCredentialsForm.test.tsx index 30ead47840b..d323dc8fb8f 100644 --- a/public/app/plugins/datasource/prometheus/configuration/AzureCredentialsForm.test.tsx +++ b/public/app/plugins/datasource/prometheus/configuration/AzureCredentialsForm.test.tsx @@ -12,7 +12,6 @@ const setup = (propsFunc?: (props: Props) => Props) => { tenantId: 'e7f3f661-a933-3h3f-0294-31c4f962ec48', clientId: '34509fad-c0r9-45df-9e25-f1ee34af6900', clientSecret: undefined, - defaultSubscriptionId: '44987801-6nn6-49he-9b2d-9106972f9789', }, azureCloudOptions: [ { value: 'azuremonitor', label: 'Azure' }, @@ -45,15 +44,4 @@ describe('AzureCredentialsForm', () => { })); expect(await screen.findByLabelText('Client Secret')).toBeDisabled(); }); - - it('should enable azure monitor load subscriptions button when all required fields are defined', async () => { - setup((props) => ({ - ...props, - credentials: { - ...props.credentials, - clientSecret: 'e7f3f661-a933-4b3f-8176-51c4f982ec48', - }, - })); - expect(await screen.findByRole('button', { name: 'Load Subscriptions' })).not.toBeDisabled(); - }); }); diff --git a/public/app/plugins/datasource/prometheus/configuration/AzureCredentialsForm.tsx b/public/app/plugins/datasource/prometheus/configuration/AzureCredentialsForm.tsx index 9bd461a8311..cecba3f9bc8 100644 --- a/public/app/plugins/datasource/prometheus/configuration/AzureCredentialsForm.tsx +++ b/public/app/plugins/datasource/prometheus/configuration/AzureCredentialsForm.tsx @@ -1,12 +1,11 @@ import { cx } from '@emotion/css'; -import { ChangeEvent, useEffect, useMemo, useReducer, useState } from 'react'; +import { ChangeEvent, useMemo } from 'react'; +import { AzureAuthType, AzureCredentials } from '@grafana/azure-sdk'; import { SelectableValue } from '@grafana/data'; import { config } from '@grafana/runtime'; import { InlineFormLabel, Button, Select, Input } from '@grafana/ui'; -import { AzureAuthType, AzureCredentials, isCredentialsComplete } from './AzureCredentials'; - export interface Props { managedIdentityEnabled: boolean; workloadIdentityEnabled: boolean; @@ -22,15 +21,10 @@ export const AzureCredentialsForm = (props: Props) => { credentials, azureCloudOptions, onCredentialsChange, - getSubscriptions, disabled, managedIdentityEnabled, workloadIdentityEnabled, } = props; - const hasRequiredFields = isCredentialsComplete(credentials); - - const [subscriptions, setSubscriptions] = useState>>([]); - const [loadSubscriptionsClicked, onLoadSubscriptions] = useReducer((val) => val + 1, 0); const authTypeOptions = useMemo(() => { let opts: Array> = [ @@ -56,42 +50,7 @@ export const AzureCredentialsForm = (props: Props) => { return opts; }, [managedIdentityEnabled, workloadIdentityEnabled]); - useEffect(() => { - if (!getSubscriptions || !hasRequiredFields) { - updateSubscriptions([]); - return; - } - let canceled = false; - getSubscriptions().then((result) => { - if (!canceled) { - updateSubscriptions(result, loadSubscriptionsClicked); - } - }); - return () => { - canceled = true; - }; - // This effect is intended to be called only once initially and on Load Subscriptions click - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loadSubscriptionsClicked]); - - const updateSubscriptions = (received: Array>, autoSelect = false) => { - setSubscriptions(received); - if (getSubscriptions) { - if (autoSelect && !credentials.defaultSubscriptionId && received.length > 0) { - // Selecting the default subscription if subscriptions received but no default subscription selected - onSubscriptionChange(received[0]); - } else if (credentials.defaultSubscriptionId) { - const found = received.find((opt) => opt.value === credentials.defaultSubscriptionId); - if (!found) { - // Unselecting the default subscription if it isn't found among the received subscriptions - onSubscriptionChange(undefined); - } - } - } - }; - const onAuthTypeChange = (selected: SelectableValue) => { - setSubscriptions([]); const defaultAuthType = managedIdentityEnabled ? 'msi' : workloadIdentityEnabled @@ -100,18 +59,15 @@ export const AzureCredentialsForm = (props: Props) => { const updated: AzureCredentials = { ...credentials, authType: selected.value || defaultAuthType, - defaultSubscriptionId: undefined, }; onCredentialsChange(updated); }; const onAzureCloudChange = (selected: SelectableValue) => { if (credentials.authType === 'clientsecret') { - setSubscriptions([]); const updated: AzureCredentials = { ...credentials, azureCloud: selected.value, - defaultSubscriptionId: undefined, }; onCredentialsChange(updated); } @@ -119,11 +75,9 @@ export const AzureCredentialsForm = (props: Props) => { const onTenantIdChange = (event: ChangeEvent) => { if (credentials.authType === 'clientsecret') { - setSubscriptions([]); const updated: AzureCredentials = { ...credentials, tenantId: event.target.value, - defaultSubscriptionId: undefined, }; onCredentialsChange(updated); } @@ -131,11 +85,9 @@ export const AzureCredentialsForm = (props: Props) => { const onClientIdChange = (event: ChangeEvent) => { if (credentials.authType === 'clientsecret') { - setSubscriptions([]); const updated: AzureCredentials = { ...credentials, clientId: event.target.value, - defaultSubscriptionId: undefined, }; onCredentialsChange(updated); } @@ -143,11 +95,9 @@ export const AzureCredentialsForm = (props: Props) => { const onClientSecretChange = (event: ChangeEvent) => { if (credentials.authType === 'clientsecret') { - setSubscriptions([]); const updated: AzureCredentials = { ...credentials, clientSecret: event.target.value, - defaultSubscriptionId: undefined, }; onCredentialsChange(updated); } @@ -155,23 +105,14 @@ export const AzureCredentialsForm = (props: Props) => { const onClientSecretReset = () => { if (credentials.authType === 'clientsecret') { - setSubscriptions([]); const updated: AzureCredentials = { ...credentials, clientSecret: '', - defaultSubscriptionId: undefined, }; onCredentialsChange(updated); } }; - const onSubscriptionChange = (selected: SelectableValue | undefined) => { - const updated: AzureCredentials = { - ...credentials, - defaultSubscriptionId: selected?.value, - }; - onCredentialsChange(updated); - }; const prometheusConfigOverhaulAuth = config.featureToggles.prometheusConfigOverhaulAuth; return ( @@ -283,42 +224,6 @@ export const AzureCredentialsForm = (props: Props) => { )} )} - {getSubscriptions && ( - <> -
-
- Default Subscription -
-