diff --git a/extensions/azurecore/src/azureResource/providers/database/databaseTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/database/databaseTreeDataProvider.ts index 303ed46c70..66ba0549b3 100644 --- a/extensions/azurecore/src/azureResource/providers/database/databaseTreeDataProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/database/databaseTreeDataProvider.ts @@ -43,7 +43,7 @@ export class AzureResourceDatabaseTreeDataProvider extends ResourceTreeDataProvi databaseName: database.name, userName: database.loginName, password: '', - authenticationType: 'SqlLogin', + authenticationType: '', savePassword: true, groupFullName: '', groupId: '', diff --git a/src/sql/platform/connection/common/connectionManagement.ts b/src/sql/platform/connection/common/connectionManagement.ts index 10b20ec2cd..2c7f43a17d 100644 --- a/src/sql/platform/connection/common/connectionManagement.ts +++ b/src/sql/platform/connection/common/connectionManagement.ts @@ -218,6 +218,11 @@ export interface IConnectionManagementService { getUniqueConnectionProvidersByNameMap(providerNameToDisplayNameMap: { [providerDisplayName: string]: string }): { [providerDisplayName: string]: string }; + /** + * Gets the default authentication type from the configuration service + */ + getDefaultAuthenticationTypeId(): string; + /** * Cancels the connection */ diff --git a/src/sql/platform/connection/common/constants.ts b/src/sql/platform/connection/common/constants.ts index 6bb3d1ee3e..000f135774 100644 --- a/src/sql/platform/connection/common/constants.ts +++ b/src/sql/platform/connection/common/constants.ts @@ -21,6 +21,9 @@ export const defaultEngine = 'defaultEngine'; export const passwordChars = '***************'; +/* default authentication type setting name*/ +export const defaultAuthenticationType = 'defaultAuthenticationType'; + /* authentication types */ export const sqlLogin = 'SqlLogin'; export const integrated = 'Integrated'; diff --git a/src/sql/platform/connection/test/common/testConnectionManagementService.ts b/src/sql/platform/connection/test/common/testConnectionManagementService.ts index ce8c9b5e16..7b7e7e0c3a 100644 --- a/src/sql/platform/connection/test/common/testConnectionManagementService.ts +++ b/src/sql/platform/connection/test/common/testConnectionManagementService.ts @@ -306,6 +306,11 @@ export class TestConnectionManagementService implements IConnectionManagementSer return undefined!; } + getDefaultAuthenticationTypeId(): string { + return undefined!; + } + + getConnections(activeConnectionsOnly?: boolean): ConnectionProfile[] { return []; } diff --git a/src/sql/workbench/common/sqlWorkbenchUtils.ts b/src/sql/workbench/common/sqlWorkbenchUtils.ts index 23610d0168..dcb13395c9 100644 --- a/src/sql/workbench/common/sqlWorkbenchUtils.ts +++ b/src/sql/workbench/common/sqlWorkbenchUtils.ts @@ -17,5 +17,5 @@ export function getSqlConfigSection(workspaceConfigService: IConfigurationServic export function getSqlConfigValue(workspaceConfigService: IConfigurationService, configName: string): T { let config = workspaceConfigService.getValue<{ [key: string]: any }>(ConnectionConstants.sqlConfigSectionName); - return config[configName]; + return config ? config[configName] : undefined; } diff --git a/src/sql/workbench/contrib/connection/browser/connection.contribution.ts b/src/sql/workbench/contrib/connection/browser/connection.contribution.ts index 83dbff1df5..2dd38f9351 100644 --- a/src/sql/workbench/contrib/connection/browser/connection.contribution.ts +++ b/src/sql/workbench/contrib/connection/browser/connection.contribution.ts @@ -176,6 +176,18 @@ configurationRegistry.registerConfiguration({ 'default': 25, 'description': localize('sql.maxRecentConnectionsDescription', "The maximum number of recently used connections to store in the connection list.") }, + 'sql.defaultAuthenticationType': { + 'type': 'string', + 'enum': ['SqlLogin', 'AzureMFA', `AzureMFAAndUser`, 'Integrated'], + 'description': localize('sql.defaultAuthenticationTypeDescription', "Default authentication type to use when connecting to Azure resources. "), + 'enumDescriptions': [ + localize('sql.defaultAuthenticationType.SqlLogin', "Sql Login"), + localize('sql.defaultAuthenticationType.AzureMFA', "Azure Active Directory - Universal with MFA support"), + localize('sql.defaultAuthenticationType.AzureMFAAndUser', "Azure Active Directory - Password"), + localize('sql.defaultAuthenticationType.Integrated', "Windows Authentication"), + ], + 'default': 'AzureMFA' + }, 'sql.defaultEngine': { 'type': 'string', 'description': localize('sql.defaultEngineDescription', "Default SQL Engine to use. This drives default language provider in .sql files and the default to use when creating a new connection."), diff --git a/src/sql/workbench/services/connection/browser/connectionDialogService.ts b/src/sql/workbench/services/connection/browser/connectionDialogService.ts index 5bb31d506e..2ab518c9d6 100644 --- a/src/sql/workbench/services/connection/browser/connectionDialogService.ts +++ b/src/sql/workbench/services/connection/browser/connectionDialogService.ts @@ -152,6 +152,16 @@ export class ConnectionDialogService implements IConnectionDialogService { return defaultProvider || Constants.mssqlProviderName; } + private getDefaultAuthenticationTypeName(): string { + let defaultAuthenticationType: string; + if (!defaultAuthenticationType && this._configurationService) { + defaultAuthenticationType = WorkbenchUtils.getSqlConfigValue(this._configurationService, Constants.defaultAuthenticationType); + } + + return defaultAuthenticationType || Constants.sqlLogin; // as a fallback, default to sql login if the value from settings is not available + + } + private handleOnConnect(params: INewConnectionParams, profile?: IConnectionProfile): void { this._logService.debug('ConnectionDialogService: onConnect event is received'); if (!this._connecting) { @@ -383,6 +393,14 @@ export class ConnectionDialogService implements IConnectionDialogService { if (model && !model.providerName) { model.providerName = providerName; } + + const defaultAuthenticationType = this.getDefaultAuthenticationTypeName(); + let authenticationTypeName = model ? model.authenticationType : defaultAuthenticationType; + authenticationTypeName = authenticationTypeName ? authenticationTypeName : defaultAuthenticationType; + if (model && !model.authenticationType) { + model.authenticationType = authenticationTypeName; + } + let newProfile = new ConnectionProfile(this._capabilitiesService, model || providerName); newProfile.saveProfile = true; newProfile.generateNewId(); diff --git a/src/sql/workbench/services/connection/browser/connectionManagementService.ts b/src/sql/workbench/services/connection/browser/connectionManagementService.ts index 1037c92fbe..2b2963253e 100644 --- a/src/sql/workbench/services/connection/browser/connectionManagementService.ts +++ b/src/sql/workbench/services/connection/browser/connectionManagementService.ts @@ -306,6 +306,11 @@ export class ConnectionManagementService extends Disposable implements IConnecti // Fill in the Azure account token if needed and open the connection dialog if it fails let tokenFillSuccess = await this.fillInOrClearToken(newConnection); + // If there is no authentication type set, set it using configuration + if (!newConnection.authenticationType || newConnection.authenticationType === '') { + newConnection.authenticationType = this.getDefaultAuthenticationTypeId(); + } + // If the password is required and still not loaded show the dialog if ((!foundPassword && this._connectionStore.isPasswordRequired(newConnection) && !newConnection.password) || !tokenFillSuccess) { return this.showConnectionDialogOnError(connection, owner, { connected: false, errorMessage: undefined, callStack: undefined, errorCode: undefined }, options); @@ -792,6 +797,11 @@ export class ConnectionManagementService extends Disposable implements IConnecti return defaultProvider && this._providers.has(defaultProvider) ? defaultProvider : undefined; } + public getDefaultAuthenticationTypeId(): string { + let defaultAuthenticationType = WorkbenchUtils.getSqlConfigValue(this._configurationService, Constants.defaultAuthenticationType); + return defaultAuthenticationType; + } + /** * Previously, the only resource available for AAD access tokens was for Azure SQL / SQL Server. * Use that as a default if the provider extension does not configure a different one. If one is diff --git a/src/sql/workbench/services/objectExplorer/browser/objectExplorerViewTreeShim.ts b/src/sql/workbench/services/objectExplorer/browser/objectExplorerViewTreeShim.ts index b96a07e2ce..461d763d0b 100644 --- a/src/sql/workbench/services/objectExplorer/browser/objectExplorerViewTreeShim.ts +++ b/src/sql/workbench/services/objectExplorer/browser/objectExplorerViewTreeShim.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as azdata from 'azdata'; +import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils'; import { TreeNode } from 'sql/workbench/services/objectExplorer/common/treeNode'; import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService'; import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; @@ -14,6 +15,7 @@ import { hash } from 'vs/base/common/hash'; import { Disposable } from 'vs/base/common/lifecycle'; import { generateUuid } from 'vs/base/common/uuid'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; //TODO: Needed? import { TreeItemCollapsibleState } from 'vs/workbench/common/views'; import { localize } from 'vs/nls'; import { NodeType } from 'sql/workbench/services/objectExplorer/common/nodeType'; @@ -41,7 +43,8 @@ export class OEShimService extends Disposable implements IOEShimService { constructor( @IObjectExplorerService private oe: IObjectExplorerService, @IConnectionManagementService private cm: IConnectionManagementService, - @ICapabilitiesService private capabilities: ICapabilitiesService + @ICapabilitiesService private capabilities: ICapabilitiesService, + @IConfigurationService private configurationService: IConfigurationService ) { super(); } @@ -133,6 +136,10 @@ export class OEShimService extends Disposable implements IOEShimService { public async getChildren(node: ITreeItem, viewId: string): Promise { if (node.payload) { + if (node.payload.authenticationType !== undefined && node.payload.authenticationType === '') { + node.payload.authenticationType = this.getDefaultAuthenticationType(this.configurationService); // we need to set auth type here, because it's value is part of the session key + } + const sessionId = await this.getOrCreateSession(viewId, node); const requestHandle = this.nodeHandleMap.get(generateNodeMapKey(viewId, node)) || node.handle; const treeNode = new TreeNode(undefined!, undefined!, undefined!, requestHandle, undefined!); // hack since this entire system is a hack anyways @@ -222,6 +229,10 @@ export class OEShimService extends Disposable implements IOEShimService { } return undefined; } + + public getDefaultAuthenticationType(configurationService: IConfigurationService): string { + return WorkbenchUtils.getSqlConfigValue(configurationService, 'defaultAuthenticationType'); + } } function generateSessionMapKey(viewId: string, node: ITreeItem): number {