Update error dialog to include instructions text for SSL cert validation failure (#21036)

This commit is contained in:
Cheena Malhotra
2022-11-01 09:59:50 -07:00
committed by GitHub
parent 1de199dbd6
commit f5a3a9ad8c
7 changed files with 94 additions and 19 deletions

View File

@@ -5244,6 +5244,17 @@ Error: {1}</source>
<trans-unit id="kerberosKinit">
<source xml:lang="en">If you have previously connected you may need to re-run kinit.</source>
</trans-unit>
<trans-unit id="runKinit">
<source xml:lang="en">Run Kinit</source>
</trans-unit>
<trans-unit id="enableTrustServerCertificate">
<source xml:lang="en">Enable Trust server certificate</source>
</trans-unit>
<trans-unit id="trustServerCertInstructionText">
<source xml:lang="en">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? </source>
</trans-unit>
</body></file>
<file original="src/sql/workbench/services/connection/browser/connectionDialogWidget" source-language="en" datatype="plaintext"><body>
<trans-unit id="connectType">

View File

@@ -24,6 +24,9 @@ export const passwordChars = '***************';
/* default authentication type setting name*/
export const defaultAuthenticationType = 'defaultAuthenticationType';
/* Connection Properties */
export const trustServerCertificate = 'trustServerCertificate';
/**
* Well-known Authentication types commonly supported by connection providers.
*/

View File

@@ -10,5 +10,15 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
export const IErrorMessageService = createDecorator<IErrorMessageService>('errorMessageService');
export interface IErrorMessageService {
_serviceBrand: undefined;
showDialog(severity: Severity, headerTitle: string, message: string, messageDetails?: string, actions?: IAction[]): void;
/**
* Shows error dialog with given parameters
* @param severity Severity of the error
* @param headerTitle Title to show on Error modal dialog
* @param message Message containng error message
* @param messageDetails Message details containing stacktrace along with error message
* @param actions Custom actions to display on the error message dialog
* @param instructionText Spcial instructions to display to user when displaying error message
* @param readMoreLink External link to read more about the instructions.
*/
showDialog(severity: Severity, headerTitle: string, message: string, messageDetails?: string, actions?: IAction[], instructionText?: string, readMoreLink?: string): void;
}

View File

@@ -270,7 +270,7 @@ export class ConnectionDialogService implements IConnectionDialogService {
this._logService.debug(`ConnectionDialogService: Error handled and connection reset - Error: ${connectionResult.errorMessage}`);
} else {
this._connectionDialog.resetConnection();
this.showErrorDialog(Severity.Error, this._connectionErrorTitle, connectionResult.errorMessage, connectionResult.callStack);
this.showErrorDialog(Severity.Error, this._connectionErrorTitle, connectionResult.errorMessage, connectionResult.callStack, connectionResult.errorCode);
this._logService.debug(`ConnectionDialogService: Connection error: ${connectionResult.errorMessage}`);
}
} catch (err) {
@@ -456,7 +456,7 @@ export class ConnectionDialogService implements IConnectionDialogService {
await this.showDialogWithModel();
if (connectionResult && connectionResult.errorMessage) {
this.showErrorDialog(Severity.Error, this._connectionErrorTitle, connectionResult.errorMessage, connectionResult.callStack);
this.showErrorDialog(Severity.Error, this._connectionErrorTitle, connectionResult.errorMessage, connectionResult.callStack, connectionResult.errorCode);
}
}
@@ -488,7 +488,7 @@ export class ConnectionDialogService implements IConnectionDialogService {
recentConnections.forEach(conn => conn.dispose());
}
private showErrorDialog(severity: Severity, headerTitle: string, message: string, messageDetails?: string): void {
private showErrorDialog(severity: Severity, headerTitle: string, message: string, messageDetails?: string, errorCode?: number): void {
// Kerberos errors are currently very hard to understand, so adding handling of these to solve the common scenario
// note that ideally we would have an extensible service to handle errors by error code and provider, but for now
// this solves the most common "hard error" that we've noticed
@@ -502,7 +502,7 @@ export class ConnectionDialogService implements IConnectionDialogService {
localize('kerberosHelpLink', "Help configuring Kerberos is available at {0}", helpLink),
localize('kerberosKinit', "If you have previously connected you may need to re-run kinit.")
].join('\r\n');
actions.push(new Action('Kinit', 'Run kinit', null, true, async () => {
actions.push(new Action('Kinit', localize('runKinit', "Run Kinit"), undefined, true, async () => {
this._connectionDialog.close();
await this._clipboardService.writeText('kinit\r');
await this._commandService.executeCommand('workbench.action.terminal.focus');
@@ -512,9 +512,25 @@ export class ConnectionDialogService implements IConnectionDialogService {
}, 10);
return;
}));
}
this._logService.error(message);
this._errorMessageService.showDialog(severity, headerTitle, message, messageDetails, actions);
// Set instructionText for MSSQL Provider Encryption error code -2146893019 thrown by SqlClient when certificate validation fails.
if (errorCode === -2146893019) {
let enableTrustServerCert = localize('enableTrustServerCertificate', "Enable Trust server certificate");
let 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? `);
let readMoreLink = "https://learn.microsoft.com/sql/database-engine/configure-windows/enable-encrypted-connections-to-the-database-engine"
actions.push(new Action('trustServerCert', enableTrustServerCert, undefined, true, async () => {
this._model.options[Constants.trustServerCertificate] = true;
await this.handleOnConnect(this._connectionDialog.newConnectionParams, this._model as IConnectionProfile);
return;
}));
this._errorMessageService.showDialog(severity, headerTitle, message, messageDetails, actions, instructionText, readMoreLink);
} else {
this._errorMessageService.showDialog(severity, headerTitle, message, messageDetails, actions);
}
}
}

View File

@@ -24,6 +24,8 @@ import { attachModalDialogStyler } from 'sql/workbench/common/styler';
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
import { attachButtonStyler } from 'vs/platform/theme/common/styler';
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfiguration';
import { Link } from 'vs/platform/opener/browser/link';
import { IOpenerService } from 'vs/platform/opener/common/opener';
const maxActions = 1;
@@ -36,9 +38,12 @@ export class ErrorMessageDialog extends Modal {
private _actions: IAction[] = [];
private _severity?: Severity;
private _message?: string;
private _instructionText?: string;
private _readMoreLink?: string;
private _messageDetails?: string;
private _okLabel: string;
private _closeLabel: string;
private _readMoreLabel: string;
private _onOk = new Emitter<void>();
public onOk: Event<void> = this._onOk.event;
@@ -50,11 +55,13 @@ export class ErrorMessageDialog extends Modal {
@IAdsTelemetryService telemetryService: IAdsTelemetryService,
@IContextKeyService contextKeyService: IContextKeyService,
@ILogService logService: ILogService,
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService,
@IOpenerService private readonly _openerService: IOpenerService
) {
super('', TelemetryKeys.ModalDialogName.ErrorMessage, telemetryService, layoutService, clipboardService, themeService, logService, textResourcePropertiesService, contextKeyService, { dialogStyle: 'normal', hasTitleIcon: true });
this._okLabel = localize('errorMessageDialog.ok', "OK");
this._closeLabel = localize('errorMessageDialog.close', "Close");
this._readMoreLabel = localize('errorMessageDialog.readMore', "Read More");
}
protected renderBody(container: HTMLElement) {
@@ -88,7 +95,7 @@ export class ErrorMessageDialog extends Modal {
}
private createStandardButton(label: string, onSelect: () => void): Button {
let button = this.addFooterButton(label, onSelect, 'right', true);
let button = this.addFooterButton(label, onSelect, 'right', false);
this._register(attachButtonStyler(button, this._themeService));
return button;
}
@@ -109,6 +116,17 @@ export class ErrorMessageDialog extends Modal {
protected updateDialogBody(): void {
DOM.clearNode(this._body!);
DOM.append(this._body!, DOM.$('div.error-message')).innerText = this._message!;
if (this._instructionText) {
let childElement = DOM.$('div.error-instruction-text');
childElement.innerText = this._instructionText!;
if (this._readMoreLink) {
new Link(childElement, {
label: this._readMoreLabel,
href: this._readMoreLink
}, undefined, this._openerService);
}
DOM.append(this._body!, childElement);
}
}
protected getBody(): HTMLElement {
@@ -148,9 +166,11 @@ export class ErrorMessageDialog extends Modal {
this.hide(hideReason);
}
public open(severity: Severity, headerTitle: string, message: string, messageDetails?: string, actions?: IAction[]) {
public open(severity: Severity, headerTitle: string, message: string, messageDetails?: string, actions?: IAction[], instructionText?: string, readMoreLink?: string): void {
this._severity = severity;
this._message = message;
this._instructionText = instructionText;
this._readMoreLink = readMoreLink;
this.title = headerTitle;
this._messageDetails = messageDetails;
if (this._messageDetails) {
@@ -162,21 +182,31 @@ export class ErrorMessageDialog extends Modal {
this._bodyContainer.setAttribute('aria-description', this._message);
}
this.resetActions();
if (actions && actions.length > 0) {
if (actions?.length > 0) {
for (let i = 0; i < maxActions && i < actions.length; i++) {
this._actions.push(actions[i]);
let button = this._actionButtons[i];
button.label = actions[i].label;
button.element.style.visibility = 'visible';
}
this._okButton!.label = this._closeLabel;
//Remove and add button again to update style.
this.removeFooterButton(this._okLabel);
this.removeFooterButton(this._closeLabel);
this._okButton = this.addFooterButton(this._closeLabel, () => this.ok(), undefined, true);
} else {
this._okButton!.label = this._okLabel;
//Remove and add button again to update style
this.removeFooterButton(this._okLabel);
this.removeFooterButton(this._closeLabel);
this._okButton = this.addFooterButton(this._okLabel, () => this.ok());
}
this.updateIconTitle();
this.updateDialogBody();
this.show();
this._okButton!.focus();
if (actions?.length > 0) {
this._actionButtons[0].focus();
} else {
this._okButton!.focus();
}
}
private resetActions(): void {

View File

@@ -24,11 +24,11 @@ export class ErrorMessageService implements IErrorMessageService {
@IInstantiationService private _instantiationService: IInstantiationService
) { }
public showDialog(severity: Severity, headerTitle: string, message: string, messageDetails?: string, actions?: IAction[]): void {
this.doShowDialog(severity, headerTitle, message, messageDetails, actions);
public showDialog(severity: Severity, headerTitle: string, message: string, messageDetails?: string, actions?: IAction[], instructionText?: string, readMoreLink?: string): void {
this.doShowDialog(severity, headerTitle, message, messageDetails, actions, instructionText, readMoreLink);
}
private doShowDialog(severity: Severity, headerTitle: string, message: string, messageDetails?: string, actions?: IAction[]): void {
private doShowDialog(severity: Severity, headerTitle: string, message: string, messageDetails?: string, actions?: IAction[], instructionText?: string, readMoreLink?: string): void {
if (!this._errorDialog) {
this._errorDialog = this._instantiationService.createInstance(ErrorMessageDialog);
this._errorDialog.onOk(() => this.handleOnOk());
@@ -36,7 +36,7 @@ export class ErrorMessageService implements IErrorMessageService {
}
let title = headerTitle ? headerTitle : this.getDefaultTitle(severity);
return this._errorDialog.open(severity, title, message, messageDetails, actions);
return this._errorDialog.open(severity, title, message, messageDetails, actions, instructionText, readMoreLink);
}
private getDefaultTitle(severity: Severity) {

View File

@@ -6,7 +6,7 @@
.error-dialog {
padding: 15px;
overflow: auto;
height: 200px;
height: 210px;
}
.error-dialog .codicon.error, .error-dialog .codicon.warning , .error-dialog .codicon.info {
@@ -22,6 +22,11 @@
user-select: text;
}
.error-dialog .error-instruction-text{
margin-top: 40px;
font-weight: 700;
}
.modal .footer-button a.monaco-button.monaco-text-button.codicon.scriptToClipboard {
width: 120px;
}