Fix missing password in Connection pane for Server connections with remembered passwords (#21813)

* Fix missing password in Connection pane

* Get saved password for SQL login default auth type

* Clean up

* Fix build hygiene errors

* Captures input

* Add timeout waiting for all promises to resolve

* Add missing semicolon

* Code review feedback

* Minor clean up

* Code review feedback

* Improved error messaging

* Update src/sql/workbench/services/connection/browser/connectionDialogService.ts

Co-authored-by: Charles Gagnon <chgagnon@microsoft.com>

* Improves UX around loading passwords

* Remove unused import

* Uses await instead of promise chaining.

* Removes async

* Revert back to resolving password promise.

* Asserts controller map and model have values.

---------

Co-authored-by: Charles Gagnon <chgagnon@microsoft.com>
This commit is contained in:
Lewis Sanchez
2023-02-14 20:49:55 -08:00
committed by GitHub
parent a683b1b777
commit d9b24522e5
3 changed files with 44 additions and 12 deletions

View File

@@ -361,15 +361,20 @@ export class ConnectionDialogService implements IConnectionDialogService {
this.uiController.initDialog(this._params && this._params.providers, this._model); this.uiController.initDialog(this._params && this._params.providers, this._model);
} }
private handleFillInConnectionInputs(connectionInfo: IConnectionProfile): void { private async handleFillInConnectionInputs(connectionInfo: IConnectionProfile): Promise<void> {
this._connectionManagementService.addSavedPassword(connectionInfo).then(connectionWithPassword => { try {
const connectionWithPassword = await this.createModel(connectionInfo);
if (this._model) { if (this._model) {
this._model.dispose(); this._model.dispose();
} }
this._model = this.createModel(connectionWithPassword); this._model = connectionWithPassword;
this.uiController.fillInConnectionInputs(this._model); this.uiController.fillInConnectionInputs(this._model);
}).catch(err => onUnexpectedError(err)); }
catch (err) {
this._logService.error(`Error filling in connection inputs with password. Original error message: ${err}`);
}
this._connectionDialog.updateProvider(this._providerNameToDisplayNameMap[connectionInfo.providerName]); this._connectionDialog.updateProvider(this._providerNameToDisplayNameMap[connectionInfo.providerName]);
} }
@@ -381,11 +386,11 @@ export class ConnectionDialogService implements IConnectionDialogService {
this.uiController.handleOnConnecting(); this.uiController.handleOnConnecting();
} }
private updateModelServerCapabilities(model: IConnectionProfile) { private async updateModelServerCapabilities(model: IConnectionProfile) {
if (this._model) { if (this._model) {
this._model.dispose(); this._model.dispose();
} }
this._model = this.createModel(model); this._model = await this.createModel(model);
if (this._model.providerName) { if (this._model.providerName) {
this._currentProviderType = this._model.providerName; this._currentProviderType = this._model.providerName;
if (this._connectionDialog) { if (this._connectionDialog) {
@@ -394,7 +399,7 @@ export class ConnectionDialogService implements IConnectionDialogService {
} }
} }
private createModel(model: IConnectionProfile): ConnectionProfile { private async createModel(model: IConnectionProfile): Promise<ConnectionProfile> {
const defaultProvider = this.getDefaultProviderName(); const defaultProvider = this.getDefaultProviderName();
let providerName = model ? model.providerName : defaultProvider; let providerName = model ? model.providerName : defaultProvider;
providerName = providerName ? providerName : defaultProvider; providerName = providerName ? providerName : defaultProvider;
@@ -410,6 +415,14 @@ export class ConnectionDialogService implements IConnectionDialogService {
} }
let newProfile = new ConnectionProfile(this._capabilitiesService, model || providerName); let newProfile = new ConnectionProfile(this._capabilitiesService, model || providerName);
if (!model.password) {
try {
await this._connectionManagementService.addSavedPassword(newProfile);
}
catch (err) {
this._logService.error(`Error filling in password for connection dialog model. Original error message: ${err}`);
}
}
newProfile.saveProfile = true; newProfile.saveProfile = true;
newProfile.generateNewId(); newProfile.generateNewId();
// If connecting from a query editor set "save connection" to false // If connecting from a query editor set "save connection" to false
@@ -420,7 +433,7 @@ export class ConnectionDialogService implements IConnectionDialogService {
} }
private async showDialogWithModel(): Promise<void> { private async showDialogWithModel(): Promise<void> {
this.updateModelServerCapabilities(this._inputModel); await this.updateModelServerCapabilities(this._inputModel);
await this.doShowDialog(this._params); await this.doShowDialog(this._params);
} }
@@ -459,7 +472,7 @@ export class ConnectionDialogService implements IConnectionDialogService {
this._params = params; this._params = params;
this._inputModel = model; this._inputModel = model;
this.updateModelServerCapabilities(model); await this.updateModelServerCapabilities(model);
// If connecting from a query editor set "save connection" to false // If connecting from a query editor set "save connection" to false
if (params && (params.input && params.connectionType === ConnectionType.editor || if (params && (params.input && params.connectionType === ConnectionType.editor ||

View File

@@ -44,6 +44,7 @@ import { createCSSRule } from 'vs/base/browser/dom';
const ConnectionStringText = localize('connectionWidget.connectionString', "Connection string"); const ConnectionStringText = localize('connectionWidget.connectionString', "Connection string");
export class ConnectionWidget extends lifecycle.Disposable { export class ConnectionWidget extends lifecycle.Disposable {
private _initialConnectionInfo: IConnectionProfile;
private _defaultInputOptionRadioButton: RadioButton; private _defaultInputOptionRadioButton: RadioButton;
private _connectionStringRadioButton: RadioButton; private _connectionStringRadioButton: RadioButton;
private _previousGroupOption: string; private _previousGroupOption: string;
@@ -612,7 +613,7 @@ export class ConnectionWidget extends lifecycle.Disposable {
this._callbacks.onSetConnectButton(shouldEnableConnectButton); this._callbacks.onSetConnectButton(shouldEnableConnectButton);
} }
protected onAuthTypeSelected(selectedAuthType: string) { protected onAuthTypeSelected(selectedAuthType: string): void {
let currentAuthType = this.getMatchingAuthType(selectedAuthType); let currentAuthType = this.getMatchingAuthType(selectedAuthType);
if (currentAuthType !== AuthenticationType.SqlLogin) { if (currentAuthType !== AuthenticationType.SqlLogin) {
if (currentAuthType !== AuthenticationType.AzureMFA && currentAuthType !== AuthenticationType.AzureMFAAndUser) { if (currentAuthType !== AuthenticationType.AzureMFA && currentAuthType !== AuthenticationType.AzureMFAAndUser) {
@@ -668,6 +669,16 @@ export class ConnectionWidget extends lifecycle.Disposable {
this._userNameInputBox.enable(); this._userNameInputBox.enable();
this._passwordInputBox.enable(); this._passwordInputBox.enable();
this._rememberPasswordCheckBox.enabled = true; this._rememberPasswordCheckBox.enabled = true;
this._initialConnectionInfo.authenticationType = AuthenticationType.SqlLogin;
if (this._initialConnectionInfo && this._initialConnectionInfo.userName) {
const setPasswordInputBox = (profile: IConnectionProfile) => {
this._passwordInputBox.value = profile.password;
};
this._rememberPasswordCheckBox.checked = this._initialConnectionInfo.savePassword;
this._connectionManagementService.addSavedPassword(this._initialConnectionInfo, true).then(setPasswordInputBox)
}
} }
} }
@@ -822,6 +833,7 @@ export class ConnectionWidget extends lifecycle.Disposable {
} }
public initDialog(connectionInfo: IConnectionProfile): void { public initDialog(connectionInfo: IConnectionProfile): void {
this._initialConnectionInfo = connectionInfo;
this.fillInConnectionInputs(connectionInfo); this.fillInConnectionInputs(connectionInfo);
} }

View File

@@ -306,9 +306,16 @@ suite('ConnectionDialogService tests', () => {
mockWidget.setup(x => x.fillInConnectionInputs(TypeMoq.It.isAny())).returns(() => { mockWidget.setup(x => x.fillInConnectionInputs(TypeMoq.It.isAny())).returns(() => {
called = true; called = true;
}); });
await connectionDialogService.showDialog(mockConnectionManagementService.object, testConnectionParams, connectionProfile); await connectionDialogService.showDialog(mockConnectionManagementService.object, testConnectionParams, connectionProfile);
await (connectionDialogService as any).handleFillInConnectionInputs(connectionProfile); await (connectionDialogService as any).handleFillInConnectionInputs(connectionProfile);
let returnedModel = ((connectionDialogService as any)._connectionControllerMap['MSSQL'] as any)._model;
let connectionControllerMap = (connectionDialogService as any)._connectionControllerMap;
assert(!!connectionControllerMap);
let returnedModel = (connectionControllerMap['MSSQL'] as any)._model;
assert(!!returnedModel);
assert.strictEqual(returnedModel._groupName, 'testGroup'); assert.strictEqual(returnedModel._groupName, 'testGroup');
assert(called); assert(called);
}); });