Add prompt for enabling Trust Server Certificate (#18147)

* Adding dialog to prompt for enabling Trust Server Certificate

* generated loc files

* adding ability to set flag in connection string

* updated loc files

* comments for regex
This commit is contained in:
Benjin Dubishar 2024-10-08 14:15:37 -07:00 коммит произвёл GitHub
Родитель 5b510caf9c
Коммит 0d3180de23
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
12 изменённых файлов: 205 добавлений и 22 удалений

Просмотреть файл

@ -194,6 +194,11 @@
"Location": "Location",
"Server": "Server",
"Database": "Database",
"Connection Error": "Connection Error",
"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.": "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?": "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?",
"Read more": "Read more",
"Enable 'Trust Server Certificate'": "Enable 'Trust Server Certificate'",
"Query {0}: Query cost (relative to the script): {1}%/{0} is the query number{1} is the query cost": {
"message": "Query {0}: Query cost (relative to the script): {1}%",
"comment": [
@ -363,7 +368,6 @@
"Mandatory (True)": "Mandatory (True)",
"Mandatory (Recommended)": "Mandatory (Recommended)",
"Enable Trust Server Certificate": "Enable Trust Server Certificate",
"Read more": "Read more",
"Copy code and open webpage": "Copy code and open webpage",
"Choose a Microsoft Entra account": "Choose a Microsoft Entra account",
"Add a Microsoft Entra account...": "Add a Microsoft Entra account...",
@ -434,7 +438,7 @@
"Refresh Credentials": "Refresh Credentials",
"Failed to fetch user tokens.": "Failed to fetch user tokens.",
"Error: Login failed. Retry using different credentials?": "Error: Login failed. Retry using different credentials?",
"Encryption was enabled on this connection, review your SSL and certificate configuration for the target SQL Server, or set 'Trust server certificate' to 'true' in the settings file. 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?": "Encryption was enabled on this connection, review your SSL and certificate configuration for the target SQL Server, or set 'Trust server certificate' to 'true' in the settings file. 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?",
"Encryption was enabled on this connection; review your SSL and certificate configuration for the target SQL Server, or set 'Trust server certificate' to 'true' in the settings file. 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?": "Encryption was enabled on this connection; review your SSL and certificate configuration for the target SQL Server, or set 'Trust server certificate' to 'true' in the settings file. 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?",
"Your client IP address does not have access to the server. Add a Microsoft Entra account and create a new firewall rule to enable access.": "Your client IP address does not have access to the server. Add a Microsoft Entra account and create a new firewall rule to enable access.",
"Your client IP Address '{0}' does not have access to the server '{1}' you're attempting to connect to. Would you like to create new firewall rule?/{0} is the client IP address{1} is the server name": {
"message": "Your client IP Address '{0}' does not have access to the server '{1}' you're attempting to connect to. Would you like to create new firewall rule?",

Просмотреть файл

@ -262,6 +262,9 @@
<trans-unit id="++CODE++dba530f818587a0d79d7bb8f59e07d372d10691f49c560e3e4c3d42d105fe9e7">
<source xml:lang="en">Connection Dialog</source>
</trans-unit>
<trans-unit id="++CODE++ddb621f87c5c0baf1158d527d62ad1bf9752e53483d2540591ce3ae35e06fd6b">
<source xml:lang="en">Connection Error</source>
</trans-unit>
<trans-unit id="++CODE++52d8d284995954ca5f68adbc705bb4ff2a930cc1ff2a3885a1002ff477394eee">
<source xml:lang="en">Connection Errors</source>
</trans-unit>
@ -378,14 +381,20 @@
<trans-unit id="++CODE++464c4ffd019e1e9691dcf0537c797353ef2b1c1d4833d3d463e5b74ae4547344">
<source xml:lang="en">Edit</source>
</trans-unit>
<trans-unit id="++CODE++1e71b5c8f3211f1f01b27341d3ddc6a2ad651800264b495c12b50867f998662c">
<source xml:lang="en">Enable &apos;Trust Server Certificate&apos;</source>
</trans-unit>
<trans-unit id="++CODE++45e33ceba31737f1cb793c231fafc9dd99074ca1f26476a70b0c830ef155c9d3">
<source xml:lang="en">Enable Trust Server Certificate</source>
</trans-unit>
<trans-unit id="++CODE++4f03bf1cdf8e7d1882e5198384139c078fa527857e7371ac6cb2d030dede15a3">
<source xml:lang="en">Encrypt</source>
</trans-unit>
<trans-unit id="++CODE++476c508b94c7f515d2276689b1eb5c8d1eb0d109b274cced59a3e1a1e3a247c9">
<source xml:lang="en">Encryption was enabled on this connection, review your SSL and certificate configuration for the target SQL Server, or set &apos;Trust server certificate&apos; to &apos;true&apos; in the settings file. Note: A self-signed certificate offers only limited protection and is not a recommended practice for production environments. Do you want to enable &apos;Trust server certificate&apos; on this connection and retry?</source>
<trans-unit id="++CODE++ac8fee089cebcddef6529d9c8b467120173dbd02a46992ecbaf62fe25c925ae0">
<source xml:lang="en">Encryption was enabled on this connection; review your SSL and certificate configuration for the target SQL Server, or enable &apos;Trust server certificate&apos; in the connection dialog.</source>
</trans-unit>
<trans-unit id="++CODE++a04452986b48c7c3c21f78712b31a68f2aecb85566c134d87d55348c7b16b9a8">
<source xml:lang="en">Encryption was enabled on this connection; review your SSL and certificate configuration for the target SQL Server, or set &apos;Trust server certificate&apos; to &apos;true&apos; in the settings file. Note: A self-signed certificate offers only limited protection and is not a recommended practice for production environments. Do you want to enable &apos;Trust server certificate&apos; on this connection and retry?</source>
</trans-unit>
<trans-unit id="++CODE++6051d2070e8e51a99adfa6b3f5171cfa92107943f46417bcd51a8f5e995c881f">
<source xml:lang="en">End IP Address</source>
@ -775,6 +784,9 @@
<trans-unit id="++CODE++ba35f0c47d862763dafa955d6716942f79b8bfe1d01d5968520db6f9ba665f6f">
<source xml:lang="en">Not started</source>
</trans-unit>
<trans-unit id="++CODE++07b555a4dd9446c56aae7bbe326905f8aabc205d5eee96191a71e15510fbb53f">
<source xml:lang="en">Note: A self-signed certificate offers only limited protection and is not a recommended practice for production environments. Do you want to enable &apos;Trust server certificate&apos; on this connection and retry?</source>
</trans-unit>
<trans-unit id="++CODE++6091a3057f7b1b8140c1ad2fc9232cc4d483a484667f76fe8bb305d77ca3deb1">
<source xml:lang="en">Number of Rows Read</source>
</trans-unit>

Просмотреть файл

@ -0,0 +1,6 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export const connectionCertValidationFailedErrorCode = -2146893019;

Просмотреть файл

@ -49,6 +49,7 @@ import {
import { getErrorMessage, listAllIterator } from "../utils/utils";
import { l10n } from "vscode";
import { UserSurvey } from "../nps/userSurvey";
import { connectionCertValidationFailedErrorCode } from "./connectionConstants";
export class ConnectionDialogWebviewController extends ReactWebviewPanelController<
ConnectionDialogWebviewState,
@ -86,6 +87,7 @@ export class ConnectionDialogWebviewController extends ReactWebviewPanelControll
formError: "",
loadingAzureSubscriptionsStatus: ApiStatus.NotStarted,
loadingAzureServersStatus: ApiStatus.NotStarted,
trustServerCertError: undefined,
}),
vscode.ViewColumn.Active,
{
@ -903,7 +905,16 @@ export class ConnectionDialogWebviewController extends ReactWebviewPanelControll
this.state.connectionProfile as any,
);
if (result?.errorMessage) {
if (result.errorMessage) {
if (
result.errorNumber ===
connectionCertValidationFailedErrorCode
) {
this.state.connectionStatus = ApiStatus.Error;
this.state.trustServerCertError =
result.errorMessage;
return state;
}
this.state.formError = result.errorMessage;
this.state.connectionStatus = ApiStatus.Error;
return state;
@ -965,6 +976,11 @@ export class ConnectionDialogWebviewController extends ReactWebviewPanelControll
return state;
});
this.registerReducer("cancelTrustServerCertDialog", async (state) => {
state.trustServerCertError = undefined;
return state;
});
this.registerReducer("refreshMruConnections", async (state) => {
state.recentConnections = await this.loadRecentConnections();
this.updateState();

Просмотреть файл

@ -282,7 +282,7 @@ export let msgPromptRetryConnectionDifferentCredentials = l10n.t(
"Error: Login failed. Retry using different credentials?",
);
export let msgPromptSSLCertificateValidationFailed = l10n.t(
"Encryption was enabled on this connection, review your SSL and certificate configuration for the target SQL Server, or set 'Trust server certificate' to 'true' in the settings file. 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?",
"Encryption was enabled on this connection; review your SSL and certificate configuration for the target SQL Server, or set 'Trust server certificate' to 'true' in the settings file. 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 let msgPromptRetryFirewallRuleNotSignedIn = l10n.t(
"Your client IP address does not have access to the server. Add a Microsoft Entra account and create a new firewall rule to enable access.",

Просмотреть файл

@ -151,6 +151,18 @@ export class LocConstants {
location: l10n.t("Location"),
server: l10n.t("Server"),
database: l10n.t("Database"),
connectionErrorTitle: l10n.t("Connection Error"),
trustServerCertMessage: l10n.t(
"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.",
),
trustServerCertPrompt: l10n.t(
"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?",
),
readMore: l10n.t("Read more"),
enableTrustServerCertificateButton: l10n.t(
"Enable 'Trust Server Certificate'",
),
close: l10n.t("Close"),
};
}

Просмотреть файл

@ -140,6 +140,10 @@ export function VscodeWebviewProvider<State, Reducers>({
setState(params as State);
});
function isInitialized(): boolean {
return state !== undefined;
}
return (
<VscodeWebviewContext.Provider
value={{
@ -158,7 +162,10 @@ export function VscodeWebviewProvider<State, Reducers>({
}}
theme={theme}
>
{children}
{
// don't render webview unless necessary dependencies are initialized
isInitialized() && children
}
</FluentProvider>
</VscodeWebviewContext.Provider>
);

Просмотреть файл

@ -0,0 +1,113 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { useContext } from "react";
import { ConnectionDialogContext } from "../connectionDialogStateProvider";
import {
Button,
Dialog,
DialogActions,
DialogBody,
DialogContent,
DialogSurface,
DialogTitle,
Link,
MessageBar,
} from "@fluentui/react-components";
import { locConstants } from "../../../common/locConstants";
import { connectionCertValidationReadMoreUrl } from "../connectionConstants";
import { ConnectionInputMode } from "../../../../sharedInterfaces/connectionDialog";
export const TrustServerCertificateDialog = ({}) => {
const context = useContext(ConnectionDialogContext)!;
return (
<Dialog open={context.state.trustServerCertError !== undefined}>
<DialogSurface>
<DialogBody>
<DialogTitle>
{locConstants.connectionDialog.connectionErrorTitle}
</DialogTitle>
<DialogContent>
<MessageBar
intent="error"
style={{ paddingRight: "12px" }}
>
{context.state.trustServerCertError}
</MessageBar>
<br />
{locConstants.connectionDialog.trustServerCertMessage}
<br />
<br />
{locConstants.connectionDialog.trustServerCertPrompt}
{" " /* extra space before the 'Read More' link*/}
<Link href={connectionCertValidationReadMoreUrl}>
{locConstants.connectionDialog.readMore}
</Link>
</DialogContent>
<DialogActions>
<Button
appearance="primary"
onClick={() => {
context.cancelTrustServerCertDialog();
if (
context.state.selectedInputMode ===
ConnectionInputMode.ConnectionString
) {
context.formAction({
propertyName: "connectionString",
value: setConnectionStringProperty(
context.state.formState
.connectionString ?? "",
"trustServerCertificate",
true,
),
isAction: false,
});
} else {
context.formAction({
propertyName: "trustServerCertificate",
value: true,
isAction: false,
});
}
context.connect();
}}
>
{
locConstants.connectionDialog
.enableTrustServerCertificateButton
}
</Button>
<Button
appearance="secondary"
onClick={() => {
context.cancelTrustServerCertDialog();
}}
>
{locConstants.connectionDialog.close}
</Button>
</DialogActions>
</DialogBody>
</DialogSurface>
</Dialog>
);
};
function setConnectionStringProperty(
connectionString: string,
propertyName: string,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value: any,
): string {
const regex = new RegExp(`${propertyName}\\s*=`); // check for existence of the property
if (regex.test(connectionString)) {
const valueRegex = new RegExp(`${propertyName}\\s*=\\s*[^;]*`); // grab the entirety of "propertyName=value"
return connectionString.replace(valueRegex, `${propertyName}=${value}`);
} else {
return `${connectionString};${propertyName}=${value}`;
}
}

Просмотреть файл

@ -0,0 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export const connectionCertValidationReadMoreUrl =
"https://learn.microsoft.com/sql/database-engine/configure-windows/enable-encrypted-connections-to-the-database-engine";

Просмотреть файл

@ -64,6 +64,11 @@ const ConnectionDialogStateProvider: React.FC<
subscriptionId: subscriptionId,
});
},
cancelTrustServerCertDialog: function (): void {
webviewState?.extensionRpc.action(
"cancelTrustServerCertDialog",
);
},
refreshMruConnections: function (): void {
webviewState.extensionRpc.action("refreshMruConnections");
},

Просмотреть файл

@ -25,6 +25,7 @@ import { FormField, useFormStyles } from "../../common/forms/form.component";
import { FormItemSpec } from "../../common/forms/form";
import { locConstants } from "../../common/locConstants";
import { AzureBrowsePage } from "./azureBrowsePage";
import { TrustServerCertificateDialog } from "./components/trustServerCertificateDialog.component";
function renderContent(
connectionDialogContext: ConnectionDialogContextProps,
@ -40,28 +41,24 @@ function renderContent(
}
export const ConnectionInfoFormContainer = () => {
const connectionDialogContext = useContext(ConnectionDialogContext);
const context = useContext(ConnectionDialogContext)!;
const formStyles = useFormStyles();
if (!connectionDialogContext?.state) {
return undefined;
}
return (
<div className={formStyles.formRoot}>
<ConnectionHeader />
<div className={formStyles.formDiv} style={{ overflow: "auto" }}>
{connectionDialogContext?.state.formError && (
{context.state.formError && (
<MessageBar intent="error">
{connectionDialogContext.state.formError}
{context.state.formError}
</MessageBar>
)}
<TrustServerCertificateDialog />
<FormField
context={connectionDialogContext}
context={context}
component={
connectionDialogContext.state.connectionComponents
.components[
context.state.connectionComponents.components[
"profileName"
] as FormItemSpec<IConnectionDialogProfile>
}
@ -73,13 +70,11 @@ export const ConnectionInfoFormContainer = () => {
<Field label="Input type" orientation="horizontal">
<RadioGroup
onChange={(_, data) => {
connectionDialogContext.setConnectionInputType(
context.setConnectionInputType(
data.value as ConnectionInputMode,
);
}}
value={
connectionDialogContext.state.selectedInputMode
}
value={context.state.selectedInputMode}
>
<Radio
value={ConnectionInputMode.Parameters}
@ -101,7 +96,7 @@ export const ConnectionInfoFormContainer = () => {
</RadioGroup>
</Field>
</div>
{renderContent(connectionDialogContext)}
{renderContent(context)}
</div>
</div>
);

Просмотреть файл

@ -44,6 +44,7 @@ export class ConnectionDialogWebviewState
public formError: string;
public loadingAzureSubscriptionsStatus: ApiStatus;
public loadingAzureServersStatus: ApiStatus;
public trustServerCertError: string | undefined;
constructor({
connectionProfile,
@ -56,6 +57,7 @@ export class ConnectionDialogWebviewState
formError,
loadingAzureSubscriptionsStatus,
loadingAzureServersStatus,
trustServerCertError,
}: {
connectionProfile: IConnectionDialogProfile;
selectedInputMode: ConnectionInputMode;
@ -78,6 +80,7 @@ export class ConnectionDialogWebviewState
formError: string;
loadingAzureSubscriptionsStatus: ApiStatus;
loadingAzureServersStatus: ApiStatus;
trustServerCertError: string | undefined;
}) {
this.formState = connectionProfile;
this.selectedInputMode = selectedInputMode;
@ -89,6 +92,7 @@ export class ConnectionDialogWebviewState
this.formError = formError;
this.loadingAzureSubscriptionsStatus = loadingAzureSubscriptionsStatus;
this.loadingAzureServersStatus = loadingAzureServersStatus;
this.trustServerCertError = trustServerCertError;
}
}
@ -138,6 +142,7 @@ export interface ConnectionDialogContextProps
setConnectionInputType: (inputType: ConnectionInputMode) => void;
connect: () => void;
loadAzureServers: (subscriptionId: string) => void;
cancelTrustServerCertDialog: () => void;
refreshMruConnections: () => void;
}
@ -161,5 +166,6 @@ export interface ConnectionDialogReducers {
loadAzureServers: {
subscriptionId: string;
};
cancelTrustServerCertDialog: {};
refreshMruConnections: {};
}