Skip to content

Commit 195d5d8

Browse files
Fix Connection prompts
1 parent 7158c61 commit 195d5d8

File tree

7 files changed

+73
-50
lines changed

7 files changed

+73
-50
lines changed

src/constants/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export const cmdRefreshObjectExplorerNode = 'mssql.refreshObjectExplorerNode';
4545
export const cmdDisconnectObjectExplorerNode = 'mssql.disconnectObjectExplorerNode';
4646
export const cmdObjectExplorerNodeSignIn = 'mssql.objectExplorerNodeSignIn';
4747
export const cmdConnectObjectExplorerNode = 'mssql.connectObjectExplorerNode';
48+
export const cmdConnectObjectExplorerProfile = 'mssql.connectObjectExplorerProfile';
4849
export const cmdOpenObjectExplorerCommand = 'workbench.view.extension.objectExplorer';
4950
export const cmdScriptSelect = 'mssql.scriptSelect';
5051
export const cmdScriptCreate = 'mssql.scriptCreate';
@@ -94,6 +95,7 @@ export const renamedOpenTimeThreshold = 10.0;
9495
export const timeToWaitForLanguageModeChange = 10000.0;
9596
export const macOpenSslHelpLink = 'https://github.com/Microsoft/vscode-mssql/wiki/OpenSSL-Configuration';
9697
export const gettingStartedGuideLink = 'https://aka.ms/mssql-getting-started';
98+
export const encryptionReadMoreLink = 'https://learn.microsoft.com/sql/database-engine/configure-windows/enable-encrypted-connections-to-the-database-engine';
9799
export const changelogLink = 'https://aka.ms/vscode-mssql-changes';
98100
export const integratedAuthHelpLink = 'https://aka.ms/vscode-mssql-integratedauth';
99101
export const sqlToolsServiceCrashLink = 'https://github.com/Microsoft/vscode-mssql/wiki/SqlToolsService-Known-Issues';

src/controllers/connectionManager.ts

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ export class ConnectionInfo {
7474
}
7575
}
7676

77+
export interface ReconnectAction {
78+
(profile: IConnectionInfo): Promise<void>;
79+
}
80+
7781
// ConnectionManager class is the main controller for connection management
7882
export default class ConnectionManager {
7983
private _statusView: StatusView;
@@ -422,17 +426,28 @@ export default class ConnectionManager {
422426
// Check if the error is an expired password
423427
if (result.errorNumber === Constants.errorPasswordExpired || result.errorNumber === Constants.errorPasswordNeedsReset) {
424428
// TODO: we should allow the user to change their password here once corefx supports SqlConnection.ChangePassword()
425-
Utils.showErrorMsg(Utils.formatString(LocalizedConstants.msgConnectionErrorPasswordExpired, result.errorNumber, result.errorMessage));
429+
Utils.showErrorMsg(Utils.formatString(LocalizedConstants.msgConnectionErrorPasswordExpired,
430+
result.errorNumber, result.errorMessage));
431+
connection.errorNumber = result.errorNumber;
432+
connection.errorMessage = result.errorMessage;
433+
} else if (result.errorNumber === Constants.errorSSLCertificateValidationFailed) {
434+
this.showInstructionTextAsWarning(connection.credentials, async updatedConnection => {
435+
vscode.commands.executeCommand(Constants.cmdConnectObjectExplorerProfile, updatedConnection);
436+
});
437+
return;
426438
} else if (result.errorNumber !== Constants.errorLoginFailed) {
427439
Utils.showErrorMsg(Utils.formatString(LocalizedConstants.msgConnectionError, result.errorNumber, result.errorMessage));
428440
// check whether it's a firewall rule error
429441
let firewallResult = await this.firewallService.handleFirewallRule(result.errorNumber, result.errorMessage);
430442
if (firewallResult.result && firewallResult.ipAddress) {
431443
this._failedUriToFirewallIpMap.set(fileUri, firewallResult.ipAddress);
432444
}
445+
connection.errorNumber = result.errorNumber;
446+
connection.errorMessage = result.errorMessage;
447+
} else {
448+
connection.errorNumber = result.errorNumber;
449+
connection.errorMessage = result.errorMessage;
433450
}
434-
connection.errorNumber = result.errorNumber;
435-
connection.errorMessage = result.errorMessage;
436451
} else {
437452
const platformInfo = await PlatformInformation.getCurrent();
438453
if (!platformInfo.isWindows && result.errorMessage && result.errorMessage.includes('Kerberos')) {
@@ -462,6 +477,27 @@ export default class ConnectionManager {
462477
);
463478
}
464479

480+
public async showInstructionTextAsWarning(profile: IConnectionInfo, reconnectAction: ReconnectAction): Promise<void> {
481+
const instructionText = `${LocalizedConstants.msgPromptSSLCertificateValidationFailed}`;
482+
await this.vscodeWrapper.showWarningMessageAdvanced(instructionText,
483+
{ modal: false },
484+
[
485+
LocalizedConstants.enableTrustServerCertificate,
486+
LocalizedConstants.readMore,
487+
LocalizedConstants.cancel
488+
])
489+
.then(async (selection) => {
490+
if (selection && selection === LocalizedConstants.enableTrustServerCertificate) {
491+
profile.encrypt = 'Mandatory';
492+
profile.trustServerCertificate = true;
493+
await reconnectAction(profile);
494+
} else if (selection && selection === LocalizedConstants.readMore) {
495+
this.vscodeWrapper.openExternal(Constants.encryptionReadMoreLink);
496+
this.showInstructionTextAsWarning(profile, reconnectAction);
497+
}
498+
});
499+
}
500+
465501
private async tryAddMruConnection(connection: ConnectionInfo, newConnection: IConnectionInfo): Promise<void> {
466502
if (newConnection) {
467503
let connectionToSave: IConnectionInfo = Object.assign({}, newConnection);

src/controllers/mainController.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,15 @@ export default class MainController implements vscode.Disposable {
151151

152152
this.initializeObjectExplorer();
153153

154+
this.registerCommandWithArgs(Constants.cmdConnectObjectExplorerProfile);
155+
this._event.on(Constants.cmdConnectObjectExplorerProfile, async (profile: IConnectionProfile) => {
156+
await this.createObjectExplorerSession(profile).then((result) => {
157+
if(result === true){
158+
this._connectionMgr.connectionUI.saveProfile(profile);
159+
}
160+
});
161+
});
162+
154163
this.initializeQueryHistory();
155164

156165
this.sqlTasksService = new SqlTasksService(SqlToolsServerClient.instance, this._untitledSqlDocumentService);
@@ -282,15 +291,17 @@ export default class MainController implements vscode.Disposable {
282291
/**
283292
* Creates a new Object Explorer session
284293
*/
285-
private async createObjectExplorerSession(connectionCredentials?: IConnectionInfo): Promise<void> {
294+
private async createObjectExplorerSession(connectionCredentials?: IConnectionInfo): Promise<boolean> {
286295
let createSessionPromise = new Deferred<TreeNodeInfo>();
287296
const sessionId = await this._objectExplorerProvider.createSession(createSessionPromise, connectionCredentials, this._context);
288297
if (sessionId) {
289298
const newNode = await createSessionPromise;
290299
if (newNode) {
291300
this._objectExplorerProvider.refresh(undefined);
301+
return true;
292302
}
293303
}
304+
return false;
294305
}
295306

296307
/**

src/controllers/vscodeWrapper.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,13 @@ export default class VscodeWrapper {
302302
return vscode.window.showWarningMessage(Constants.extensionName + ': ' + msg, messageOptions, ...items);
303303
}
304304

305+
/**
306+
* Formats and shows a vscode warning message
307+
*/
308+
public openExternal(link: string): Thenable<boolean> {
309+
return vscode.env.openExternal(vscode.Uri.parse(link));
310+
}
311+
305312
/**
306313
* Returns a array of the text editors currently visible in the window
307314
*/

src/models/connectionCredentials.ts

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import * as LocalizedConstants from '../constants/localizedConstants';
7-
import { IConnectionProfile, AuthenticationTypes, EncryptOptions } from './interfaces';
7+
import { IConnectionProfile, AuthenticationTypes } from './interfaces';
88
import { ConnectionStore } from './connectionStore';
99
import * as utils from './utils';
1010
import { QuestionTypes, IQuestion, IPrompter, INameValueChoice } from '../prompts/question';
@@ -172,7 +172,6 @@ export class ConnectionCredentials implements IConnectionInfo {
172172
defaultProfileValues?: IConnectionInfo): Promise<IQuestion[]> {
173173

174174
let authenticationChoices: INameValueChoice[] = ConnectionCredentials.getAuthenticationTypesChoice();
175-
let encryptChoices: INameValueChoice[] = ConnectionCredentials.getEncryptChoices();
176175

177176
let connectionStringSet: () => boolean = () => Boolean(credentials.connectionString);
178177

@@ -252,21 +251,6 @@ export class ConnectionCredentials implements IConnectionInfo {
252251
}
253252
},
254253
default: defaultProfileValues ? await connectionStore.lookupPassword(defaultProfileValues) : undefined
255-
},
256-
// Encrypt may be optionally set.
257-
{
258-
type: QuestionTypes.expand,
259-
name: LocalizedConstants.encryptName,
260-
message: LocalizedConstants.encryptPrompt + ': ' + LocalizedConstants.encryptMandatoryRecommended,
261-
choices: encryptChoices,
262-
shouldPrompt: (answers) =>
263-
!connectionStringSet()
264-
&& utils.isEmpty(credentials.encrypt)
265-
&& authenticationChoices.length > 1,
266-
onAnswered: (value) => {
267-
credentials.encrypt = value;
268-
},
269-
default: EncryptOptions.Mandatory
270254
}
271255
];
272256
return questions;
@@ -334,14 +318,5 @@ export class ConnectionCredentials implements IConnectionInfo {
334318

335319
return choices;
336320
}
337-
338-
public static getEncryptChoices(): INameValueChoice[] {
339-
let choices: INameValueChoice[] = [
340-
{ name: LocalizedConstants.encryptMandatory, value: utils.encryptToString(EncryptOptions.Mandatory) },
341-
{ name: LocalizedConstants.encryptOptional, value: utils.encryptToString(EncryptOptions.Optional) }
342-
];
343-
344-
return choices;
345-
}
346321
}
347322

src/objectExplorer/objectExplorerService.ts

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ export class ObjectExplorerService {
118118
return promise.resolve(node);
119119
} else {
120120
// create session failure
121-
if (self._currentNode.connectionInfo?.password) {
121+
if (self._currentNode?.connectionInfo?.password) {
122122
self._currentNode.connectionInfo.password = '';
123123
}
124124
let error = LocalizedConstants.connectErrorLabel;
@@ -129,32 +129,24 @@ export class ObjectExplorerService {
129129
if (result.errorMessage) {
130130
error += ` : ${result.errorMessage}`;
131131
}
132+
133+
self._connectionManager.vscodeWrapper.showErrorMessage(error);
134+
132135
if (errorNumber === Constants.errorSSLCertificateValidationFailed) {
133-
let instructionText = `${LocalizedConstants.msgPromptSSLCertificateValidationFailed}`;
134-
self._connectionManager.vscodeWrapper.showWarningMessageAdvanced(instructionText,
135-
{ modal: false },
136-
[
137-
LocalizedConstants.enableTrustServerCertificate,
138-
LocalizedConstants.cancel
139-
])
140-
.then(async (selection) => {
141-
if (selection && selection === LocalizedConstants.enableTrustServerCertificate) {
142-
let profile = <IConnectionProfile>self._currentNode.connectionInfo;
143-
profile.encrypt = 'Mandatory';
144-
profile.trustServerCertificate = true;
136+
self._connectionManager.showInstructionTextAsWarning(self._currentNode.connectionInfo,
137+
async updatedProfile => {
138+
self.currentNode.connectionInfo = updatedProfile;
145139
self.updateNode(self._currentNode);
146140
let fileUri = ObjectExplorerUtils.getNodeUri(self._currentNode);
147-
if (await self._connectionManager.connectionStore.saveProfile(profile)) {
148-
const res = await self._connectionManager.connect(fileUri, profile);
149-
if (await self._connectionManager.handleConnectionResult(res, fileUri, profile)) {
141+
if (await self._connectionManager.connectionStore.saveProfile(updatedProfile as IConnectionProfile)) {
142+
const res = await self._connectionManager.connect(fileUri, updatedProfile);
143+
if (await self._connectionManager.handleConnectionResult(res, fileUri, updatedProfile)) {
150144
self.refreshNode(self._currentNode);
151145
}
152146
} else {
153147
self._connectionManager.vscodeWrapper.showErrorMessage(LocalizedConstants.msgPromptProfileUpdateFailed);
154148
}
155-
}
156-
});
157-
self._connectionManager.vscodeWrapper.showErrorMessage(error);
149+
});
158150
}
159151
const promise = self._sessionIdToPromiseMap.get(result.sessionId);
160152

src/views/connectionUI.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ export class ConnectionUI {
542542
/**
543543
* Save a connection profile using the connection store
544544
*/
545-
private async saveProfile(profile: IConnectionProfile): Promise<IConnectionProfile> {
545+
public async saveProfile(profile: IConnectionProfile): Promise<IConnectionProfile> {
546546
return await this._connectionStore.saveProfile(profile);
547547
}
548548

0 commit comments

Comments
 (0)