mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Add Secure Enclaves dropdown with customizable Advanced options (#22019)
This commit is contained in:
@@ -928,15 +928,66 @@
|
|||||||
"categoryValues": [
|
"categoryValues": [
|
||||||
{
|
{
|
||||||
"displayName": "%mssql.disabled%",
|
"displayName": "%mssql.disabled%",
|
||||||
"name": "Disabled"
|
"name": "disabled"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"displayName": "%mssql.enabled%",
|
"displayName": "%mssql.enabled%",
|
||||||
"name": "Enabled"
|
"name": "enabled"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"isRequired": false,
|
"isRequired": false,
|
||||||
"isArray": false
|
"isArray": false,
|
||||||
|
"onSelectionChange": [
|
||||||
|
{
|
||||||
|
"values": [
|
||||||
|
"disabled",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"dependentOptionActions": [
|
||||||
|
{
|
||||||
|
"optionName": "secureEnclaves",
|
||||||
|
"action": "hide"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"specialValueType": null,
|
||||||
|
"isIdentity": false,
|
||||||
|
"name": "secureEnclaves",
|
||||||
|
"displayName": "%mssql.connectionOptions.secureEnclaves.displayName%",
|
||||||
|
"description": "%mssql.connectionOptions.secureEnclaves.description%",
|
||||||
|
"groupName": "%mssql.connectionOptions.groupName.security%",
|
||||||
|
"valueType": "category",
|
||||||
|
"defaultValue": null,
|
||||||
|
"objectType": null,
|
||||||
|
"categoryValues": [
|
||||||
|
{
|
||||||
|
"displayName": "%mssql.disabled%",
|
||||||
|
"name": "disabled"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"displayName": "%mssql.enabled%",
|
||||||
|
"name": "enabled"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isRequired": false,
|
||||||
|
"isArray": false,
|
||||||
|
"onSelectionChange": [
|
||||||
|
{
|
||||||
|
"values": [
|
||||||
|
"disabled",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"dependentOptionActions": [
|
||||||
|
{
|
||||||
|
"optionName": "attestationProtocol",
|
||||||
|
"action": "hide"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"specialValueType": null,
|
"specialValueType": null,
|
||||||
@@ -951,15 +1002,33 @@
|
|||||||
"categoryValues": [
|
"categoryValues": [
|
||||||
{
|
{
|
||||||
"displayName": "%mssql.connectionOptions.enclaveAttestationProtocol.categoryValues.HGS%",
|
"displayName": "%mssql.connectionOptions.enclaveAttestationProtocol.categoryValues.HGS%",
|
||||||
"name": "HGS"
|
"name": "hgs"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"displayName": "%mssql.connectionOptions.enclaveAttestationProtocol.categoryValues.AAS%",
|
"displayName": "%mssql.connectionOptions.enclaveAttestationProtocol.categoryValues.AAS%",
|
||||||
"name": "AAS"
|
"name": "aas"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"displayName": "%mssql.connectionOptions.enclaveAttestationProtocol.categoryValues.None%",
|
||||||
|
"name": "none"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"isRequired": false,
|
"isRequired": false,
|
||||||
"isArray": false
|
"isArray": false,
|
||||||
|
"onSelectionChange": [
|
||||||
|
{
|
||||||
|
"values": [
|
||||||
|
"none",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"dependentOptionActions": [
|
||||||
|
{
|
||||||
|
"optionName": "enclaveAttestationUrl",
|
||||||
|
"action": "hide"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"specialValueType": null,
|
"specialValueType": null,
|
||||||
|
|||||||
@@ -111,10 +111,13 @@
|
|||||||
"mssql.connectionOptions.currentLanguage.description": "The SQL Server language record name",
|
"mssql.connectionOptions.currentLanguage.description": "The SQL Server language record name",
|
||||||
"mssql.connectionOptions.columnEncryptionSetting.displayName": "Always Encrypted",
|
"mssql.connectionOptions.columnEncryptionSetting.displayName": "Always Encrypted",
|
||||||
"mssql.connectionOptions.columnEncryptionSetting.description": "Enables or disables Always Encrypted for the connection",
|
"mssql.connectionOptions.columnEncryptionSetting.description": "Enables or disables Always Encrypted for the connection",
|
||||||
|
"mssql.connectionOptions.secureEnclaves.displayName": "Secure Enclaves",
|
||||||
|
"mssql.connectionOptions.secureEnclaves.description": "Enables or disables Secure Enclaves for the connection",
|
||||||
"mssql.connectionOptions.enclaveAttestationProtocol.displayName": "Attestation Protocol",
|
"mssql.connectionOptions.enclaveAttestationProtocol.displayName": "Attestation Protocol",
|
||||||
"mssql.connectionOptions.enclaveAttestationProtocol.description": "Specifies a protocol for attesting a server-side enclave used with Always Encrypted with secure enclaves",
|
"mssql.connectionOptions.enclaveAttestationProtocol.description": "Specifies a protocol for attesting a server-side enclave used with Always Encrypted with secure enclaves",
|
||||||
"mssql.connectionOptions.enclaveAttestationProtocol.categoryValues.AAS": "Azure Attestation",
|
"mssql.connectionOptions.enclaveAttestationProtocol.categoryValues.AAS": "Azure Attestation",
|
||||||
"mssql.connectionOptions.enclaveAttestationProtocol.categoryValues.HGS": "Host Guardian Service",
|
"mssql.connectionOptions.enclaveAttestationProtocol.categoryValues.HGS": "Host Guardian Service",
|
||||||
|
"mssql.connectionOptions.enclaveAttestationProtocol.categoryValues.None": "None",
|
||||||
"mssql.connectionOptions.enclaveAttestationUrl.displayName": "Enclave Attestation URL",
|
"mssql.connectionOptions.enclaveAttestationUrl.displayName": "Enclave Attestation URL",
|
||||||
"mssql.connectionOptions.enclaveAttestationUrl.description": "Specifies an endpoint for attesting a server-side enclave used with Always Encrypted with secure enclaves",
|
"mssql.connectionOptions.enclaveAttestationUrl.description": "Specifies an endpoint for attesting a server-side enclave used with Always Encrypted with secure enclaves",
|
||||||
"mssql.connectionOptions.encrypt.displayName": "Encrypt",
|
"mssql.connectionOptions.encrypt.displayName": "Encrypt",
|
||||||
|
|||||||
6
src/sql/azdata.proposed.d.ts
vendored
6
src/sql/azdata.proposed.d.ts
vendored
@@ -575,6 +575,12 @@ declare module 'azdata' {
|
|||||||
onSelectionChange?: SelectionChangeEvent[];
|
onSelectionChange?: SelectionChangeEvent[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ServiceOption {
|
||||||
|
/**
|
||||||
|
* Used to define list of values based on which another option is rendered visible/hidden.
|
||||||
|
*/
|
||||||
|
onSelectionChange?: SelectionChangeEvent[];
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This change event defines actions
|
* This change event defines actions
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -195,22 +195,22 @@ export class SelectBox extends vsSelectBox implements AdsWidget {
|
|||||||
this.applyStyles();
|
this.applyStyles();
|
||||||
}
|
}
|
||||||
|
|
||||||
public selectWithOptionName(optionName?: string, selectFirstByDefault: boolean = true): void {
|
public selectWithOptionName(optionName?: string, selectFirstByDefault: boolean = true, forceSelectionEvent: boolean = false): void {
|
||||||
let option: number | undefined;
|
let option: number | undefined;
|
||||||
if (optionName !== undefined) {
|
if (optionName !== undefined) {
|
||||||
option = this._optionsDictionary.get(optionName);
|
option = this._optionsDictionary.get(optionName);
|
||||||
}
|
}
|
||||||
if (option !== undefined) {
|
if (option !== undefined) {
|
||||||
this.select(option);
|
this.select(option, forceSelectionEvent);
|
||||||
} else if (selectFirstByDefault) {
|
} else if (selectFirstByDefault) {
|
||||||
this.select(0);
|
this.select(0, forceSelectionEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override select(index: number): void {
|
public override select(index: number, forceSelectionEvent: boolean = false): void {
|
||||||
super.select(index);
|
super.select(index);
|
||||||
let selectedOptionIndex = this._optionsDictionary.get(this._selectedOption);
|
let selectedOptionIndex = this._optionsDictionary.get(this._selectedOption);
|
||||||
if (selectedOptionIndex === index) { // Not generating an event if the same value is selected.
|
if (!forceSelectionEvent && selectedOptionIndex === index) { // Not generating an event if the same value is selected.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this._dialogOptions !== undefined) {
|
if (this._dialogOptions !== undefined) {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import * as styler from 'vs/platform/theme/common/styler';
|
|||||||
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
|
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||||
import { Widget } from 'vs/base/browser/ui/widget';
|
import { Widget } from 'vs/base/browser/ui/widget';
|
||||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||||
import { append, $, clearNode } from 'vs/base/browser/dom';
|
import { append, $, clearNode, createCSSRule } from 'vs/base/browser/dom';
|
||||||
import { IThemeService, IColorTheme } from 'vs/platform/theme/common/themeService';
|
import { IThemeService, IColorTheme } from 'vs/platform/theme/common/themeService';
|
||||||
import { ILogService } from 'vs/platform/log/common/log';
|
import { ILogService } from 'vs/platform/log/common/log';
|
||||||
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
||||||
@@ -30,6 +30,8 @@ import { ServiceOptionType } from 'sql/platform/connection/common/interfaces';
|
|||||||
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
|
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
|
||||||
import { GroupHeaderBackground } from 'sql/platform/theme/common/colorRegistry';
|
import { GroupHeaderBackground } from 'sql/platform/theme/common/colorRegistry';
|
||||||
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfiguration';
|
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfiguration';
|
||||||
|
import { AdsWidget } from 'sql/base/browser/ui/adsWidget';
|
||||||
|
import { Actions } from 'sql/platform/connection/common/constants';
|
||||||
|
|
||||||
export interface IOptionsDialogOptions extends IModalOptions {
|
export interface IOptionsDialogOptions extends IModalOptions {
|
||||||
cancelLabel?: string;
|
cancelLabel?: string;
|
||||||
@@ -123,7 +125,7 @@ export class OptionsDialog extends Modal {
|
|||||||
private fillInOptions(container: HTMLElement, options: azdata.ServiceOption[]): void {
|
private fillInOptions(container: HTMLElement, options: azdata.ServiceOption[]): void {
|
||||||
for (let i = 0; i < options.length; i++) {
|
for (let i = 0; i < options.length; i++) {
|
||||||
let option: azdata.ServiceOption = options[i];
|
let option: azdata.ServiceOption = options[i];
|
||||||
let rowContainer = DialogHelper.appendRow(container, option.displayName, 'optionsDialog-label', 'optionsDialog-input');
|
let rowContainer = DialogHelper.appendRow(container, option.displayName, 'optionsDialog-label', 'optionsDialog-input', `option-${option.name}`);
|
||||||
const optionElement = OptionsDialogHelper.createOptionElement(option, rowContainer, this._optionValues, this._optionElements, this._contextViewService, (name) => this.onOptionLinkClicked(name));
|
const optionElement = OptionsDialogHelper.createOptionElement(option, rowContainer, this._optionValues, this._optionElements, this._contextViewService, (name) => this.onOptionLinkClicked(name));
|
||||||
this.disposableStore.add(optionElement.optionWidget);
|
this.disposableStore.add(optionElement.optionWidget);
|
||||||
}
|
}
|
||||||
@@ -204,6 +206,7 @@ export class OptionsDialog extends Modal {
|
|||||||
let bodyContainer = $('table.optionsDialog-table');
|
let bodyContainer = $('table.optionsDialog-table');
|
||||||
bodyContainer.setAttribute('role', 'presentation');
|
bodyContainer.setAttribute('role', 'presentation');
|
||||||
this.fillInOptions(bodyContainer, serviceOptions);
|
this.fillInOptions(bodyContainer, serviceOptions);
|
||||||
|
this.registerOnSelectionChangeEvents(optionValues, bodyContainer);
|
||||||
append(this._optionGroupsContainer!, bodyContainer);
|
append(this._optionGroupsContainer!, bodyContainer);
|
||||||
}
|
}
|
||||||
this.updateTheme(this._themeService.getColorTheme());
|
this.updateTheme(this._themeService.getColorTheme());
|
||||||
@@ -211,6 +214,71 @@ export class OptionsDialog extends Modal {
|
|||||||
this.show();
|
this.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers on selection change event for connection options configured with 'onSelectionChange' property.
|
||||||
|
*/
|
||||||
|
private registerOnSelectionChangeEvents(options: { [name: string]: any }, container: HTMLElement): void {
|
||||||
|
//Register on selection change event for all advanced options
|
||||||
|
for (let optionName in this._optionElements) {
|
||||||
|
let widget: Widget = this._optionElements[optionName].optionWidget;
|
||||||
|
if (widget instanceof SelectBox) {
|
||||||
|
this._registerSelectionChangeEvents([this._optionElements], this._optionElements[optionName].option, widget, container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _registerSelectionChangeEvents(collections: { [optionName: string]: OptionsDialogHelper.IOptionElement }[], option: azdata.ServiceOption, widget: SelectBox, container: HTMLElement) {
|
||||||
|
if (option?.onSelectionChange) {
|
||||||
|
option.onSelectionChange.forEach((event) => {
|
||||||
|
this._register(widget.onDidSelect(value => {
|
||||||
|
let selectedValue = value.selected;
|
||||||
|
event?.dependentOptionActions?.forEach((optionAction) => {
|
||||||
|
let defaultValue: string | undefined = collections[optionAction.optionName]?.option.defaultValue ?? '';
|
||||||
|
let widget: AdsWidget | undefined = this._findWidget(collections, optionAction.optionName);
|
||||||
|
if (widget) {
|
||||||
|
createCSSRule(`.hide-${widget.id} .option-${widget.id}`, `display: none;`);
|
||||||
|
this._onValueChangeEvent(container, selectedValue, event.values, widget, defaultValue, optionAction.action);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
// Clear selection change actions once event is registered.
|
||||||
|
option.onSelectionChange = undefined;
|
||||||
|
if (this.optionValues[option.name]) {
|
||||||
|
widget.selectWithOptionName(this.optionValues[option.name], true);
|
||||||
|
} else {
|
||||||
|
widget.select(0, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onValueChangeEvent(container: HTMLElement, 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)) {
|
||||||
|
container.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;
|
||||||
|
}
|
||||||
|
container.classList.add(`hide-${widget.id}`);
|
||||||
|
widget.hideMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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: { [optionName: string]: OptionsDialogHelper.IOptionElement }[], id: string): AdsWidget | undefined {
|
||||||
|
return collections.find(collection => !!collection[id].optionWidget)[id]?.optionWidget;
|
||||||
|
}
|
||||||
|
|
||||||
protected layout(height?: number): void {
|
protected layout(height?: number): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,11 +42,11 @@ export function createOptionElement(option: azdata.ServiceOption, rowContainer:
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
ariaLabel: option.displayName
|
ariaLabel: option.displayName
|
||||||
});
|
}, option.name);
|
||||||
optionWidget.value = optionValue;
|
optionWidget.value = optionValue;
|
||||||
inputElement = findElement(rowContainer, 'input');
|
inputElement = findElement(rowContainer, 'input');
|
||||||
} else if (option.valueType === ServiceOptionType.category || option.valueType === ServiceOptionType.boolean) {
|
} else if (option.valueType === ServiceOptionType.category || option.valueType === ServiceOptionType.boolean) {
|
||||||
optionWidget = new SelectBox(possibleInputs, optionValue.toString(), contextViewService, undefined, { ariaLabel: option.displayName });
|
optionWidget = new SelectBox(possibleInputs, optionValue.toString(), contextViewService, undefined, { ariaLabel: option.displayName }, option.name);
|
||||||
DialogHelper.appendInputSelectBox(rowContainer, optionWidget);
|
DialogHelper.appendInputSelectBox(rowContainer, optionWidget);
|
||||||
inputElement = findElement(rowContainer, 'monaco-select-box');
|
inputElement = findElement(rowContainer, 'monaco-select-box');
|
||||||
} else if (option.valueType === ServiceOptionType.string || option.valueType === ServiceOptionType.password) {
|
} else if (option.valueType === ServiceOptionType.string || option.valueType === ServiceOptionType.password) {
|
||||||
@@ -55,7 +55,7 @@ export function createOptionElement(option: azdata.ServiceOption, rowContainer:
|
|||||||
validation: (value: string) => (!value && option.isRequired) ? ({ type: MessageType.ERROR, content: option.displayName + missingErrorMessage }) : null
|
validation: (value: string) => (!value && option.isRequired) ? ({ type: MessageType.ERROR, content: option.displayName + missingErrorMessage }) : null
|
||||||
},
|
},
|
||||||
ariaLabel: option.displayName
|
ariaLabel: option.displayName
|
||||||
});
|
}, option.name);
|
||||||
optionWidget.value = optionValue;
|
optionWidget.value = optionValue;
|
||||||
if (option.valueType === ServiceOptionType.password) {
|
if (option.valueType === ServiceOptionType.password) {
|
||||||
optionWidget.inputElement.type = 'password';
|
optionWidget.inputElement.type = 'password';
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ export class AdvancedPropertiesController {
|
|||||||
categoryValues: connectionOption.categoryValues,
|
categoryValues: connectionOption.categoryValues,
|
||||||
isRequired: connectionOption.isRequired,
|
isRequired: connectionOption.isRequired,
|
||||||
isArray: undefined,
|
isArray: undefined,
|
||||||
|
onSelectionChange: connectionOption.onSelectionChange
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user