Migrate cert validation error handling to mssql extension (#21829)

This commit is contained in:
Cheena Malhotra
2023-02-07 09:21:35 -08:00
committed by GitHub
parent e1b35d266a
commit 66410edf02
29 changed files with 352 additions and 92 deletions

View File

@@ -3,5 +3,21 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
// Error code reference comes from here: https://learn.microsoft.com/en-us/sql/relational-databases/errors-events/database-engine-events-and-errors?view=sql-server-ver16
export const MssqlPasswordResetErrorCode: number = 18488;
export const MssqlCertValidationFailedErrorCode: number = -2146893019;
export const MssqlConnectionTelemetryView = 'MssqlConnectionErrorDialog';
export const ConnectionErrorDialogTitle = localize('connectionError', "Connection error");
// Trust Server certificate custom dialog constants.
export const TSC_ActionId = 'enableTrustServerCertificate';
export const TSC_OptionName = 'trustServerCertificate';
export const TSC_EnableTrustServerCert = localize('enableTrustServerCertificate', "Enable Trust server certificate");
export const TSC_InstructionText = localize('trustServerCertInstructionText', `Encryption was enabled on this connection, review your SSL and certificate configuration for the target SQL Server, or enable 'Trust server certificate' in the connection dialog.
Note: A self-signed certificate offers only limited protection and is not a recommended practice for production environments. Do you want to enable 'Trust server certificate' on this connection and retry? `);
export const TSC_ReadMoreLink = "https://learn.microsoft.com/sql/database-engine/configure-windows/enable-encrypted-connections-to-the-database-engine"

View File

@@ -51,13 +51,18 @@ export class ErrorDiagnosticsProvider extends SqlOpsFeature<any> {
}
protected override registerProvider(options: any): Disposable {
let handleConnectionError = async (errorCode: number, errorMessage: string, connection: azdata.connection.ConnectionProfile): Promise<azdata.diagnostics.ConnectionDiagnosticsResult> => {
let handleConnectionError = async (errorInfo: azdata.diagnostics.IErrorInformation, connection: azdata.connection.ConnectionProfile): Promise<azdata.diagnostics.ConnectionDiagnosticsResult> => {
let restoredProfile = this.convertToIConnectionProfile(connection);
if (errorCode === ErrorDiagnosticsConstants.MssqlPasswordResetErrorCode) {
logDebug(`Error Code ${errorCode} requires user to change their password, launching change password dialog.`)
if (errorInfo.errorCode === ErrorDiagnosticsConstants.MssqlPasswordResetErrorCode) {
logDebug(`ErrorDiagnosticsProvider: Error Code ${errorInfo.errorCode} requires user to change their password, launching change password dialog.`);
return await this.handleChangePassword(restoredProfile);
}
logDebug(`No error handler found for errorCode ${errorCode}.`);
else if (errorInfo.errorCode === ErrorDiagnosticsConstants.MssqlCertValidationFailedErrorCode) {
logDebug(`ErrorDiagnosticsProvider: Error Code ${errorInfo.errorCode} indicates certificate validation has failed, launching error dialog with instructionText.`);
return await this.showCertValidationDialog(restoredProfile, errorInfo.errorMessage, errorInfo.messageDetails);
}
logDebug(`ErrorDiagnosticsProvider: No error handler found for errorCode ${errorInfo.errorCode}.`);
return { handled: false };
}
@@ -68,6 +73,43 @@ export class ErrorDiagnosticsProvider extends SqlOpsFeature<any> {
});
}
private async showCertValidationDialog(connection: azdata.IConnectionProfile, errorMessage: string, callStack: string): Promise<azdata.diagnostics.ConnectionDiagnosticsResult> {
try {
let actions: azdata.window.IDialogAction[] = [];
let trustServerCertAction: azdata.window.IDialogAction = {
id: ErrorDiagnosticsConstants.TSC_ActionId,
label: ErrorDiagnosticsConstants.TSC_EnableTrustServerCert,
isPrimary: true
};
actions.push(trustServerCertAction);
const result = await azdata.window.openCustomErrorDialog(
{
severity: azdata.window.MessageLevel.Error,
headerTitle: ErrorDiagnosticsConstants.ConnectionErrorDialogTitle,
message: errorMessage,
messageDetails: callStack,
telemetryView: ErrorDiagnosticsConstants.MssqlConnectionTelemetryView,
instructionText: ErrorDiagnosticsConstants.TSC_InstructionText,
readMoreLink: ErrorDiagnosticsConstants.TSC_ReadMoreLink,
actions: actions
}
);
// Result represents id of action taken by user.
if (result === ErrorDiagnosticsConstants.TSC_ActionId) {
connection.options[ErrorDiagnosticsConstants.TSC_OptionName] = true;
return { handled: true, reconnect: true, options: connection.options };
} else {
return { handled: true, reconnect: false };
}
}
catch (e) {
console.error(`Unexpected exception occurred when showing certificate validation custom dialog: ${e}`);
}
return { handled: false };
}
private async handleChangePassword(connection: azdata.IConnectionProfile): Promise<azdata.diagnostics.ConnectionDiagnosticsResult> {
try {
const result = await azdata.connection.openChangePasswordDialog(connection);
@@ -75,7 +117,7 @@ export class ErrorDiagnosticsProvider extends SqlOpsFeature<any> {
if (result) {
// MSSQL uses 'password' as the option key for connection profile.
connection.options['password'] = result;
return { handled: true, options: connection.options };
return { handled: true, reconnect: true, options: connection.options };
}
}
catch (e) {