diff --git a/src/sql/azdata.d.ts b/src/sql/azdata.d.ts index e31b09608d..d5d456d9b2 100644 --- a/src/sql/azdata.d.ts +++ b/src/sql/azdata.d.ts @@ -132,7 +132,7 @@ declare module 'azdata' { export function getConnectionString(connectionId: string, includePassword: boolean): Thenable; /** - * Get the credentials for an active connection + * Get the credentials for a connection * @param connectionId The id of the connection * @returns A dictionary containing the credentials as they would be included in the connection's options dictionary */ diff --git a/src/sql/platform/connection/common/connectionManagement.ts b/src/sql/platform/connection/common/connectionManagement.ts index ad6a814ea2..5649910acb 100644 --- a/src/sql/platform/connection/common/connectionManagement.ts +++ b/src/sql/platform/connection/common/connectionManagement.ts @@ -268,12 +268,12 @@ export interface IConnectionManagementService { removeConnectionProfileCredentials(profile: IConnectionProfile): IConnectionProfile; /** - * Get the credentials for a connected connection profile, as they would appear in the options dictionary + * Get the credentials for a connection profile, as they would appear in the options dictionary * @param profileId The id of the connection profile to get the password for * @returns A dictionary containing the credentials as they would be included - * in the connection profile's options dictionary, or undefined if the profile is not connected + * in the connection profile's options dictionary, or undefined if the profile was not found */ - getActiveConnectionCredentials(profileId: string): { [name: string]: string }; + getConnectionCredentials(profileId: string): Promise<{ [name: string]: string }>; /** * Get the ServerInfo for a connected connection profile diff --git a/src/sql/platform/connection/test/common/testConnectionManagementService.ts b/src/sql/platform/connection/test/common/testConnectionManagementService.ts index 2a358c79d9..52edf4001b 100644 --- a/src/sql/platform/connection/test/common/testConnectionManagementService.ts +++ b/src/sql/platform/connection/test/common/testConnectionManagementService.ts @@ -259,8 +259,8 @@ export class TestConnectionManagementService implements IConnectionManagementSer return undefined!; } - getActiveConnectionCredentials(profileId: string): { [name: string]: string } { - return undefined!; + getConnectionCredentials(profileId: string): Promise<{ [name: string]: string }> { + return Promise.resolve(undefined); } getServerInfo(profileId: string): azdata.ServerInfo { diff --git a/src/sql/workbench/api/browser/mainThreadConnectionManagement.ts b/src/sql/workbench/api/browser/mainThreadConnectionManagement.ts index b51f65879b..73ec39b27a 100644 --- a/src/sql/workbench/api/browser/mainThreadConnectionManagement.ts +++ b/src/sql/workbench/api/browser/mainThreadConnectionManagement.ts @@ -121,7 +121,7 @@ export class MainThreadConnectionManagement extends Disposable implements MainTh } public $getCredentials(connectionId: string): Thenable<{ [name: string]: string }> { - return Promise.resolve(this._connectionManagementService.getActiveConnectionCredentials(connectionId)); + return Promise.resolve(this._connectionManagementService.getConnectionCredentials(connectionId)); } public $getServerInfo(connectionId: string): Thenable { diff --git a/src/sql/workbench/services/connection/browser/connectionManagementService.ts b/src/sql/workbench/services/connection/browser/connectionManagementService.ts index 372203e19f..0a74a7f201 100644 --- a/src/sql/workbench/services/connection/browser/connectionManagementService.ts +++ b/src/sql/workbench/services/connection/browser/connectionManagementService.ts @@ -1357,10 +1357,15 @@ export class ConnectionManagementService extends Disposable implements IConnecti return this._connectionStore.getProfileWithoutPassword(originalProfile); } - public getActiveConnectionCredentials(profileId: string): { [name: string]: string } { + public async getConnectionCredentials(profileId: string): Promise<{ [name: string]: string }> { let profile = find(this.getActiveConnections(), connectionProfile => connectionProfile.id === profileId); if (!profile) { - return undefined; + // Couldn't find an active profile so try all profiles now - fetching the password if we found one + profile = find(this.getConnections(), connectionProfile => connectionProfile.id === profileId); + if (!profile) { + return undefined; + } + await this.addSavedPassword(profile); } // Find the password option for the connection provider diff --git a/src/sql/workbench/services/connection/test/browser/connectionManagementService.test.ts b/src/sql/workbench/services/connection/test/browser/connectionManagementService.test.ts index 5e99000077..12909c2fe2 100644 --- a/src/sql/workbench/services/connection/test/browser/connectionManagementService.test.ts +++ b/src/sql/workbench/services/connection/test/browser/connectionManagementService.test.ts @@ -1199,16 +1199,57 @@ suite('SQL ConnectionManagementService tests', () => { }); }); - test('getActiveConnectionCredentials returns the credentials dictionary for a connection profile', () => { + test('getConnectionCredentials returns the credentials dictionary for an active connection profile', async () => { let profile = assign({}, connectionProfile); profile.options = { password: profile.password }; profile.id = 'test_id'; connectionStatusManager.addConnection(profile, 'test_uri'); (connectionManagementService as any)._connectionStatusManager = connectionStatusManager; - let credentials = connectionManagementService.getActiveConnectionCredentials(profile.id); + let credentials = await connectionManagementService.getConnectionCredentials(profile.id); assert.equal(credentials['password'], profile.options['password']); }); + test('getConnectionCredentials returns the credentials dictionary for a recently used connection profile', async () => { + const test_password = 'test_password'; + const profile = createConnectionProfile('test_id', ''); + const connectionStoreMock = TypeMoq.Mock.ofType(ConnectionStore, TypeMoq.MockBehavior.Loose, new TestStorageService()); + connectionStoreMock.setup(x => x.getRecentlyUsedConnections(undefined)).returns(() => { + return [profile]; + }); + connectionStoreMock.setup(x => x.addSavedPassword(TypeMoq.It.isAny())).returns(async () => { + profile.password = test_password; + return { profile: profile, savedCred: true }; + }); + const connectionManagementService = new ConnectionManagementService(connectionStoreMock.object, undefined, undefined, undefined, undefined, undefined, undefined, new TestCapabilitiesService(), undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, getBasicExtensionService()); + assert.equal(profile.password, '', 'Profile should not have password initially'); + assert.equal(profile.options['password'], '', 'Profile options should not have password initially'); + let credentials = await connectionManagementService.getConnectionCredentials(profile.id); + assert.equal(credentials['password'], test_password); + }); + + test('getConnectionCredentials returns the credentials dictionary for a saved connection profile', async () => { + const test_password = 'test_password'; + const profile = createConnectionProfile('test_id', ''); + const connectionStoreMock = TypeMoq.Mock.ofType(ConnectionStore, TypeMoq.MockBehavior.Loose, new TestStorageService()); + const group1 = createConnectionGroup('group1'); + group1.connections = [profile]; + connectionStoreMock.setup(x => x.getRecentlyUsedConnections(undefined)).returns(() => { + return []; + }); + connectionStoreMock.setup(x => x.getConnectionProfileGroups(TypeMoq.It.isAny(), undefined)).returns(() => { + return [group1]; + }); + connectionStoreMock.setup(x => x.addSavedPassword(TypeMoq.It.isAny())).returns(async () => { + profile.password = test_password; + return { profile: profile, savedCred: true }; + }); + const connectionManagementService = new ConnectionManagementService(connectionStoreMock.object, undefined, undefined, undefined, undefined, undefined, undefined, new TestCapabilitiesService(), undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, getBasicExtensionService()); + assert.equal(profile.password, '', 'Profile should not have password initially'); + assert.equal(profile.options['password'], '', 'Profile options should not have password initially'); + let credentials = await connectionManagementService.getConnectionCredentials(profile.id); + assert.equal(credentials['password'], test_password); + }); + test('getConnectionUriFromId returns a URI of an active connection with the given id', () => { let profile = assign({}, connectionProfile); profile.options = { password: profile.password }; @@ -1428,8 +1469,7 @@ test('clearRecentConnection and ConnectionsList should call connectionStore func assert(called); }); -function createConnectionProfile(id: string): ConnectionProfile { - +function createConnectionProfile(id: string, password?: string): ConnectionProfile { const capabilitiesService = new TestCapabilitiesService(); return new ConnectionProfile(capabilitiesService, { connectionName: 'newName', @@ -1438,7 +1478,7 @@ function createConnectionProfile(id: string): ConnectionProfile { serverName: 'testServerName', databaseName: 'testDatabaseName', authenticationType: Constants.integrated, - password: 'test', + password: password ?? 'test', userName: 'testUsername', groupId: undefined, providerName: Constants.mssqlProviderName,