Skip to content

Commit d9a52b7

Browse files
committed
Updated MSSQL Files for Grafana
1 parent bcc32b5 commit d9a52b7

19 files changed

Lines changed: 407 additions & 324 deletions

Source/Applications/openHistorian/openHistorian/Grafana/public/app/plugins/datasource/mssql/MSSqlMetaQuery.test.ts

Lines changed: 0 additions & 10 deletions
This file was deleted.

Source/Applications/openHistorian/openHistorian/Grafana/public/app/plugins/datasource/mssql/MSSqlMetaQuery.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export function getSchemaAndName(database?: string) {
1010

1111
export function getSchema(database?: string, table?: string) {
1212
return `
13-
USE [${database}]
13+
USE ${database}
1414
SELECT COLUMN_NAME as 'column',DATA_TYPE as 'type'
1515
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='${table}';`;
1616
}

Source/Applications/openHistorian/openHistorian/Grafana/public/app/plugins/datasource/mssql/azureauth/AzureAuth.test.ts

Lines changed: 64 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,115 +1,92 @@
1-
import {
2-
AzureCredentials,
3-
AzureCloud,
4-
ConcealedSecret,
5-
AzureClientSecretCredentials,
6-
instanceOfAzureCredential,
7-
updateDatasourceCredentials,
8-
} from '@grafana/azure-sdk';
9-
import { config } from '@grafana/runtime';
1+
import { AzureAuthType, AzureCloud, AzureCredentialsType, ConcealedSecretType } from '../types';
102

113
import {
4+
configWithManagedIdentityEnabled,
5+
configWithManagedIdentityDisabled,
126
dataSourceSettingsWithMsiCredentials,
137
dataSourceSettingsWithClientSecretOnServer,
148
dataSourceSettingsWithClientSecretInSecureJSONData,
159
} from './AzureAuth.testMocks';
16-
import { getDefaultCredentials, getCredentials } from './AzureCredentialsConfig';
10+
import { getDefaultCredentials, getSecret, getCredentials, updateCredentials } from './AzureCredentialsConfig';
1711

1812
// NOTE: @ts-ignores are used to ignore the type errors that are thrown when passing in the mocks.
1913
// This is because the mocks are partials of the actual types, so the types are not complete.
2014

21-
export const CLIENT_SECRET_SYMBOL: ConcealedSecret = Symbol('Concealed client secret');
15+
export const CLIENT_SECRET_SYMBOL: ConcealedSecretType = Symbol('Concealed client secret');
2216

2317
export const CLIENT_SECRET_STRING = 'XXXX-super-secret-secret-XXXX';
2418

25-
jest.mock('@grafana/runtime', () => ({
26-
...jest.requireActual('@grafana/runtime'), // Keep the rest of the actual module
27-
}));
28-
2919
describe('AzureAuth', () => {
30-
beforeEach(() => {
31-
jest.resetModules();
32-
});
33-
3420
describe('AzureCredentialsConfig', () => {
3521
it('`getDefaultCredentials()` should return the correct credentials based on whether the managed identity is enabled', () => {
36-
jest.mocked(config).azure.managedIdentityEnabled = true;
37-
const resultForManagedIdentityEnabled = getDefaultCredentials();
38-
39-
jest.mocked(config).azure.managedIdentityEnabled = false;
40-
const resultForManagedIdentityDisabled = getDefaultCredentials();
22+
const resultForManagedIdentityEnabled = getDefaultCredentials(true, AzureCloud.Public);
23+
const resultForManagedIdentityDisabled = getDefaultCredentials(false, AzureCloud.Public);
4124

4225
expect(resultForManagedIdentityEnabled).toEqual({ authType: 'msi' });
4326
expect(resultForManagedIdentityDisabled).toEqual({ authType: 'clientsecret', azureCloud: 'AzureCloud' });
4427
});
4528

29+
it("`getSecret()` should correctly return the client secret if it's not concealed", () => {
30+
const resultFromServerSideSecret = getSecret(false, CLIENT_SECRET_STRING);
31+
expect(resultFromServerSideSecret).toBe(CLIENT_SECRET_STRING);
32+
33+
const resultFromSecureJSONDataSecret = typeof getSecret(true, '');
34+
expect(resultFromSecureJSONDataSecret).toBe('symbol');
35+
});
36+
4637
describe('getCredentials()', () => {
4738
it('should return the correct managed identity credentials', () => {
48-
// If `dataSourceSettings.authType === 'msi'` && `config.azure.managedIdentityEnabled === true`.
49-
jest.mocked(config).azure.managedIdentityEnabled = true;
39+
// If `dataSourceSettings.authType === AzureAuthType.MSI` && `config.azure.managedIdentityEnabled === true`.
5040
const resultForManagedIdentityEnabled = getCredentials(
5141
// @ts-ignore
52-
dataSourceSettingsWithMsiCredentials
42+
dataSourceSettingsWithMsiCredentials,
43+
configWithManagedIdentityEnabled
5344
);
54-
expect(resultForManagedIdentityEnabled).toEqual({ authType: 'msi' });
45+
expect(resultForManagedIdentityEnabled).toEqual({ authType: AzureAuthType.MSI });
5546

56-
// If `dataSourceSettings.authType === 'msi'` but `config.azure.managedIdentityEnabled !== true`.
47+
// If `dataSourceSettings.authType === AzureAuthType.MSI` but `config.azure.managedIdentityEnabled !== true`.
5748
// Default to basic client secret credentials.
58-
jest.mocked(config).azure.managedIdentityEnabled = false;
5949
const resultForManagedIdentityEnabledInJSONButDisabledInConfig = getCredentials(
6050
// @ts-ignore
61-
dataSourceSettingsWithMsiCredentials
51+
dataSourceSettingsWithMsiCredentials,
52+
configWithManagedIdentityDisabled
6253
);
6354
expect(resultForManagedIdentityEnabledInJSONButDisabledInConfig).toEqual({
64-
authType: 'clientsecret',
55+
authType: AzureAuthType.CLIENT_SECRET,
6556
azureCloud: 'AzureCloud',
6657
});
6758
});
6859

6960
it('should return the correct client secret credentials', () => {
7061
const basicExpectedResult = {
71-
authType: 'clientsecret',
62+
authType: AzureAuthType.CLIENT_SECRET,
7263
azureCloud: 'AzureCloud',
7364
tenantId: 'XXXX-tenant-id-XXXX',
7465
clientId: 'XXXX-client-id-XXXX',
7566
};
7667

77-
// If `dataSourceSettings.authType === 'clientsecret'` && `secureJsonFields.azureClientSecret == true`,
68+
// If `dataSourceSettings.authType === AzureAuthType.CLIENT_SECRET` && `secureJsonFields.azureClientSecret == true`,
7869
// i.e. the client secret is stored on the server.
79-
jest.mocked(config).azure.managedIdentityEnabled = false;
8070
const resultForClientSecretCredentialsOnServer = getCredentials(
8171
// @ts-ignore
82-
dataSourceSettingsWithClientSecretOnServer
72+
dataSourceSettingsWithClientSecretOnServer,
73+
configWithManagedIdentityDisabled
8374
);
8475

8576
// Here we test the properties separately because the client secret is a symbol,
8677
// and since JS symobls are unique, we test via the `typeof` operator.
87-
expect(resultForClientSecretCredentialsOnServer.authType).toEqual('clientsecret');
88-
expect(
89-
instanceOfAzureCredential<AzureClientSecretCredentials>(
90-
'clientsecret',
91-
resultForClientSecretCredentialsOnServer
92-
)
93-
).toEqual(true);
94-
expect((resultForClientSecretCredentialsOnServer as AzureClientSecretCredentials).azureCloud).toEqual(
95-
'AzureCloud'
96-
);
97-
expect((resultForClientSecretCredentialsOnServer as AzureClientSecretCredentials).tenantId).toEqual(
98-
'XXXX-tenant-id-XXXX'
99-
);
100-
expect((resultForClientSecretCredentialsOnServer as AzureClientSecretCredentials).clientId).toEqual(
101-
'XXXX-client-id-XXXX'
102-
);
103-
expect(typeof (resultForClientSecretCredentialsOnServer as AzureClientSecretCredentials).clientSecret).toEqual(
104-
'symbol'
105-
);
78+
expect(resultForClientSecretCredentialsOnServer.authType).toEqual(AzureAuthType.CLIENT_SECRET);
79+
expect(resultForClientSecretCredentialsOnServer.azureCloud).toEqual('AzureCloud');
80+
expect(resultForClientSecretCredentialsOnServer.tenantId).toEqual('XXXX-tenant-id-XXXX');
81+
expect(resultForClientSecretCredentialsOnServer.clientId).toEqual('XXXX-client-id-XXXX');
82+
expect(typeof resultForClientSecretCredentialsOnServer.clientSecret).toEqual('symbol');
10683

107-
// If `dataSourceSettings.authType === 'clientsecret'` && `secureJsonFields.azureClientSecret == false`,
84+
// If `dataSourceSettings.authType === AzureAuthType.CLIENT_SECRET` && `secureJsonFields.azureClientSecret == false`,
10885
// i.e. the client secret is stored in the secureJson.
109-
jest.mocked(config).azure.managedIdentityEnabled = false;
11086
const resultForClientSecretCredentialsInSecureJSON = getCredentials(
11187
// @ts-ignore
112-
dataSourceSettingsWithClientSecretInSecureJSONData
88+
dataSourceSettingsWithClientSecretInSecureJSONData,
89+
configWithManagedIdentityDisabled
11390
);
11491
expect(resultForClientSecretCredentialsInSecureJSON).toEqual({
11592
...basicExpectedResult,
@@ -120,68 +97,66 @@ describe('AzureAuth', () => {
12097

12198
describe('updateCredentials()', () => {
12299
it('should update the credentials for managed service identity correctly', () => {
123-
// If `dataSourceSettings.authType === 'msi'` && `config.azure.managedIdentityEnabled === true`.
124-
jest.mocked(config).azure.managedIdentityEnabled = true;
125-
const resultForMsiCredentials = updateDatasourceCredentials(
100+
// If `dataSourceSettings.authType === AzureAuthType.MSI` && `config.azure.managedIdentityEnabled === true`.
101+
const resultForMsiCredentials = updateCredentials(
126102
// @ts-ignore
127103
dataSourceSettingsWithMsiCredentials,
104+
configWithManagedIdentityEnabled,
128105
{
129-
authType: 'msi',
106+
authType: AzureAuthType.MSI,
130107
}
131108
);
132109
expect(resultForMsiCredentials).toEqual({ jsonData: { azureCredentials: { authType: 'msi' } } });
133110

134-
// If `dataSourceSettings.authType === 'msi'` but `config.azure.managedIdentityEnabled !== true`.
135-
jest.mocked(config).azure.managedIdentityEnabled = false;
111+
// If `dataSourceSettings.authType === AzureAuthType.MSI` but `config.azure.managedIdentityEnabled !== true`.
136112
expect(() =>
137-
updateDatasourceCredentials(
113+
updateCredentials(
138114
// @ts-ignore
139115
dataSourceSettingsWithMsiCredentials,
116+
configWithManagedIdentityDisabled,
140117
{
141-
authType: 'msi',
118+
authType: AzureAuthType.MSI,
142119
}
143120
)
144121
).toThrow('Managed Identity authentication is not enabled in Grafana config.');
145122
});
146123

147124
it('should update the credentials for client secret correctly', () => {
148-
const basicClientSecretCredentials: AzureCredentials = {
149-
authType: 'clientsecret',
150-
azureCloud: AzureCloud.Public,
125+
const basicClientSecretCredentials: AzureCredentialsType = {
126+
authType: AzureAuthType.CLIENT_SECRET,
127+
azureCloud: 'AzureCloud',
151128
tenantId: 'XXXX-tenant-id-XXXX',
152129
clientId: 'XXXX-client-id-XXXX',
153130
};
154131

155-
// If `dataSourceSettings.authType === 'clientsecret'` && `secureJsonFields.azureClientSecret == true`.
156-
jest.mocked(config).azure.managedIdentityEnabled = false;
157-
const resultForClientSecretCredentials1 = updateDatasourceCredentials(
132+
// If `dataSourceSettings.authType === AzureAuthType.CLIENT_SECRET` && `secureJsonFields.azureClientSecret == true`.
133+
const resultForClientSecretCredentials1 = updateCredentials(
158134
// @ts-ignore
159135
dataSourceSettingsWithClientSecretOnServer,
136+
configWithManagedIdentityDisabled,
160137
basicClientSecretCredentials
161138
);
162-
163-
expect(resultForClientSecretCredentials1.jsonData.azureCredentials).toEqual(basicClientSecretCredentials);
164-
expect(resultForClientSecretCredentials1.secureJsonData).toEqual({ azureClientSecret: undefined });
165-
expect(resultForClientSecretCredentials1.secureJsonFields).toEqual({
166-
azureClientSecret: false,
167-
clientSecret: false,
139+
expect(resultForClientSecretCredentials1).toEqual({
140+
jsonData: {
141+
azureCredentials: { ...basicClientSecretCredentials },
142+
},
143+
secureJsonData: { azureClientSecret: undefined },
144+
secureJsonFields: { azureClientSecret: false },
168145
});
169146

170-
// If `dataSourceSettings.authType === 'clientsecret'` && `secureJsonFields.azureClientSecret == false`.
171-
jest.mocked(config).azure.managedIdentityEnabled = false;
172-
const resultForClientSecretCredentials2 = updateDatasourceCredentials(
147+
// If `dataSourceSettings.authType === AzureAuthType.CLIENT_SECRET` && `secureJsonFields.azureClientSecret == false`.
148+
const resultForClientSecretCredentials2 = updateCredentials(
173149
// @ts-ignore
174150
dataSourceSettingsWithClientSecretInSecureJSONData,
151+
configWithManagedIdentityDisabled,
175152
{ ...basicClientSecretCredentials, clientSecret: 'XXXX-super-secret-secret-XXXX' }
176153
);
177-
178-
expect(resultForClientSecretCredentials2.jsonData.azureCredentials).toEqual(basicClientSecretCredentials);
179-
expect(resultForClientSecretCredentials2.secureJsonData).toEqual({
180-
azureClientSecret: 'XXXX-super-secret-secret-XXXX',
181-
});
182-
expect(resultForClientSecretCredentials2.secureJsonFields).toEqual({
183-
azureClientSecret: false,
184-
clientSecret: false,
154+
expect(resultForClientSecretCredentials2).toEqual({
155+
jsonData: {
156+
azureCredentials: { ...basicClientSecretCredentials },
157+
},
158+
secureJsonData: { azureClientSecret: 'XXXX-super-secret-secret-XXXX' },
159+
secureJsonFields: { azureClientSecret: false },
185160
});
186161
});
187162
});

Source/Applications/openHistorian/openHistorian/Grafana/public/app/plugins/datasource/mssql/azureauth/AzureAuth.testMocks.ts

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
import { AzureDataSourceSettings } from '@grafana/azure-sdk';
1+
import { DataSourceSettings } from '@grafana/data';
22
import { GrafanaBootConfig } from '@grafana/runtime';
33

4+
import { AzureAuthSecureJSONDataType, AzureAuthJSONDataType, AzureAuthType } from '../types';
5+
46
export const configWithManagedIdentityEnabled: Partial<GrafanaBootConfig> = {
57
azure: {
68
managedIdentityEnabled: true,
@@ -22,22 +24,31 @@ export const configWithManagedIdentityDisabled: Partial<GrafanaBootConfig> = {
2224
},
2325
};
2426

25-
export const dataSourceSettingsWithMsiCredentials: Partial<AzureDataSourceSettings> = {
26-
jsonData: { azureCredentials: { authType: 'msi' } },
27+
export const dataSourceSettingsWithMsiCredentials: Partial<
28+
DataSourceSettings<AzureAuthJSONDataType, AzureAuthSecureJSONDataType>
29+
> = {
30+
jsonData: { azureCredentials: { authType: AzureAuthType.MSI } },
2731
};
2832

29-
// Will return symbol as the secret is concealed
30-
export const dataSourceSettingsWithClientSecretOnServer: Partial<AzureDataSourceSettings> = {
33+
const basicJSONData = {
3134
jsonData: {
32-
azureCredentials: { authType: 'clientsecret', clientId: 'XXXX-client-id-XXXX', tenantId: 'XXXX-tenant-id-XXXX' },
35+
azureCredentials: {
36+
authType: AzureAuthType.CLIENT_SECRET,
37+
tenantId: 'XXXX-tenant-id-XXXX',
38+
clientId: 'XXXX-client-id-XXXX',
39+
},
3340
},
34-
secureJsonFields: { azureClientSecret: true },
3541
};
42+
43+
// Will return symbol as the secret is concealed
44+
export const dataSourceSettingsWithClientSecretOnServer: Partial<
45+
DataSourceSettings<AzureAuthJSONDataType, AzureAuthSecureJSONDataType>
46+
> = { ...basicJSONData, secureJsonFields: { azureClientSecret: true } };
47+
3648
// Will return the secret as a string from the secureJsonData
37-
export const dataSourceSettingsWithClientSecretInSecureJSONData: Partial<AzureDataSourceSettings> = {
38-
jsonData: {
39-
azureCredentials: { authType: 'clientsecret', clientId: 'XXXX-client-id-XXXX', tenantId: 'XXXX-tenant-id-XXXX' },
40-
},
41-
secureJsonFields: { azureClientSecret: false },
42-
secureJsonData: { azureClientSecret: 'XXXX-super-secret-secret-XXXX' },
49+
export const dataSourceSettingsWithClientSecretInSecureJSONData: Partial<
50+
DataSourceSettings<AzureAuthJSONDataType, AzureAuthSecureJSONDataType>
51+
> = {
52+
...basicJSONData,
53+
secureJsonData: { azureClientSecret: 'XXXX-super-secret-secret-XXXX', password: undefined },
4354
};

Source/Applications/openHistorian/openHistorian/Grafana/public/app/plugins/datasource/mssql/azureauth/AzureAuthSettings.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,24 @@
11
import { useMemo } from 'react';
22
import { useEffectOnce } from 'react-use';
33

4-
import { AzureCredentials, AzureCloud, updateDatasourceCredentials } from '@grafana/azure-sdk';
5-
import { SelectableValue } from '@grafana/data';
64
import { config } from '@grafana/runtime';
75
import { HttpSettingsBaseProps } from '@grafana/ui/src/components/DataSourceSettings/types';
86

9-
import { getCredentials } from './AzureCredentialsConfig';
10-
import { AzureCredentialsForm } from './AzureCredentialsForm';
7+
import { AzureCredentialsType } from '../types';
118

12-
export const KnownAzureClouds: Array<SelectableValue<AzureCloud>> = [{ value: AzureCloud.Public, label: 'Azure' }];
9+
import { KnownAzureClouds } from './AzureCredentials';
10+
import { getCredentials, updateCredentials } from './AzureCredentialsConfig';
11+
import { AzureCredentialsForm } from './AzureCredentialsForm';
1312

1413
export const AzureAuthSettings = (props: HttpSettingsBaseProps) => {
1514
const { dataSourceConfig: dsSettings, onChange } = props;
1615
const managedIdentityEnabled = config.azure.managedIdentityEnabled;
1716
const azureEntraPasswordCredentialsEnabled = config.azure.azureEntraPasswordCredentialsEnabled;
1817

19-
const credentials = useMemo(() => getCredentials(dsSettings), [dsSettings]);
18+
const credentials = useMemo(() => getCredentials(dsSettings, config), [dsSettings]);
2019

21-
const onCredentialsChange = (credentials: AzureCredentials): void => {
22-
onChange(updateDatasourceCredentials(dsSettings, credentials));
20+
const onCredentialsChange = (credentials: AzureCredentialsType): void => {
21+
onChange(updateCredentials(dsSettings, config, credentials));
2322
};
2423

2524
// The auth type needs to be set on the first load of the data source
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { SelectableValue } from '@grafana/data';
2+
3+
import { AzureCredentialsType, AzureAuthType } from '../types';
4+
5+
export enum AzureCloud {
6+
Public = 'AzureCloud',
7+
None = '',
8+
}
9+
10+
export const KnownAzureClouds: Array<SelectableValue<AzureCloud>> = [{ value: AzureCloud.Public, label: 'Azure' }];
11+
12+
export function isCredentialsComplete(credentials: AzureCredentialsType): boolean {
13+
switch (credentials.authType) {
14+
case AzureAuthType.MSI:
15+
return true;
16+
case AzureAuthType.CLIENT_SECRET:
17+
return !!(credentials.azureCloud && credentials.tenantId && credentials.clientId && credentials.clientSecret);
18+
case AzureAuthType.AD_PASSWORD:
19+
return !!(credentials.clientId && credentials.password && credentials.userId);
20+
}
21+
}

0 commit comments

Comments
 (0)