mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
[Azure Arc] Update MIAA model to include encryption properties (#21702)
This commit is contained in:
@@ -190,3 +190,7 @@ export namespace cssStyles {
|
||||
}
|
||||
|
||||
export const iconSize = '20px';
|
||||
|
||||
export const encryptOption = 'encrypt';
|
||||
export const trustServerCertificateOption = 'trustServerCertificate';
|
||||
export const encryptReadMoreLink = 'https://learn.microsoft.com/sql/database-engine/configure-windows/enable-encrypted-connections-to-the-database-engine';
|
||||
|
||||
@@ -142,12 +142,21 @@ export const controllerPassword = localize('arc.controllerPassword', "Controller
|
||||
export const username = localize('arc.username', "Username");
|
||||
export const password = localize('arc.password', "Password");
|
||||
export const rememberPassword = localize('arc.rememberPassword', "Remember Password");
|
||||
export const encrypt = localize('arc.encrypt', "Encrypt");
|
||||
export const encryptDescription = localize('arc.encryptDescription', "When true, SQL Server uses SSL encryption for all data sent between the client and server if the server has a certificate installed.");
|
||||
export const trustServerCertificate = localize('arc.trustServerCertificate', "Trust Server Certificate");
|
||||
export const trustServerCertDescription = localize('arc.trustServerCertDescription', "When true (and encrypt=true), SQL Server uses SSL encryption for all data sent between the client and server without validating the server certificate.");
|
||||
export const enableTrustServerCert = localize('arc.enableTrustServerCert', "Enable Trust Server Certificate");
|
||||
export const msgPromptSSLCertificateValidationFailed = localize('arc.msgPromptSSLCertificateValidationFailed', '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 const connect = localize('arc.connect', "Connect");
|
||||
export const readMore = localize('arc.readMore', "Read more");
|
||||
export const cancel = localize('arc.cancel', "Cancel");
|
||||
export const apply = localize('arc.apply', "Apply");
|
||||
export const ok = localize('arc.ok', "Ok");
|
||||
export const on = localize('arc.on', "On");
|
||||
export const off = localize('arc.off', "Off");
|
||||
export const booleantrue = localize('arc.booleantrue', "True");
|
||||
export const booleanfalse = localize('arc.booleanfalse', "False");
|
||||
export const notConfigured = localize('arc.notConfigured', "Not Configured");
|
||||
|
||||
// Database States - see https://docs.microsoft.com/sql/relational-databases/databases/database-states
|
||||
|
||||
@@ -219,7 +219,10 @@ export class MiaaModel extends ResourceModel {
|
||||
saveProfile: true,
|
||||
id: '',
|
||||
groupId: undefined,
|
||||
options: {}
|
||||
options: {
|
||||
encrypt: this._miaaInfo.encrypt || true,
|
||||
trustServerCertificate: this._miaaInfo.trustServerCertificate || false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -240,6 +243,8 @@ export class MiaaModel extends ResourceModel {
|
||||
this._activeConnectionId = connectionProfile.id;
|
||||
this.info.connectionId = connectionProfile.id;
|
||||
this._miaaInfo.userName = connectionProfile.userName;
|
||||
this._miaaInfo.encrypt = connectionProfile.options.encrypt;
|
||||
this._miaaInfo.trustServerCertificate = connectionProfile.options.trustServerCertificate;
|
||||
await this._treeDataProvider.saveControllers();
|
||||
}
|
||||
|
||||
@@ -270,6 +275,5 @@ export class MiaaModel extends ResourceModel {
|
||||
this._databaseTimeWindow.set(dbName, ['', '']);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
4
extensions/arc/src/typings/arc.d.ts
vendored
4
extensions/arc/src/typings/arc.d.ts
vendored
@@ -20,7 +20,9 @@ declare module 'arc' {
|
||||
}
|
||||
|
||||
export type MiaaResourceInfo = ResourceInfo & {
|
||||
userName?: string
|
||||
userName?: string,
|
||||
encrypt?: string,
|
||||
trustServerCertificate?: boolean
|
||||
};
|
||||
|
||||
export type PGResourceInfo = ResourceInfo & {
|
||||
|
||||
@@ -8,11 +8,15 @@ import * as vscode from 'vscode';
|
||||
import { Deferred } from '../../common/promise';
|
||||
import * as loc from '../../localizedConstants';
|
||||
import { createCredentialId } from '../../common/utils';
|
||||
import { credentialNamespace } from '../../constants';
|
||||
import * as constants from '../../constants';
|
||||
import { InitializingComponent } from '../components/initializingComponent';
|
||||
import { ResourceModel } from '../../models/resourceModel';
|
||||
import { ControllerModel } from '../../models/controllerModel';
|
||||
|
||||
export interface IReconnectAction {
|
||||
(profile: azdata.IConnectionProfile): Promise<boolean>;
|
||||
}
|
||||
|
||||
export abstract class ConnectToSqlDialog extends InitializingComponent {
|
||||
protected modelBuilder!: azdata.ModelBuilder;
|
||||
|
||||
@@ -20,6 +24,9 @@ export abstract class ConnectToSqlDialog extends InitializingComponent {
|
||||
protected usernameInputBox!: azdata.InputBoxComponent;
|
||||
protected passwordInputBox!: azdata.InputBoxComponent;
|
||||
protected rememberPwCheckBox!: azdata.CheckBoxComponent;
|
||||
protected encryptSelectBox!: azdata.DropDownComponent;
|
||||
protected trustServerCertificateSelectBox!: azdata.DropDownComponent;
|
||||
|
||||
private options: { [name: string]: any } = {};
|
||||
|
||||
protected _completionPromise = new Deferred<azdata.IConnectionProfile | undefined>();
|
||||
@@ -29,7 +36,11 @@ export abstract class ConnectToSqlDialog extends InitializingComponent {
|
||||
}
|
||||
|
||||
public showDialog(dialogTitle: string, connectionProfile?: azdata.IConnectionProfile): azdata.window.Dialog {
|
||||
const dialog = azdata.window.createModelViewDialog(dialogTitle);
|
||||
const dialog = azdata.window.createModelViewDialog(dialogTitle, undefined, 'narrow');
|
||||
const trueCategory: azdata.CategoryValue = { displayName: loc.booleantrue, name: 'true' }
|
||||
const falseCategory: azdata.CategoryValue = { displayName: loc.booleanfalse, name: 'false' }
|
||||
const booleanCategoryValues: azdata.CategoryValue[] = [trueCategory, falseCategory];
|
||||
|
||||
dialog.cancelButton.onClick(() => this.handleCancel());
|
||||
dialog.registerContent(async view => {
|
||||
this.modelBuilder = view.modelBuilder;
|
||||
@@ -47,13 +58,22 @@ export abstract class ConnectToSqlDialog extends InitializingComponent {
|
||||
.withProps({
|
||||
inputType: 'password',
|
||||
value: connectionProfile?.password
|
||||
})
|
||||
.component();
|
||||
}).component();
|
||||
this.rememberPwCheckBox = this.modelBuilder.checkBox()
|
||||
.withProps({
|
||||
label: loc.rememberPassword,
|
||||
checked: connectionProfile?.savePassword
|
||||
}).component();
|
||||
this.encryptSelectBox = this.modelBuilder.dropDown()
|
||||
.withProps({
|
||||
values: booleanCategoryValues,
|
||||
value: connectionProfile?.options[constants.encryptOption] ? trueCategory : falseCategory
|
||||
}).component();
|
||||
this.trustServerCertificateSelectBox = this.modelBuilder.dropDown()
|
||||
.withProps({
|
||||
values: booleanCategoryValues,
|
||||
value: connectionProfile?.options[constants.trustServerCertificateOption] ? trueCategory : falseCategory
|
||||
}).component();
|
||||
|
||||
let formModel = this.modelBuilder.formContainer()
|
||||
.withFormItems([{
|
||||
@@ -73,6 +93,18 @@ export abstract class ConnectToSqlDialog extends InitializingComponent {
|
||||
}, {
|
||||
component: this.rememberPwCheckBox,
|
||||
title: ''
|
||||
}, {
|
||||
component: this.encryptSelectBox,
|
||||
title: loc.encrypt,
|
||||
layout: {
|
||||
info: loc.encryptDescription,
|
||||
}
|
||||
}, {
|
||||
component: this.trustServerCertificateSelectBox,
|
||||
title: loc.trustServerCertificate,
|
||||
layout: {
|
||||
info: loc.trustServerCertDescription,
|
||||
}
|
||||
}
|
||||
],
|
||||
title: ''
|
||||
@@ -94,6 +126,10 @@ export abstract class ConnectToSqlDialog extends InitializingComponent {
|
||||
if (!this.serverNameInputBox.value || !this.usernameInputBox.value || !this.passwordInputBox.value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.options.encrypt = this.encryptSelectBox.value;
|
||||
this.options.trustServerCertificate = this.trustServerCertificateSelectBox.value;
|
||||
|
||||
const connectionProfile: azdata.IConnectionProfile = {
|
||||
serverName: this.serverNameInputBox.value,
|
||||
databaseName: '',
|
||||
@@ -109,10 +145,15 @@ export abstract class ConnectToSqlDialog extends InitializingComponent {
|
||||
groupId: undefined,
|
||||
options: this.options
|
||||
};
|
||||
|
||||
return await this.connect(connectionProfile);
|
||||
}
|
||||
|
||||
private async connect(connectionProfile: azdata.IConnectionProfile): Promise<boolean> {
|
||||
const result = await azdata.connection.connect(connectionProfile, false, false);
|
||||
if (result.connected) {
|
||||
connectionProfile.id = result.connectionId!;
|
||||
const credentialProvider = await azdata.credentials.getProvider(credentialNamespace);
|
||||
const credentialProvider = await azdata.credentials.getProvider(constants.credentialNamespace);
|
||||
if (connectionProfile.savePassword) {
|
||||
await credentialProvider.saveCredential(createCredentialId(this._controllerModel.info.id, this._model.info.resourceType, this._model.info.name), connectionProfile.password);
|
||||
} else {
|
||||
@@ -123,10 +164,40 @@ export abstract class ConnectToSqlDialog extends InitializingComponent {
|
||||
}
|
||||
else {
|
||||
vscode.window.showErrorMessage(this.connectionFailedMessage(result.errorMessage));
|
||||
return false;
|
||||
// Show error with instructions for MSSQL Provider Encryption error code -2146893019 thrown by SqlClient when certificate validation fails.
|
||||
if (result.errorCode === -2146893019) {
|
||||
return this.showInstructionTextAsWarning(connectionProfile, async updatedConnection => {
|
||||
return await this.connect(updatedConnection);
|
||||
});
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async showInstructionTextAsWarning(profile: azdata.IConnectionProfile, reconnectAction: IReconnectAction): Promise<boolean> {
|
||||
while (true) {
|
||||
const selection = await vscode.window.showWarningMessage(
|
||||
loc.msgPromptSSLCertificateValidationFailed,
|
||||
{ modal: false },
|
||||
...[
|
||||
loc.enableTrustServerCert,
|
||||
loc.readMore,
|
||||
loc.cancel
|
||||
]);
|
||||
if (selection === loc.enableTrustServerCert) {
|
||||
profile.options.encrypt = true;
|
||||
profile.options.trustServerCertificate = true;
|
||||
return await reconnectAction(profile);
|
||||
} else if (selection === loc.readMore) {
|
||||
vscode.env.openExternal(vscode.Uri.parse(constants.encryptReadMoreLink));
|
||||
// Show the dialog again so the user can still pick yes or no after they've read the docs
|
||||
continue;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
protected abstract get providerName(): string;
|
||||
|
||||
protected abstract connectionFailedMessage(error: any): string;
|
||||
|
||||
@@ -110,10 +110,15 @@ export class ControllerTreeNode extends TreeNode {
|
||||
node = new PostgresTreeNode(postgresModel, this.model);
|
||||
break;
|
||||
case ResourceType.sqlManagedInstances:
|
||||
// Fill in the username too if we already have it
|
||||
(resourceInfo as MiaaResourceInfo).userName = (this.model.info.resources.find(info =>
|
||||
// Fill in the username and connection properties too if we already have them
|
||||
let miaaResourceInfo = this.model.info.resources.find(info =>
|
||||
info.name === resourceInfo.name &&
|
||||
info.resourceType === resourceInfo.resourceType) as MiaaResourceInfo)?.userName;
|
||||
info.resourceType === resourceInfo.resourceType) as MiaaResourceInfo;
|
||||
if (miaaResourceInfo) {
|
||||
(resourceInfo as MiaaResourceInfo).userName = miaaResourceInfo.userName;
|
||||
(resourceInfo as MiaaResourceInfo).encrypt = miaaResourceInfo.encrypt;
|
||||
(resourceInfo as MiaaResourceInfo).trustServerCertificate = miaaResourceInfo.trustServerCertificate;
|
||||
}
|
||||
const miaaModel = new MiaaModel(this.model, resourceInfo, registration, this._treeDataProvider);
|
||||
node = new MiaaTreeNode(miaaModel, this.model);
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user