mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-20 01:25:37 -05:00
Add support for Encrypt=Strict for TDS 8.0 connections with SQL Server 2022 (#21256)
This commit is contained in:
32
src/sql/azdata.proposed.d.ts
vendored
32
src/sql/azdata.proposed.d.ts
vendored
@@ -556,6 +556,38 @@ declare module 'azdata' {
|
||||
* and not the Advanced Options window.
|
||||
*/
|
||||
showOnConnectionDialog?: boolean;
|
||||
|
||||
/**
|
||||
* Used to define list of values based on which another option is rendered visible/hidden.
|
||||
*/
|
||||
onSelectionChange?: SelectionChangeEvent[];
|
||||
}
|
||||
|
||||
/**
|
||||
* This change event defines actions
|
||||
*/
|
||||
export interface SelectionChangeEvent {
|
||||
/**
|
||||
* Values that affect actions defined in this event.
|
||||
*/
|
||||
values: string[];
|
||||
|
||||
/**
|
||||
* Action to be taken on another option when selected value matches to the list of values provided.
|
||||
*/
|
||||
dependentOptionActions: DependentOptionAction[];
|
||||
}
|
||||
|
||||
export interface DependentOptionAction {
|
||||
/**
|
||||
* Name of option affected by defined action.
|
||||
*/
|
||||
optionName: string,
|
||||
|
||||
/**
|
||||
* Action to be taken, Supported values: 'show', 'hide'.
|
||||
*/
|
||||
action: string;
|
||||
}
|
||||
|
||||
// Object Explorer interfaces --------------------------------
|
||||
|
||||
25
src/sql/base/browser/ui/adsWidget.ts
Normal file
25
src/sql/base/browser/ui/adsWidget.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Widget } from 'vs/base/browser/ui/widget';
|
||||
``
|
||||
/**
|
||||
* This interface is implemented by SelectBox and InputBox to provide a common API surface area in connectionWidget.ts
|
||||
* If more widgets must be used in Connection Dialog, they should implement this interface.
|
||||
*/
|
||||
export interface AdsWidget extends Widget {
|
||||
|
||||
get value(): string;
|
||||
|
||||
get id(): string;
|
||||
|
||||
getAriaLabel(): string;
|
||||
|
||||
enable(): void;
|
||||
|
||||
disable(): void;
|
||||
|
||||
hideMessage(): void;
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { InputBox as vsInputBox, IInputOptions as vsIInputBoxOptions, IInputBoxS
|
||||
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { AdsWidget } from 'sql/base/browser/ui/adsWidget';
|
||||
|
||||
export interface OnLoseFocusParams {
|
||||
value: string;
|
||||
@@ -29,7 +30,7 @@ export interface IInputOptions extends vsIInputBoxOptions {
|
||||
ariaDescription?: string;
|
||||
}
|
||||
|
||||
export class InputBox extends vsInputBox {
|
||||
export class InputBox extends vsInputBox implements AdsWidget {
|
||||
private enabledInputBackground?: Color;
|
||||
private enabledInputForeground?: Color;
|
||||
private enabledInputBorder?: Color;
|
||||
@@ -48,7 +49,7 @@ export class InputBox extends vsInputBox {
|
||||
private _isTextAreaInput = false;
|
||||
private _hideErrors = false;
|
||||
|
||||
constructor(container: HTMLElement, contextViewProvider: IContextViewProvider, private _sqlOptions?: IInputOptions) {
|
||||
constructor(container: HTMLElement, contextViewProvider: IContextViewProvider, private _sqlOptions?: IInputOptions, id?: string) {
|
||||
super(container, contextViewProvider, _sqlOptions);
|
||||
this.enabledInputBackground = this.inputBackground;
|
||||
this.enabledInputForeground = this.inputForeground;
|
||||
@@ -74,6 +75,7 @@ export class InputBox extends vsInputBox {
|
||||
if (this._sqlOptions.ariaDescription) {
|
||||
this.inputElement.setAttribute('aria-description', this._sqlOptions.ariaDescription);
|
||||
}
|
||||
this.inputElement.id = id;
|
||||
}
|
||||
|
||||
public override style(styles: IInputBoxStyles): void {
|
||||
@@ -197,4 +199,8 @@ export class InputBox extends vsInputBox {
|
||||
this._lastLoseFocusValue = newValue;
|
||||
super.value = newValue;
|
||||
}
|
||||
|
||||
public get id(): string {
|
||||
return this.input.id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { SelectBoxList } from 'vs/base/browser/ui/selectBox/selectBoxCustom';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { AdsWidget } from 'sql/base/browser/ui/adsWidget';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
@@ -39,7 +40,7 @@ export interface ISelectBoxStyles extends vsISelectBoxStyles {
|
||||
inputValidationErrorForeground?: Color;
|
||||
}
|
||||
|
||||
export class SelectBox extends vsSelectBox {
|
||||
export class SelectBox extends vsSelectBox implements AdsWidget {
|
||||
private _optionsDictionary: Map<string, number>;
|
||||
private _dialogOptions: SelectOptionItemSQL[];
|
||||
private _selectedOption: string;
|
||||
@@ -67,7 +68,7 @@ export class SelectBox extends vsSelectBox {
|
||||
|
||||
private element?: HTMLElement;
|
||||
|
||||
constructor(options: SelectOptionItemSQL[] | string[], selectedOption: string, contextViewProvider: IContextViewProvider, container?: HTMLElement, selectBoxOptions?: ISelectBoxOptions) {
|
||||
constructor(options: SelectOptionItemSQL[] | string[], selectedOption: string, contextViewProvider: IContextViewProvider, container?: HTMLElement, selectBoxOptions?: ISelectBoxOptions, id?: string) {
|
||||
let optionItems: SelectOptionItemSQL[] = SelectBox.createOptions(options);
|
||||
super(optionItems, 0, contextViewProvider, undefined, selectBoxOptions);
|
||||
|
||||
@@ -98,6 +99,7 @@ export class SelectBox extends vsSelectBox {
|
||||
this.element = dom.append(container, $('.monaco-selectbox.idle'));
|
||||
}
|
||||
|
||||
this.selectElement.id = id;
|
||||
this._selectBoxOptions = selectBoxOptions;
|
||||
let focusTracker = dom.trackFocus(this.selectElement);
|
||||
this._register(focusTracker);
|
||||
@@ -220,7 +222,6 @@ export class SelectBox extends vsSelectBox {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public override setOptions(options: string[] | SelectOptionItemSQL[] | ISelectOptionItem[], selected?: number): void {
|
||||
let selectOptions: SelectOptionItemSQL[] = SelectBox.createOptions(options);
|
||||
this.populateOptionsDictionary(selectOptions);
|
||||
@@ -255,6 +256,14 @@ export class SelectBox extends vsSelectBox {
|
||||
this.applyStyles();
|
||||
}
|
||||
|
||||
public getAriaLabel(): string {
|
||||
return this.selectElem.ariaLabel;
|
||||
}
|
||||
|
||||
public get id(): string {
|
||||
return this.selectElem.id;
|
||||
}
|
||||
|
||||
public hasFocus(): boolean {
|
||||
return document.activeElement === this.selectElement;
|
||||
}
|
||||
|
||||
@@ -57,6 +57,20 @@ export enum AuthenticationType {
|
||||
None = 'None'
|
||||
}
|
||||
|
||||
/*
|
||||
* Actions for the connection dialog to show/hide connection options.
|
||||
*/
|
||||
export enum Actions {
|
||||
/**
|
||||
* Shows a connection option
|
||||
*/
|
||||
Show = 'show',
|
||||
/**
|
||||
* Hides a connection option
|
||||
*/
|
||||
Hide = 'hide'
|
||||
}
|
||||
|
||||
/* CMS constants */
|
||||
export const cmsProviderName = 'MSSQL-CMS';
|
||||
|
||||
|
||||
@@ -214,6 +214,21 @@ suite('SQL ProviderConnectionInfo tests', () => {
|
||||
assert.strictEqual(conn.options['encrypt'], 'true');
|
||||
});
|
||||
|
||||
test('constructor should initialize the options with encrypt strict', () => {
|
||||
let options: { [key: string]: string } = {};
|
||||
options['encrypt'] = 'strict';
|
||||
let conn2 = Object.assign({}, connectionProfile, { options: options });
|
||||
let conn = new ProviderConnectionInfo(capabilitiesService, conn2);
|
||||
|
||||
assert.strictEqual(conn.connectionName, conn2.connectionName);
|
||||
assert.strictEqual(conn.serverName, conn2.serverName);
|
||||
assert.strictEqual(conn.databaseName, conn2.databaseName);
|
||||
assert.strictEqual(conn.authenticationType, conn2.authenticationType);
|
||||
assert.strictEqual(conn.password, conn2.password);
|
||||
assert.strictEqual(conn.userName, conn2.userName);
|
||||
assert.strictEqual(conn.options['encrypt'], 'strict');
|
||||
});
|
||||
|
||||
test('getOptionsKey should create a valid unique id', () => {
|
||||
let conn = new ProviderConnectionInfo(capabilitiesService, connectionProfile);
|
||||
// **IMPORTANT** This should NEVER change without thorough review and consideration of side effects. This key controls
|
||||
|
||||
@@ -35,9 +35,11 @@ import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMess
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { ConnectionStringOptions } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||
import { isFalsyOrWhitespace } from 'vs/base/common/strings';
|
||||
import { AuthenticationType } from 'sql/platform/connection/common/constants';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { AuthLibrary, filterAccounts } from 'sql/workbench/services/accountManagement/browser/accountDialog';
|
||||
import { AuthenticationType, Actions } from 'sql/platform/connection/common/constants';
|
||||
import { AdsWidget } from 'sql/base/browser/ui/adsWidget';
|
||||
import { createCSSRule } from 'vs/base/browser/dom';
|
||||
|
||||
const ConnectionStringText = localize('connectionWidget.connectionString', "Connection string");
|
||||
|
||||
@@ -78,7 +80,7 @@ export class ConnectionWidget extends lifecycle.Disposable {
|
||||
protected _providerName: string;
|
||||
protected _connectionNameInputBox: InputBox;
|
||||
protected _databaseNameInputBox: Dropdown;
|
||||
protected _customOptionWidgets: (InputBox | SelectBox)[];
|
||||
protected _customOptionWidgets: AdsWidget[];
|
||||
protected _advancedButton: Button;
|
||||
private static readonly _authTypes: AuthenticationType[] =
|
||||
[AuthenticationType.AzureMFA, AuthenticationType.AzureMFAAndUser, AuthenticationType.Integrated, AuthenticationType.SqlLogin, AuthenticationType.DSTSAuth, AuthenticationType.None];
|
||||
@@ -192,6 +194,7 @@ export class ConnectionWidget extends lifecycle.Disposable {
|
||||
this.addConnectionNameOptions();
|
||||
this.addAdvancedOptions();
|
||||
this.updateRequiredStateForOptions();
|
||||
this.registerOnSelectionChangeEvents();
|
||||
if (this._connectionStringOptions.isEnabled) {
|
||||
// update the UI based on connection string setting after initialization
|
||||
this.handleConnectionStringOptionChange();
|
||||
@@ -255,7 +258,8 @@ export class ConnectionWidget extends lifecycle.Disposable {
|
||||
|
||||
protected addAuthenticationTypeOption(authTypeChanged: boolean = false): void {
|
||||
if (this._optionsMaps[ConnectionOptionSpecialType.authType]) {
|
||||
let authType = DialogHelper.appendRow(this._tableContainer, this._optionsMaps[ConnectionOptionSpecialType.authType].displayName, 'connection-label', 'connection-input', 'auth-type-row');
|
||||
let authType = DialogHelper.appendRow(this._tableContainer, this._optionsMaps[ConnectionOptionSpecialType.authType].displayName,
|
||||
'connection-label', 'connection-input', 'auth-type-row');
|
||||
DialogHelper.appendInputSelectBox(authType, this._authTypeSelectBox);
|
||||
}
|
||||
}
|
||||
@@ -264,10 +268,12 @@ export class ConnectionWidget extends lifecycle.Disposable {
|
||||
if (this._customOptions.length > 0) {
|
||||
this._customOptionWidgets = [];
|
||||
this._customOptions.forEach((option, i) => {
|
||||
let customOptionsContainer = DialogHelper.appendRow(this._tableContainer, option.displayName, 'connection-label', 'connection-input', 'custom-connection-options', false, option.description, 100);
|
||||
let customOptionsContainer = DialogHelper.appendRow(this._tableContainer, option.displayName, 'connection-label', 'connection-input',
|
||||
['custom-connection-options', `option-${option.name}`], false, option.description, 100);
|
||||
switch (option.valueType) {
|
||||
case ServiceOptionType.boolean:
|
||||
case ServiceOptionType.category:
|
||||
|
||||
let selectedValue = option.defaultValue;
|
||||
|
||||
let options = option.valueType === ServiceOptionType.category
|
||||
@@ -282,7 +288,7 @@ export class ConnectionWidget extends lifecycle.Disposable {
|
||||
return { text: v.displayName, value: v.value } as SelectOptionItemSQL;
|
||||
});
|
||||
|
||||
this._customOptionWidgets[i] = new SelectBox(options, selectedValue, this._contextViewService, customOptionsContainer, { ariaLabel: option.displayName });
|
||||
this._customOptionWidgets[i] = new SelectBox(options, selectedValue, this._contextViewService, customOptionsContainer, { ariaLabel: option.displayName }, option.name);
|
||||
DialogHelper.appendInputSelectBox(customOptionsContainer, this._customOptionWidgets[i] as SelectBox);
|
||||
this._register(styler.attachSelectBoxStyler(this._customOptionWidgets[i] as SelectBox, this._themeService));
|
||||
break;
|
||||
@@ -296,6 +302,70 @@ export class ConnectionWidget extends lifecycle.Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers on selection change event for connection options configured with 'onSelectionChange' property.
|
||||
* TODO extend this to include collection of other main and advanced option widgets here.
|
||||
*/
|
||||
protected registerOnSelectionChangeEvents(): void {
|
||||
//Register on selection change event for custom options
|
||||
this._customOptionWidgets.forEach((widget, i) => {
|
||||
if (widget instanceof SelectBox) {
|
||||
this._registerSelectionChangeEvents([this._customOptionWidgets], this._customOptions[i], widget);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _registerSelectionChangeEvents(collections: AdsWidget[][], option: azdata.ConnectionOption, widget: SelectBox) {
|
||||
if (option.onSelectionChange) {
|
||||
option.onSelectionChange.forEach((event) => {
|
||||
this._register(widget.onDidSelect(value => {
|
||||
let selectedValue = value.selected;
|
||||
event?.dependentOptionActions?.forEach((optionAction) => {
|
||||
let defaultValue: string | undefined = this._customOptions.find(o => o.name === optionAction.optionName)?.defaultValue;
|
||||
let widget: AdsWidget | undefined = this._findWidget(collections, optionAction.optionName);
|
||||
if (widget) {
|
||||
createCSSRule(`.hide-${widget.id} .option-${widget.id}`, `display: none;`);
|
||||
this._onValueChangeEvent(selectedValue, event.values, widget, defaultValue, optionAction.action);
|
||||
}
|
||||
});
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds Widget from provided collection of widgets using option name.
|
||||
* @param collections collections of widgets to search for the widget with the widget Id
|
||||
* @param id Widget Id
|
||||
* @returns Widget if found, undefined otherwise
|
||||
*/
|
||||
private _findWidget(collections: AdsWidget[][], id: string): AdsWidget | undefined {
|
||||
let foundWidget: AdsWidget | undefined;
|
||||
collections.forEach((collection) => {
|
||||
if (!foundWidget) {
|
||||
foundWidget = collection.find(widget => widget.id === id);
|
||||
}
|
||||
});
|
||||
return foundWidget;
|
||||
}
|
||||
|
||||
private _onValueChangeEvent(selectedValue: string, acceptedValues: string[],
|
||||
widget: AdsWidget, defaultValue: string, action: string): void {
|
||||
if ((acceptedValues.includes(selectedValue.toLocaleLowerCase()) && action === Actions.Show)
|
||||
|| (!acceptedValues.includes(selectedValue.toLocaleLowerCase()) && action === Actions.Hide)) {
|
||||
this._tableContainer.classList.remove(`hide-${widget.id}`);
|
||||
} else {
|
||||
// Support more Widget classes here as needed.
|
||||
if (widget instanceof SelectBox) {
|
||||
widget.select(widget.values.indexOf(defaultValue));
|
||||
} else if (widget instanceof InputBox) {
|
||||
widget.value = defaultValue;
|
||||
}
|
||||
this._tableContainer.classList.add(`hide-${widget.id}`);
|
||||
widget.hideMessage();
|
||||
}
|
||||
}
|
||||
|
||||
protected addServerNameOption(): void {
|
||||
// Server name
|
||||
let serverNameOption = this._optionsMaps[ConnectionOptionSpecialType.serverName];
|
||||
@@ -832,7 +902,7 @@ export class ConnectionWidget extends lifecycle.Disposable {
|
||||
if (value !== '') {
|
||||
if (widget instanceof SelectBox) {
|
||||
widget.selectWithOptionName(value);
|
||||
} else {
|
||||
} else if (widget instanceof InputBox) {
|
||||
widget.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user