mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-07 01:25:38 -05:00
add securable settings (#22936)
* wip * Update typings * nullable * update test service * support securables * updata test data * fix issues * fix build failure * update test mocks * fix typo * fix reference * fix findobjectdialog issue * update SearchResultItem type * fix table component perf issue * hide effective permission for server role * hide effective permission for app role and db role * vbump sts and fix a couple issues * STS update and UI update * fix user login display issue * vbump sts
This commit is contained in:
@@ -3,14 +3,15 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as azdata from 'azdata';
|
||||
import { ObjectManagementDialogBase, ObjectManagementDialogOptions } from './objectManagementDialogBase';
|
||||
import { ObjectManagementDialogOptions } from './objectManagementDialogBase';
|
||||
import { IObjectManagementService, ObjectManagement } from 'mssql';
|
||||
import * as localizedConstants from '../localizedConstants';
|
||||
import { AlterApplicationRoleDocUrl, CreateApplicationRoleDocUrl } from '../constants';
|
||||
import { isValidSQLPassword } from '../utils';
|
||||
import { DefaultMaxTableHeight } from '../../ui/dialogBase';
|
||||
import { DefaultMaxTableRowCount } from '../../ui/dialogBase';
|
||||
import { PrincipalDialogBase } from './principalDialogBase';
|
||||
|
||||
export class ApplicationRoleDialog extends ObjectManagementDialogBase<ObjectManagement.ApplicationRoleInfo, ObjectManagement.ApplicationRoleViewInfo> {
|
||||
export class ApplicationRoleDialog extends PrincipalDialogBase<ObjectManagement.ApplicationRoleInfo, ObjectManagement.ApplicationRoleViewInfo> {
|
||||
// Sections
|
||||
private generalSection: azdata.GroupContainer;
|
||||
private ownedSchemasSection: azdata.GroupContainer;
|
||||
@@ -25,7 +26,7 @@ export class ApplicationRoleDialog extends ObjectManagementDialogBase<ObjectMana
|
||||
private ownedSchemaTable: azdata.TableComponent;
|
||||
|
||||
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
|
||||
super(objectManagementService, options);
|
||||
super(objectManagementService, options, true, false);
|
||||
}
|
||||
|
||||
protected override postInitializeData(): void {
|
||||
@@ -51,10 +52,11 @@ export class ApplicationRoleDialog extends ObjectManagementDialogBase<ObjectMana
|
||||
return errors;
|
||||
}
|
||||
|
||||
protected async initializeUI(): Promise<void> {
|
||||
protected override async initializeUI(): Promise<void> {
|
||||
await super.initializeUI();
|
||||
this.initializeGeneralSection();
|
||||
this.initializeOwnedSchemasSection();
|
||||
this.formContainer.addItems([this.generalSection, this.ownedSchemasSection]);
|
||||
this.formContainer.addItems([this.generalSection, this.ownedSchemasSection, this.securableSection], this.getSectionItemLayout());
|
||||
}
|
||||
|
||||
private initializeGeneralSection(): void {
|
||||
@@ -84,7 +86,7 @@ export class ApplicationRoleDialog extends ObjectManagementDialogBase<ObjectMana
|
||||
[localizedConstants.SchemaText],
|
||||
this.viewInfo.schemas,
|
||||
this.objectInfo.ownedSchemas,
|
||||
DefaultMaxTableHeight,
|
||||
DefaultMaxTableRowCount,
|
||||
(item) => {
|
||||
// It is not allowed to have unassigned schema.
|
||||
return this.objectInfo.ownedSchemas.indexOf(item) === -1;
|
||||
|
||||
@@ -3,14 +3,15 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as azdata from 'azdata';
|
||||
import { ObjectManagementDialogBase, ObjectManagementDialogOptions } from './objectManagementDialogBase';
|
||||
import { ObjectManagementDialogOptions } from './objectManagementDialogBase';
|
||||
import { IObjectManagementService, ObjectManagement } from 'mssql';
|
||||
import * as localizedConstants from '../localizedConstants';
|
||||
import { AlterDatabaseRoleDocUrl, CreateDatabaseRoleDocUrl } from '../constants';
|
||||
import { FindObjectDialog } from './findObjectDialog';
|
||||
import { DefaultMaxTableHeight } from '../../ui/dialogBase';
|
||||
import { DefaultMaxTableRowCount } from '../../ui/dialogBase';
|
||||
import { PrincipalDialogBase } from './principalDialogBase';
|
||||
|
||||
export class DatabaseRoleDialog extends ObjectManagementDialogBase<ObjectManagement.DatabaseRoleInfo, ObjectManagement.DatabaseRoleViewInfo> {
|
||||
export class DatabaseRoleDialog extends PrincipalDialogBase<ObjectManagement.DatabaseRoleInfo, ObjectManagement.DatabaseRoleViewInfo> {
|
||||
// Sections
|
||||
private generalSection: azdata.GroupContainer;
|
||||
private ownedSchemasSection: azdata.GroupContainer;
|
||||
@@ -27,18 +28,19 @@ export class DatabaseRoleDialog extends ObjectManagementDialogBase<ObjectManagem
|
||||
private memberTable: azdata.TableComponent;
|
||||
|
||||
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
|
||||
super(objectManagementService, options);
|
||||
super(objectManagementService, options, true, false);
|
||||
}
|
||||
|
||||
protected override get helpUrl(): string {
|
||||
return this.options.isNewObject ? CreateDatabaseRoleDocUrl : AlterDatabaseRoleDocUrl;
|
||||
}
|
||||
|
||||
protected async initializeUI(): Promise<void> {
|
||||
protected override async initializeUI(): Promise<void> {
|
||||
await super.initializeUI();
|
||||
this.initializeGeneralSection();
|
||||
this.initializeOwnedSchemasSection();
|
||||
this.initializeMemberSection();
|
||||
this.formContainer.addItems([this.generalSection, this.ownedSchemasSection, this.memberSection]);
|
||||
this.formContainer.addItems([this.generalSection, this.ownedSchemasSection, this.memberSection, this.securableSection], this.getSectionItemLayout());
|
||||
}
|
||||
|
||||
private initializeGeneralSection(): void {
|
||||
@@ -53,9 +55,11 @@ export class DatabaseRoleDialog extends ObjectManagementDialogBase<ObjectManagem
|
||||
const browseOwnerButton = this.createButton(localizedConstants.BrowseText, localizedConstants.BrowseOwnerButtonAriaLabel, async () => {
|
||||
const dialog = new FindObjectDialog(this.objectManagementService, {
|
||||
objectTypes: [ObjectManagement.NodeType.ApplicationRole, ObjectManagement.NodeType.DatabaseRole, ObjectManagement.NodeType.User],
|
||||
selectAllObjectTypes: true,
|
||||
multiSelect: false,
|
||||
contextId: this.contextId,
|
||||
title: localizedConstants.SelectDatabaseRoleOwnerDialogTitle
|
||||
title: localizedConstants.SelectDatabaseRoleOwnerDialogTitle,
|
||||
showSchemaColumn: false
|
||||
});
|
||||
await dialog.open();
|
||||
const result = await dialog.waitForClose();
|
||||
@@ -70,48 +74,45 @@ export class DatabaseRoleDialog extends ObjectManagementDialogBase<ObjectManagem
|
||||
}
|
||||
|
||||
private initializeMemberSection(): void {
|
||||
this.memberTable = this.createTable(localizedConstants.MemberSectionHeader, [
|
||||
{
|
||||
type: azdata.ColumnType.text,
|
||||
value: localizedConstants.NameText
|
||||
}
|
||||
], this.objectInfo.members.map(m => [m]));
|
||||
this.memberTable = this.createTable(localizedConstants.MemberSectionHeader, [localizedConstants.NameText], this.objectInfo.members.map(m => [m]));
|
||||
const buttonContainer = this.addButtonsForTable(this.memberTable, localizedConstants.AddMemberAriaLabel, localizedConstants.RemoveMemberAriaLabel,
|
||||
async () => {
|
||||
const dialog = new FindObjectDialog(this.objectManagementService, {
|
||||
objectTypes: [ObjectManagement.NodeType.DatabaseRole, ObjectManagement.NodeType.User],
|
||||
selectAllObjectTypes: true,
|
||||
multiSelect: true,
|
||||
contextId: this.contextId,
|
||||
title: localizedConstants.SelectDatabaseRoleMemberDialogTitle
|
||||
title: localizedConstants.SelectDatabaseRoleMemberDialogTitle,
|
||||
showSchemaColumn: false
|
||||
});
|
||||
await dialog.open();
|
||||
const result = await dialog.waitForClose();
|
||||
this.addMembers(result.selectedObjects.map(r => r.name));
|
||||
await this.addMembers(result.selectedObjects.map(r => r.name));
|
||||
},
|
||||
async () => {
|
||||
if (this.memberTable.selectedRows.length === 1) {
|
||||
this.removeMember(this.memberTable.selectedRows[0]);
|
||||
await this.removeMember(this.memberTable.selectedRows[0]);
|
||||
}
|
||||
});
|
||||
this.memberSection = this.createGroup(localizedConstants.MemberSectionHeader, [this.memberTable, buttonContainer]);
|
||||
}
|
||||
|
||||
private addMembers(names: string[]): void {
|
||||
private async addMembers(names: string[]): Promise<void> {
|
||||
names.forEach(n => {
|
||||
if (this.objectInfo.members.indexOf(n) === -1) {
|
||||
this.objectInfo.members.push(n);
|
||||
}
|
||||
});
|
||||
this.updateMembersTable();
|
||||
await this.updateMembersTable();
|
||||
}
|
||||
|
||||
private removeMember(idx: number): void {
|
||||
private async removeMember(idx: number): Promise<void> {
|
||||
this.objectInfo.members.splice(idx, 1);
|
||||
this.updateMembersTable();
|
||||
await this.updateMembersTable();
|
||||
}
|
||||
|
||||
private updateMembersTable(): void {
|
||||
this.setTableData(this.memberTable, this.objectInfo.members.map(m => [m]));
|
||||
private async updateMembersTable(): Promise<void> {
|
||||
await this.setTableData(this.memberTable, this.objectInfo.members.map(m => [m]));
|
||||
this.onFormFieldChange();
|
||||
}
|
||||
|
||||
@@ -120,7 +121,7 @@ export class DatabaseRoleDialog extends ObjectManagementDialogBase<ObjectManagem
|
||||
[localizedConstants.SchemaText],
|
||||
this.viewInfo.schemas,
|
||||
this.objectInfo.ownedSchemas,
|
||||
DefaultMaxTableHeight,
|
||||
DefaultMaxTableRowCount,
|
||||
(item) => {
|
||||
// It is not allowed to have unassigned schema.
|
||||
return this.objectInfo.ownedSchemas.indexOf(item) === -1;
|
||||
|
||||
@@ -5,15 +5,19 @@
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as mssql from 'mssql';
|
||||
import { DefaultTableListItemEnabledStateGetter, DefaultMaxTableHeight, DialogBase, TableListItemComparer, TableListItemValueGetter } from '../../ui/dialogBase';
|
||||
import { DefaultTableListItemEnabledStateGetter, DefaultMaxTableRowCount, DialogBase, TableListItemComparer } from '../../ui/dialogBase';
|
||||
import * as localizedConstants from '../localizedConstants';
|
||||
import { getErrorMessage } from '../../utils';
|
||||
|
||||
type ObjectType = string | { name: string, displayName: string };
|
||||
|
||||
export interface FindObjectDialogOptions {
|
||||
objectTypes: mssql.ObjectManagement.NodeType[];
|
||||
objectTypes: ObjectType[];
|
||||
selectAllObjectTypes: boolean;
|
||||
multiSelect: boolean;
|
||||
contextId: string;
|
||||
title: string;
|
||||
showSchemaColumn?: boolean;
|
||||
}
|
||||
|
||||
export interface FindObjectDialogResult {
|
||||
@@ -22,15 +26,10 @@ export interface FindObjectDialogResult {
|
||||
|
||||
const ObjectComparer: TableListItemComparer<mssql.ObjectManagement.SearchResultItem> =
|
||||
(item1, item2) => {
|
||||
return item1.name === item2.name && item1.type === item2.type;
|
||||
return item1.name === item2.name && item1.type === item2.type && item1.schema === item2.schema;
|
||||
};
|
||||
|
||||
const ObjectRowValueGetter: TableListItemValueGetter<mssql.ObjectManagement.SearchResultItem> =
|
||||
(item) => {
|
||||
return [item.name, localizedConstants.getNodeTypeDisplayName(item.type, true)];
|
||||
};
|
||||
|
||||
const ObjectsTableMaxHeight = 700;
|
||||
const ObjectsTableMaxRowCount = 20;
|
||||
|
||||
export class FindObjectDialog extends DialogBase<FindObjectDialogResult> {
|
||||
private objectTypesTable: azdata.TableComponent;
|
||||
@@ -38,7 +37,7 @@ export class FindObjectDialog extends DialogBase<FindObjectDialogResult> {
|
||||
private objectsTable: azdata.TableComponent;
|
||||
private objectsLoadingComponent: azdata.LoadingComponent;
|
||||
private result: FindObjectDialogResult;
|
||||
private selectedObjectTypes: string[] = [];
|
||||
private selectedObjectTypes: ObjectType[] = [];
|
||||
private allObjects: mssql.ObjectManagement.SearchResultItem[] = [];
|
||||
|
||||
constructor(private readonly objectManagementService: mssql.IObjectManagementService, private readonly options: FindObjectDialogOptions) {
|
||||
@@ -47,40 +46,52 @@ export class FindObjectDialog extends DialogBase<FindObjectDialogResult> {
|
||||
this.result = {
|
||||
selectedObjects: []
|
||||
};
|
||||
this.selectedObjectTypes = [...options.objectTypes];
|
||||
this.selectedObjectTypes = options.selectAllObjectTypes ? [...options.objectTypes] : [];
|
||||
}
|
||||
|
||||
private getObjectTypeName(objectType: ObjectType): string {
|
||||
return typeof objectType === 'string' ? objectType : objectType.name;
|
||||
}
|
||||
|
||||
private getObjectTypeDisplayName(objectType: ObjectType): string {
|
||||
return typeof objectType === 'string' ? localizedConstants.getNodeTypeDisplayName(objectType, true) : objectType.displayName;
|
||||
}
|
||||
|
||||
protected override async initialize(): Promise<void> {
|
||||
this.dialogObject.okButton.enabled = false;
|
||||
this.objectTypesTable = this.createTableList<string>(localizedConstants.ObjectTypeText,
|
||||
this.objectTypesTable = this.createTableList<ObjectType>(localizedConstants.ObjectTypeText,
|
||||
[localizedConstants.ObjectTypeText],
|
||||
this.options.objectTypes,
|
||||
this.selectedObjectTypes,
|
||||
DefaultMaxTableHeight,
|
||||
DefaultMaxTableRowCount,
|
||||
DefaultTableListItemEnabledStateGetter, (item) => {
|
||||
return [localizedConstants.getNodeTypeDisplayName(item, true)];
|
||||
return [this.getObjectTypeDisplayName(item)];
|
||||
}, (item1, item2) => {
|
||||
return this.getObjectTypeName(item1) === this.getObjectTypeName(item2);
|
||||
});
|
||||
this.findButton = this.createButton(localizedConstants.FindText, localizedConstants.FindText, async () => {
|
||||
await this.onFindObjectButtonClick();
|
||||
});
|
||||
}, this.options.selectAllObjectTypes);
|
||||
const buttonContainer = this.createButtonContainer([this.findButton]);
|
||||
const objectTypeSection = this.createGroup(localizedConstants.ObjectTypeText, [this.objectTypesTable, buttonContainer]);
|
||||
const columns = [localizedConstants.NameText, localizedConstants.ObjectTypeText];
|
||||
if (this.options.showSchemaColumn) {
|
||||
columns.splice(1, 0, localizedConstants.SchemaText);
|
||||
}
|
||||
|
||||
if (this.options.multiSelect) {
|
||||
this.objectsTable = this.createTableList<mssql.ObjectManagement.SearchResultItem>(localizedConstants.ObjectsText,
|
||||
[localizedConstants.NameText, localizedConstants.ObjectTypeText],
|
||||
columns,
|
||||
this.allObjects,
|
||||
this.result.selectedObjects,
|
||||
ObjectsTableMaxHeight,
|
||||
ObjectsTableMaxRowCount,
|
||||
DefaultTableListItemEnabledStateGetter,
|
||||
ObjectRowValueGetter,
|
||||
(item) => {
|
||||
return this.getObjectRowValue(item);
|
||||
},
|
||||
ObjectComparer);
|
||||
} else {
|
||||
this.objectsTable = this.createTable(localizedConstants.ObjectsText, [{
|
||||
value: localizedConstants.NameText,
|
||||
}, {
|
||||
value: localizedConstants.ObjectTypeText
|
||||
}], []);
|
||||
this.objectsTable = this.createTable(localizedConstants.ObjectsText, columns, [], ObjectsTableMaxRowCount);
|
||||
this.disposables.push(this.objectsTable.onRowSelected(async () => {
|
||||
if (this.objectsTable.selectedRows.length > 0) {
|
||||
this.result.selectedObjects = [this.allObjects[this.objectsTable.selectedRows[0]]];
|
||||
@@ -95,7 +106,7 @@ export class FindObjectDialog extends DialogBase<FindObjectDialogResult> {
|
||||
}).component();
|
||||
const objectsSection = this.createGroup(localizedConstants.ObjectsText, [this.objectsLoadingComponent]);
|
||||
|
||||
this.formContainer.addItems([objectTypeSection, objectsSection]);
|
||||
this.formContainer.addItems([objectTypeSection, objectsSection], this.getSectionItemLayout());
|
||||
}
|
||||
|
||||
protected override get dialogResult(): FindObjectDialogResult | undefined {
|
||||
@@ -107,16 +118,18 @@ export class FindObjectDialog extends DialogBase<FindObjectDialogResult> {
|
||||
this.objectsLoadingComponent.loading = true;
|
||||
this.findButton.enabled = false;
|
||||
try {
|
||||
const results = await this.objectManagementService.search(this.options.contextId, <mssql.ObjectManagement.NodeType[]>this.selectedObjectTypes);
|
||||
const results = await this.objectManagementService.search(this.options.contextId, this.selectedObjectTypes.map(item => this.getObjectTypeName(item)));
|
||||
this.allObjects.splice(0, this.allObjects.length, ...results);
|
||||
let data;
|
||||
if (this.options.multiSelect) {
|
||||
data = this.getDataForTableList(this.allObjects, this.result.selectedObjects, DefaultTableListItemEnabledStateGetter, ObjectRowValueGetter, ObjectComparer);
|
||||
data = this.getDataForTableList(this.allObjects, this.result.selectedObjects, DefaultTableListItemEnabledStateGetter, (item) => {
|
||||
return this.getObjectRowValue(item);
|
||||
}, ObjectComparer);
|
||||
}
|
||||
else {
|
||||
data = this.allObjects.map(item => ObjectRowValueGetter(item));
|
||||
data = this.allObjects.map(item => { return this.getObjectRowValue(item); });
|
||||
}
|
||||
this.setTableData(this.objectsTable, data, ObjectsTableMaxHeight);
|
||||
await this.setTableData(this.objectsTable, data, ObjectsTableMaxRowCount);
|
||||
this.objectsLoadingComponent.loadingCompletedText = localizedConstants.LoadingObjectsCompletedText(results.length);
|
||||
} catch (err) {
|
||||
this.dialogObject.message = {
|
||||
@@ -132,4 +145,12 @@ export class FindObjectDialog extends DialogBase<FindObjectDialogResult> {
|
||||
this.findButton.enabled = this.selectedObjectTypes.length > 0;
|
||||
this.dialogObject.okButton.enabled = this.result.selectedObjects.length > 0;
|
||||
}
|
||||
|
||||
private getObjectRowValue(item: mssql.ObjectManagement.SearchResultItem): string[] {
|
||||
const row = [item.name, this.getObjectTypeName(item.type)];
|
||||
if (this.options.showSchemaColumn) {
|
||||
row.splice(1, 0, item.schema);
|
||||
}
|
||||
return row;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,16 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import { ObjectManagementDialogBase, ObjectManagementDialogOptions } from './objectManagementDialogBase';
|
||||
import { ObjectManagementDialogOptions } from './objectManagementDialogBase';
|
||||
import { IObjectManagementService, ObjectManagement } from 'mssql';
|
||||
import * as objectManagementLoc from '../localizedConstants';
|
||||
import * as uiLoc from '../../ui/localizedConstants';
|
||||
import { AlterLoginDocUrl, CreateLoginDocUrl, PublicServerRoleName } from '../constants';
|
||||
import { isValidSQLPassword } from '../utils';
|
||||
import { DefaultMaxTableHeight } from '../../ui/dialogBase';
|
||||
import { DefaultMaxTableRowCount } from '../../ui/dialogBase';
|
||||
import { PrincipalDialogBase } from './principalDialogBase';
|
||||
|
||||
export class LoginDialog extends ObjectManagementDialogBase<ObjectManagement.Login, ObjectManagement.LoginViewInfo> {
|
||||
export class LoginDialog extends PrincipalDialogBase<ObjectManagement.Login, ObjectManagement.LoginViewInfo> {
|
||||
private generalSection: azdata.GroupContainer;
|
||||
private sqlAuthSection: azdata.GroupContainer;
|
||||
private serverRoleSection: azdata.GroupContainer;
|
||||
@@ -34,7 +35,7 @@ export class LoginDialog extends ObjectManagementDialogBase<ObjectManagement.Log
|
||||
private lockedOutCheckbox: azdata.CheckBoxComponent;
|
||||
|
||||
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
|
||||
super(objectManagementService, options);
|
||||
super(objectManagementService, options, false);
|
||||
}
|
||||
|
||||
protected override get helpUrl(): string {
|
||||
@@ -82,7 +83,8 @@ export class LoginDialog extends ObjectManagementDialogBase<ObjectManagement.Log
|
||||
this.objectInfo.password = this.objectInfo.password ?? '';
|
||||
}
|
||||
|
||||
protected async initializeUI(): Promise<void> {
|
||||
protected override async initializeUI(): Promise<void> {
|
||||
await super.initializeUI();
|
||||
const sections: azdata.Component[] = [];
|
||||
this.initializeGeneralSection();
|
||||
sections.push(this.generalSection);
|
||||
@@ -94,12 +96,13 @@ export class LoginDialog extends ObjectManagementDialogBase<ObjectManagement.Log
|
||||
|
||||
this.initializeServerRolesSection();
|
||||
sections.push(this.serverRoleSection);
|
||||
sections.push(this.securableSection);
|
||||
|
||||
if (this.viewInfo.supportAdvancedOptions) {
|
||||
this.initializeAdvancedSection();
|
||||
sections.push(this.advancedSection);
|
||||
}
|
||||
this.formContainer.addItems(sections);
|
||||
this.formContainer.addItems(sections, this.getSectionItemLayout());
|
||||
}
|
||||
|
||||
private initializeGeneralSection(): void {
|
||||
@@ -203,7 +206,7 @@ export class LoginDialog extends ObjectManagementDialogBase<ObjectManagement.Log
|
||||
items.push(defaultDatabaseContainer, defaultLanguageContainer, this.connectPermissionCheckbox);
|
||||
}
|
||||
|
||||
this.advancedSection = this.createGroup(objectManagementLoc.AdvancedSectionHeader, items);
|
||||
this.advancedSection = this.createGroup(objectManagementLoc.AdvancedSectionHeader, items, true, true);
|
||||
}
|
||||
|
||||
private initializeServerRolesSection(): void {
|
||||
@@ -211,7 +214,7 @@ export class LoginDialog extends ObjectManagementDialogBase<ObjectManagement.Log
|
||||
[objectManagementLoc.ServerRoleTypeDisplayNameInTitle],
|
||||
this.viewInfo.serverRoles,
|
||||
this.objectInfo.serverRoles,
|
||||
DefaultMaxTableHeight,
|
||||
DefaultMaxTableRowCount,
|
||||
(item) => {
|
||||
return item !== PublicServerRoleName
|
||||
});
|
||||
@@ -220,7 +223,7 @@ export class LoginDialog extends ObjectManagementDialogBase<ObjectManagement.Log
|
||||
|
||||
private setViewByAuthenticationType(): void {
|
||||
if (this.authTypeDropdown.value === objectManagementLoc.SQLAuthenticationTypeDisplayText) {
|
||||
this.addItem(this.formContainer, this.sqlAuthSection, 1);
|
||||
this.addItem(this.formContainer, this.sqlAuthSection, this.getSectionItemLayout(), 1);
|
||||
} else if (this.authTypeDropdown.value !== objectManagementLoc.SQLAuthenticationTypeDisplayText) {
|
||||
this.removeItem(this.formContainer, this.sqlAuthSection);
|
||||
}
|
||||
|
||||
228
extensions/mssql/src/objectManagement/ui/principalDialogBase.ts
Normal file
228
extensions/mssql/src/objectManagement/ui/principalDialogBase.ts
Normal file
@@ -0,0 +1,228 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as mssql from 'mssql';
|
||||
import * as localizedConstants from '../localizedConstants';
|
||||
|
||||
import { ObjectManagementDialogBase, ObjectManagementDialogOptions } from './objectManagementDialogBase';
|
||||
import { FindObjectDialog } from './findObjectDialog';
|
||||
import { deepClone } from '../../util/objects';
|
||||
import { DefaultTableWidth, getTableHeight } from '../../ui/dialogBase';
|
||||
|
||||
const GrantColumnIndex = 2;
|
||||
const WithGrantColumnIndex = 3;
|
||||
const DenyColumnIndex = 4;
|
||||
|
||||
/**
|
||||
* Base class for security principal dialogs such as user, role, etc.
|
||||
*/
|
||||
export abstract class PrincipalDialogBase<ObjectInfoType extends mssql.ObjectManagement.SecurityPrincipalObject, ViewInfoType extends mssql.ObjectManagement.SecurityPrincipalViewInfo<ObjectInfoType>> extends ObjectManagementDialogBase<ObjectInfoType, ViewInfoType> {
|
||||
protected securableTable: azdata.TableComponent;
|
||||
protected permissionTable: azdata.TableComponent;
|
||||
protected effectivePermissionTable: azdata.TableComponent;
|
||||
protected securableSection: azdata.GroupContainer;
|
||||
protected explicitPermissionTableLabel: azdata.TextComponent;
|
||||
protected effectivePermissionTableLabel: azdata.TextComponent;
|
||||
private securablePermissions: mssql.ObjectManagement.SecurablePermissions[] = [];
|
||||
|
||||
constructor(objectManagementService: mssql.IObjectManagementService, options: ObjectManagementDialogOptions, private readonly showSchemaColumn: boolean, private readonly supportEffectivePermissions: boolean = true) {
|
||||
super(objectManagementService, options);
|
||||
}
|
||||
|
||||
protected override async initializeUI(): Promise<void> {
|
||||
this.securablePermissions = deepClone(this.objectInfo.securablePermissions);
|
||||
this.initializeSecurableSection();
|
||||
}
|
||||
|
||||
private initializeSecurableSection(): void {
|
||||
const items: azdata.Component[] = [];
|
||||
const securableTableColumns = [localizedConstants.NameText, localizedConstants.ObjectTypeText];
|
||||
if (this.showSchemaColumn) {
|
||||
securableTableColumns.splice(1, 0, localizedConstants.SchemaText);
|
||||
}
|
||||
this.securableTable = this.createTable(localizedConstants.SecurablesText, securableTableColumns, this.getSecurableTableData());
|
||||
const buttonContainer = this.addButtonsForTable(this.securableTable, localizedConstants.AddSecurableAriaLabel, localizedConstants.RemoveSecurableAriaLabel,
|
||||
() => this.onAddSecurableButtonClicked(), () => this.onRemoveSecurableButtonClicked());
|
||||
this.disposables.push(this.securableTable.onRowSelected(async () => {
|
||||
await this.updatePermissionsTable();
|
||||
}));
|
||||
this.explicitPermissionTableLabel = this.modelView.modelBuilder.text().withProps({ value: localizedConstants.ExplicitPermissionsTableLabel }).component();
|
||||
this.permissionTable = this.modelView.modelBuilder.table().withProps({
|
||||
ariaLabel: localizedConstants.ExplicitPermissionsTableLabel,
|
||||
columns:
|
||||
[{
|
||||
type: azdata.ColumnType.text,
|
||||
value: localizedConstants.PermissionColumnHeader
|
||||
}, {
|
||||
type: azdata.ColumnType.text,
|
||||
value: localizedConstants.GrantorColumnHeader
|
||||
}, {
|
||||
type: azdata.ColumnType.checkBox,
|
||||
value: localizedConstants.GrantColumnHeader
|
||||
}, {
|
||||
type: azdata.ColumnType.checkBox,
|
||||
value: localizedConstants.WithGrantColumnHeader
|
||||
}, {
|
||||
type: azdata.ColumnType.checkBox,
|
||||
value: localizedConstants.DenyColumnHeader
|
||||
}],
|
||||
data: [],
|
||||
height: getTableHeight(0),
|
||||
width: DefaultTableWidth
|
||||
}).component();
|
||||
this.disposables.push(this.permissionTable.onCellAction(async (arg: azdata.ICheckboxCellActionEventArgs) => {
|
||||
const permissionName = this.permissionTable.data[arg.row][0];
|
||||
const securable = this.securablePermissions[this.securableTable.selectedRows[0]];
|
||||
let permission: mssql.ObjectManagement.SecurablePermissionItem = securable.permissions.find(securablePermission => securablePermission.permission === permissionName);
|
||||
if (!permission) {
|
||||
permission = {
|
||||
permission: permissionName,
|
||||
grantor: ''
|
||||
};
|
||||
securable.permissions.push(permission);
|
||||
}
|
||||
if (arg.column === GrantColumnIndex) {
|
||||
permission.grant = arg.checked ? true : undefined;
|
||||
if (!arg.checked) {
|
||||
permission.withGrant = undefined;
|
||||
}
|
||||
} else if (arg.column === WithGrantColumnIndex) {
|
||||
permission.withGrant = arg.checked ? true : undefined;
|
||||
if (arg.checked) {
|
||||
permission.grant = true;
|
||||
}
|
||||
} else if (arg.column === DenyColumnIndex) {
|
||||
permission.grant = arg.checked ? false : undefined;
|
||||
if (arg.checked) {
|
||||
permission.withGrant = undefined;
|
||||
}
|
||||
}
|
||||
await this.updatePermissionsTable();
|
||||
this.updateSecurablePermissions();
|
||||
// Restore the focus to previously selected cell.
|
||||
this.permissionTable.setActiveCell(arg.row, arg.column);
|
||||
}));
|
||||
|
||||
items.push(this.securableTable, buttonContainer, this.explicitPermissionTableLabel, this.permissionTable);
|
||||
if (this.showEffectivePermissions) {
|
||||
this.effectivePermissionTableLabel = this.modelView.modelBuilder.text().withProps({ value: localizedConstants.EffectivePermissionsTableLabel }).component();
|
||||
this.effectivePermissionTable = this.createTable(localizedConstants.EffectivePermissionsTableLabel, [localizedConstants.PermissionColumnHeader], []);
|
||||
items.push(this.effectivePermissionTableLabel, this.effectivePermissionTable);
|
||||
}
|
||||
this.securableSection = this.createGroup(localizedConstants.SecurablesText, items, true, true);
|
||||
}
|
||||
|
||||
private async onAddSecurableButtonClicked(): Promise<void> {
|
||||
const dialog = new FindObjectDialog(this.objectManagementService, {
|
||||
objectTypes: this.viewInfo.supportedSecurableTypes,
|
||||
selectAllObjectTypes: false,
|
||||
multiSelect: true,
|
||||
contextId: this.contextId,
|
||||
title: localizedConstants.SelectSecurablesDialogTitle,
|
||||
showSchemaColumn: this.showSchemaColumn
|
||||
});
|
||||
await dialog.open();
|
||||
const result = await dialog.waitForClose();
|
||||
if (result && result.selectedObjects.length > 0) {
|
||||
result.selectedObjects.forEach(obj => {
|
||||
if (this.securablePermissions.find(securable => securable.type === obj.type && securable.name === obj.name && securable.schema === obj.schema)) {
|
||||
return;
|
||||
}
|
||||
const securableTypeMetadata = this.viewInfo.supportedSecurableTypes.find(securableType => securableType.name === obj.type);
|
||||
this.securablePermissions.push({
|
||||
name: obj.name,
|
||||
schema: obj.schema,
|
||||
type: obj.type,
|
||||
permissions: securableTypeMetadata.permissions.map(permission => {
|
||||
return {
|
||||
permission: permission.name,
|
||||
grantor: '',
|
||||
grant: undefined,
|
||||
withGrant: undefined
|
||||
};
|
||||
}),
|
||||
effectivePermissions: []
|
||||
});
|
||||
});
|
||||
const data = this.getSecurableTableData();
|
||||
await this.setTableData(this.securableTable, data);
|
||||
}
|
||||
}
|
||||
|
||||
private async onRemoveSecurableButtonClicked(): Promise<void> {
|
||||
if (this.securableTable.selectedRows.length === 1) {
|
||||
this.securablePermissions.splice(this.securableTable.selectedRows[0], 1);
|
||||
const data = this.getSecurableTableData();
|
||||
await this.setTableData(this.securableTable, data);
|
||||
this.updateSecurablePermissions();
|
||||
}
|
||||
}
|
||||
|
||||
private getSecurableTableData(): string[][] {
|
||||
return this.securablePermissions.map(securable => {
|
||||
const row = [securable.name, this.getSecurableTypeDisplayName(securable.type)];
|
||||
if (this.showSchemaColumn) {
|
||||
row.splice(1, 0, securable.schema);
|
||||
}
|
||||
return row;
|
||||
});
|
||||
}
|
||||
|
||||
private async updatePermissionsTable(): Promise<void> {
|
||||
let permissionsTableData: any[][] = [];
|
||||
let effectivePermissionsTableData: any[][] = [];
|
||||
let explicitPermissionsLabel = localizedConstants.ExplicitPermissionsTableLabel;
|
||||
let effectivePermissionsLabel = localizedConstants.EffectivePermissionsTableLabel;
|
||||
if (this.securableTable.selectedRows.length === 1) {
|
||||
const securable = this.securablePermissions[this.securableTable.selectedRows[0]];
|
||||
if (securable) {
|
||||
const securableDisplayName = securable.schema ? `${securable.schema}.${securable.name}` : securable.name;
|
||||
explicitPermissionsLabel = localizedConstants.ExplicitPermissionsTableLabelSelected(securableDisplayName);
|
||||
effectivePermissionsLabel = localizedConstants.EffectivePermissionsTableLabelSelected(securableDisplayName);
|
||||
const securableTypeMetadata = this.viewInfo.supportedSecurableTypes.find(securableType => securableType.name === securable.type);
|
||||
permissionsTableData = securable.permissions.map(permission => {
|
||||
return [permission.permission, permission.grantor, { checked: permission.grant === true }, { checked: permission.withGrant === true }, { checked: permission.grant === false }];
|
||||
});
|
||||
permissionsTableData = securableTypeMetadata.permissions.map(permissionMetadata => {
|
||||
const permission = securable.permissions.find(securablePermission => securablePermission.permission === permissionMetadata.name);
|
||||
return [
|
||||
permissionMetadata.name,
|
||||
permission?.grantor ?? '',
|
||||
{ checked: permission?.grant === true },
|
||||
{ checked: permission?.withGrant === true },
|
||||
{ checked: permission?.grant === false }];
|
||||
});
|
||||
effectivePermissionsTableData = securable.effectivePermissions.map(permission => [permission]);
|
||||
}
|
||||
}
|
||||
this.explicitPermissionTableLabel.value = explicitPermissionsLabel;
|
||||
await this.setTableData(this.permissionTable, permissionsTableData);
|
||||
if (this.showEffectivePermissions) {
|
||||
this.effectivePermissionTableLabel.value = effectivePermissionsLabel;
|
||||
await this.setTableData(this.effectivePermissionTable, effectivePermissionsTableData);
|
||||
}
|
||||
}
|
||||
|
||||
private updateSecurablePermissions(): void {
|
||||
// Only save securable permissions that have grant or deny value.
|
||||
this.objectInfo.securablePermissions = deepClone(this.securablePermissions.filter((securablePermissions) => {
|
||||
return securablePermissions.permissions.some(permission => permission.grant !== undefined);
|
||||
}));
|
||||
this.objectInfo.securablePermissions.forEach(securablePermissions => {
|
||||
securablePermissions.permissions = securablePermissions.permissions.filter(permission => permission.grant !== undefined);
|
||||
});
|
||||
this.onFormFieldChange();
|
||||
}
|
||||
|
||||
private getSecurableTypeDisplayName(securableType: string): string {
|
||||
const securableTypeMetadata = this.viewInfo.supportedSecurableTypes.find(securableTypeMetadata => securableTypeMetadata.name === securableType);
|
||||
return securableTypeMetadata ? securableTypeMetadata.displayName : securableType;
|
||||
}
|
||||
|
||||
private get showEffectivePermissions(): boolean {
|
||||
return !this.options.isNewObject && this.supportEffectivePermissions;
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,14 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as azdata from 'azdata';
|
||||
import { ObjectManagementDialogBase, ObjectManagementDialogOptions } from './objectManagementDialogBase';
|
||||
import { ObjectManagementDialogOptions } from './objectManagementDialogBase';
|
||||
import { IObjectManagementService, ObjectManagement } from 'mssql';
|
||||
import * as localizedConstants from '../localizedConstants';
|
||||
import { AlterServerRoleDocUrl, CreateServerRoleDocUrl } from '../constants';
|
||||
import { FindObjectDialog } from './findObjectDialog';
|
||||
import { PrincipalDialogBase } from './principalDialogBase';
|
||||
|
||||
export class ServerRoleDialog extends ObjectManagementDialogBase<ObjectManagement.ServerRoleInfo, ObjectManagement.ServerRoleViewInfo> {
|
||||
export class ServerRoleDialog extends PrincipalDialogBase<ObjectManagement.ServerRoleInfo, ObjectManagement.ServerRoleViewInfo> {
|
||||
// Sections
|
||||
private generalSection: azdata.GroupContainer;
|
||||
private membershipSection: azdata.GroupContainer;
|
||||
@@ -27,22 +28,24 @@ export class ServerRoleDialog extends ObjectManagementDialogBase<ObjectManagemen
|
||||
|
||||
|
||||
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
|
||||
super(objectManagementService, options);
|
||||
super(objectManagementService, options, false, false);
|
||||
}
|
||||
|
||||
protected override get helpUrl(): string {
|
||||
return this.options.isNewObject ? CreateServerRoleDocUrl : AlterServerRoleDocUrl;
|
||||
}
|
||||
|
||||
protected async initializeUI(): Promise<void> {
|
||||
protected override async initializeUI(): Promise<void> {
|
||||
await super.initializeUI();
|
||||
this.initializeGeneralSection();
|
||||
this.initializeMemberSection();
|
||||
const sections: azdata.Component[] = [this.generalSection, this.memberSection];
|
||||
if (!this.viewInfo.isFixedRole) {
|
||||
this.initializeMembershipSection();
|
||||
sections.push(this.membershipSection);
|
||||
sections.push(this.securableSection);
|
||||
}
|
||||
this.formContainer.addItems(sections);
|
||||
this.formContainer.addItems(sections, this.getSectionItemLayout());
|
||||
}
|
||||
|
||||
private initializeGeneralSection(): void {
|
||||
@@ -57,6 +60,7 @@ export class ServerRoleDialog extends ObjectManagementDialogBase<ObjectManagemen
|
||||
const browseOwnerButton = this.createButton(localizedConstants.BrowseText, localizedConstants.BrowseOwnerButtonAriaLabel, async () => {
|
||||
const dialog = new FindObjectDialog(this.objectManagementService, {
|
||||
objectTypes: [ObjectManagement.NodeType.ServerLevelLogin, ObjectManagement.NodeType.ServerLevelServerRole],
|
||||
selectAllObjectTypes: true,
|
||||
multiSelect: false,
|
||||
contextId: this.contextId,
|
||||
title: localizedConstants.SelectServerRoleOwnerDialogTitle
|
||||
@@ -76,48 +80,44 @@ export class ServerRoleDialog extends ObjectManagementDialogBase<ObjectManagemen
|
||||
}
|
||||
|
||||
private initializeMemberSection(): void {
|
||||
this.memberTable = this.createTable(localizedConstants.MemberSectionHeader, [
|
||||
{
|
||||
type: azdata.ColumnType.text,
|
||||
value: localizedConstants.NameText
|
||||
}
|
||||
], this.objectInfo.members.map(m => [m]));
|
||||
this.memberTable = this.createTable(localizedConstants.MemberSectionHeader, [localizedConstants.NameText], this.objectInfo.members.map(m => [m]));
|
||||
const buttonContainer = this.addButtonsForTable(this.memberTable, localizedConstants.AddMemberAriaLabel, localizedConstants.RemoveMemberAriaLabel,
|
||||
async () => {
|
||||
const dialog = new FindObjectDialog(this.objectManagementService, {
|
||||
objectTypes: [ObjectManagement.NodeType.ServerLevelLogin, ObjectManagement.NodeType.ServerLevelServerRole],
|
||||
selectAllObjectTypes: true,
|
||||
multiSelect: true,
|
||||
contextId: this.contextId,
|
||||
title: localizedConstants.SelectServerRoleMemberDialogTitle
|
||||
});
|
||||
await dialog.open();
|
||||
const result = await dialog.waitForClose();
|
||||
this.addMembers(result.selectedObjects.map(r => r.name));
|
||||
await this.addMembers(result.selectedObjects.map(r => r.name));
|
||||
},
|
||||
async () => {
|
||||
if (this.memberTable.selectedRows.length === 1) {
|
||||
this.removeMember(this.memberTable.selectedRows[0]);
|
||||
await this.removeMember(this.memberTable.selectedRows[0]);
|
||||
}
|
||||
});
|
||||
this.memberSection = this.createGroup(localizedConstants.MemberSectionHeader, [this.memberTable, buttonContainer]);
|
||||
}
|
||||
|
||||
private addMembers(names: string[]): void {
|
||||
private async addMembers(names: string[]): Promise<void> {
|
||||
names.forEach(n => {
|
||||
if (this.objectInfo.members.indexOf(n) === -1) {
|
||||
this.objectInfo.members.push(n);
|
||||
}
|
||||
});
|
||||
this.updateMembersTable();
|
||||
await this.updateMembersTable();
|
||||
}
|
||||
|
||||
private removeMember(idx: number): void {
|
||||
private async removeMember(idx: number): Promise<void> {
|
||||
this.objectInfo.members.splice(idx, 1);
|
||||
this.updateMembersTable();
|
||||
await this.updateMembersTable();
|
||||
}
|
||||
|
||||
private updateMembersTable(): void {
|
||||
this.setTableData(this.memberTable, this.objectInfo.members.map(m => [m]));
|
||||
private async updateMembersTable(): Promise<void> {
|
||||
await this.setTableData(this.memberTable, this.objectInfo.members.map(m => [m]));
|
||||
this.onFormFieldChange();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,14 +3,15 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as azdata from 'azdata';
|
||||
import { ObjectManagementDialogBase, ObjectManagementDialogOptions } from './objectManagementDialogBase';
|
||||
import { ObjectManagementDialogOptions } from './objectManagementDialogBase';
|
||||
import { IObjectManagementService, ObjectManagement } from 'mssql';
|
||||
import * as localizedConstants from '../localizedConstants';
|
||||
import { AlterUserDocUrl, CreateUserDocUrl } from '../constants';
|
||||
import { isValidSQLPassword } from '../utils';
|
||||
import { DefaultMaxTableHeight } from '../../ui/dialogBase';
|
||||
import { DefaultMaxTableRowCount } from '../../ui/dialogBase';
|
||||
import { PrincipalDialogBase } from './principalDialogBase';
|
||||
|
||||
export class UserDialog extends ObjectManagementDialogBase<ObjectManagement.User, ObjectManagement.UserViewInfo> {
|
||||
export class UserDialog extends PrincipalDialogBase<ObjectManagement.User, ObjectManagement.UserViewInfo> {
|
||||
private generalSection: azdata.GroupContainer;
|
||||
private ownedSchemaSection: azdata.GroupContainer;
|
||||
private membershipSection: azdata.GroupContainer;
|
||||
@@ -31,7 +32,7 @@ export class UserDialog extends ObjectManagementDialogBase<ObjectManagement.User
|
||||
private membershipTable: azdata.TableComponent;
|
||||
|
||||
constructor(objectManagementService: IObjectManagementService, options: ObjectManagementDialogOptions) {
|
||||
super(objectManagementService, options);
|
||||
super(objectManagementService, options, true);
|
||||
}
|
||||
|
||||
protected override get helpUrl(): string {
|
||||
@@ -61,12 +62,13 @@ export class UserDialog extends ObjectManagementDialogBase<ObjectManagement.User
|
||||
return errors;
|
||||
}
|
||||
|
||||
protected async initializeUI(): Promise<void> {
|
||||
protected override async initializeUI(): Promise<void> {
|
||||
await super.initializeUI();
|
||||
this.initializeGeneralSection();
|
||||
this.initializeOwnedSchemaSection();
|
||||
this.initializeMembershipSection();
|
||||
this.initializeAdvancedSection();
|
||||
this.formContainer.addItems([this.generalSection, this.ownedSchemaSection, this.membershipSection, this.advancedSection]);
|
||||
this.formContainer.addItems([this.generalSection, this.ownedSchemaSection, this.membershipSection, this.securableSection, this.advancedSection], this.getSectionItemLayout());
|
||||
setTimeout(() => {
|
||||
this.setViewByUserType();
|
||||
}, 100);
|
||||
@@ -94,7 +96,7 @@ export class UserDialog extends ObjectManagementDialogBase<ObjectManagement.User
|
||||
|
||||
this.loginDropdown = this.createDropdown(localizedConstants.LoginText, async (newValue) => {
|
||||
this.objectInfo.loginName = newValue;
|
||||
}, this.viewInfo.logins, this.objectInfo.loginName, this.options.isNewObject);
|
||||
}, this.options.isNewObject ? this.viewInfo.logins : [this.objectInfo.loginName], this.objectInfo.loginName, this.options.isNewObject);
|
||||
this.loginContainer = this.createLabelInputContainer(localizedConstants.LoginText, this.loginDropdown);
|
||||
|
||||
this.passwordInput = this.createPasswordInputBox(localizedConstants.PasswordText, async (newValue) => {
|
||||
@@ -119,7 +121,7 @@ export class UserDialog extends ObjectManagementDialogBase<ObjectManagement.User
|
||||
[localizedConstants.SchemaText],
|
||||
this.viewInfo.schemas,
|
||||
this.objectInfo.ownedSchemas,
|
||||
DefaultMaxTableHeight,
|
||||
DefaultMaxTableRowCount,
|
||||
(item) => {
|
||||
// It is not allowed to have unassigned schema.
|
||||
return this.objectInfo.ownedSchemas.indexOf(item) === -1;
|
||||
@@ -157,6 +159,11 @@ export class UserDialog extends ObjectManagementDialogBase<ObjectManagement.User
|
||||
this.addItem(this.generalSection, this.confirmPasswordContainer);
|
||||
this.addItem(this.formContainer, this.advancedSection);
|
||||
break;
|
||||
case ObjectManagement.UserType.WindowsUser:
|
||||
if (this.objectInfo.loginName) {
|
||||
this.addItem(this.generalSection, this.loginContainer);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user