From 678b2737bdbe170b1f5dbe66edf8681d5cc9593b Mon Sep 17 00:00:00 2001 From: Aditya Bist Date: Mon, 1 Jul 2019 11:40:11 -0700 Subject: [PATCH] CMS - SQL Login (#5989) * initial SQL Login with save password working * fix switching auth types * remove metadata from package file * allow editing connections for unsaved password connections * review comments * change thenables to async/awaits * review comments * changed thenables to promises * remove authTypeChanged bool * removed unused import * review comments * removed try catches * cr comments * review comments --- extensions/cms/package.json | 4 + extensions/cms/package.nls.json | 1 - .../src/cmsResource/cmsResourceCommands.ts | 116 ++++----- .../cmsResource/tree/cmsResourceTreeNode.ts | 82 +++---- .../cms/src/cmsResource/tree/treeProvider.ts | 15 +- extensions/cms/src/cmsUtils.ts | 221 ++++++++++++------ .../connection/common/connectionProfile.ts | 11 +- .../node/mainThreadConnectionManagement.ts | 3 + .../browser/cmsConnectionController.ts | 4 +- .../connection/browser/cmsConnectionWidget.ts | 54 +++-- .../browser/connectionDialogService.ts | 7 +- .../connection/browser/connectionWidget.ts | 10 +- 12 files changed, 317 insertions(+), 211 deletions(-) diff --git a/extensions/cms/package.json b/extensions/cms/package.json index b6b444e2fc..cba7025fdf 100644 --- a/extensions/cms/package.json +++ b/extensions/cms/package.json @@ -87,6 +87,10 @@ "defaultValue": null, "objectType": null, "categoryValues": [ + { + "displayName": "%cms.connectionOptions.authType.categoryValues.sqlLogin%", + "name": "SqlLogin" + }, { "displayName": "%cms.connectionOptions.authType.categoryValues.integrated%", "name": "Integrated" diff --git a/extensions/cms/package.nls.json b/extensions/cms/package.nls.json index 3705851e3a..9dc673c1d2 100644 --- a/extensions/cms/package.nls.json +++ b/extensions/cms/package.nls.json @@ -12,7 +12,6 @@ "cms.resource.addServerGroup.title": "New Server Group...", "cms.resource.registerCmsServer.title": "Add Central Management Server", "cms.resource.deleteCmsServer.title": "Delete", - "cms.configuration.title": "MSSQL configuration", "cms.query.displayBitAsNumber": "Should BIT columns be displayed as numbers (1 or 0)? If false, BIT columns will be displayed as 'true' or 'false'", "cms.format.alignColumnDefinitionsInColumns": "Should column definitions be aligned?", diff --git a/extensions/cms/src/cmsResource/cmsResourceCommands.ts b/extensions/cms/src/cmsResource/cmsResourceCommands.ts index 5832541dc1..ae68a830f9 100644 --- a/extensions/cms/src/cmsResource/cmsResourceCommands.ts +++ b/extensions/cms/src/cmsResource/cmsResourceCommands.ts @@ -18,40 +18,37 @@ const localize = nls.loadMessageBundle(); export function registerCmsServerCommand(appContext: AppContext, tree: CmsResourceTreeProvider): void { // Create a CMS Server - appContext.apiWrapper.registerCommand('cms.resource.registerCmsServer', async (node?: TreeNode) => { + appContext.apiWrapper.registerCommand('cms.resource.registerCmsServer', async (node?: TreeNode, connectionProfile?: azdata.IConnectionProfile) => { if (node && !(node instanceof CmsResourceEmptyTreeNode)) { return; } - await appContext.cmsUtils.connection.then(async (connection) => { - if (connection && connection.options) { - let registeredCmsServerName = connection.options.registeredServerName ? - connection.options.registeredServerName : connection.options.server; - // check if a CMS with the same name is registered or not - let cachedServers = appContext.cmsUtils.registeredCmsServers; - let serverExists: boolean = false; - if (cachedServers) { - serverExists = cachedServers.some((server) => { - return server.name === registeredCmsServerName; - }); - } - if (!serverExists) { - // remove any group ID if user selects a connection from - // recent connection list - connection.options.groupId = null; - let registeredCmsServerDescription = connection.options.registeredServerDescription; - // remove server description from connection uri - connection.options.registeredCmsServerDescription = null; - let ownerUri = await azdata.connection.getUriForConnection(connection.connectionId); - appContext.cmsUtils.cacheRegisteredCmsServer(registeredCmsServerName, registeredCmsServerDescription, ownerUri, connection); - tree.notifyNodeChanged(undefined); - } else { - // error out for same server name - let errorText = localize('cms.errors.sameCmsServerName', 'Central Management Server Group already has a Registered Server with the name {0}', registeredCmsServerName); - appContext.apiWrapper.showErrorMessage(errorText); - return; - } + let connection = await appContext.cmsUtils.makeConnection(connectionProfile); + if (connection && connection.options) { + let registeredCmsServerName = connection.options.registeredServerName ? + connection.options.registeredServerName : connection.options.server; + // check if a CMS with the same name is registered or not + let cachedServers = appContext.cmsUtils.registeredCmsServers; + let serverExists: boolean = false; + if (cachedServers) { + serverExists = cachedServers.some((server) => { + return server.name === registeredCmsServerName; + }); } - }); + if (!serverExists) { + // remove any group ID if user selects a connection from + // recent connection list + connection.options.groupId = null; + let registeredCmsServerDescription = connection.options.registeredServerDescription; + let ownerUri = await azdata.connection.getUriForConnection(connection.connectionId); + appContext.cmsUtils.cacheRegisteredCmsServer(registeredCmsServerName, registeredCmsServerDescription, ownerUri, connection); + tree.notifyNodeChanged(undefined); + } else { + // error out for same server name + let errorText = localize('cms.errors.sameCmsServerName', 'Central Management Server Group already has a Registered Server with the name {0}', registeredCmsServerName); + appContext.apiWrapper.showErrorMessage(errorText); + throw new Error(errorText); + } + } }); } @@ -61,7 +58,7 @@ export function deleteCmsServerCommand(appContext: AppContext, tree: CmsResource if (!(node instanceof CmsResourceTreeNode)) { return; } - await appContext.cmsUtils.deleteCmsServer(node.name); + await appContext.cmsUtils.deleteCmsServer(node.name, node.connection); tree.isSystemInitialized = false; tree.notifyNodeChanged(undefined); }); @@ -76,16 +73,8 @@ export function addRegisteredServerCommand(appContext: AppContext, tree: CmsReso let relativePath = node instanceof CmsResourceTreeNode ? '' : node.relativePath; let serverName = node instanceof CmsResourceTreeNode ? node.connection.options.registeredServerName === '' ? node.connection.options.server : node.connection.options.registeredServerName : null; - await appContext.cmsUtils.addRegisteredServer(relativePath, node.ownerUri, serverName).then((result) => { - if (result) { - tree.notifyNodeChanged(node); - } - }, (error) => { - // error out - let errorText = localize('cms.errors.addRegisterServerFail', 'Could not add the Registered Server {0}', error); - appContext.apiWrapper.showErrorMessage(errorText); - return; - }); + await appContext.cmsUtils.addRegisteredServer(relativePath, node.ownerUri, serverName); + tree.notifyNodeChanged(node); }); } @@ -95,18 +84,14 @@ export function deleteRegisteredServerCommand(appContext: AppContext, tree: CmsR if (!(node instanceof RegisteredServerTreeNode)) { return; } - appContext.apiWrapper.showWarningMessage( + let result = await appContext.apiWrapper.showWarningMessage( `${localize('cms.confirmDeleteServer', 'Are you sure you want to delete')} ${node.name}?`, localize('cms.yes', 'Yes'), - localize('cms.no', 'No')).then((result) => { - if (result && result === localize('cms.yes', 'Yes')) { - appContext.cmsUtils.removeRegisteredServer(node.name, node.relativePath, node.ownerUri).then((result) => { - if (result) { - tree.notifyNodeChanged(node.parent); - } - }); - } - }); + localize('cms.no', 'No')); + if (result && result === localize('cms.yes', 'Yes')) { + await appContext.cmsUtils.removeRegisteredServer(node.name, node.relativePath, node.ownerUri); + tree.notifyNodeChanged(node.parent); + } }); } @@ -151,22 +136,19 @@ export function addServerGroupCommand(appContext: AppContext, tree: CmsResourceT dialog.content = [mainTab]; azdata.window.openDialog(dialog); let groupExists = false; - dialog.okButton.onClick(() => { + dialog.okButton.onClick(async () => { let path = node instanceof ServerGroupTreeNode ? node.relativePath : ''; if (node.serverGroupNodes.some(node => node.name === serverGroupName)) { groupExists = true; } if (!groupExists) { - appContext.cmsUtils.addServerGroup(serverGroupName, serverDescription, path, node.ownerUri).then((result) => { - if (result) { - tree.notifyNodeChanged(node); - } - }); + await appContext.cmsUtils.addServerGroup(serverGroupName, serverDescription, path, node.ownerUri); + tree.notifyNodeChanged(node); } else { // error out for same server group - let errorText = localize('cms.errors.sameServerGroupName', '{0} already has a Server Group with the name {1}', node.name, serverGroupName); + const errorText = localize('cms.errors.sameServerGroupName', '{0} already has a Server Group with the name {1}', node.name, serverGroupName); appContext.apiWrapper.showErrorMessage(errorText); - return; + throw new Error(errorText); } }); }); @@ -178,18 +160,14 @@ export function deleteServerGroupCommand(appContext: AppContext, tree: CmsResour if (!(node instanceof ServerGroupTreeNode)) { return; } - appContext.apiWrapper.showWarningMessage( + let result = await appContext.apiWrapper.showWarningMessage( `${localize('cms.confirmDeleteGroup', 'Are you sure you want to delete')} ${node.name}?`, localize('cms.yes', 'Yes'), - localize('cms.no', 'No')).then((result) => { - if (result && result === localize('cms.yes', 'Yes')) { - appContext.cmsUtils.removeServerGroup(node.name, node.relativePath, node.ownerUri).then((result) => { - if (result) { - tree.notifyNodeChanged(node.parent); - } - }); - } - }); + localize('cms.no', 'No')); + if (result && result === localize('cms.yes', 'Yes')) { + await appContext.cmsUtils.removeServerGroup(node.name, node.relativePath, node.ownerUri); + tree.notifyNodeChanged(node.parent); + } }); } diff --git a/extensions/cms/src/cmsResource/tree/cmsResourceTreeNode.ts b/extensions/cms/src/cmsResource/tree/cmsResourceTreeNode.ts index 9ba6de8413..b1c15e0546 100644 --- a/extensions/cms/src/cmsResource/tree/cmsResourceTreeNode.ts +++ b/extensions/cms/src/cmsResource/tree/cmsResourceTreeNode.ts @@ -6,7 +6,7 @@ 'use strict'; import * as azdata from 'azdata'; import * as nls from 'vscode-nls'; -import { TreeItemCollapsibleState } from 'vscode'; +import { TreeItemCollapsibleState, TreeItem } from 'vscode'; import { AppContext } from '../../appContext'; import { TreeNode } from '../treeNode'; import { CmsResourceTreeNodeBase } from './baseTreeNodes'; @@ -38,48 +38,52 @@ export class CmsResourceTreeNode extends CmsResourceTreeNodeBase { try { let nodes: CmsResourceTreeNodeBase[] = []; if (!this.ownerUri) { - this._ownerUri = await this.appContext.cmsUtils.getUriForConnection(this.connection); + // Set back password to get ownerUri + if (this.connection.options.authenticationType === 'SqlLogin' && this.connection.options.savePassword === true) { + this.connection.options.password = await this.appContext.cmsUtils.getPassword(this.connection.options.user); + } } return this.appContext.cmsUtils.createCmsServer(this.connection, this.name, this.description).then((result) => { - if (result) { - if (result.registeredServersList) { - result.registeredServersList.forEach((registeredServer) => { - nodes.push(new RegisteredServerTreeNode( - registeredServer.name, - registeredServer.description, - registeredServer.serverName, - registeredServer.relativePath, - this.ownerUri, - this.appContext, - this.treeChangeHandler, this)); - }); - } - if (result.registeredServerGroups) { - if (result.registeredServerGroups) { - this._serverGroupNodes = []; - result.registeredServerGroups.forEach((serverGroup) => { - let serverGroupNode = new ServerGroupTreeNode( - serverGroup.name, - serverGroup.description, - serverGroup.relativePath, - this.ownerUri, - this.appContext, - this.treeChangeHandler, this); - nodes.push(serverGroupNode); - this._serverGroupNodes.push(serverGroupNode); - }); - } - } - if (nodes.length > 0) { - return nodes.sort((node1, node2) => node1.name > node2.name ? 1 : -1); - } else { - return [CmsResourceMessageTreeNode.create(CmsResourceTreeNode.noResourcesLabel, undefined)]; - } + // cache new connection is different from old one + if (this.appContext.cmsUtils.didConnectionChange(this._connection, result.connection)) { + this._connection = result.connection; + this._ownerUri = result.ownerUri; + this.appContext.cmsUtils.cacheRegisteredCmsServer(this.name, this.description, this.ownerUri, this.connection); + } + if (result.listRegisteredServersResult.registeredServersList) { + result.listRegisteredServersResult.registeredServersList.forEach((registeredServer) => { + nodes.push(new RegisteredServerTreeNode( + registeredServer.name, + registeredServer.description, + registeredServer.serverName, + registeredServer.relativePath, + this.ownerUri, + this.appContext, + this.treeChangeHandler, this)); + }); + } + if (result.listRegisteredServersResult.registeredServerGroups) { + this._serverGroupNodes = []; + result.listRegisteredServersResult.registeredServerGroups.forEach((serverGroup) => { + let serverGroupNode = new ServerGroupTreeNode( + serverGroup.name, + serverGroup.description, + serverGroup.relativePath, + this.ownerUri, + this.appContext, + this.treeChangeHandler, this); + nodes.push(serverGroupNode); + this._serverGroupNodes.push(serverGroupNode); + }); + } + if (nodes.length > 0) { + return nodes.sort((node1, node2) => node1.name > node2.name ? 1 : -1); + } else { + return [CmsResourceMessageTreeNode.create(CmsResourceTreeNode.noResourcesLabel, undefined)]; } }, (error) => { - let errorText = localize('cms.errors.expandCmsFail', 'The Central Management Server {0} could not be found or is offline', this.name); - this.appContext.apiWrapper.showErrorMessage(error ? error : errorText); - return []; + this.treeChangeHandler.notifyNodeChanged(undefined); + throw error; }); } catch { return []; diff --git a/extensions/cms/src/cmsResource/tree/treeProvider.ts b/extensions/cms/src/cmsResource/tree/treeProvider.ts index 55f216ea2c..c7ef36d7e0 100644 --- a/extensions/cms/src/cmsResource/tree/treeProvider.ts +++ b/extensions/cms/src/cmsResource/tree/treeProvider.ts @@ -14,6 +14,7 @@ import { CmsResourceEmptyTreeNode } from './cmsResourceEmptyTreeNode'; import { ICmsResourceTreeChangeHandler } from './treeChangeHandler'; import { CmsResourceMessageTreeNode } from '../messageTreeNode'; import { CmsResourceTreeNode } from './cmsResourceTreeNode'; +import { ICmsResourceNodeInfo } from './baseTreeNodes'; export class CmsResourceTreeProvider implements TreeDataProvider, ICmsResourceTreeChangeHandler { @@ -27,7 +28,8 @@ export class CmsResourceTreeProvider implements TreeDataProvider, ICms public async getChildren(element?: TreeNode): Promise { if (element) { - return element.getChildren(true); + let children = await element.getChildren(true); + return children; } if (!this.isSystemInitialized) { @@ -42,11 +44,11 @@ export class CmsResourceTreeProvider implements TreeDataProvider, ICms servers.push(new CmsResourceTreeNode( server.name, server.description, - undefined, + server.ownerUri, server.connection, this._appContext, this, null)); this.appContext.cmsUtils.cacheRegisteredCmsServer(server.name, server.description, - undefined, server.connection); + server.ownerUri, server.connection); }); return servers; } @@ -62,13 +64,6 @@ export class CmsResourceTreeProvider implements TreeDataProvider, ICms let registeredCmsServers = this.appContext.cmsUtils.registeredCmsServers; if (registeredCmsServers && registeredCmsServers.length > 0) { this.isSystemInitialized = true; - // save the CMS Servers for future use - let toSaveCmsServers = JSON.parse(JSON.stringify(registeredCmsServers)); - toSaveCmsServers.forEach(server => { - server.ownerUri = undefined, - server.connection.options.password = ''; - }); - await this._appContext.cmsUtils.setConfiguration(toSaveCmsServers); return registeredCmsServers.map((server) => { return new CmsResourceTreeNode( server.name, diff --git a/extensions/cms/src/cmsUtils.ts b/extensions/cms/src/cmsUtils.ts index 5651f29035..f71a192016 100644 --- a/extensions/cms/src/cmsUtils.ts +++ b/extensions/cms/src/cmsUtils.ts @@ -14,6 +14,14 @@ import { ICmsResourceNodeInfo } from './cmsResource/tree/baseTreeNodes'; const localize = nls.loadMessageBundle(); const cmsProvider: string = 'MSSQL-CMS'; const mssqlProvider: string = 'MSSQL'; +const CredentialNamespace = 'cmsCredentials'; +const sqlLoginAuthType: string = 'SqlLogin'; + +export interface CreateCmsResult { + listRegisteredServersResult: mssql.ListRegisteredServersResult; + connection: azdata.connection.Connection; + ownerUri: string; +} /** * Wrapper class to act as a facade over VSCode and Data APIs and allow us to test / mock callbacks into @@ -24,8 +32,21 @@ const mssqlProvider: string = 'MSSQL'; */ export class CmsUtils { + private _credentialProvider: azdata.CredentialProvider; private _cmsService: mssql.CmsService; - private _registeredCmsServers: ICmsResourceNodeInfo[]; + private _registeredCmsServers: ICmsResourceNodeInfo[] = []; + + public async savePassword(username: string, password: string): Promise { + let provider = await this.credentialProvider(); + let result = await provider.saveCredential(username, password); + return result; + } + + public async getPassword(username: string): Promise { + let provider = await this.credentialProvider(); + let credential = await provider.readCredential(username); + return credential ? credential.password : undefined; + } public showErrorMessage(message: string, ...items: string[]): Thenable { return vscode.window.showErrorMessage(message, ...items); @@ -53,9 +74,10 @@ export class CmsUtils { let ownerUri = await azdata.connection.getUriForConnection(connection.connectionId); if (!ownerUri) { // Make a connection if it's not already connected - await azdata.connection.connect(Utils.toConnectionProfile(connection), false, false).then(async (result) => { + let result = await azdata.connection.connect(Utils.toConnectionProfile(connection), false, false); + if (result) { ownerUri = await azdata.connection.getUriForConnection(result.connectionId); - }); + } } return ownerUri; } @@ -70,59 +92,79 @@ export class CmsUtils { } public async getRegisteredServers(ownerUri: string, relativePath: string): Promise { - return this.getCmsService().then((service) => { - return service.getRegisteredServers(ownerUri, relativePath).then((result) => { - if (result && result.registeredServersList && result.registeredServersList) { - return result; - } - }); - }); + const cmsService = await this.getCmsService(); + const result = await cmsService.getRegisteredServers(ownerUri, relativePath); + if (result && result.registeredServersList && result.registeredServersList) { + return result; + } } public async createCmsServer(connection: azdata.connection.Connection, - name: string, description: string): Promise { + name: string, description: string): Promise { let provider = await this.getCmsService(); connection.providerName = connection.providerName === cmsProvider ? mssqlProvider : connection.providerName; let ownerUri = await azdata.connection.getUriForConnection(connection.connectionId); if (!ownerUri) { // Make a connection if it's not already connected - await azdata.connection.connect(Utils.toConnectionProfile(connection), false, false).then(async (result) => { - ownerUri = await azdata.connection.getUriForConnection(result.connectionId); - }); - } - return provider.createCmsServer(name, description, connection, ownerUri).then((result) => { - if (result) { - return Promise.resolve(result); - } else { - return Promise.reject(null); + let initialConnectionProfile = this.getConnectionProfile(connection); + let result = await azdata.connection.connect(initialConnectionProfile, false, false); + ownerUri = await azdata.connection.getUriForConnection(result.connectionId); + // If the ownerUri is still undefined, then open a connection dialog with the connection + if (!ownerUri) { + let result = await this.makeConnection(initialConnectionProfile); + if (result) { + ownerUri = await azdata.connection.getUriForConnection(result.connectionId); + connection = result; + } } - }); + } + let result = await provider.createCmsServer(name, description, connection, ownerUri); + const createCmsResult: CreateCmsResult = { + listRegisteredServersResult: result, + connection: connection, + ownerUri: ownerUri + }; + return createCmsResult; } - public async deleteCmsServer(cmsServer: any): Promise { + public async deleteCmsServer(cmsServerName: string, connection: azdata.connection.Connection): Promise { let config = this.getConfiguration(); if (config && config.servers) { let newServers = config.servers.filter((cachedServer) => { - return cachedServer.name !== cmsServer; + return cachedServer.name !== cmsServerName; }); await this.setConfiguration(newServers); this._registeredCmsServers = this._registeredCmsServers.filter((cachedServer) => { - return cachedServer.name !== cmsServer; + return cachedServer.name !== cmsServerName; }); } + if (connection.options.authenticationType === sqlLoginAuthType && connection.options.savePassword) { + this._credentialProvider.deleteCredential(connection.options.user); + } } - public cacheRegisteredCmsServer(name: string, description: string, ownerUri: string, connection: azdata.connection.Connection): void { - if (!this._registeredCmsServers) { - this._registeredCmsServers = []; - } + public async cacheRegisteredCmsServer(name: string, description: string, ownerUri: string, connection: azdata.connection.Connection): Promise { let cmsServerNode: ICmsResourceNodeInfo = { name: name, description: description, connection: connection, ownerUri: ownerUri }; + + // update a server if a server with same name exists + this._registeredCmsServers = this._registeredCmsServers.filter((server) => { + return server.name !== name; + }); this._registeredCmsServers.push(cmsServerNode); + + // save the CMS Servers for future use + let toSaveCmsServers: ICmsResourceNodeInfo[] = this._registeredCmsServers.map(server => Object.assign({}, server)); + toSaveCmsServers.forEach(server => { + server.ownerUri = undefined; + // don't save password in config + server.connection.options.password = ''; + }); + await this.setConfiguration(toSaveCmsServers); } public async addRegisteredServer(relativePath: string, ownerUri: string, @@ -143,47 +185,41 @@ export class CmsUtils { providerName: undefined, saveProfile: undefined, id: undefined, - options: {} - }; - return this.openConnectionDialog([cmsProvider], initialProfile, { saveConnection: false }).then(async (connection) => { - if (connection && connection.options) { - if (connection.options.server === parentServerName) { - // error out for same server registration - let errorText = localize('cms.errors.sameServerUnderCms', 'You cannot add a shared registered server with the same name as the Configuration Server'); - this.showErrorMessage(errorText); - return false; - } else { - let registeredServerName = connection.options.registeredServerName === '' ? connection.options.server : connection.options.registeredServerName; - let result = await provider.addRegisteredServer(ownerUri, relativePath, registeredServerName, connection.options.registeredServerDescription, connection); - if (result) { - return Promise.resolve(result); - } else { - return Promise.reject(registeredServerName); - } - } + options: { + authTypeChanged: true } - }); + }; + let connection = await this.openConnectionDialog([cmsProvider], initialProfile, { saveConnection: false }); + if (connection && connection.options) { + if (connection.options.server === parentServerName) { + // error out for same server registration + let errorText = localize('cms.errors.sameServerUnderCms', 'You cannot add a shared registered server with the same name as the Configuration Server'); + this.showErrorMessage(errorText); + throw new Error(errorText); + } else { + let registeredServerName = connection.options.registeredServerName === '' ? connection.options.server : connection.options.registeredServerName; + let result = await provider.addRegisteredServer(ownerUri, relativePath, registeredServerName, connection.options.registeredServerDescription, connection); + return result; + } + } } public async removeRegisteredServer(registeredServerName: string, relativePath: string, ownerUri: string): Promise { let provider = await this.getCmsService(); - return provider.removeRegisteredServer(ownerUri, relativePath, registeredServerName).then((result) => { - return result; - }); + let result = await provider.removeRegisteredServer(ownerUri, relativePath, registeredServerName); + return result; } public async addServerGroup(groupName: string, groupDescription: string, relativePath: string, ownerUri: string): Promise { let provider = await this.getCmsService(); - return provider.addServerGroup(ownerUri, relativePath, groupName, groupDescription).then((result) => { - return result; - }); + let result = await provider.addServerGroup(ownerUri, relativePath, groupName, groupDescription); + return result; } public async removeServerGroup(groupName: string, relativePath: string, ownerUri: string): Promise { let provider = await this.getCmsService(); - return provider.removeServerGroup(ownerUri, relativePath, groupName).then((result) => { - return result; - }); + let result = await provider.removeServerGroup(ownerUri, relativePath, groupName); + return result; } // Getters @@ -191,15 +227,68 @@ export class CmsUtils { return this._registeredCmsServers; } - public get connection(): Thenable { - return this.openConnectionDialog([cmsProvider], undefined, { saveConnection: false }).then((connection) => { - if (connection) { - // remove group ID from connection if a user chose connection - // from the recent connections list - connection.options['groupId'] = null; - connection.providerName = mssqlProvider; - return connection; - } - }); + public async credentialProvider(): Promise { + if (!this._credentialProvider) { + this._credentialProvider = await azdata.credentials.getProvider(CredentialNamespace); + } + return this._credentialProvider; } + + public async makeConnection(initialConnectionProfile?: azdata.IConnectionProfile): Promise { + if (!initialConnectionProfile) { + initialConnectionProfile = { + connectionName: undefined, + serverName: undefined, + databaseName: undefined, + userName: undefined, + password: undefined, + authenticationType: undefined, + savePassword: undefined, + groupFullName: undefined, + groupId: undefined, + providerName: undefined, + saveProfile: undefined, + id: undefined, + options: {} + }; + } + let connection = await this.openConnectionDialog([cmsProvider], initialConnectionProfile, { saveConnection: false }); + if (connection) { + // remove group ID from connection if a user chose connection + // from the recent connections list + connection.options['groupId'] = null; + connection.providerName = mssqlProvider; + if (connection.options.savePassword) { + await this.savePassword(connection.options.user, connection.options.password); + } + return connection; + } + } + + // Static Functions + + public getConnectionProfile(connection: azdata.connection.Connection): azdata.IConnectionProfile { + let connectionProfile: azdata.IConnectionProfile = { + connectionName: connection.options.connectionName, + serverName: connection.options.server, + databaseName: undefined, + userName: connection.options.user, + password: connection.options.password, + authenticationType: connection.options.authenticationType, + savePassword: connection.options.savePassword, + groupFullName: undefined, + groupId: undefined, + providerName: connection.providerName, + saveProfile: false, + id: connection.connectionId, + options: connection.options + }; + return connectionProfile; + } + + public didConnectionChange(connectionA: azdata.connection.Connection, connectionB: azdata.connection.Connection): boolean { + return (connectionA !== connectionB) || ((connectionA.connectionId === connectionB.connectionId) && + (connectionA.options.savePassword !== connectionA.options.savePassword)); + } + } \ No newline at end of file diff --git a/src/sql/platform/connection/common/connectionProfile.ts b/src/sql/platform/connection/common/connectionProfile.ts index 76f1652fc0..dcf6af4afd 100644 --- a/src/sql/platform/connection/common/connectionProfile.ts +++ b/src/sql/platform/connection/common/connectionProfile.ts @@ -53,6 +53,9 @@ export class ConnectionProfile extends ProviderConnectionInfo implements interfa this.options[appNameKey] = Constants.applicationName; } } + if (model.options.registeredServerDescription) { + this.registeredServerDescription = model.options.registeredServerDescription; + } } } else { //Default for a new connection @@ -109,12 +112,12 @@ export class ConnectionProfile extends ProviderConnectionInfo implements interfa this.options['azureTenantId'] = value; } - public get registeredCmsServerDescription(): string { - return this.options['registeredCmsServerDescription']; + public get registeredServerDescription(): string { + return this.options['registeredServerDescription']; } - public set registeredCmsServerDescription(value: string) { - this.options['registeredCmsServerDescription'] = value; + public set registeredServerDescription(value: string) { + this.options['registeredServerDescription'] = value; } public get groupFullName(): string { diff --git a/src/sql/workbench/api/node/mainThreadConnectionManagement.ts b/src/sql/workbench/api/node/mainThreadConnectionManagement.ts index e11c6c01a2..3454dabeee 100644 --- a/src/sql/workbench/api/node/mainThreadConnectionManagement.ts +++ b/src/sql/workbench/api/node/mainThreadConnectionManagement.ts @@ -75,6 +75,9 @@ export class MainThreadConnectionManagement implements MainThreadConnectionManag } let connectionProfile = await this._connectionDialogService.openDialogAndWait(this._connectionManagementService, { connectionType: connectionType, providers: providers }, initialConnectionProfile, undefined); + if (connectionProfile) { + connectionProfile.options.savePassword = connectionProfile.savePassword; + } const connection = connectionProfile ? { connectionId: connectionProfile.id, options: connectionProfile.options, diff --git a/src/sql/workbench/services/connection/browser/cmsConnectionController.ts b/src/sql/workbench/services/connection/browser/cmsConnectionController.ts index 98fef9df02..487add5fb4 100644 --- a/src/sql/workbench/services/connection/browser/cmsConnectionController.ts +++ b/src/sql/workbench/services/connection/browser/cmsConnectionController.ts @@ -36,8 +36,8 @@ export class CmsConnectionController extends ConnectionController { }, providerName); } - public showUiComponent(container: HTMLElement): void { + public showUiComponent(container: HTMLElement, authTypeChanged: boolean = false): void { this._databaseCache = new Map(); - this._connectionWidget.createConnectionWidget(container); + this._connectionWidget.createConnectionWidget(container, authTypeChanged); } } \ No newline at end of file diff --git a/src/sql/workbench/services/connection/browser/cmsConnectionWidget.ts b/src/sql/workbench/services/connection/browser/cmsConnectionWidget.ts index d3de23907c..da0ebbd89e 100644 --- a/src/sql/workbench/services/connection/browser/cmsConnectionWidget.ts +++ b/src/sql/workbench/services/connection/browser/cmsConnectionWidget.ts @@ -35,7 +35,7 @@ export class CmsConnectionWidget extends ConnectionWidget { private _serverDescriptionInputBox: InputBox; protected _authTypeMap: { [providerName: string]: AuthenticationType[] } = { - [Constants.cmsProviderName]: [AuthenticationType.Integrated] + [Constants.cmsProviderName]: [AuthenticationType.SqlLogin, AuthenticationType.Integrated] }; constructor(options: azdata.ConnectionOption[], @@ -53,8 +53,14 @@ export class CmsConnectionWidget extends ConnectionWidget { super(options, callbacks, providerName, _themeService, _contextViewService, _connectionManagementService, _capabilitiesService, _clipboardService, _configurationService, _accountManagementService); let authTypeOption = this._optionsMaps[ConnectionOptionSpecialType.authType]; - authTypeOption.defaultValue = this.getAuthTypeDisplayName(AuthenticationType.Integrated); - this._authTypeSelectBox = new SelectBox(authTypeOption.categoryValues.map(c => c.displayName), authTypeOption.defaultValue, this._contextViewService, undefined, { ariaLabel: authTypeOption.displayName }); + if (authTypeOption) { + if (OS === OperatingSystem.Windows) { + authTypeOption.defaultValue = this.getAuthTypeDisplayName(AuthenticationType.Integrated); + } else { + authTypeOption.defaultValue = this.getAuthTypeDisplayName(AuthenticationType.SqlLogin); + } + this._authTypeSelectBox = new SelectBox(authTypeOption.categoryValues.map(c => c.displayName), authTypeOption.defaultValue, this._contextViewService, undefined, { ariaLabel: authTypeOption.displayName }); + } } protected registerListeners(): void { @@ -64,12 +70,12 @@ export class CmsConnectionWidget extends ConnectionWidget { } } - protected fillInConnectionForm(): void { + protected fillInConnectionForm(authTypeChanged: boolean = false): void { // Server Name this.addServerNameOption(); // Authentication type - this.addAuthenticationTypeOption(); + this.addAuthenticationTypeOption(authTypeChanged); // Login Options this.addLoginOptions(); @@ -84,16 +90,28 @@ export class CmsConnectionWidget extends ConnectionWidget { this.addAdvancedOptions(); } - protected addAuthenticationTypeOption(): void { - super.addAuthenticationTypeOption(); + protected addAuthenticationTypeOption(authTypeChanged: boolean = false): void { + super.addAuthenticationTypeOption(authTypeChanged); let authTypeOption = this._optionsMaps[ConnectionOptionSpecialType.authType]; let newAuthTypes = authTypeOption.categoryValues; - // CMS only supports Integrated Auth - newAuthTypes = authTypeOption.categoryValues.filter((option) => option.name === AuthenticationType.Integrated); - this._authTypeSelectBox.setOptions(newAuthTypes.map(c => c.displayName), 0); - authTypeOption.defaultValue = AuthenticationType.Integrated; - this._authTypeSelectBox.setOptions(authTypeOption.categoryValues.map(c => c.displayName), 1); + // True when opening a CMS dialog to add a registered server + if (authTypeChanged) { + // Registered Servers only support Integrated Auth + newAuthTypes = authTypeOption.categoryValues.filter((option) => option.name === AuthenticationType.Integrated); + this._authTypeSelectBox.setOptions(newAuthTypes.map(c => c.displayName)); + authTypeOption.defaultValue = AuthenticationType.Integrated; + } else { + // CMS supports all auth types + newAuthTypes = authTypeOption.categoryValues; + this._authTypeSelectBox.setOptions(newAuthTypes.map(c => c.displayName)); + if (OS === OperatingSystem.Windows) { + authTypeOption.defaultValue = this.getAuthTypeDisplayName(AuthenticationType.Integrated); + } else { + authTypeOption.defaultValue = this.getAuthTypeDisplayName(AuthenticationType.SqlLogin); + } + } + this._authTypeSelectBox.selectWithOptionName(authTypeOption.defaultValue); } private addServerDescriptionOption(): void { @@ -107,10 +125,10 @@ export class CmsConnectionWidget extends ConnectionWidget { } } - public createConnectionWidget(container: HTMLElement): void { + public createConnectionWidget(container: HTMLElement, authTypeChanged: boolean = false): void { this._container = DOM.append(container, DOM.$('div.connection-table')); this._tableContainer = DOM.append(this._container, DOM.$('table.connection-table-content')); - this.fillInConnectionForm(); + this.fillInConnectionForm(authTypeChanged); this.registerListeners(); if (this._authTypeSelectBox) { this.onAuthTypeSelected(this._authTypeSelectBox.value); @@ -147,4 +165,12 @@ export class CmsConnectionWidget extends ConnectionWidget { } return validInputs; } + + public fillInConnectionInputs(connectionInfo: IConnectionProfile) { + super.fillInConnectionInputs(connectionInfo); + if (connectionInfo) { + let description = connectionInfo.options.registeredServerDescription ? connectionInfo.options.registeredServerDescription : ''; + this._serverDescriptionInputBox.value = description; + } + } } diff --git a/src/sql/workbench/services/connection/browser/connectionDialogService.ts b/src/sql/workbench/services/connection/browser/connectionDialogService.ts index 7d46dc3298..e254561c30 100644 --- a/src/sql/workbench/services/connection/browser/connectionDialogService.ts +++ b/src/sql/workbench/services/connection/browser/connectionDialogService.ts @@ -318,7 +318,12 @@ export class ConnectionDialogService implements IConnectionDialogService { this._model.providerName = this._currentProviderType; this._model = new ConnectionProfile(this._capabilitiesService, this._model); - this.uiController.showUiComponent(input.container); + if (this._inputModel && this._inputModel.options) { + this.uiController.showUiComponent(input.container, + this._inputModel.options.authTypeChanged); + } else { + this.uiController.showUiComponent(input.container); + } } diff --git a/src/sql/workbench/services/connection/browser/connectionWidget.ts b/src/sql/workbench/services/connection/browser/connectionWidget.ts index 020674a986..aa8701ade7 100644 --- a/src/sql/workbench/services/connection/browser/connectionWidget.ts +++ b/src/sql/workbench/services/connection/browser/connectionWidget.ts @@ -123,13 +123,13 @@ export class ConnectionWidget { this._providerName = providerName; } - public createConnectionWidget(container: HTMLElement): void { + public createConnectionWidget(container: HTMLElement, authTypeChanged: boolean = false): void { this._serverGroupOptions = [this.DefaultServerGroup]; this._serverGroupSelectBox = new SelectBox(this._serverGroupOptions.map(g => g.name), this.DefaultServerGroup.name, this._contextViewService, undefined, { ariaLabel: this._serverGroupDisplayString }); this._previousGroupOption = this._serverGroupSelectBox.value; this._container = DOM.append(container, DOM.$('div.connection-table')); this._tableContainer = DOM.append(this._container, DOM.$('table.connection-table-content')); - this.fillInConnectionForm(); + this.fillInConnectionForm(authTypeChanged); this.registerListeners(); if (this._authTypeSelectBox) { this.onAuthTypeSelected(this._authTypeSelectBox.value); @@ -155,12 +155,12 @@ export class ConnectionWidget { } } - protected fillInConnectionForm(): void { + protected fillInConnectionForm(authTypeChanged: boolean = false): void { // Server Name this.addServerNameOption(); // Authentication type - this.addAuthenticationTypeOption(); + this.addAuthenticationTypeOption(authTypeChanged); // Login Options this.addLoginOptions(); @@ -178,7 +178,7 @@ export class ConnectionWidget { this.addAdvancedOptions(); } - protected addAuthenticationTypeOption(): void { + protected addAuthenticationTypeOption(authTypeChanged: boolean = false): void { if (this._optionsMaps[ConnectionOptionSpecialType.authType]) { let authType = DialogHelper.appendRow(this._tableContainer, this._optionsMaps[ConnectionOptionSpecialType.authType].displayName, 'connection-label', 'connection-input'); DialogHelper.appendInputSelectBox(authType, this._authTypeSelectBox);