mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Notify STS when encryption keys are updated in azurecore (#22384)
This commit is contained in:
@@ -12,7 +12,7 @@ import { promises as fsPromises } from 'fs';
|
|||||||
import { SimpleTokenCache } from './utils/simpleTokenCache';
|
import { SimpleTokenCache } from './utils/simpleTokenCache';
|
||||||
import providerSettings from './providerSettings';
|
import providerSettings from './providerSettings';
|
||||||
import { AzureAccountProvider as AzureAccountProvider } from './azureAccountProvider';
|
import { AzureAccountProvider as AzureAccountProvider } from './azureAccountProvider';
|
||||||
import { AzureAccountProviderMetadata } from 'azurecore';
|
import { AzureAccountProviderMetadata, CacheEncryptionKeys } from 'azurecore';
|
||||||
import { ProviderSettings } from './interfaces';
|
import { ProviderSettings } from './interfaces';
|
||||||
import { MsalCachePluginProvider } from './utils/msalCachePlugin';
|
import { MsalCachePluginProvider } from './utils/msalCachePlugin';
|
||||||
import * as loc from '../localizedConstants';
|
import * as loc from '../localizedConstants';
|
||||||
@@ -41,10 +41,12 @@ export class AzureAccountProviderService implements vscode.Disposable {
|
|||||||
private _event: events.EventEmitter = new events.EventEmitter();
|
private _event: events.EventEmitter = new events.EventEmitter();
|
||||||
private readonly _uriEventHandler: UriEventHandler = new UriEventHandler();
|
private readonly _uriEventHandler: UriEventHandler = new UriEventHandler();
|
||||||
public clientApplication!: PublicClientApplication;
|
public clientApplication!: PublicClientApplication;
|
||||||
|
private _onEncryptionKeysUpdated: vscode.EventEmitter<CacheEncryptionKeys>;
|
||||||
|
|
||||||
constructor(private _context: vscode.ExtensionContext,
|
constructor(private _context: vscode.ExtensionContext,
|
||||||
private _userStoragePath: string,
|
private _userStoragePath: string,
|
||||||
private _authLibrary: string) {
|
private _authLibrary: string) {
|
||||||
|
this._onEncryptionKeysUpdated = new vscode.EventEmitter<CacheEncryptionKeys>();
|
||||||
this._disposables.push(vscode.window.registerUriHandler(this._uriEventHandler));
|
this._disposables.push(vscode.window.registerUriHandler(this._uriEventHandler));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,6 +77,10 @@ export class AzureAccountProviderService implements vscode.Disposable {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getEncryptionKeysEmitter(): vscode.EventEmitter<CacheEncryptionKeys> {
|
||||||
|
return this._onEncryptionKeysUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
public dispose() {
|
public dispose() {
|
||||||
while (this._disposables.length) {
|
while (this._disposables.length) {
|
||||||
const item = this._disposables.pop();
|
const item = this._disposables.pop();
|
||||||
@@ -155,10 +161,12 @@ export class AzureAccountProviderService implements vscode.Disposable {
|
|||||||
|
|
||||||
// ADAL Token Cache
|
// ADAL Token Cache
|
||||||
let simpleTokenCache = new SimpleTokenCache(tokenCacheKey, this._userStoragePath, noSystemKeychain, this._credentialProvider);
|
let simpleTokenCache = new SimpleTokenCache(tokenCacheKey, this._userStoragePath, noSystemKeychain, this._credentialProvider);
|
||||||
await simpleTokenCache.init();
|
if (this._authLibrary === Constants.AuthLibrary.ADAL) {
|
||||||
|
await simpleTokenCache.init();
|
||||||
|
}
|
||||||
|
|
||||||
// MSAL Cache Plugin
|
// MSAL Cache Plugin
|
||||||
this._cachePluginProvider = new MsalCachePluginProvider(tokenCacheKeyMsal, this._userStoragePath, this._credentialProvider);
|
this._cachePluginProvider = new MsalCachePluginProvider(tokenCacheKeyMsal, this._userStoragePath, this._credentialProvider, this._onEncryptionKeysUpdated);
|
||||||
|
|
||||||
const msalConfiguration: Configuration = {
|
const msalConfiguration: Configuration = {
|
||||||
auth: {
|
auth: {
|
||||||
|
|||||||
@@ -9,12 +9,14 @@ import * as vscode from 'vscode';
|
|||||||
import { AuthLibrary } from '../../constants';
|
import { AuthLibrary } from '../../constants';
|
||||||
import * as LocalizedConstants from '../../localizedConstants';
|
import * as LocalizedConstants from '../../localizedConstants';
|
||||||
import { Logger } from '../../utils/Logger';
|
import { Logger } from '../../utils/Logger';
|
||||||
|
import { CacheEncryptionKeys } from 'azurecore';
|
||||||
|
|
||||||
export class FileEncryptionHelper {
|
export class FileEncryptionHelper {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _authLibrary: AuthLibrary,
|
private readonly _authLibrary: AuthLibrary,
|
||||||
private readonly _credentialService: azdata.CredentialProvider,
|
private readonly _credentialService: azdata.CredentialProvider,
|
||||||
protected readonly _fileName: string
|
protected readonly _fileName: string,
|
||||||
|
private readonly _onEncryptionKeysUpdated?: vscode.EventEmitter<CacheEncryptionKeys>
|
||||||
) {
|
) {
|
||||||
this._algorithm = this._authLibrary === AuthLibrary.MSAL ? 'aes-256-cbc' : 'aes-256-gcm';
|
this._algorithm = this._authLibrary === AuthLibrary.MSAL ? 'aes-256-cbc' : 'aes-256-gcm';
|
||||||
this._bufferEncoding = this._authLibrary === AuthLibrary.MSAL ? 'utf16le' : 'hex';
|
this._bufferEncoding = this._authLibrary === AuthLibrary.MSAL ? 'utf16le' : 'hex';
|
||||||
@@ -48,6 +50,14 @@ export class FileEncryptionHelper {
|
|||||||
this._ivBuffer = Buffer.from(iv, this._bufferEncoding);
|
this._ivBuffer = Buffer.from(iv, this._bufferEncoding);
|
||||||
this._keyBuffer = Buffer.from(key, this._bufferEncoding);
|
this._keyBuffer = Buffer.from(key, this._bufferEncoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Emit event with cache encryption keys to send notification to provider services.
|
||||||
|
if (this._authLibrary === AuthLibrary.MSAL && this._onEncryptionKeysUpdated) {
|
||||||
|
this._onEncryptionKeysUpdated.fire({
|
||||||
|
iv: this._ivBuffer.toString(this._bufferEncoding),
|
||||||
|
key: this._keyBuffer.toString(this._bufferEncoding)
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileSaver = async (content: string): Promise<string> => {
|
fileSaver = async (content: string): Promise<string> => {
|
||||||
|
|||||||
@@ -9,18 +9,21 @@ import { promises as fsPromises } from 'fs';
|
|||||||
import * as lockFile from 'lockfile';
|
import * as lockFile from 'lockfile';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
|
import * as vscode from 'vscode';
|
||||||
import { AccountsClearTokenCacheCommand, AuthLibrary } from '../../constants';
|
import { AccountsClearTokenCacheCommand, AuthLibrary } from '../../constants';
|
||||||
import { Logger } from '../../utils/Logger';
|
import { Logger } from '../../utils/Logger';
|
||||||
import { FileEncryptionHelper } from './fileEncryptionHelper';
|
import { FileEncryptionHelper } from './fileEncryptionHelper';
|
||||||
|
import { CacheEncryptionKeys } from 'azurecore';
|
||||||
|
|
||||||
export class MsalCachePluginProvider {
|
export class MsalCachePluginProvider {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _serviceName: string,
|
private readonly _serviceName: string,
|
||||||
private readonly _msalFilePath: string,
|
private readonly _msalFilePath: string,
|
||||||
private readonly _credentialService: azdata.CredentialProvider
|
private readonly _credentialService: azdata.CredentialProvider,
|
||||||
|
private readonly _onEncryptionKeysUpdated: vscode.EventEmitter<CacheEncryptionKeys>
|
||||||
) {
|
) {
|
||||||
this._msalFilePath = path.join(this._msalFilePath, this._serviceName);
|
this._msalFilePath = path.join(this._msalFilePath, this._serviceName);
|
||||||
this._fileEncryptionHelper = new FileEncryptionHelper(AuthLibrary.MSAL, this._credentialService, this._serviceName);
|
this._fileEncryptionHelper = new FileEncryptionHelper(AuthLibrary.MSAL, this._credentialService, this._serviceName, this._onEncryptionKeysUpdated);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _lockTaken: boolean = false;
|
private _lockTaken: boolean = false;
|
||||||
|
|||||||
10
extensions/azurecore/src/azurecore.d.ts
vendored
10
extensions/azurecore/src/azurecore.d.ts
vendored
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
declare module 'azurecore' {
|
declare module 'azurecore' {
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import { TreeDataProvider } from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { BlobItem } from '@azure/storage-blob';
|
import { BlobItem } from '@azure/storage-blob';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -314,8 +314,13 @@ declare module 'azurecore' {
|
|||||||
getRegionDisplayName(region?: string): string;
|
getRegionDisplayName(region?: string): string;
|
||||||
getProviderMetadataForAccount(account: AzureAccount): AzureAccountProviderMetadata;
|
getProviderMetadataForAccount(account: AzureAccount): AzureAccountProviderMetadata;
|
||||||
provideResources(): azureResource.IAzureResourceProvider[];
|
provideResources(): azureResource.IAzureResourceProvider[];
|
||||||
|
|
||||||
runGraphQuery<T extends azureResource.AzureGraphResource>(account: AzureAccount, subscriptions: azureResource.AzureResourceSubscription[], ignoreErrors: boolean, query: string): Promise<ResourceQueryResult<T>>;
|
runGraphQuery<T extends azureResource.AzureGraphResource>(account: AzureAccount, subscriptions: azureResource.AzureResourceSubscription[], ignoreErrors: boolean, query: string): Promise<ResourceQueryResult<T>>;
|
||||||
|
/**
|
||||||
|
* Event emitted when MSAL cache encryption keys are updated in credential store.
|
||||||
|
* Returns encryption keys used for encryption/decryption of MSAL cache that can be used
|
||||||
|
* by connection providers to read/write to the same access token cache for stable connectivity.
|
||||||
|
*/
|
||||||
|
onEncryptionKeysUpdated: vscode.Event<CacheEncryptionKeys>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GetSubscriptionsResult = { subscriptions: azureResource.AzureResourceSubscription[], errors: Error[] };
|
export type GetSubscriptionsResult = { subscriptions: azureResource.AzureResourceSubscription[], errors: Error[] };
|
||||||
@@ -333,6 +338,7 @@ declare module 'azurecore' {
|
|||||||
export type AzureRestResponse = { response: any, errors: Error[] };
|
export type AzureRestResponse = { response: any, errors: Error[] };
|
||||||
export type GetBlobsResult = { blobs: azureResource.Blob[], errors: Error[] };
|
export type GetBlobsResult = { blobs: azureResource.Blob[], errors: Error[] };
|
||||||
export type GetStorageAccountAccessKeyResult = { keyName1: string, keyName2: string, errors: Error[] };
|
export type GetStorageAccountAccessKeyResult = { keyName1: string, keyName2: string, errors: Error[] };
|
||||||
|
export type CacheEncryptionKeys = { key: string; iv: string; }
|
||||||
|
|
||||||
export namespace azureResource {
|
export namespace azureResource {
|
||||||
|
|
||||||
|
|||||||
@@ -99,38 +99,43 @@ export async function activate(context: vscode.ExtensionContext): Promise<azurec
|
|||||||
|
|
||||||
updatePiiLoggingLevel();
|
updatePiiLoggingLevel();
|
||||||
|
|
||||||
|
let eventEmitter: vscode.EventEmitter<azurecore.CacheEncryptionKeys>;
|
||||||
// Create the provider service and activate
|
// Create the provider service and activate
|
||||||
initAzureAccountProvider(extensionContext, storagePath, authLibrary!).catch((err) => Logger.error(err));
|
let providerService = await initAzureAccountProvider(extensionContext, storagePath, authLibrary!).catch((err) => Logger.error(err));
|
||||||
|
if (providerService) {
|
||||||
registerAzureServices(appContext);
|
eventEmitter = providerService.getEncryptionKeysEmitter();
|
||||||
const azureResourceTree = new AzureResourceTreeProvider(appContext, authLibrary);
|
|
||||||
const connectionDialogTree = new ConnectionDialogTreeProvider(appContext, authLibrary);
|
|
||||||
pushDisposable(vscode.window.registerTreeDataProvider('azureResourceExplorer', azureResourceTree));
|
|
||||||
pushDisposable(vscode.window.registerTreeDataProvider('connectionDialog/azureResourceExplorer', connectionDialogTree));
|
|
||||||
pushDisposable(vscode.workspace.onDidChangeConfiguration(e => onDidChangeConfiguration(e)));
|
|
||||||
registerAzureResourceCommands(appContext, azureResourceTree, connectionDialogTree, authLibrary);
|
|
||||||
azdata.dataprotocol.registerDataGridProvider(new AzureDataGridProvider(appContext, authLibrary));
|
|
||||||
vscode.commands.registerCommand('azure.dataGrid.openInAzurePortal', async (item: azdata.DataGridItem) => {
|
|
||||||
const portalEndpoint = item.portalEndpoint;
|
|
||||||
const subscriptionId = item.subscriptionId;
|
|
||||||
const resourceGroup = item.resourceGroup;
|
|
||||||
const type = item.type;
|
|
||||||
const name = item.name;
|
|
||||||
if (portalEndpoint && subscriptionId && resourceGroup && type && name) {
|
|
||||||
await vscode.env.openExternal(vscode.Uri.parse(`${portalEndpoint}/#resource/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/${type}/${name}`));
|
|
||||||
} else {
|
|
||||||
Logger.error(`Missing required values - subscriptionId : ${subscriptionId} resourceGroup : ${resourceGroup} type: ${type} name: ${name}`);
|
|
||||||
void vscode.window.showErrorMessage(loc.unableToOpenAzureLink);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
registerAzureServices(appContext);
|
||||||
|
const azureResourceTree = new AzureResourceTreeProvider(appContext, authLibrary);
|
||||||
|
const connectionDialogTree = new ConnectionDialogTreeProvider(appContext, authLibrary);
|
||||||
|
pushDisposable(vscode.window.registerTreeDataProvider('azureResourceExplorer', azureResourceTree));
|
||||||
|
pushDisposable(vscode.window.registerTreeDataProvider('connectionDialog/azureResourceExplorer', connectionDialogTree));
|
||||||
|
pushDisposable(vscode.workspace.onDidChangeConfiguration(e => onDidChangeConfiguration(e)));
|
||||||
|
registerAzureResourceCommands(appContext, azureResourceTree, connectionDialogTree, authLibrary);
|
||||||
|
azdata.dataprotocol.registerDataGridProvider(new AzureDataGridProvider(appContext, authLibrary));
|
||||||
|
vscode.commands.registerCommand('azure.dataGrid.openInAzurePortal', async (item: azdata.DataGridItem) => {
|
||||||
|
const portalEndpoint = item.portalEndpoint;
|
||||||
|
const subscriptionId = item.subscriptionId;
|
||||||
|
const resourceGroup = item.resourceGroup;
|
||||||
|
const type = item.type;
|
||||||
|
const name = item.name;
|
||||||
|
if (portalEndpoint && subscriptionId && resourceGroup && type && name) {
|
||||||
|
await vscode.env.openExternal(vscode.Uri.parse(`${portalEndpoint}/#resource/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/${type}/${name}`));
|
||||||
|
} else {
|
||||||
|
Logger.error(`Missing required values - subscriptionId : ${subscriptionId} resourceGroup : ${resourceGroup} type: ${type} name: ${name}`);
|
||||||
|
void vscode.window.showErrorMessage(loc.unableToOpenAzureLink);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
getSubscriptions(account?: azurecore.AzureAccount, ignoreErrors?: boolean, selectedOnly: boolean = false): Promise<azurecore.GetSubscriptionsResult> {
|
getSubscriptions(account?: azurecore.AzureAccount, ignoreErrors?: boolean, selectedOnly: boolean = false): Promise<azurecore.GetSubscriptionsResult> {
|
||||||
return selectedOnly
|
return selectedOnly
|
||||||
? azureResourceUtils.getSelectedSubscriptions(appContext, account, ignoreErrors)
|
? azureResourceUtils.getSelectedSubscriptions(appContext, account, ignoreErrors)
|
||||||
: azureResourceUtils.getSubscriptions(appContext, account, ignoreErrors);
|
: azureResourceUtils.getSubscriptions(appContext, account, ignoreErrors);
|
||||||
},
|
},
|
||||||
getResourceGroups(account?: azurecore.AzureAccount, subscription?: azurecore.azureResource.AzureResourceSubscription, ignoreErrors?: boolean): Promise<azurecore.GetResourceGroupsResult> { return azureResourceUtils.getResourceGroups(appContext, account, subscription, ignoreErrors); },
|
getResourceGroups(account?: azurecore.AzureAccount, subscription?: azurecore.azureResource.AzureResourceSubscription, ignoreErrors?: boolean): Promise<azurecore.GetResourceGroupsResult> {
|
||||||
|
return azureResourceUtils.getResourceGroups(appContext, account, subscription, ignoreErrors);
|
||||||
|
},
|
||||||
getLocations(account?: azurecore.AzureAccount,
|
getLocations(account?: azurecore.AzureAccount,
|
||||||
subscription?: azurecore.azureResource.AzureResourceSubscription,
|
subscription?: azurecore.azureResource.AzureResourceSubscription,
|
||||||
ignoreErrors?: boolean): Promise<azurecore.GetLocationsResult> {
|
ignoreErrors?: boolean): Promise<azurecore.GetLocationsResult> {
|
||||||
@@ -235,7 +240,8 @@ export async function activate(context: vscode.ExtensionContext): Promise<azurec
|
|||||||
ignoreErrors: boolean,
|
ignoreErrors: boolean,
|
||||||
query: string): Promise<azurecore.ResourceQueryResult<T>> {
|
query: string): Promise<azurecore.ResourceQueryResult<T>> {
|
||||||
return azureResourceUtils.runResourceQuery(account, subscriptions, ignoreErrors, query);
|
return azureResourceUtils.runResourceQuery(account, subscriptions, ignoreErrors, query);
|
||||||
}
|
},
|
||||||
|
onEncryptionKeysUpdated: eventEmitter!.event
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,13 +273,15 @@ async function findOrMakeStoragePath() {
|
|||||||
return storagePath;
|
return storagePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function initAzureAccountProvider(extensionContext: vscode.ExtensionContext, storagePath: string, authLibrary: string): Promise<void> {
|
async function initAzureAccountProvider(extensionContext: vscode.ExtensionContext, storagePath: string, authLibrary: string): Promise<AzureAccountProviderService | undefined> {
|
||||||
try {
|
try {
|
||||||
const accountProviderService = new AzureAccountProviderService(extensionContext, storagePath, authLibrary);
|
const accountProviderService = new AzureAccountProviderService(extensionContext, storagePath, authLibrary);
|
||||||
extensionContext.subscriptions.push(accountProviderService);
|
extensionContext.subscriptions.push(accountProviderService);
|
||||||
await accountProviderService.activate();
|
await accountProviderService.activate();
|
||||||
|
return accountProviderService;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Logger.error('Unexpected error starting account provider: ' + err.message);
|
Logger.error('Unexpected error starting account provider: ' + err.message);
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,5 +61,6 @@ export class AzurecoreApiStub implements azurecore.IExtension {
|
|||||||
provideResources(): azurecore.azureResource.IAzureResourceProvider[] {
|
provideResources(): azurecore.azureResource.IAzureResourceProvider[] {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
onEncryptionKeysUpdated: any
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1604,3 +1604,25 @@ export namespace DropObjectRequest {
|
|||||||
export const type = new RequestType<DropObjectRequestParams, void, void, void>('objectManagement/drop');
|
export const type = new RequestType<DropObjectRequestParams, void, void, void>('objectManagement/drop');
|
||||||
}
|
}
|
||||||
// ------------------------------- < Object Management > ------------------------------------
|
// ------------------------------- < Object Management > ------------------------------------
|
||||||
|
|
||||||
|
// ------------------------------- < Encryption IV/KEY updation Event > ------------------------------------
|
||||||
|
/**
|
||||||
|
* Parameters for the MSAL cache encryption key notification
|
||||||
|
*/
|
||||||
|
export class DidChangeEncryptionIVKeyParams {
|
||||||
|
/**
|
||||||
|
* Buffer encoded IV string for MSAL cache encryption
|
||||||
|
*/
|
||||||
|
public iv: string;
|
||||||
|
/**
|
||||||
|
* Buffer encoded Key string for MSAL cache encryption
|
||||||
|
*/
|
||||||
|
public key: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification sent when the encryption keys are changed.
|
||||||
|
*/
|
||||||
|
export namespace EncryptionKeysChangedNotification {
|
||||||
|
export const type = new NotificationType<DidChangeEncryptionIVKeyParams, void>('connection/encryptionKeysChanged');
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import * as Constants from './constants';
|
|||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
import * as azurecore from 'azurecore';
|
||||||
import { getAzureAuthenticationLibraryConfig, getCommonLaunchArgsAndCleanupOldLogFiles, getConfigTracingLevel, getEnableSqlAuthenticationProviderConfig, getOrDownloadServer, getParallelMessageProcessingConfig, TracingLevel } from './utils';
|
import { getAzureAuthenticationLibraryConfig, getCommonLaunchArgsAndCleanupOldLogFiles, getConfigTracingLevel, getEnableSqlAuthenticationProviderConfig, getOrDownloadServer, getParallelMessageProcessingConfig, TracingLevel } from './utils';
|
||||||
import { TelemetryReporter, LanguageClientErrorHandler } from './telemetry';
|
import { TelemetryReporter, LanguageClientErrorHandler } from './telemetry';
|
||||||
import { SqlOpsDataClient, ClientOptions } from 'dataprotocol-client';
|
import { SqlOpsDataClient, ClientOptions } from 'dataprotocol-client';
|
||||||
@@ -19,7 +20,7 @@ import { SchemaCompareService } from './schemaCompare/schemaCompareService';
|
|||||||
import { AppContext } from './appContext';
|
import { AppContext } from './appContext';
|
||||||
import { DacFxService } from './dacfx/dacFxService';
|
import { DacFxService } from './dacfx/dacFxService';
|
||||||
import { CmsService } from './cms/cmsService';
|
import { CmsService } from './cms/cmsService';
|
||||||
import { CompletionExtensionParams, CompletionExtLoadRequest } from './contracts';
|
import { CompletionExtensionParams, CompletionExtLoadRequest, DidChangeEncryptionIVKeyParams, EncryptionKeysChangedNotification } from './contracts';
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
import { LanguageExtensionService } from './languageExtension/languageExtensionService';
|
import { LanguageExtensionService } from './languageExtension/languageExtensionService';
|
||||||
@@ -82,6 +83,7 @@ export class SqlToolsServer {
|
|||||||
statusView.text = localize('startingServiceStatusMsg', "Starting {0}", Constants.serviceName);
|
statusView.text = localize('startingServiceStatusMsg', "Starting {0}", Constants.serviceName);
|
||||||
this.client.start();
|
this.client.start();
|
||||||
await Promise.all([this.activateFeatures(context), clientReadyPromise]);
|
await Promise.all([this.activateFeatures(context), clientReadyPromise]);
|
||||||
|
await this.handleEncryptionKeyEventNotification(this.client);
|
||||||
return this.client;
|
return this.client;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
TelemetryReporter.sendTelemetryEvent('ServiceInitializingFailed');
|
TelemetryReporter.sendTelemetryEvent('ServiceInitializingFailed');
|
||||||
@@ -90,6 +92,35 @@ export class SqlToolsServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a hop notification handler to send Encryption Key and Iv information from Azure Core extension to backend
|
||||||
|
* SqlToolsService. This notification is needed for Azure authentication flows to be able to read/write into
|
||||||
|
* shared MSAL cache.
|
||||||
|
* @param client SqlOpsDataClient instance
|
||||||
|
*/
|
||||||
|
private async handleEncryptionKeyEventNotification(client: SqlOpsDataClient) {
|
||||||
|
if (getAzureAuthenticationLibraryConfig() === 'MSAL' && getEnableSqlAuthenticationProviderConfig()) {
|
||||||
|
let onDidEncryptionKeysChanged = (await this.getAzureCoreAPI()).onEncryptionKeysUpdated;
|
||||||
|
// Register event listener from Azure Core extension
|
||||||
|
onDidEncryptionKeysChanged((keys: azurecore.CacheEncryptionKeys) => {
|
||||||
|
// Send client notification for updated encryption keys
|
||||||
|
client.sendNotification(EncryptionKeysChangedNotification.type,
|
||||||
|
<DidChangeEncryptionIVKeyParams>{
|
||||||
|
key: keys.key,
|
||||||
|
iv: keys.iv
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getAzureCoreAPI(): Promise<azurecore.IExtension> {
|
||||||
|
const api = (await vscode.extensions.getExtension(azurecore.extension.name)?.activate()) as azurecore.IExtension;
|
||||||
|
if (!api) {
|
||||||
|
throw new Error('Azure core extension could not be activated.');
|
||||||
|
}
|
||||||
|
return api;
|
||||||
|
}
|
||||||
|
|
||||||
private async download(context: AppContext): Promise<string> {
|
private async download(context: AppContext): Promise<string> {
|
||||||
const configDir = context.extensionContext.extensionPath;
|
const configDir = context.extensionContext.extensionPath;
|
||||||
const rawConfig = await fs.readFile(path.join(configDir, 'config.json'));
|
const rawConfig = await fs.readFile(path.join(configDir, 'config.json'));
|
||||||
|
|||||||
Reference in New Issue
Block a user