Add AuthenticationType enum to typings (#20699)

* Add AuthenticationType enum to typings

* fix

* const

* Add comments

* fix comment

* remove unused
This commit is contained in:
Charles Gagnon
2022-09-30 16:49:36 -07:00
committed by GitHub
parent d6d75d8817
commit 85dc506a3c
49 changed files with 236 additions and 164 deletions
+1 -1
View File
@@ -129,7 +129,7 @@ async function launchSsmsDialog(action: string, connectionContext: azdata.Object
server: connectionContext.connectionProfile.serverName, server: connectionContext.connectionProfile.serverName,
database: connectionContext.connectionProfile.databaseName, database: connectionContext.connectionProfile.databaseName,
user: connectionContext.connectionProfile.userName, user: connectionContext.connectionProfile.userName,
useAad: connectionContext.connectionProfile.authenticationType === 'AzureMFA', useAad: connectionContext.connectionProfile.authenticationType === azdata.connection.AuthenticationType.AzureMFA,
urn: urn urn: urn
}; };
+1 -1
View File
@@ -209,7 +209,7 @@ export class MiaaModel extends ResourceModel {
return { return {
serverName: `${ipAndPort.ip},${ipAndPort.port}`, serverName: `${ipAndPort.ip},${ipAndPort.port}`,
databaseName: '', databaseName: '',
authenticationType: 'SqlLogin', authenticationType: azdata.connection.AuthenticationType.SqlLogin,
providerName: loc.miaaProviderName, providerName: loc.miaaProviderName,
connectionName: '', connectionName: '',
userName: this._miaaInfo.userName || '', userName: this._miaaInfo.userName || '',
+1 -1
View File
@@ -192,7 +192,7 @@ export class PostgresModel extends ResourceModel {
return { return {
serverName: `${ipAndPort.ip},${ipAndPort.port}`, serverName: `${ipAndPort.ip},${ipAndPort.port}`,
databaseName: '', databaseName: '',
authenticationType: 'SqlLogin', authenticationType: azdata.connection.AuthenticationType.SqlLogin,
providerName: loc.postgresProviderName, providerName: loc.postgresProviderName,
connectionName: '', connectionName: '',
userName: this._pgInfo.userName || '', userName: this._pgInfo.userName || '',
@@ -97,7 +97,7 @@ export abstract class ConnectToSqlDialog extends InitializingComponent {
const connectionProfile: azdata.IConnectionProfile = { const connectionProfile: azdata.IConnectionProfile = {
serverName: this.serverNameInputBox.value, serverName: this.serverNameInputBox.value,
databaseName: '', databaseName: '',
authenticationType: 'SqlLogin', authenticationType: azdata.connection.AuthenticationType.SqlLogin,
providerName: this.providerName, providerName: this.providerName,
connectionName: '', connectionName: '',
userName: this.usernameInputBox.value, userName: this.usernameInputBox.value,
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { ExtensionNodeType, TreeItem } from 'azdata'; import { ExtensionNodeType, TreeItem, connection } from 'azdata';
import { TreeItemCollapsibleState, ExtensionContext } from 'vscode'; import { TreeItemCollapsibleState, ExtensionContext } from 'vscode';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
@@ -43,7 +43,7 @@ export class AzureMonitorTreeDataProvider extends ResourceTreeDataProviderBase<a
databaseName: databaseServer.defaultDatabaseName, databaseName: databaseServer.defaultDatabaseName,
userName: databaseServer.loginName, userName: databaseServer.loginName,
password: '', password: '',
authenticationType: 'AzureMFA', authenticationType: connection.AuthenticationType.AzureMFA,
savePassword: true, savePassword: true,
groupFullName: '', groupFullName: '',
groupId: '', groupId: '',
@@ -42,7 +42,7 @@ export class CosmosDbMongoTreeDataProvider extends ResourceTreeDataProviderBase<
serverName: databaseServer.name, serverName: databaseServer.name,
userName: databaseServer.loginName, userName: databaseServer.loginName,
password: '', password: '',
authenticationType: 'AzureMFA', authenticationType: azdata.connection.AuthenticationType.AzureMFA,
savePassword: true, savePassword: true,
groupFullName: '', groupFullName: '',
groupId: '', groupId: '',
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { ExtensionNodeType, TreeItem } from 'azdata'; import { connection, ExtensionNodeType, TreeItem } from 'azdata';
import { TreeItemCollapsibleState, ExtensionContext } from 'vscode'; import { TreeItemCollapsibleState, ExtensionContext } from 'vscode';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
@@ -43,7 +43,7 @@ export class KustoTreeDataProvider extends ResourceTreeDataProviderBase<azureRes
databaseName: databaseServer.defaultDatabaseName, databaseName: databaseServer.defaultDatabaseName,
userName: databaseServer.loginName, userName: databaseServer.loginName,
password: '', password: '',
authenticationType: 'AzureMFA', authenticationType: connection.AuthenticationType.AzureMFA,
savePassword: true, savePassword: true,
groupFullName: '', groupFullName: '',
groupId: '', groupId: '',
@@ -12,7 +12,7 @@ import { generateGuid } from '../../utils';
import { IAzureResourceService } from '../../interfaces'; import { IAzureResourceService } from '../../interfaces';
import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase';
import { azureResource } from 'azurecore'; import { azureResource } from 'azurecore';
import { Account, ExtensionNodeType, TreeItem } from 'azdata'; import { Account, ExtensionNodeType, TreeItem, connection } from 'azdata';
export class MysqlFlexibleServerTreeDataProvider extends ResourceTreeDataProviderBase<azureResource.AzureResourceDatabaseServer> { export class MysqlFlexibleServerTreeDataProvider extends ResourceTreeDataProviderBase<azureResource.AzureResourceDatabaseServer> {
private static readonly MYSQL_FLEXIBLE_SERVER_PROVIDER_ID = 'MySQL'; private static readonly MYSQL_FLEXIBLE_SERVER_PROVIDER_ID = 'MySQL';
@@ -43,7 +43,7 @@ export class MysqlFlexibleServerTreeDataProvider extends ResourceTreeDataProvide
databaseName: databaseServer.defaultDatabaseName, databaseName: databaseServer.defaultDatabaseName,
userName: databaseServer.loginName, userName: databaseServer.loginName,
password: '', password: '',
authenticationType: 'SqlLogin', authenticationType: connection.AuthenticationType.SqlLogin,
savePassword: true, savePassword: true,
groupFullName: '', groupFullName: '',
groupId: '', groupId: '',
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { ExtensionNodeType, TreeItem } from 'azdata'; import { ExtensionNodeType, TreeItem, connection } from 'azdata';
import { TreeItemCollapsibleState, ExtensionContext } from 'vscode'; import { TreeItemCollapsibleState, ExtensionContext } from 'vscode';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
@@ -43,7 +43,7 @@ export class PostgresServerArcTreeDataProvider extends ResourceTreeDataProviderB
databaseName: databaseServer.defaultDatabaseName, databaseName: databaseServer.defaultDatabaseName,
userName: `${databaseServer.loginName}@${databaseServer.fullName}`, userName: `${databaseServer.loginName}@${databaseServer.fullName}`,
password: '', password: '',
authenticationType: 'SqlLogin', authenticationType: connection.AuthenticationType.SqlLogin,
savePassword: true, savePassword: true,
groupFullName: '', groupFullName: '',
groupId: '', groupId: '',
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { ExtensionNodeType, TreeItem } from 'azdata'; import { ExtensionNodeType, TreeItem, connection } from 'azdata';
import { TreeItemCollapsibleState, ExtensionContext } from 'vscode'; import { TreeItemCollapsibleState, ExtensionContext } from 'vscode';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
@@ -43,7 +43,7 @@ export class PostgresServerTreeDataProvider extends ResourceTreeDataProviderBase
databaseName: databaseServer.defaultDatabaseName, databaseName: databaseServer.defaultDatabaseName,
userName: `${databaseServer.loginName}@${databaseServer.fullName}`, userName: `${databaseServer.loginName}@${databaseServer.fullName}`,
password: '', password: '',
authenticationType: 'SqlLogin', authenticationType: connection.AuthenticationType.SqlLogin,
savePassword: true, savePassword: true,
groupFullName: '', groupFullName: '',
groupId: '', groupId: '',
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { ExtensionNodeType, TreeItem } from 'azdata'; import { ExtensionNodeType, TreeItem, connection } from 'azdata';
import { TreeItemCollapsibleState, ExtensionContext } from 'vscode'; import { TreeItemCollapsibleState, ExtensionContext } from 'vscode';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
@@ -43,7 +43,7 @@ export class SqlInstanceTreeDataProvider extends ResourceTreeDataProviderBase<az
databaseName: databaseServer.defaultDatabaseName, databaseName: databaseServer.defaultDatabaseName,
userName: databaseServer.loginName, userName: databaseServer.loginName,
password: '', password: '',
authenticationType: 'SqlLogin', authenticationType: connection.AuthenticationType.SqlLogin,
savePassword: true, savePassword: true,
groupFullName: '', groupFullName: '',
groupId: '', groupId: '',
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { ExtensionNodeType, TreeItem } from 'azdata'; import { ExtensionNodeType, TreeItem, connection } from 'azdata';
import { TreeItemCollapsibleState, ExtensionContext } from 'vscode'; import { TreeItemCollapsibleState, ExtensionContext } from 'vscode';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
@@ -43,7 +43,7 @@ export class SqlInstanceArcTreeDataProvider extends ResourceTreeDataProviderBase
databaseName: databaseServer.defaultDatabaseName, databaseName: databaseServer.defaultDatabaseName,
userName: databaseServer.loginName, userName: databaseServer.loginName,
password: '', password: '',
authenticationType: 'SqlLogin', authenticationType: connection.AuthenticationType.SqlLogin,
savePassword: true, savePassword: true,
groupFullName: '', groupFullName: '',
groupId: '', groupId: '',
+1 -1
View File
@@ -139,7 +139,7 @@ async function getMountProps(explorerContext?: azdata.ObjectExplorerContext): Pr
let profile = explorerContext.connectionProfile; let profile = explorerContext.connectionProfile;
let mountProps: MountHdfsProperties = { let mountProps: MountHdfsProperties = {
url: endpoint, url: endpoint,
auth: profile.authenticationType === 'SqlLogin' ? 'basic' : 'integrated', auth: profile.authenticationType === azdata.connection.AuthenticationType.SqlLogin ? 'basic' : 'integrated',
username: profile.userName, username: profile.userName,
password: profile.password, password: profile.password,
hdfsPath: getHdsfPath(explorerContext.nodeInfo.nodePath) hdfsPath: getHdsfPath(explorerContext.nodeInfo.nodePath)
@@ -38,7 +38,7 @@ export class CmsResourceTreeNode extends CmsResourceTreeNodeBase {
let nodes: CmsResourceTreeNodeBase[] = []; let nodes: CmsResourceTreeNodeBase[] = [];
if (!this.ownerUri) { if (!this.ownerUri) {
// Set back password to get ownerUri // Set back password to get ownerUri
if (this.connection.options.authenticationType === 'SqlLogin' && this.connection.options.savePassword === true) { if (this.connection.options.authenticationType === azdata.connection.AuthenticationType.SqlLogin && this.connection.options.savePassword === true) {
this.connection.options.password = await this.appContext.cmsUtils.getPassword(this.connection.options.user); this.connection.options.password = await this.appContext.cmsUtils.getPassword(this.connection.options.user);
} }
} }
@@ -38,7 +38,7 @@ export class RegisteredServerTreeNode extends CmsResourceTreeNodeBase {
databaseName: '', databaseName: '',
userName: undefined as string, userName: undefined as string,
password: undefined as string, password: undefined as string,
authenticationType: 'Integrated', authenticationType: azdata.connection.AuthenticationType.Integrated,
savePassword: false, savePassword: false,
groupFullName: '', groupFullName: '',
groupId: '', groupId: '',
+1 -2
View File
@@ -14,7 +14,6 @@ const localize = nls.loadMessageBundle();
const cmsProvider: string = 'MSSQL-CMS'; const cmsProvider: string = 'MSSQL-CMS';
const mssqlProvider: string = 'MSSQL'; const mssqlProvider: string = 'MSSQL';
const CredentialNamespace = 'cmsCredentials'; const CredentialNamespace = 'cmsCredentials';
const sqlLoginAuthType: string = 'SqlLogin';
interface CreateCmsResult { interface CreateCmsResult {
listRegisteredServersResult: mssql.ListRegisteredServersResult; listRegisteredServersResult: mssql.ListRegisteredServersResult;
@@ -121,7 +120,7 @@ export class CmsUtils {
return cachedServer.name !== cmsServerName; return cachedServer.name !== cmsServerName;
}); });
} }
if (connection.options.authenticationType === sqlLoginAuthType && connection.options.savePassword) { if (connection.options.authenticationType === azdata.connection.AuthenticationType.SqlLogin && connection.options.savePassword) {
this._credentialProvider.deleteCredential(connection.options.user); this._credentialProvider.deleteCredential(connection.options.user);
} }
} }
@@ -163,7 +163,7 @@ describe('import extension summary page tests', function () {
options: { options: {
azureAccount: getAzureAccounts()[1].key.accountId, azureAccount: getAzureAccounts()[1].key.accountId,
azureTenantId: 'azureAccount2Tenant', azureTenantId: 'azureAccount2Tenant',
authenticationType: 'AzureMFA' authenticationType: azdata.connection.AuthenticationType.AzureMFA
} }
}; };
@@ -136,11 +136,11 @@ export class SummaryPage extends ImportPage {
let err; let err;
const currentServer = this.model.server; const currentServer = this.model.server;
const includePasswordInConnectionString = (currentServer.options.authenticationType === 'Integrated') ? false : true; const includePasswordInConnectionString = (currentServer.options.authenticationType === azdata.connection.AuthenticationType.Integrated) ? false : true;
const connectionString = await azdata.connection.getConnectionString(currentServer.connectionId, includePasswordInConnectionString); const connectionString = await azdata.connection.getConnectionString(currentServer.connectionId, includePasswordInConnectionString);
let accessToken = undefined; let accessToken = undefined;
if (currentServer.options.authenticationType === 'AzureMFA') { if (currentServer.options.authenticationType === azdata.connection.AuthenticationType.AzureMFA) {
const azureAccount = (await azdata.accounts.getAllAccounts()).filter(v => v.key.accountId === currentServer.options.azureAccount)[0]; const azureAccount = (await azdata.accounts.getAllAccounts()).filter(v => v.key.accountId === currentServer.options.azureAccount)[0];
accessToken = (await azdata.accounts.getAccountSecurityToken(azureAccount, currentServer.options.azureTenantId, azdata.AzureResource.Sql)).token; accessToken = (await azdata.accounts.getAccountSecurityToken(azureAccount, currentServer.options.azureTenantId, azdata.AzureResource.Sql)).token;
} }
@@ -3,6 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
/* /*
TODO: Due to a runtime error, I duplicated this file at these 2 locations: TODO: Due to a runtime error, I duplicated this file at these 2 locations:
$/extensions/integration-test/src/testConfig.ts $/extensions/integration-test/src/testConfig.ts
@@ -45,7 +47,7 @@ let connectionProviderMapping: { [key: string]: { name: string; displayName: str
let authenticationTypeMapping: { [key: string]: { name: string; displayName: string } } = {}; let authenticationTypeMapping: { [key: string]: { name: string; displayName: string } } = {};
connectionProviderMapping[ConnectionProvider.SQLServer] = { name: 'MSSQL', displayName: 'Microsoft SQL Server' }; connectionProviderMapping[ConnectionProvider.SQLServer] = { name: 'MSSQL', displayName: 'Microsoft SQL Server' };
authenticationTypeMapping[AuthenticationType.SqlLogin] = { name: 'SqlLogin', displayName: 'SQL Login' }; authenticationTypeMapping[AuthenticationType.SqlLogin] = { name: azdata.connection.AuthenticationType.SqlLogin, displayName: 'SQL Login' };
authenticationTypeMapping[AuthenticationType.Windows] = { name: 'Integrated', displayName: 'Windows Authentication' }; authenticationTypeMapping[AuthenticationType.Windows] = { name: 'Integrated', displayName: 'Windows Authentication' };
export function getConfigValue(name: string): string { export function getConfigValue(name: string): string {
@@ -263,7 +263,7 @@ describe('Jupyter Session', function (): void {
id: 'id', id: 'id',
providerName: 'MSSQL', providerName: 'MSSQL',
options: { options: {
authenticationType: 'SqlLogin', authenticationType: connection.AuthenticationType.SqlLogin,
}, },
password: '', password: '',
savePassword: false, savePassword: false,
@@ -340,7 +340,7 @@ describe('Jupyter Session', function (): void {
id: 'id', id: 'id',
providerName: 'MSSQL', providerName: 'MSSQL',
options: { options: {
authenticationType: 'SqlLogin', authenticationType: connection.AuthenticationType.SqlLogin,
}, },
password: '', password: '',
savePassword: false, savePassword: false,
@@ -366,7 +366,7 @@ describe('Jupyter Session', function (): void {
id: 'id', id: 'id',
providerName: 'provider', providerName: 'provider',
options: { options: {
authenticationType: 'SqlLogin', authenticationType: connection.AuthenticationType.SqlLogin,
}, },
password: '', password: '',
savePassword: false, savePassword: false,
@@ -15,7 +15,7 @@ export const mockIConnectionProfile: azdata.IConnectionProfile = {
databaseName: 'My Database', databaseName: 'My Database',
userName: 'My User', userName: 'My User',
password: 'My Pwd', password: 'My Pwd',
authenticationType: 'SqlLogin', authenticationType: azdata.connection.AuthenticationType.SqlLogin,
savePassword: false, savePassword: false,
groupFullName: 'My groupName', groupFullName: 'My groupName',
groupId: 'My GroupId', groupId: 'My GroupId',
@@ -38,7 +38,7 @@ export const mockConnectionInfo = {
databaseName: 'My Database', databaseName: 'My Database',
userName: 'My User', userName: 'My User',
password: 'My Pwd', password: 'My Pwd',
authenticationType: 'SqlLogin' authenticationType: azdata.connection.AuthenticationType.SqlLogin
}; };
export const mockFilePath: string = 'test.dacpac'; export const mockFilePath: string = 'test.dacpac';
+1 -1
View File
@@ -120,7 +120,7 @@ export async function verifyConnectionAndGetOwnerUri(endpoint: mssql.SchemaCompa
let userConnection; let userConnection;
userConnection = connectionList.find(connection => userConnection = connectionList.find(connection =>
(endpoint.connectionDetails['authenticationType'] === 'SqlLogin' (endpoint.connectionDetails['authenticationType'] === azdata.connection.AuthenticationType.SqlLogin
&& endpoint.connectionDetails['serverName'] === connection.options.server && endpoint.connectionDetails['serverName'] === connection.options.server
&& endpoint.connectionDetails['userName'] === connection.options.user && endpoint.connectionDetails['userName'] === connection.options.user
&& (endpoint.connectionDetails['databaseName'].toLowerCase() === connection.options.database.toLowerCase() && (endpoint.connectionDetails['databaseName'].toLowerCase() === connection.options.database.toLowerCase()
@@ -2,6 +2,8 @@
* Copyright (c) Microsoft Corporation. All rights reserved. * Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import * as vscodeMssql from 'vscode-mssql';
import * as fs from 'fs'; import * as fs from 'fs';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import * as path from 'path'; import * as path from 'path';
@@ -9,7 +11,6 @@ import * as utils from './utils';
import * as constants from './constants'; import * as constants from './constants';
import * as azureFunctionsContracts from '../contracts/azureFunctions/azureFunctionsContracts'; import * as azureFunctionsContracts from '../contracts/azureFunctions/azureFunctionsContracts';
import { BindingType, IConnectionStringInfo, ObjectType } from 'sql-bindings'; import { BindingType, IConnectionStringInfo, ObjectType } from 'sql-bindings';
import { ConnectionDetails, IConnectionInfo } from 'vscode-mssql';
// https://github.com/microsoft/vscode-azurefunctions/blob/main/src/vscode-azurefunctions.api.d.ts // https://github.com/microsoft/vscode-azurefunctions/blob/main/src/vscode-azurefunctions.api.d.ts
import { AzureFunctionsExtensionApi } from '../../../types/vscode-azurefunctions.api'; import { AzureFunctionsExtensionApi } from '../../../types/vscode-azurefunctions.api';
// https://github.com/microsoft/vscode-azuretools/blob/main/ui/api.d.ts // https://github.com/microsoft/vscode-azuretools/blob/main/ui/api.d.ts
@@ -306,7 +307,7 @@ export async function promptForObjectType(): Promise<ObjectType | undefined> {
* @param objectType (optional) type of object to query/upsert into * @param objectType (optional) type of object to query/upsert into
* @returns the object name from user's input or menu choice * @returns the object name from user's input or menu choice
*/ */
export async function promptForObjectName(bindingType: BindingType, connectionInfo?: IConnectionInfo, objectType?: ObjectType): Promise<string | undefined> { export async function promptForObjectName(bindingType: BindingType, connectionInfo?: vscodeMssql.IConnectionInfo, objectType?: ObjectType): Promise<string | undefined> {
// show the connection string methods (user input and connection profile options) // show the connection string methods (user input and connection profile options)
let connectionURI: string | undefined; let connectionURI: string | undefined;
let selectedDatabase: string | undefined; let selectedDatabase: string | undefined;
@@ -344,7 +345,7 @@ export async function promptForObjectName(bindingType: BindingType, connectionIn
* if left undefined we prompt the user for the connection info * if left undefined we prompt the user for the connection info
* @returns connection string setting name to be used for the createFunction API * @returns connection string setting name to be used for the createFunction API
*/ */
export async function promptAndUpdateConnectionStringSetting(projectUri: vscode.Uri | undefined, connectionInfo?: IConnectionInfo): Promise<IConnectionStringInfo | undefined> { export async function promptAndUpdateConnectionStringSetting(projectUri: vscode.Uri | undefined, connectionInfo?: vscodeMssql.IConnectionInfo): Promise<IConnectionStringInfo | undefined> {
let connectionStringSettingName: string | undefined; let connectionStringSettingName: string | undefined;
// show the settings from project's local.settings.json if there's an AF functions project // show the settings from project's local.settings.json if there's an AF functions project
@@ -495,16 +496,16 @@ export async function promptAndUpdateConnectionStringSetting(projectUri: vscode.
* @param localSettingsPath path to the local.settings.json file * @param localSettingsPath path to the local.settings.json file
* @returns the updated connection string based on password prompts * @returns the updated connection string based on password prompts
*/ */
export async function promptConnectionStringPasswordAndUpdateConnectionString(connectionInfo: IConnectionInfo, localSettingsPath: string): Promise<string | undefined> { export async function promptConnectionStringPasswordAndUpdateConnectionString(connectionInfo: vscodeMssql.IConnectionInfo, localSettingsPath: string): Promise<string | undefined> {
let includePassword: string | undefined; let includePassword: string | undefined;
let connectionString: string = ''; let connectionString: string = '';
let connectionDetails: ConnectionDetails; let connectionDetails: vscodeMssql.ConnectionDetails;
let userPassword: string | undefined; let userPassword: string | undefined;
const vscodeMssqlApi = await utils.getVscodeMssqlApi(); const vscodeMssqlApi = await utils.getVscodeMssqlApi();
connectionDetails = { options: connectionInfo }; connectionDetails = { options: connectionInfo };
try { try {
if (connectionInfo.authenticationType === 'SqlLogin' && connectionInfo.password) { if (connectionInfo.authenticationType === vscodeMssql.AuthenticationType.SqlLogin && connectionInfo.password) {
// Prompt to include password in connection string if authentication type is SqlLogin and connection has password saved // Prompt to include password in connection string if authentication type is SqlLogin and connection has password saved
includePassword = await vscode.window.showQuickPick([constants.yesString, constants.noString], { includePassword = await vscode.window.showQuickPick([constants.yesString, constants.noString], {
title: constants.includePassword, title: constants.includePassword,
@@ -517,18 +518,18 @@ export async function promptConnectionStringPasswordAndUpdateConnectionString(co
} }
} }
if (includePassword !== constants.yesString || !connectionInfo.password || connectionInfo.authenticationType !== 'SqlLogin') { if (includePassword !== constants.yesString || !connectionInfo.password || connectionInfo.authenticationType !== vscodeMssql.AuthenticationType.SqlLogin) {
// get connection string to not include the password if connection info does not include password, // get connection string to not include the password if connection info does not include password,
// or user chooses to not include password (or if user cancels out of include password prompt), or authentication type is not SQL login // or user chooses to not include password (or if user cancels out of include password prompt), or authentication type is not SQL login
connectionString = await vscodeMssqlApi.getConnectionString(connectionDetails, false, false); connectionString = await vscodeMssqlApi.getConnectionString(connectionDetails, false, false);
if (connectionInfo.authenticationType !== 'SqlLogin') { if (connectionInfo.authenticationType !== vscodeMssql.AuthenticationType.SqlLogin) {
// temporarily fix until STS is fix to not include the placeholder: https://github.com/microsoft/sqltoolsservice/issues/1508 // temporarily fix until STS is fix to not include the placeholder: https://github.com/microsoft/sqltoolsservice/issues/1508
// if authentication type is not SQL login, remove password in connection string // if authentication type is not SQL login, remove password in connection string
connectionString = connectionString.replace(`Password=${constants.passwordPlaceholder};`, ''); connectionString = connectionString.replace(`Password=${constants.passwordPlaceholder};`, '');
} }
if (!connectionInfo.password && connectionInfo.authenticationType === 'SqlLogin') { if (!connectionInfo.password && connectionInfo.authenticationType === vscodeMssql.AuthenticationType.SqlLogin) {
// if a connection exists but does not have password saved we ask user if they would like to enter it and save it in local.settings.json // if a connection exists but does not have password saved we ask user if they would like to enter it and save it in local.settings.json
userPassword = await vscode.window.showInputBox({ userPassword = await vscode.window.showInputBox({
prompt: constants.enterPasswordPrompt, prompt: constants.enterPasswordPrompt,
@@ -542,7 +543,7 @@ export async function promptConnectionStringPasswordAndUpdateConnectionString(co
} }
} }
if (!userPassword && connectionInfo.authenticationType === 'SqlLogin') { if (!userPassword && connectionInfo.authenticationType === vscodeMssql.AuthenticationType.SqlLogin) {
// show warning message that user will have to enter password manually later in local.settings.json // show warning message that user will have to enter password manually later in local.settings.json
// if they choose to not to include password, if connection info does not include password // if they choose to not to include password, if connection info does not include password
void vscode.window.showWarningMessage(constants.userPasswordLater, constants.openFile, constants.closeButton).then(async (result) => { void vscode.window.showWarningMessage(constants.userPasswordLater, constants.openFile, constants.closeButton).then(async (result) => {
@@ -580,7 +581,7 @@ export async function promptSelectDatabase(connectionURI: string): Promise<strin
return selectedDatabase; return selectedDatabase;
} }
export async function getConnectionURI(connectionInfo: IConnectionInfo): Promise<string | undefined> { export async function getConnectionURI(connectionInfo: vscodeMssql.IConnectionInfo): Promise<string | undefined> {
const vscodeMssqlApi = await utils.getVscodeMssqlApi(); const vscodeMssqlApi = await utils.getVscodeMssqlApi();
let connectionURI: string = ''; let connectionURI: string = '';
try { try {
@@ -88,7 +88,7 @@ export function createTestCredentials(): vscodeMssql.IConnectionInfo {
accountId: 'test-account-id', accountId: 'test-account-id',
tenantId: 'test-tenant-id', tenantId: 'test-tenant-id',
port: 1234, port: 1234,
authenticationType: 'SqlLogin', authenticationType: vscodeMssql.AuthenticationType.SqlLogin,
azureAccountToken: '', azureAccountToken: '',
expiresOn: 0, expiresOn: 0,
encrypt: false, encrypt: false,
@@ -535,11 +535,6 @@ export const activeDirectoryInteractive = 'active directory interactive';
export const userIdSetting = 'User ID'; export const userIdSetting = 'User ID';
export const passwordSetting = 'Password'; export const passwordSetting = 'Password';
// Authentication types
export const integratedAuth = 'Integrated';
export const azureMfaAuth = 'AzureMFA';
export const sqlAuth = 'SqlAuth';
export const azureAddAccount = localize('azureAddAccount', "Add an Account..."); export const azureAddAccount = localize('azureAddAccount', "Add an Account...");
// Tree item types // Tree item types
@@ -3,10 +3,10 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import * as constants from '../../common/constants'; import * as constants from '../../common/constants';
import * as utils from '../../common/utils'; import * as utils from '../../common/utils';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { ConnectionResult } from 'azdata';
import { IFireWallRuleError } from 'vscode-mssql'; import { IFireWallRuleError } from 'vscode-mssql';
import { ISqlConnectionProperties } from 'sqldbproj'; import { ISqlConnectionProperties } from 'sqldbproj';
@@ -28,7 +28,7 @@ export class ConnectionService {
* @param database database name * @param database database name
* @returns * @returns
*/ */
private async connectToDatabase(profile: ISqlConnectionProperties, saveConnectionAndPassword: boolean, database: string): Promise<ConnectionResult | string | undefined> { private async connectToDatabase(profile: ISqlConnectionProperties, saveConnectionAndPassword: boolean, database: string): Promise<azdata.ConnectionResult | string | undefined> {
const azdataApi = utils.getAzdataApi(); const azdataApi = utils.getAzdataApi();
const vscodeMssqlApi = azdataApi ? undefined : await utils.getVscodeMssqlApi(); const vscodeMssqlApi = azdataApi ? undefined : await utils.getVscodeMssqlApi();
if (azdataApi) { if (azdataApi) {
@@ -43,7 +43,7 @@ export class ConnectionService {
id: '', id: '',
connectionName: profile.profileName, connectionName: profile.profileName,
options: [], options: [],
authenticationType: 'SqlLogin' authenticationType: azdata.connection.AuthenticationType.SqlLogin
}; };
return await azdataApi.connection.connect(connectionProfile, saveConnectionAndPassword, false); return await azdataApi.connection.connect(connectionProfile, saveConnectionAndPassword, false);
} else if (vscodeMssqlApi) { } else if (vscodeMssqlApi) {
@@ -54,7 +54,7 @@ export class ConnectionService {
database: database, database: database,
savePassword: saveConnectionAndPassword, savePassword: saveConnectionAndPassword,
user: profile.userName, user: profile.userName,
authenticationType: 'SqlLogin', authenticationType: azdata.connection.AuthenticationType.SqlLogin,
encrypt: false, encrypt: false,
connectTimeout: 30, connectTimeout: 30,
applicationName: 'SQL Database Project', applicationName: 'SQL Database Project',
@@ -112,12 +112,12 @@ export class ConnectionService {
* @param connection connection result or connection Id * @param connection connection result or connection Id
* @returns validation result * @returns validation result
*/ */
private async validateConnection(connection: ConnectionResult | string | undefined): Promise<utils.ValidationResult> { private async validateConnection(connection: azdata.ConnectionResult | string | undefined): Promise<utils.ValidationResult> {
const azdataApi = utils.getAzdataApi(); const azdataApi = utils.getAzdataApi();
if (!connection) { if (!connection) {
return { validated: false, errorMessage: constants.connectionFailedError('No result returned') }; return { validated: false, errorMessage: constants.connectionFailedError('No result returned') };
} else if (azdataApi) { } else if (azdataApi) {
const connectionResult = <ConnectionResult>connection; const connectionResult = <azdata.ConnectionResult>connection;
if (connectionResult) { if (connectionResult) {
const connected = connectionResult !== undefined && connectionResult.connected && connectionResult.connectionId !== undefined; const connected = connectionResult !== undefined && connectionResult.connected && connectionResult.connectionId !== undefined;
return { validated: connected, errorMessage: connected ? '' : constants.connectionFailedError(connectionResult?.errorMessage!) }; return { validated: connected, errorMessage: connected ? '' : constants.connectionFailedError(connectionResult?.errorMessage!) };
@@ -134,9 +134,9 @@ export class ConnectionService {
* @param connection connection result or connection Id * @param connection connection result or connection Id
* @returns formatted connection result * @returns formatted connection result
*/ */
private async formatConnectionResult(connection: ConnectionResult | string | undefined): Promise<string> { private async formatConnectionResult(connection: azdata.ConnectionResult | string | undefined): Promise<string> {
const azdataApi = utils.getAzdataApi(); const azdataApi = utils.getAzdataApi();
const connectionResult = connection !== undefined && azdataApi ? <ConnectionResult>connection : undefined; const connectionResult = connection !== undefined && azdataApi ? <azdata.ConnectionResult>connection : undefined;
return connectionResult?.connected ? connectionResult.connectionId! : <string>connection; return connectionResult?.connected ? connectionResult.connectionId! : <string>connection;
} }
@@ -160,7 +160,7 @@ export class ConnectionService {
this.defaultSqlNumberOfRetries, profile.connectionRetryTimeout || this.defaultSqlRetryTimeoutInSec); this.defaultSqlNumberOfRetries, profile.connectionRetryTimeout || this.defaultSqlRetryTimeoutInSec);
if (connection) { if (connection) {
const connectionResult = <ConnectionResult>connection; const connectionResult = <azdata.ConnectionResult>connection;
if (azdataApi) { if (azdataApi) {
utils.throwIfNotConnected(connectionResult); utils.throwIfNotConnected(connectionResult);
return azdataApi.connection.getUriForConnection(connectionResult.connectionId!); return azdataApi.connection.getUriForConnection(connectionResult.connectionId!);
@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import type * as azdataType from 'azdata'; import type * as azdataType from 'azdata';
import * as vscodeMssql from 'vscode-mssql';
import { DataSource } from './dataSources'; import { DataSource } from './dataSources';
import * as constants from '../../common/constants'; import * as constants from '../../common/constants';
@@ -44,11 +45,11 @@ export class SqlConnectionDataSource extends DataSource {
public get authType(): string { public get authType(): string {
if (this.azureMFA) { if (this.azureMFA) {
return constants.azureMfaAuth; return vscodeMssql.AuthenticationType.AzureMFA;
} else if (this.integratedSecurity) { } else if (this.integratedSecurity) {
return constants.integratedAuth; return vscodeMssql.AuthenticationType.Integrated;
} else { } else {
return constants.sqlAuth; return 'SqlAuth';
} }
} }
@@ -110,7 +110,7 @@ export const mockConnectionProfile: azdata.IConnectionProfile = {
databaseName: 'My Database', databaseName: 'My Database',
userName: 'My User', userName: 'My User',
password: 'My Pwd', password: 'My Pwd',
authenticationType: 'SqlLogin', authenticationType: azdata.connection.AuthenticationType.SqlLogin,
savePassword: false, savePassword: false,
groupFullName: 'My groupName', groupFullName: 'My groupName',
groupId: 'My GroupId', groupId: 'My GroupId',
@@ -122,7 +122,7 @@ export const mockConnectionProfile: azdata.IConnectionProfile = {
database: 'My Database', database: 'My Database',
user: 'My User', user: 'My User',
password: 'My Pwd', password: 'My Pwd',
authenticationType: 'SqlLogin', authenticationType: azdata.connection.AuthenticationType.SqlLogin,
connectionName: 'My Connection Name' connectionName: 'My Connection Name'
} }
}; };
+4 -9
View File
@@ -50,11 +50,6 @@ export const excludeDatabses: string[] = [
'model' 'model'
]; ];
export enum AuthenticationType {
Integrated = 'Integrated',
SqlLogin = 'SqlLogin'
}
export interface TableInfo { export interface TableInfo {
databaseName: string; databaseName: string;
tableName: string; tableName: string;
@@ -89,14 +84,14 @@ function getSqlDbConnectionProfile(
databaseName: databaseName, databaseName: databaseName,
userName: userName, userName: userName,
password: password, password: password,
authenticationType: AuthenticationType.SqlLogin, authenticationType: azdata.connection.AuthenticationType.SqlLogin,
savePassword: false, savePassword: false,
saveProfile: false, saveProfile: false,
options: { options: {
conectionName: '', conectionName: '',
server: serverName, server: serverName,
database: databaseName, database: databaseName,
authenticationType: AuthenticationType.SqlLogin, authenticationType: azdata.connection.AuthenticationType.SqlLogin,
user: userName, user: userName,
password: password, password: password,
connectionTimeout: 60, connectionTimeout: 60,
@@ -125,7 +120,7 @@ function getConnectionProfile(
azureResourceId: azureResourceId, azureResourceId: azureResourceId,
userName: userName, userName: userName,
password: password, password: password,
authenticationType: AuthenticationType.SqlLogin, authenticationType: azdata.connection.AuthenticationType.SqlLogin,
savePassword: false, savePassword: false,
groupFullName: '', groupFullName: '',
groupId: '', groupId: '',
@@ -134,7 +129,7 @@ function getConnectionProfile(
options: { options: {
conectionName: '', conectionName: '',
server: serverName, server: serverName,
authenticationType: AuthenticationType.SqlLogin, authenticationType: azdata.connection.AuthenticationType.SqlLogin,
user: userName, user: userName,
password: password, password: password,
connectionTimeout: 60, connectionTimeout: 60,
@@ -16,7 +16,6 @@ import * as utils from '../api/utils';
import { logError, TelemetryViews } from '../telemtery'; import { logError, TelemetryViews } from '../telemtery';
import * as styles from '../constants/styles'; import * as styles from '../constants/styles';
import { TableMigrationSelectionDialog } from '../dialog/tableMigrationSelection/tableMigrationSelectionDialog'; import { TableMigrationSelectionDialog } from '../dialog/tableMigrationSelection/tableMigrationSelectionDialog';
import { AuthenticationType } from '../api/sqlUtils';
const WIZARD_TABLE_COLUMN_WIDTH = '200px'; const WIZARD_TABLE_COLUMN_WIDTH = '200px';
const WIZARD_TABLE_COLUMN_WIDTH_SMALL = '170px'; const WIZARD_TABLE_COLUMN_WIDTH_SMALL = '170px';
@@ -728,9 +727,9 @@ export class DatabaseBackupPage extends MigrationWizardPage {
this.migrationStateModel.sourceConnectionId)), query); this.migrationStateModel.sourceConnectionId)), query);
const username = results.rows[0][0].displayValue; const username = results.rows[0][0].displayValue;
this.migrationStateModel._authenticationType = connectionProfile.authenticationType === AuthenticationType.SqlLogin this.migrationStateModel._authenticationType = connectionProfile.authenticationType === azdata.connection.AuthenticationType.SqlLogin
? MigrationSourceAuthenticationType.Sql ? MigrationSourceAuthenticationType.Sql
: connectionProfile.authenticationType === AuthenticationType.Integrated : connectionProfile.authenticationType === azdata.connection.AuthenticationType.Integrated
? MigrationSourceAuthenticationType.Integrated ? MigrationSourceAuthenticationType.Integrated
: undefined!; : undefined!;
this._sourceHelpText.value = constants.SQL_SOURCE_DETAILS( this._sourceHelpText.value = constants.SQL_SOURCE_DETAILS(
@@ -9,7 +9,6 @@ import { MigrationWizardPage } from '../models/migrationWizardPage';
import { MigrationSourceAuthenticationType, MigrationStateModel, StateChangeEvent } from '../models/stateMachine'; import { MigrationSourceAuthenticationType, MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
import * as constants from '../constants/strings'; import * as constants from '../constants/strings';
import { createLabelTextComponent, createHeadingTextComponent, WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController'; import { createLabelTextComponent, createHeadingTextComponent, WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
import { AuthenticationType } from '../api/sqlUtils';
export class SqlSourceConfigurationPage extends MigrationWizardPage { export class SqlSourceConfigurationPage extends MigrationWizardPage {
private _view!: azdata.ModelView; private _view!: azdata.ModelView;
@@ -60,9 +59,9 @@ export class SqlSourceConfigurationPage extends MigrationWizardPage {
const query = 'select SUSER_NAME()'; const query = 'select SUSER_NAME()';
const results = await queryProvider.runQueryAndReturn(await (azdata.connection.getUriForConnection(this.migrationStateModel.sourceConnectionId)), query); const results = await queryProvider.runQueryAndReturn(await (azdata.connection.getUriForConnection(this.migrationStateModel.sourceConnectionId)), query);
const username = results.rows[0][0].displayValue; const username = results.rows[0][0].displayValue;
this.migrationStateModel._authenticationType = connectionProfile.authenticationType === AuthenticationType.SqlLogin this.migrationStateModel._authenticationType = connectionProfile.authenticationType === azdata.connection.AuthenticationType.SqlLogin
? MigrationSourceAuthenticationType.Sql ? MigrationSourceAuthenticationType.Sql
: connectionProfile.authenticationType === AuthenticationType.Integrated : connectionProfile.authenticationType === azdata.connection.AuthenticationType.Integrated
? MigrationSourceAuthenticationType.Integrated ? MigrationSourceAuthenticationType.Integrated
: undefined!; : undefined!;
+31 -1
View File
@@ -122,11 +122,41 @@ declare module 'vscode-mssql' {
getServerInfo(connectionInfo: IConnectionInfo): ServerInfo getServerInfo(connectionInfo: IConnectionInfo): ServerInfo
} }
/**
* Well-known Authentication types.
*/
export const enum AuthenticationType {
/**
* Username and password
*/
SqlLogin = 'SqlLogin',
/**
* Windows Authentication
*/
Integrated = 'Integrated',
/**
* Azure Active Directory - Universal with MFA support
*/
AzureMFA = 'AzureMFA',
/**
* Azure Active Directory - Password
*/
AzureMFAAndUser = 'AzureMFAAndUser',
/**
* Datacenter Security Token Service Authentication
*/
DSTSAuth = 'dstsAuth',
/**
* No authentication required
*/
None = 'None'
}
/** /**
* The possible values of the server engine edition * The possible values of the server engine edition
* EngineEdition under https://docs.microsoft.com/sql/t-sql/functions/serverproperty-transact-sql is associated with these values * EngineEdition under https://docs.microsoft.com/sql/t-sql/functions/serverproperty-transact-sql is associated with these values
*/ */
export const enum DatabaseEngineEdition { export const enum DatabaseEngineEdition {
Unknown = 0, Unknown = 0,
Personal = 1, Personal = 1,
Standard = 2, Standard = 2,
+36 -3
View File
@@ -399,15 +399,48 @@ declare module 'azdata' {
title: string; title: string;
} }
/*
* Add optional azureAccount for connectionWidget.
*/
export interface IConnectionProfile extends ConnectionInfo { export interface IConnectionProfile extends ConnectionInfo {
/**
* The type of authentication to use when connecting
*/
authenticationType: string | connection.AuthenticationType;
azureAccount?: string; azureAccount?: string;
azureResourceId?: string; azureResourceId?: string;
azurePortalEndpoint?: string; azurePortalEndpoint?: string;
} }
export namespace connection {
/**
* Well-known Authentication types commonly supported by connection providers.
*/
export enum AuthenticationType {
/**
* Username and password
*/
SqlLogin = 'SqlLogin',
/**
* Windows Authentication
*/
Integrated = 'Integrated',
/**
* Azure Active Directory - Universal with MFA support
*/
AzureMFA = 'AzureMFA',
/**
* Azure Active Directory - Password
*/
AzureMFAAndUser = 'AzureMFAAndUser',
/**
* Datacenter Security Token Service Authentication
*/
DSTSAuth = 'dstsAuth',
/**
* No authentication required
*/
None = 'None'
}
}
/* /*
* Add optional per-OS default value. * Add optional per-OS default value.
*/ */
@@ -9,6 +9,7 @@ import { ConnectionConfig } from 'sql/platform/connection/common/connectionConfi
import { fixupConnectionCredentials } from 'sql/platform/connection/common/connectionInfo'; import { fixupConnectionCredentials } from 'sql/platform/connection/common/connectionInfo';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup'; import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import { AuthenticationType } from 'sql/platform/connection/common/constants';
import { IConnectionProfile, ProfileMatcher } from 'sql/platform/connection/common/interfaces'; import { IConnectionProfile, ProfileMatcher } from 'sql/platform/connection/common/interfaces';
import { ICredentialsService } from 'sql/platform/credentials/common/credentialsService'; import { ICredentialsService } from 'sql/platform/credentials/common/credentialsService';
import { isDisposable } from 'vs/base/common/lifecycle'; import { isDisposable } from 'vs/base/common/lifecycle';
@@ -90,9 +91,9 @@ export class ConnectionStore {
} }
return { profile: credentialsItem, savedCred: !!savedCred }; return { profile: credentialsItem, savedCred: !!savedCred };
}); });
} else if (credentialsItem.authenticationType === 'AzureMFA' || credentialsItem.authenticationType === 'dstsAuth' && credentialsItem.azureAccount) { } else if (credentialsItem.authenticationType === AuthenticationType.AzureMFA || credentialsItem.authenticationType === AuthenticationType.DSTSAuth && credentialsItem.azureAccount) {
return Promise.resolve({ profile: credentialsItem, savedCred: true }); return Promise.resolve({ profile: credentialsItem, savedCred: true });
} else if (credentialsItem.authenticationType === 'None') { } else if (credentialsItem.authenticationType === AuthenticationType.None) {
// Kusto supports no authentication // Kusto supports no authentication
return Promise.resolve({ profile: credentialsItem, savedCred: true }); return Promise.resolve({ profile: credentialsItem, savedCred: true });
} else { } else {
@@ -24,12 +24,35 @@ export const passwordChars = '***************';
/* default authentication type setting name*/ /* default authentication type setting name*/
export const defaultAuthenticationType = 'defaultAuthenticationType'; export const defaultAuthenticationType = 'defaultAuthenticationType';
/* authentication types */ /**
export const sqlLogin = 'SqlLogin'; * Well-known Authentication types commonly supported by connection providers.
export const integrated = 'Integrated'; */
export const azureMFA = 'AzureMFA'; export enum AuthenticationType {
export const azureMFAAndUser = 'AzureMFAAndUser'; /**
export const dstsAuth = 'dstsAuth'; * Username and password
*/
SqlLogin = 'SqlLogin',
/**
* Windows Authentication
*/
Integrated = 'Integrated',
/**
* Azure Active Directory - Universal with MFA support
*/
AzureMFA = 'AzureMFA',
/**
* Azure Active Directory - Password
*/
AzureMFAAndUser = 'AzureMFAAndUser',
/**
* Datacenter Security Token Service Authentication
*/
DSTSAuth = 'dstsAuth',
/**
* No authentication required
*/
None = 'None'
}
/* CMS constants */ /* CMS constants */
export const cmsProviderName = 'MSSQL-CMS'; export const cmsProviderName = 'MSSQL-CMS';
@@ -19,7 +19,6 @@ export class ProviderConnectionInfo extends Disposable implements azdata.Connect
options: { [name: string]: any } = {}; options: { [name: string]: any } = {};
private _providerName?: string; private _providerName?: string;
private static readonly SqlAuthentication = 'SqlLogin';
public static readonly ProviderPropertyName = 'providerName'; public static readonly ProviderPropertyName = 'providerName';
public constructor( public constructor(
@@ -194,7 +193,7 @@ export class ProviderConnectionInfo extends Disposable implements azdata.Connect
option => option.specialValueType === ConnectionOptionSpecialType.password)!; // i guess we are going to assume there is a password field option => option.specialValueType === ConnectionOptionSpecialType.password)!; // i guess we are going to assume there is a password field
let isPasswordRequired = optionMetadata.isRequired; let isPasswordRequired = optionMetadata.isRequired;
if (this.providerName === Constants.mssqlProviderName) { if (this.providerName === Constants.mssqlProviderName) {
isPasswordRequired = this.authenticationType === ProviderConnectionInfo.SqlAuthentication && optionMetadata.isRequired; isPasswordRequired = this.authenticationType === Constants.AuthenticationType.SqlLogin && optionMetadata.isRequired;
} }
return isPasswordRequired; return isPasswordRequired;
} }
@@ -15,7 +15,7 @@ import { TestCredentialsService } from 'sql/platform/credentials/test/common/tes
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService'; import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
import { deepClone, deepFreeze } from 'vs/base/common/objects'; import { deepClone, deepFreeze } from 'vs/base/common/objects';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { mssqlProviderName } from 'sql/platform/connection/common/constants'; import { AuthenticationType, mssqlProviderName } from 'sql/platform/connection/common/constants';
import { ConnectionProviderProperties } from 'sql/platform/capabilities/common/capabilitiesService'; import { ConnectionProviderProperties } from 'sql/platform/capabilities/common/capabilitiesService';
import { InMemoryStorageService } from 'vs/platform/storage/common/storage'; import { InMemoryStorageService } from 'vs/platform/storage/common/storage';
import { generateUuid } from 'vs/base/common/uuid'; import { generateUuid } from 'vs/base/common/uuid';
@@ -25,7 +25,7 @@ suite('ConnectionStore', () => {
connectionName: 'new name', connectionName: 'new name',
serverName: 'namedServer', serverName: 'namedServer',
databaseName: 'bcd', databaseName: 'bcd',
authenticationType: 'SqlLogin', authenticationType: AuthenticationType.SqlLogin,
userName: 'cde', // [SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification="Mock value, never actually used to connect")] userName: 'cde', // [SuppressMessage("Microsoft.Security", "CS001:SecretInline", Justification="Mock value, never actually used to connect")]
password: generateUuid(), password: generateUuid(),
savePassword: true, savePassword: true,
@@ -214,7 +214,7 @@ suite('ConnectionStore', () => {
credentialsService, capabilitiesService); credentialsService, capabilitiesService);
const integratedCred = Object.assign({}, defaultNamedProfile, { const integratedCred = Object.assign({}, defaultNamedProfile, {
serverName: defaultNamedProfile.serverName + 'Integrated', serverName: defaultNamedProfile.serverName + 'Integrated',
authenticationType: 'Integrated', authenticationType: AuthenticationType.Integrated,
userName: '', userName: '',
password: '' password: ''
}); });
@@ -40,6 +40,7 @@ import { ITelemetryEventProperties } from 'sql/platform/telemetry/common/telemet
import { ExtHostAzureBlob } from 'sql/workbench/api/common/extHostAzureBlob'; import { ExtHostAzureBlob } from 'sql/workbench/api/common/extHostAzureBlob';
import { ExtHostAzureAccount } from 'sql/workbench/api/common/extHostAzureAccount'; import { ExtHostAzureAccount } from 'sql/workbench/api/common/extHostAzureAccount';
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
import { AuthenticationType } from 'sql/platform/connection/common/constants';
export interface IAzdataExtensionApiFactory { export interface IAzdataExtensionApiFactory {
(extension: IExtensionDescription): typeof azdata; (extension: IExtensionDescription): typeof azdata;
@@ -106,6 +107,9 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp
// namespace: connection // namespace: connection
const connection: typeof azdata.connection = { const connection: typeof azdata.connection = {
// "azdata" API definition // "azdata" API definition
AuthenticationType: AuthenticationType,
ConnectionProfile: sqlExtHostTypes.ConnectionProfile, ConnectionProfile: sqlExtHostTypes.ConnectionProfile,
getCurrentConnection(): Thenable<azdata.connection.ConnectionProfile> { getCurrentConnection(): Thenable<azdata.connection.ConnectionProfile> {
@@ -291,10 +291,10 @@ export class CommandLineWorkbenchContribution implements IWorkbenchContribution,
*/ */
profile.authenticationType = profile.authenticationType =
args.authenticationType ? args.authenticationType : args.authenticationType ? args.authenticationType :
args.integrated ? Constants.integrated : args.integrated ? Constants.AuthenticationType.Integrated :
args.aad ? Constants.azureMFA : args.aad ? Constants.AuthenticationType.AzureMFA :
(args.user && args.user.length > 0) ? args.user.includes('@') ? Constants.azureMFA : Constants.sqlLogin : (args.user && args.user.length > 0) ? args.user.includes('@') ? Constants.AuthenticationType.AzureMFA : Constants.AuthenticationType.SqlLogin :
Constants.integrated; Constants.AuthenticationType.Integrated;
profile.connectionName = ''; profile.connectionName = '';
profile.setOptionValue('applicationName', Constants.applicationName); profile.setOptionValue('applicationName', Constants.applicationName);
@@ -195,13 +195,13 @@ suite('commandLineService tests', () => {
args.server = 'myserver'; args.server = 'myserver';
args.database = 'mydatabase'; args.database = 'mydatabase';
args.user = 'myuser'; args.user = 'myuser';
args.authenticationType = Constants.sqlLogin; args.authenticationType = Constants.AuthenticationType.SqlLogin;
connectionManagementService.setup((c) => c.showConnectionDialog()).verifiable(TypeMoq.Times.never()); connectionManagementService.setup((c) => c.showConnectionDialog()).verifiable(TypeMoq.Times.never());
connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => true).verifiable(TypeMoq.Times.atMostOnce()); connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => true).verifiable(TypeMoq.Times.atMostOnce());
connectionManagementService.setup(c => c.getConnectionGroups(TypeMoq.It.isAny())).returns(() => []); connectionManagementService.setup(c => c.getConnectionGroups(TypeMoq.It.isAny())).returns(() => []);
let originalProfile: IConnectionProfile = undefined; let originalProfile: IConnectionProfile = undefined;
connectionManagementService.setup(c => c.connectIfNotConnected(TypeMoq.It.is<ConnectionProfile>(p => p.serverName === 'myserver' && p.authenticationType === Constants.sqlLogin), 'connection', true)) connectionManagementService.setup(c => c.connectIfNotConnected(TypeMoq.It.is<ConnectionProfile>(p => p.serverName === 'myserver' && p.authenticationType === Constants.AuthenticationType.SqlLogin), 'connection', true))
.returns((conn) => { .returns((conn) => {
originalProfile = conn; originalProfile = conn;
return Promise.resolve('unused'); return Promise.resolve('unused');
@@ -308,7 +308,7 @@ suite('commandLineService tests', () => {
connectionManagementService.setup((c) => c.showConnectionDialog()).verifiable(TypeMoq.Times.never()); connectionManagementService.setup((c) => c.showConnectionDialog()).verifiable(TypeMoq.Times.never());
connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => true).verifiable(TypeMoq.Times.atMostOnce()); connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => true).verifiable(TypeMoq.Times.atMostOnce());
let originalProfile: IConnectionProfile = undefined; let originalProfile: IConnectionProfile = undefined;
connectionManagementService.setup(c => c.connectIfNotConnected(TypeMoq.It.is<ConnectionProfile>(p => p.serverName === 'myserver' && p.authenticationType === Constants.integrated), 'connection', true)) connectionManagementService.setup(c => c.connectIfNotConnected(TypeMoq.It.is<ConnectionProfile>(p => p.serverName === 'myserver' && p.authenticationType === Constants.AuthenticationType.Integrated), 'connection', true))
.returns((conn) => { .returns((conn) => {
originalProfile = conn; originalProfile = conn;
return Promise.resolve('unused'); return Promise.resolve('unused');
@@ -333,7 +333,7 @@ suite('commandLineService tests', () => {
groupFullName: 'testGroup', groupFullName: 'testGroup',
serverName: 'myserver', serverName: 'myserver',
databaseName: 'mydatabase', databaseName: 'mydatabase',
authenticationType: Constants.integrated, authenticationType: Constants.AuthenticationType.Integrated,
password: undefined, password: undefined,
userName: '', userName: '',
groupId: undefined, groupId: undefined,
@@ -351,7 +351,7 @@ suite('commandLineService tests', () => {
connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => true).verifiable(TypeMoq.Times.atMostOnce()); connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => true).verifiable(TypeMoq.Times.atMostOnce());
let originalProfile: IConnectionProfile = undefined; let originalProfile: IConnectionProfile = undefined;
connectionManagementService.setup(c => c.connectIfNotConnected( connectionManagementService.setup(c => c.connectIfNotConnected(
TypeMoq.It.is<ConnectionProfile>(p => p.serverName === 'myserver' && p.authenticationType === Constants.integrated && p.connectionName === 'Test' && p.id === 'testID'), 'connection', true)) TypeMoq.It.is<ConnectionProfile>(p => p.serverName === 'myserver' && p.authenticationType === Constants.AuthenticationType.Integrated && p.connectionName === 'Test' && p.id === 'testID'), 'connection', true))
.returns((conn) => { .returns((conn) => {
originalProfile = conn; originalProfile = conn;
return Promise.resolve('unused'); return Promise.resolve('unused');
@@ -373,13 +373,13 @@ suite('commandLineService tests', () => {
args.server = 'myserver'; args.server = 'myserver';
args.database = 'mydatabase'; args.database = 'mydatabase';
args.user = 'myuser'; args.user = 'myuser';
args.authenticationType = Constants.sqlLogin; args.authenticationType = Constants.AuthenticationType.SqlLogin;
args._ = ['c:\\dir\\file.sql']; args._ = ['c:\\dir\\file.sql'];
connectionManagementService.setup((c) => c.showConnectionDialog()).verifiable(TypeMoq.Times.never()); connectionManagementService.setup((c) => c.showConnectionDialog()).verifiable(TypeMoq.Times.never());
connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => true).verifiable(TypeMoq.Times.atMostOnce()); connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => true).verifiable(TypeMoq.Times.atMostOnce());
connectionManagementService.setup(c => c.getConnectionGroups(TypeMoq.It.isAny())).returns(() => []); connectionManagementService.setup(c => c.getConnectionGroups(TypeMoq.It.isAny())).returns(() => []);
let originalProfile: IConnectionProfile = undefined; let originalProfile: IConnectionProfile = undefined;
connectionManagementService.setup(c => c.connectIfNotConnected(TypeMoq.It.is<ConnectionProfile>(p => p.serverName === 'myserver' && p.authenticationType === Constants.sqlLogin), 'connection', true)) connectionManagementService.setup(c => c.connectIfNotConnected(TypeMoq.It.is<ConnectionProfile>(p => p.serverName === 'myserver' && p.authenticationType === Constants.AuthenticationType.SqlLogin), 'connection', true))
.returns((conn) => { .returns((conn) => {
originalProfile = conn; originalProfile = conn;
return Promise.resolve('unused'); return Promise.resolve('unused');
@@ -400,7 +400,7 @@ suite('commandLineService tests', () => {
const editorService: TypeMoq.Mock<IEditorService> = TypeMoq.Mock.ofType<IEditorService>(TestEditorService, TypeMoq.MockBehavior.Strict); const editorService: TypeMoq.Mock<IEditorService> = TypeMoq.Mock.ofType<IEditorService>(TestEditorService, TypeMoq.MockBehavior.Strict);
editorService.setup(e => e.editors).returns(() => [queryInput]); editorService.setup(e => e.editors).returns(() => [queryInput]);
connectionManagementService.setup(c => connectionManagementService.setup(c =>
c.connect(TypeMoq.It.is<ConnectionProfile>(p => p.serverName === 'myserver' && p.authenticationType === Constants.sqlLogin), c.connect(TypeMoq.It.is<ConnectionProfile>(p => p.serverName === 'myserver' && p.authenticationType === Constants.AuthenticationType.SqlLogin),
uri.toString(), uri.toString(),
TypeMoq.It.is<IConnectionCompletionOptions>(i => i.params.input === queryInput && i.params.connectionType === ConnectionType.editor)) TypeMoq.It.is<IConnectionCompletionOptions>(i => i.params.input === queryInput && i.params.connectionType === ConnectionType.editor))
).verifiable(TypeMoq.Times.once()); ).verifiable(TypeMoq.Times.once());
@@ -447,7 +447,7 @@ suite('commandLineService tests', () => {
connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => true).verifiable(TypeMoq.Times.atMostOnce()); connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => true).verifiable(TypeMoq.Times.atMostOnce());
connectionManagementService.setup(c => c.getConnectionGroups(TypeMoq.It.isAny())).returns(() => []); connectionManagementService.setup(c => c.getConnectionGroups(TypeMoq.It.isAny())).returns(() => []);
let originalProfile: IConnectionProfile = undefined; let originalProfile: IConnectionProfile = undefined;
connectionManagementService.setup(c => c.connectIfNotConnected(TypeMoq.It.is<ConnectionProfile>(p => p.serverName === 'myserver' && p.authenticationType === Constants.sqlLogin), 'connection', true)) connectionManagementService.setup(c => c.connectIfNotConnected(TypeMoq.It.is<ConnectionProfile>(p => p.serverName === 'myserver' && p.authenticationType === Constants.AuthenticationType.SqlLogin), 'connection', true))
.returns((conn) => { .returns((conn) => {
originalProfile = conn; originalProfile = conn;
return Promise.resolve('unused'); return Promise.resolve('unused');
@@ -478,7 +478,7 @@ suite('commandLineService tests', () => {
connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => true).verifiable(TypeMoq.Times.atMostOnce()); connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => true).verifiable(TypeMoq.Times.atMostOnce());
connectionManagementService.setup(c => c.getConnectionGroups(TypeMoq.It.isAny())).returns(() => []); connectionManagementService.setup(c => c.getConnectionGroups(TypeMoq.It.isAny())).returns(() => []);
let originalProfile: IConnectionProfile = undefined; let originalProfile: IConnectionProfile = undefined;
connectionManagementService.setup(c => c.connectIfNotConnected(TypeMoq.It.is<ConnectionProfile>(p => p.serverName === 'myserver' && p.authenticationType === Constants.sqlLogin), 'connection', true)) connectionManagementService.setup(c => c.connectIfNotConnected(TypeMoq.It.is<ConnectionProfile>(p => p.serverName === 'myserver' && p.authenticationType === Constants.AuthenticationType.SqlLogin), 'connection', true))
.returns((conn) => { .returns((conn) => {
originalProfile = conn; originalProfile = conn;
return Promise.resolve('unused'); return Promise.resolve('unused');
@@ -538,7 +538,7 @@ suite('commandLineService tests', () => {
connectionManagementService.setup((c) => c.showConnectionDialog()).verifiable(TypeMoq.Times.never()); connectionManagementService.setup((c) => c.showConnectionDialog()).verifiable(TypeMoq.Times.never());
connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => true).verifiable(TypeMoq.Times.atMostOnce()); connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => true).verifiable(TypeMoq.Times.atMostOnce());
connectionManagementService.setup(c => c.getConnectionGroups(TypeMoq.It.isAny())).returns(() => []); connectionManagementService.setup(c => c.getConnectionGroups(TypeMoq.It.isAny())).returns(() => []);
connectionManagementService.setup(c => c.connectIfNotConnected(TypeMoq.It.is<ConnectionProfile>(p => p.serverName === 'myserver' && p.authenticationType === Constants.sqlLogin), 'connection', true)) connectionManagementService.setup(c => c.connectIfNotConnected(TypeMoq.It.is<ConnectionProfile>(p => p.serverName === 'myserver' && p.authenticationType === Constants.AuthenticationType.SqlLogin), 'connection', true))
.returns((conn) => { .returns((conn) => {
return Promise.resolve('unused'); return Promise.resolve('unused');
}) })
@@ -15,8 +15,6 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService'; import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { integrated, azureMFA } from 'sql/platform/connection/common/constants';
import { AuthenticationType } from 'sql/workbench/services/connection/browser/connectionWidget';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { ConnectionViewletPanel } from 'sql/workbench/contrib/dataExplorer/browser/connectionViewletPanel'; import { ConnectionViewletPanel } from 'sql/workbench/contrib/dataExplorer/browser/connectionViewletPanel';
@@ -31,6 +29,7 @@ workbenchRegistry.registerWorkbenchContribution(ConnectionStatusbarItem, Lifecyc
import 'sql/workbench/contrib/connection/common/connectionTreeProviderExentionPoint'; import 'sql/workbench/contrib/connection/common/connectionTreeProviderExentionPoint';
import { ServerTreeViewView } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService'; import { ServerTreeViewView } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService';
import { AuthenticationType } from 'sql/platform/connection/common/constants';
// Connection Dashboard registration // Connection Dashboard registration
@@ -124,8 +123,8 @@ CommandsRegistry.registerCommand('azdata.connect',
const capabilitiesServices = accessor.get(ICapabilitiesService); const capabilitiesServices = accessor.get(ICapabilitiesService);
const connectionManagementService = accessor.get(IConnectionManagementService); const connectionManagementService = accessor.get(IConnectionManagementService);
if (args && args.serverName && args.providerName if (args && args.serverName && args.providerName
&& (args.authenticationType === integrated && (args.authenticationType === AuthenticationType.Integrated
|| args.authenticationType === azureMFA || args.authenticationType === AuthenticationType.AzureMFA
|| (args.userName && args.password))) { || (args.userName && args.password))) {
const profile: azdata.IConnectionProfile = { const profile: azdata.IConnectionProfile = {
serverName: args.serverName, serverName: args.serverName,
@@ -178,7 +177,7 @@ configurationRegistry.registerConfiguration({
}, },
'sql.defaultAuthenticationType': { 'sql.defaultAuthenticationType': {
'type': 'string', 'type': 'string',
'enum': ['SqlLogin', 'AzureMFA', `AzureMFAAndUser`, 'Integrated'], 'enum': [AuthenticationType.SqlLogin, AuthenticationType.AzureMFA, AuthenticationType.AzureMFAAndUser, AuthenticationType.Integrated],
'description': localize('sql.defaultAuthenticationTypeDescription', "Default authentication type to use when connecting to Azure resources. "), 'description': localize('sql.defaultAuthenticationTypeDescription', "Default authentication type to use when connecting to Azure resources. "),
'enumDescriptions': [ 'enumDescriptions': [
localize('sql.defaultAuthenticationType.SqlLogin', "Sql Login"), localize('sql.defaultAuthenticationType.SqlLogin', "Sql Login"),
@@ -186,7 +185,7 @@ configurationRegistry.registerConfiguration({
localize('sql.defaultAuthenticationType.AzureMFAAndUser', "Azure Active Directory - Password"), localize('sql.defaultAuthenticationType.AzureMFAAndUser', "Azure Active Directory - Password"),
localize('sql.defaultAuthenticationType.Integrated', "Windows Authentication"), localize('sql.defaultAuthenticationType.Integrated', "Windows Authentication"),
], ],
'default': 'AzureMFA' 'default': AuthenticationType.AzureMFA
}, },
'sql.defaultEngine': { 'sql.defaultEngine': {
'type': 'string', 'type': 'string',
@@ -10,7 +10,7 @@ import { IConnectionManagementService } from 'sql/platform/connection/common/con
import { TestConnectionManagementService } from 'sql/platform/connection/test/common/testConnectionManagementService'; import { TestConnectionManagementService } from 'sql/platform/connection/test/common/testConnectionManagementService';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService'; import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
import { mssqlProviderName } from 'sql/platform/connection/common/constants'; import { AuthenticationType, mssqlProviderName } from 'sql/platform/connection/common/constants';
suite('Notebook Contexts', function (): void { suite('Notebook Contexts', function (): void {
const defaultContext = NotebookContexts.DefaultContext; const defaultContext = NotebookContexts.DefaultContext;
@@ -23,7 +23,7 @@ suite('Notebook Contexts', function (): void {
groupFullName: 'testGroup', groupFullName: 'testGroup',
serverName: 'testServerName', serverName: 'testServerName',
databaseName: 'testDatabaseName', databaseName: 'testDatabaseName',
authenticationType: 'integrated', authenticationType: AuthenticationType.Integrated,
password: 'test', password: 'test',
userName: 'testUsername', userName: 'testUsername',
groupId: undefined, groupId: undefined,
@@ -33,7 +33,7 @@ import { TestConnectionManagementService } from 'sql/platform/connection/test/co
import { isUndefinedOrNull } from 'vs/base/common/types'; import { isUndefinedOrNull } from 'vs/base/common/types';
import { NotebookEditorContentLoader } from 'sql/workbench/contrib/notebook/browser/models/notebookInput'; import { NotebookEditorContentLoader } from 'sql/workbench/contrib/notebook/browser/models/notebookInput';
import { SessionManager } from 'sql/workbench/contrib/notebook/test/emptySessionClasses'; import { SessionManager } from 'sql/workbench/contrib/notebook/test/emptySessionClasses';
import { mssqlProviderName } from 'sql/platform/connection/common/constants'; import { AuthenticationType, mssqlProviderName } from 'sql/platform/connection/common/constants';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { uriPrefixes } from 'sql/platform/connection/common/utils'; import { uriPrefixes } from 'sql/platform/connection/common/utils';
import { NullAdsTelemetryService } from 'sql/platform/telemetry/common/adsTelemetryService'; import { NullAdsTelemetryService } from 'sql/platform/telemetry/common/adsTelemetryService';
@@ -1071,7 +1071,7 @@ suite('notebook model', function (): void {
groupFullName: 'testGroup', groupFullName: 'testGroup',
serverName: 'testServerName', serverName: 'testServerName',
databaseName: 'testDatabaseName', databaseName: 'testDatabaseName',
authenticationType: 'integrated', authenticationType: AuthenticationType.Integrated,
password: 'test', password: 'test',
userName: 'testUsername', userName: 'testUsername',
groupId: undefined, groupId: undefined,
@@ -1091,7 +1091,7 @@ suite('notebook model', function (): void {
groupFullName: 'testGroup', groupFullName: 'testGroup',
serverName: 'testServerName', serverName: 'testServerName',
databaseName: 'testDatabaseName', databaseName: 'testDatabaseName',
authenticationType: 'integrated', authenticationType: AuthenticationType.Integrated,
password: 'test', password: 'test',
userName: 'testUsername', userName: 'testUsername',
groupId: undefined, groupId: undefined,
@@ -25,7 +25,7 @@ import { ObjectExplorerActionsContext } from 'sql/workbench/services/objectExplo
import { IConnectionResult, IConnectionParams } from 'sql/platform/connection/common/connectionManagement'; import { IConnectionResult, IConnectionParams } from 'sql/platform/connection/common/connectionManagement';
import { TreeSelectionHandler } from 'sql/workbench/services/objectExplorer/browser/treeSelectionHandler'; import { TreeSelectionHandler } from 'sql/workbench/services/objectExplorer/browser/treeSelectionHandler';
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService'; import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
import { UNSAVED_GROUP_ID, mssqlProviderName } from 'sql/platform/connection/common/constants'; import { UNSAVED_GROUP_ID, mssqlProviderName, AuthenticationType } from 'sql/platform/connection/common/constants';
import { $ } from 'vs/base/browser/dom'; import { $ } from 'vs/base/browser/dom';
import { OEManageConnectionAction } from 'sql/workbench/contrib/dashboard/browser/dashboardActions'; import { OEManageConnectionAction } from 'sql/workbench/contrib/dashboard/browser/dashboardActions';
import { IViewsService, IView, ViewContainerLocation, ViewContainer, IViewPaneContainer } from 'vs/workbench/common/views'; import { IViewsService, IView, ViewContainerLocation, ViewContainer, IViewPaneContainer } from 'vs/workbench/common/views';
@@ -96,7 +96,7 @@ suite('SQL Connection Tree Action tests', () => {
groupFullName: 'testGroup', groupFullName: 'testGroup',
serverName: 'testServerName', serverName: 'testServerName',
databaseName: 'testDatabaseName', databaseName: 'testDatabaseName',
authenticationType: 'integrated', authenticationType: AuthenticationType.Integrated,
password: 'test', password: 'test',
userName: 'testUsername', userName: 'testUsername',
groupId: undefined, groupId: undefined,
@@ -183,7 +183,7 @@ suite('SQL Connection Tree Action tests', () => {
groupFullName: 'testGroup', groupFullName: 'testGroup',
serverName: 'testServerName', serverName: 'testServerName',
databaseName: 'testDatabaseName', databaseName: 'testDatabaseName',
authenticationType: 'integrated', authenticationType: AuthenticationType.Integrated,
password: 'test', password: 'test',
userName: 'testUsername', userName: 'testUsername',
groupId: undefined, groupId: undefined,
@@ -228,7 +228,7 @@ suite('SQL Connection Tree Action tests', () => {
groupFullName: 'testGroup', groupFullName: 'testGroup',
serverName: 'testServerName', serverName: 'testServerName',
databaseName: 'testDatabaseName', databaseName: 'testDatabaseName',
authenticationType: 'integrated', authenticationType: AuthenticationType.Integrated,
password: 'test', password: 'test',
userName: 'testUsername', userName: 'testUsername',
groupId: undefined, groupId: undefined,
@@ -304,7 +304,7 @@ suite('SQL Connection Tree Action tests', () => {
groupFullName: 'testGroup', groupFullName: 'testGroup',
serverName: 'testServerName', serverName: 'testServerName',
databaseName: 'testDatabaseName', databaseName: 'testDatabaseName',
authenticationType: 'integrated', authenticationType: AuthenticationType.Integrated,
password: 'test', password: 'test',
userName: 'testUsername', userName: 'testUsername',
groupId: undefined, groupId: undefined,
@@ -349,7 +349,7 @@ suite('SQL Connection Tree Action tests', () => {
groupFullName: 'testGroup', groupFullName: 'testGroup',
serverName: 'testServerName', serverName: 'testServerName',
databaseName: 'testDatabaseName', databaseName: 'testDatabaseName',
authenticationType: 'integrated', authenticationType: AuthenticationType.Integrated,
password: 'test', password: 'test',
userName: 'testUsername', userName: 'testUsername',
groupId: undefined, groupId: undefined,
@@ -746,7 +746,7 @@ suite('SQL Connection Tree Action tests', () => {
groupFullName: 'testGroup', groupFullName: 'testGroup',
serverName: 'testServerName', serverName: 'testServerName',
databaseName: 'testDatabaseName', databaseName: 'testDatabaseName',
authenticationType: 'integrated', authenticationType: AuthenticationType.Integrated,
password: 'test', password: 'test',
userName: 'testUsername', userName: 'testUsername',
groupId: undefined, groupId: undefined,
@@ -23,7 +23,7 @@ import * as DOM from 'vs/base/browser/dom';
import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IThemeService } from 'vs/platform/theme/common/themeService';
import { OS, OperatingSystem } from 'vs/base/common/platform'; import { OS, OperatingSystem } from 'vs/base/common/platform';
import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
import { ConnectionWidget, AuthenticationType } from 'sql/workbench/services/connection/browser/connectionWidget'; import { ConnectionWidget } from 'sql/workbench/services/connection/browser/connectionWidget';
import { ILogService } from 'vs/platform/log/common/log'; import { ILogService } from 'vs/platform/log/common/log';
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService'; import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
@@ -33,8 +33,8 @@ import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMess
export class CmsConnectionWidget extends ConnectionWidget { export class CmsConnectionWidget extends ConnectionWidget {
private _serverDescriptionInputBox: InputBox; private _serverDescriptionInputBox: InputBox;
protected _authTypeMap: { [providerName: string]: AuthenticationType[] } = { protected _authTypeMap: { [providerName: string]: Constants.AuthenticationType[] } = {
[Constants.cmsProviderName]: [AuthenticationType.SqlLogin, AuthenticationType.Integrated] [Constants.cmsProviderName]: [Constants.AuthenticationType.SqlLogin, Constants.AuthenticationType.Integrated]
}; };
constructor(options: azdata.ConnectionOption[], constructor(options: azdata.ConnectionOption[],
@@ -92,17 +92,17 @@ export class CmsConnectionWidget extends ConnectionWidget {
// True when opening a CMS dialog to add a registered server // True when opening a CMS dialog to add a registered server
if (authTypeChanged) { if (authTypeChanged) {
// Registered Servers only support Integrated Auth // Registered Servers only support Integrated Auth
newAuthTypes = authTypeOption.categoryValues.filter((option) => option.name === AuthenticationType.Integrated); newAuthTypes = authTypeOption.categoryValues.filter((option) => option.name === Constants.AuthenticationType.Integrated);
this._authTypeSelectBox.setOptions(newAuthTypes.map(c => c.displayName)); this._authTypeSelectBox.setOptions(newAuthTypes.map(c => c.displayName));
authTypeOption.defaultValue = AuthenticationType.Integrated; authTypeOption.defaultValue = Constants.AuthenticationType.Integrated;
} else { } else {
// CMS supports all auth types // CMS supports all auth types
newAuthTypes = authTypeOption.categoryValues; newAuthTypes = authTypeOption.categoryValues;
this._authTypeSelectBox.setOptions(newAuthTypes.map(c => c.displayName)); this._authTypeSelectBox.setOptions(newAuthTypes.map(c => c.displayName));
if (OS === OperatingSystem.Windows) { if (OS === OperatingSystem.Windows) {
authTypeOption.defaultValue = this.getAuthTypeDisplayName(AuthenticationType.Integrated); authTypeOption.defaultValue = this.getAuthTypeDisplayName(Constants.AuthenticationType.Integrated);
} else { } else {
authTypeOption.defaultValue = this.getAuthTypeDisplayName(AuthenticationType.SqlLogin); authTypeOption.defaultValue = this.getAuthTypeDisplayName(Constants.AuthenticationType.SqlLogin);
} }
} }
this._authTypeSelectBox.selectWithOptionName(authTypeOption.defaultValue); this._authTypeSelectBox.selectWithOptionName(authTypeOption.defaultValue);
@@ -158,7 +158,7 @@ export class ConnectionDialogService implements IConnectionDialogService {
defaultAuthenticationType = WorkbenchUtils.getSqlConfigValue<string>(this._configurationService, Constants.defaultAuthenticationType); defaultAuthenticationType = WorkbenchUtils.getSqlConfigValue<string>(this._configurationService, Constants.defaultAuthenticationType);
} }
return defaultAuthenticationType || Constants.sqlLogin; // as a fallback, default to sql login if the value from settings is not available return defaultAuthenticationType || Constants.AuthenticationType.SqlLogin; // as a fallback, default to sql login if the value from settings is not available
} }
@@ -865,9 +865,9 @@ export class ConnectionManagementService extends Disposable implements IConnecti
* @param connection The connection to fill in or update * @param connection The connection to fill in or update
*/ */
private async fillInOrClearToken(connection: interfaces.IConnectionProfile): Promise<boolean> { private async fillInOrClearToken(connection: interfaces.IConnectionProfile): Promise<boolean> {
if (connection.authenticationType !== Constants.azureMFA if (connection.authenticationType !== Constants.AuthenticationType.AzureMFA
&& connection.authenticationType !== Constants.azureMFAAndUser && connection.authenticationType !== Constants.AuthenticationType.AzureMFAAndUser
&& connection.authenticationType !== Constants.dstsAuth) { && connection.authenticationType !== Constants.AuthenticationType.DSTSAuth) {
connection.options['azureAccountToken'] = undefined; connection.options['azureAccountToken'] = undefined;
return true; return true;
} }
@@ -875,7 +875,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
let azureResource = this.getAzureResourceForConnection(connection); let azureResource = this.getAzureResourceForConnection(connection);
const accounts = await this._accountManagementService.getAccounts(); const accounts = await this._accountManagementService.getAccounts();
if (connection.authenticationType === Constants.dstsAuth) { if (connection.authenticationType === Constants.AuthenticationType.DSTSAuth) {
let dstsAccounts = accounts.filter(a => a.key.providerId.startsWith('dstsAuth')); let dstsAccounts = accounts.filter(a => a.key.providerId.startsWith('dstsAuth'));
if (dstsAccounts.length <= 0) { if (dstsAccounts.length <= 0) {
connection.options['azureAccountToken'] = undefined; connection.options['azureAccountToken'] = undefined;
@@ -894,7 +894,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
const azureAccounts = accounts.filter(a => a.key.providerId.startsWith('azure')); const azureAccounts = accounts.filter(a => a.key.providerId.startsWith('azure'));
if (azureAccounts && azureAccounts.length > 0) { if (azureAccounts && azureAccounts.length > 0) {
let accountId = (connection.authenticationType === Constants.azureMFA || connection.authenticationType === Constants.azureMFAAndUser) ? connection.azureAccount : connection.userName; let accountId = (connection.authenticationType === Constants.AuthenticationType.AzureMFA || connection.authenticationType === Constants.AuthenticationType.AzureMFAAndUser) ? connection.azureAccount : connection.userName;
let account = azureAccounts.find(account => account.key.accountId === accountId); let account = azureAccounts.find(account => account.key.accountId === accountId);
if (account) { if (account) {
this._logService.debug(`Getting security token for Azure account ${account.key.accountId}`); this._logService.debug(`Getting security token for Azure account ${account.key.accountId}`);
@@ -35,15 +35,7 @@ import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMess
import Severity from 'vs/base/common/severity'; import Severity from 'vs/base/common/severity';
import { ConnectionStringOptions } from 'sql/platform/capabilities/common/capabilitiesService'; import { ConnectionStringOptions } from 'sql/platform/capabilities/common/capabilitiesService';
import { isFalsyOrWhitespace } from 'vs/base/common/strings'; import { isFalsyOrWhitespace } from 'vs/base/common/strings';
import { AuthenticationType } from 'sql/platform/connection/common/constants';
export enum AuthenticationType {
SqlLogin = 'SqlLogin',
Integrated = 'Integrated',
AzureMFA = 'AzureMFA',
AzureMFAAndUser = 'AzureMFAAndUser',
dSTSAuth = 'dstsAuth',
None = 'None' // Kusto supports no authentication
}
const ConnectionStringText = localize('connectionWidget.connectionString', "Connection string"); const ConnectionStringText = localize('connectionWidget.connectionString', "Connection string");
@@ -83,7 +75,7 @@ export class ConnectionWidget extends lifecycle.Disposable {
protected _databaseNameInputBox: Dropdown; protected _databaseNameInputBox: Dropdown;
protected _advancedButton: Button; protected _advancedButton: Button;
private static readonly _authTypes: AuthenticationType[] = private static readonly _authTypes: AuthenticationType[] =
[AuthenticationType.AzureMFA, AuthenticationType.AzureMFAAndUser, AuthenticationType.Integrated, AuthenticationType.SqlLogin, AuthenticationType.dSTSAuth, AuthenticationType.None]; [AuthenticationType.AzureMFA, AuthenticationType.AzureMFAAndUser, AuthenticationType.Integrated, AuthenticationType.SqlLogin, AuthenticationType.DSTSAuth, AuthenticationType.None];
private static readonly _osByName = { private static readonly _osByName = {
Windows: OperatingSystem.Windows, Windows: OperatingSystem.Windows,
Macintosh: OperatingSystem.Macintosh, Macintosh: OperatingSystem.Macintosh,
@@ -528,7 +520,7 @@ export class ConnectionWidget extends lifecycle.Disposable {
// Immediately show/hide appropriate elements though so user gets immediate feedback while we load accounts // Immediately show/hide appropriate elements though so user gets immediate feedback while we load accounts
this._tableContainer.classList.remove('hide-username'); this._tableContainer.classList.remove('hide-username');
this._tableContainer.classList.remove('hide-azure-accounts'); this._tableContainer.classList.remove('hide-azure-accounts');
} else if (currentAuthType === AuthenticationType.dSTSAuth) { } else if (currentAuthType === AuthenticationType.DSTSAuth) {
this._accountManagementService.getAccountsForProvider('dstsAuth').then(accounts => { this._accountManagementService.getAccountsForProvider('dstsAuth').then(accounts => {
if (accounts && accounts.length > 0) { if (accounts && accounts.length > 0) {
accounts[0].key.providerArgs = { accounts[0].key.providerArgs = {
@@ -891,7 +883,7 @@ export class ConnectionWidget extends lifecycle.Disposable {
if (this.authenticationType === AuthenticationType.AzureMFAAndUser || this.authenticationType === AuthenticationType.AzureMFA) { if (this.authenticationType === AuthenticationType.AzureMFAAndUser || this.authenticationType === AuthenticationType.AzureMFA) {
return this._azureAccountDropdown.value; return this._azureAccountDropdown.value;
} }
if (this.authenticationType === AuthenticationType.dSTSAuth) { if (this.authenticationType === AuthenticationType.DSTSAuth) {
return this._token; return this._token;
} }
return undefined; return undefined;
@@ -61,7 +61,7 @@ suite('SQL ConnectionManagementService tests', () => {
databaseName: 'database', databaseName: 'database',
userName: 'user', userName: 'user',
password: 'password', password: 'password',
authenticationType: 'integrated', authenticationType: Constants.AuthenticationType.Integrated,
savePassword: true, savePassword: true,
groupFullName: 'g2/g2-2', groupFullName: 'g2/g2-2',
groupId: 'group id', groupId: 'group id',
@@ -121,7 +121,7 @@ suite('SQL ConnectionManagementService tests', () => {
c => c.serverName === connectionProfileWithEmptyUnsavedPassword.serverName))).returns( c => c.serverName === connectionProfileWithEmptyUnsavedPassword.serverName))).returns(
() => Promise.resolve({ profile: connectionProfileWithEmptyUnsavedPassword, savedCred: false })); () => Promise.resolve({ profile: connectionProfileWithEmptyUnsavedPassword, savedCred: false }));
connectionStore.setup(x => x.isPasswordRequired(TypeMoq.It.isAny())).returns((profile) => { connectionStore.setup(x => x.isPasswordRequired(TypeMoq.It.isAny())).returns((profile) => {
if (profile.authenticationType === Constants.azureMFA) { if (profile.authenticationType === Constants.AuthenticationType.AzureMFA) {
return false; return false;
} }
return true; return true;
@@ -1649,7 +1649,7 @@ suite('SQL ConnectionManagementService tests', () => {
test('addSavedPassword fills in Azure access tokens for Azure accounts', async () => { test('addSavedPassword fills in Azure access tokens for Azure accounts', async () => {
// Set up a connection profile that uses Azure // Set up a connection profile that uses Azure
let azureConnectionProfile = ConnectionProfile.fromIConnectionProfile(capabilitiesService, connectionProfile); let azureConnectionProfile = ConnectionProfile.fromIConnectionProfile(capabilitiesService, connectionProfile);
azureConnectionProfile.authenticationType = 'AzureMFA'; azureConnectionProfile.authenticationType = Constants.AuthenticationType.AzureMFA;
let username = 'testuser@microsoft.com'; let username = 'testuser@microsoft.com';
azureConnectionProfile.azureAccount = username; azureConnectionProfile.azureAccount = username;
let servername = 'test-database.database.windows.net'; let servername = 'test-database.database.windows.net';
@@ -1688,7 +1688,7 @@ suite('SQL ConnectionManagementService tests', () => {
token: testToken, token: testToken,
tokenType: 'Bearer' tokenType: 'Bearer'
})); }));
connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is(profile => profile.authenticationType === 'AzureMFA'))).returns(profile => Promise.resolve({ connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is(profile => profile.authenticationType === Constants.AuthenticationType.AzureMFA))).returns(profile => Promise.resolve({
profile: profile, profile: profile,
savedCred: false savedCred: false
})); }));
@@ -1705,7 +1705,7 @@ suite('SQL ConnectionManagementService tests', () => {
const uri: string = 'Editor Uri'; const uri: string = 'Editor Uri';
// Set up a connection profile that uses Azure // Set up a connection profile that uses Azure
const azureConnectionProfile = ConnectionProfile.fromIConnectionProfile(capabilitiesService, connectionProfile); const azureConnectionProfile = ConnectionProfile.fromIConnectionProfile(capabilitiesService, connectionProfile);
azureConnectionProfile.authenticationType = 'AzureMFA'; azureConnectionProfile.authenticationType = Constants.AuthenticationType.AzureMFA;
const username = 'testuser@microsoft.com'; const username = 'testuser@microsoft.com';
azureConnectionProfile.azureAccount = username; azureConnectionProfile.azureAccount = username;
const servername = 'test-database.database.windows.net'; const servername = 'test-database.database.windows.net';
@@ -1747,7 +1747,7 @@ suite('SQL ConnectionManagementService tests', () => {
]); ]);
}); });
connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is(profile => profile.authenticationType === 'AzureMFA'))).returns(profile => Promise.resolve({ connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is(profile => profile.authenticationType === Constants.AuthenticationType.AzureMFA))).returns(profile => Promise.resolve({
profile: profile, profile: profile,
savedCred: false savedCred: false
})); }));
@@ -1776,7 +1776,7 @@ suite('SQL ConnectionManagementService tests', () => {
test('addSavedPassword fills in Azure access token for selected tenant', async () => { test('addSavedPassword fills in Azure access token for selected tenant', async () => {
// Set up a connection profile that uses Azure // Set up a connection profile that uses Azure
let azureConnectionProfile = ConnectionProfile.fromIConnectionProfile(capabilitiesService, connectionProfile); let azureConnectionProfile = ConnectionProfile.fromIConnectionProfile(capabilitiesService, connectionProfile);
azureConnectionProfile.authenticationType = 'AzureMFA'; azureConnectionProfile.authenticationType = Constants.AuthenticationType.AzureMFA;
let username = 'testuser@microsoft.com'; let username = 'testuser@microsoft.com';
azureConnectionProfile.azureAccount = username; azureConnectionProfile.azureAccount = username;
let servername = 'test-database.database.windows.net'; let servername = 'test-database.database.windows.net';
@@ -1814,7 +1814,7 @@ suite('SQL ConnectionManagementService tests', () => {
let returnedToken = { token: 'testToken', tokenType: 'Bearer' }; let returnedToken = { token: 'testToken', tokenType: 'Bearer' };
accountManagementService.setup(x => x.getAccountSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(returnedToken)); accountManagementService.setup(x => x.getAccountSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(returnedToken));
connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is(profile => profile.authenticationType === 'AzureMFA'))).returns(profile => Promise.resolve({ connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is(profile => profile.authenticationType === Constants.AuthenticationType.AzureMFA))).returns(profile => Promise.resolve({
profile: profile, profile: profile,
savedCred: false savedCred: false
})); }));
@@ -1918,7 +1918,7 @@ export function createConnectionProfile(id: string, password?: string): Connecti
groupFullName: 'testGroup', groupFullName: 'testGroup',
serverName: 'testServerName', serverName: 'testServerName',
databaseName: 'testDatabaseName', databaseName: 'testDatabaseName',
authenticationType: Constants.integrated, authenticationType: Constants.AuthenticationType.Integrated,
password: password ?? 'test', password: password ?? 'test',
userName: 'testUsername', userName: 'testUsername',
groupId: undefined, groupId: undefined,