mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-30 09:35:39 -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:
@@ -30,9 +30,22 @@ export const RenameObjectDialogTitle: string = localize('objectManagement.rename
|
||||
export const OwnerText: string = localize('objectManagement.ownerText', "Owner");
|
||||
export const BrowseText = localize('objectManagement.browseText', "Browse…");
|
||||
export const BrowseOwnerButtonAriaLabel = localize('objectManagement.browseForOwnerText', "Browse for an owner");
|
||||
export const AddMemberAriaLabel = localize('objectManagement.addMemberText', "Add a member");
|
||||
export const AddMemberAriaLabel = localize('objectManagement.addMembersText', "Add members");
|
||||
export const RemoveMemberAriaLabel = localize('objectManagement.removeMemberText', "Remove selected member");
|
||||
export const AddSecurableAriaLabel = localize('objectManagement.addSecurablesText', "Add securables");
|
||||
export const RemoveSecurableAriaLabel = localize('objectManagement.removeSecurablesText', "Remove selected securable");
|
||||
export const SecurablesText = localize('objectManagement.securablesText', "Securables");
|
||||
export const ExplicitPermissionsTableLabel = localize('objectManagement.explicitPermissionsTableLabel', "Explicit permissions for selected securable");
|
||||
export const EffectivePermissionsTableLabel = localize('objectManagement.effectivePermissionsTableLabel', "Effective permissions for selected securable");
|
||||
export const PermissionColumnHeader = localize('objectManagement.permissionColumnHeader', "Permission");
|
||||
export const GrantorColumnHeader = localize('objectManagement.grantorColumnHeader', "Grantor");
|
||||
export const GrantColumnHeader = localize('objectManagement.grantColumnHeader', "Grant");
|
||||
export const WithGrantColumnHeader = localize('objectManagement.withGrantColumnHeader', "With Grant");
|
||||
export const DenyColumnHeader = localize('objectManagement.denyColumnHeader', "Deny");
|
||||
export const SelectSecurablesDialogTitle = localize('objectManagement.selectSecurablesDialogTitle', "Select Securables");
|
||||
|
||||
export function ExplicitPermissionsTableLabelSelected(name: string): string { return localize('objectManagement.explicitPermissionsTableLabelSelected', "Explicit permissions for: {0}", name); }
|
||||
export function EffectivePermissionsTableLabelSelected(name: string): string { return localize('objectManagement.effectivePermissionsTableLabelSelected', "Effective permissions for: {0}", name); }
|
||||
|
||||
export function RefreshObjectExplorerError(error: string): string {
|
||||
return localize({
|
||||
@@ -133,12 +146,15 @@ export const LoginNotSelectedError = localize('objectManagement.loginNotSelected
|
||||
export const MembershipSectionHeader = localize('objectManagement.membershipLabel', "Membership");
|
||||
export const MemberSectionHeader = localize('objectManagement.membersLabel', "Members");
|
||||
export const SchemaText = localize('objectManagement.schemaLabel', "Schema");
|
||||
|
||||
// Database
|
||||
export const DatabaseExistsError = (dbName: string) => localize('objectManagement.databaseExistsError', "Database '{0}' already exists. Choose a different database name.", dbName);
|
||||
export const CollationText = localize('objectManagement.collationLabel', "Collation");
|
||||
export const RecoveryModelText = localize('objectManagement.recoveryModelLabel', "Recovery Model");
|
||||
export const CompatibilityLevelText = localize('objectManagement.compatibilityLevelLabel', "Compatibility Level");
|
||||
export const ContainmentTypeText = localize('objectManagement.containmentTypeLabel', "Containment Type");
|
||||
|
||||
|
||||
// Login
|
||||
export const BlankPasswordConfirmationText: string = localize('objectManagement.blankPasswordConfirmation', "Creating a login with a blank password is a security risk. Are you sure you want to continue?");
|
||||
export const DeleteLoginConfirmationText: string = localize('objectManagement.deleteLoginConfirmation', "Deleting server logins does not delete the database users associated with the logins. To complete the process, delete the users in each database. It may be necessary to first transfer the ownership of schemas to new users.");
|
||||
|
||||
@@ -67,6 +67,126 @@ export class ObjectManagementService extends BaseService implements IObjectManag
|
||||
}
|
||||
}
|
||||
|
||||
const ServerLevelSecurableTypes: ObjectManagement.SecurableTypeMetadata[] = [
|
||||
{
|
||||
name: 'Server',
|
||||
displayName: 'Server',
|
||||
permissions: [{
|
||||
name: 'CONNECT SQL',
|
||||
displayName: 'CONNECT SQL'
|
||||
}, {
|
||||
name: 'VIEW ANY DATABASE',
|
||||
displayName: 'VIEW ANY DATABASE'
|
||||
}]
|
||||
}, {
|
||||
name: 'ServerRole',
|
||||
displayName: 'Server Role',
|
||||
permissions: [{
|
||||
name: 'ALTER',
|
||||
displayName: 'ALTER'
|
||||
}, {
|
||||
name: 'CONTROL',
|
||||
displayName: 'CONTROL'
|
||||
}, {
|
||||
name: 'TAKE OWNERSHIP',
|
||||
displayName: 'TAKE OWNERSHIP'
|
||||
}]
|
||||
}
|
||||
];
|
||||
|
||||
const DatabaseLevelSecurableTypes: ObjectManagement.SecurableTypeMetadata[] = [
|
||||
{
|
||||
name: 'AggregateFunction',
|
||||
displayName: 'Aggregate Function',
|
||||
permissions: [{
|
||||
name: 'EXECUTE',
|
||||
displayName: 'EXECUTE'
|
||||
}, {
|
||||
name: 'ALTER',
|
||||
displayName: 'ALTER'
|
||||
}]
|
||||
}, {
|
||||
name: 'Table',
|
||||
displayName: 'Table',
|
||||
permissions: [{
|
||||
name: 'SELECT',
|
||||
displayName: 'SELECT'
|
||||
}, {
|
||||
name: 'ALTER',
|
||||
displayName: 'ALTER'
|
||||
}, {
|
||||
name: 'CONTROL',
|
||||
displayName: 'CONTROL'
|
||||
}, {
|
||||
name: 'TAKE OWNERSHIP',
|
||||
displayName: 'TAKE OWNERSHIP'
|
||||
}]
|
||||
}, {
|
||||
name: 'View',
|
||||
displayName: 'View',
|
||||
permissions: [{
|
||||
name: 'ALTER',
|
||||
displayName: 'ALTER'
|
||||
}, {
|
||||
name: 'CONTROL',
|
||||
displayName: 'CONTROL'
|
||||
}, {
|
||||
name: 'TAKE OWNERSHIP',
|
||||
displayName: 'TAKE OWNERSHIP'
|
||||
}]
|
||||
}
|
||||
]
|
||||
|
||||
const ServerLevelPermissions: ObjectManagement.SecurablePermissions[] = [
|
||||
{
|
||||
name: 'Server',
|
||||
type: 'Server',
|
||||
permissions: [
|
||||
{
|
||||
permission: 'CONNECT SQL',
|
||||
grant: true,
|
||||
grantor: 'sa',
|
||||
withGrant: undefined
|
||||
}, {
|
||||
permission: 'VIEW ANY DATABASE',
|
||||
grant: false,
|
||||
grantor: 'sa',
|
||||
withGrant: undefined
|
||||
}
|
||||
],
|
||||
effectivePermissions: ['CONNECT SQL', 'VIEW ANY DATABASE']
|
||||
}
|
||||
];
|
||||
|
||||
const DatabaseLevelPermissions: ObjectManagement.SecurablePermissions[] = [
|
||||
{
|
||||
name: 'table1',
|
||||
type: 'Table',
|
||||
schema: 'dbo',
|
||||
permissions: [
|
||||
{
|
||||
permission: 'SELECT',
|
||||
grant: true,
|
||||
grantor: '',
|
||||
withGrant: undefined
|
||||
}
|
||||
],
|
||||
effectivePermissions: ['SELECT']
|
||||
}, {
|
||||
name: 'view1',
|
||||
type: 'View',
|
||||
schema: 'Sales',
|
||||
permissions: [
|
||||
{
|
||||
permission: 'ALTER',
|
||||
grant: true,
|
||||
grantor: '',
|
||||
withGrant: undefined
|
||||
}
|
||||
],
|
||||
effectivePermissions: ['ALTER']
|
||||
}
|
||||
];
|
||||
export class TestObjectManagementService implements IObjectManagementService {
|
||||
initializeView(contextId: string, objectType: ObjectManagement.NodeType, connectionUri: string, database: string, isNewObject: boolean, parentUrn: string, objectUrn: string): Thenable<ObjectManagement.ObjectViewInfo<ObjectManagement.SqlObject>> {
|
||||
let obj;
|
||||
@@ -102,18 +222,18 @@ export class TestObjectManagementService implements IObjectManagementService {
|
||||
return this.delayAndResolve();
|
||||
}
|
||||
|
||||
async search(contextId: string, objectTypes: ObjectManagement.NodeType[], searchText: string, schema: string): Promise<ObjectManagement.SearchResultItem[]> {
|
||||
async search(contextId: string, objectTypes: ObjectManagement.NodeType[], searchText?: string, schema?: string): Promise<ObjectManagement.SearchResultItem[]> {
|
||||
const items: ObjectManagement.SearchResultItem[] = [];
|
||||
objectTypes.forEach(type => {
|
||||
items.push(...this.generateSearchResult(type, 15));
|
||||
items.push(...this.generateSearchResult(type, schema, 15));
|
||||
});
|
||||
return this.delayAndResolve(items);
|
||||
}
|
||||
|
||||
private generateSearchResult(objectType: ObjectManagement.NodeType, count: number): ObjectManagement.SearchResultItem[] {
|
||||
private generateSearchResult(objectType: ObjectManagement.NodeType, schema: string | undefined, count: number): ObjectManagement.SearchResultItem[] {
|
||||
let items: ObjectManagement.SearchResultItem[] = [];
|
||||
for (let i = 0; i < count; i++) {
|
||||
items.push(<ObjectManagement.SearchResultItem>{ name: `${objectType} ${i}`, type: objectType });
|
||||
items.push(<ObjectManagement.SearchResultItem>{ name: `${objectType} ${i}`, schema: schema, type: objectType });
|
||||
}
|
||||
return items;
|
||||
}
|
||||
@@ -136,7 +256,8 @@ export class TestObjectManagementService implements IObjectManagementService {
|
||||
serverRoles: ['public', 'bulkadmin'],
|
||||
connectPermission: true,
|
||||
isEnabled: true,
|
||||
isLockedOut: false
|
||||
isLockedOut: false,
|
||||
securablePermissions: []
|
||||
},
|
||||
authenticationTypes: [ObjectManagement.AuthenticationType.Sql, ObjectManagement.AuthenticationType.Windows],
|
||||
supportAdvancedOptions: true,
|
||||
@@ -144,7 +265,8 @@ export class TestObjectManagementService implements IObjectManagementService {
|
||||
canEditLockedOutState: false,
|
||||
languages: languages,
|
||||
databases: databases,
|
||||
serverRoles: serverRoles
|
||||
serverRoles: serverRoles,
|
||||
supportedSecurableTypes: ServerLevelSecurableTypes
|
||||
};
|
||||
} else {
|
||||
login = <ObjectManagement.LoginViewInfo>{
|
||||
@@ -160,7 +282,8 @@ export class TestObjectManagementService implements IObjectManagementService {
|
||||
connectPermission: true,
|
||||
isEnabled: true,
|
||||
isLockedOut: false,
|
||||
password: '******************'
|
||||
password: '******************',
|
||||
securablePermissions: ServerLevelPermissions
|
||||
},
|
||||
authenticationTypes: [ObjectManagement.AuthenticationType.Sql, ObjectManagement.AuthenticationType.Windows],
|
||||
supportAdvancedOptions: true,
|
||||
@@ -168,7 +291,8 @@ export class TestObjectManagementService implements IObjectManagementService {
|
||||
canEditLockedOutState: false,
|
||||
languages: languages,
|
||||
databases: databases,
|
||||
serverRoles: serverRoles
|
||||
serverRoles: serverRoles,
|
||||
supportedSecurableTypes: ServerLevelSecurableTypes
|
||||
};
|
||||
}
|
||||
return login;
|
||||
@@ -192,7 +316,8 @@ export class TestObjectManagementService implements IObjectManagementService {
|
||||
loginName: 'sa',
|
||||
ownedSchemas: [],
|
||||
databaseRoles: [],
|
||||
password: ''
|
||||
password: '',
|
||||
securablePermissions: []
|
||||
},
|
||||
languages: languages,
|
||||
schemas: schemas,
|
||||
@@ -203,7 +328,8 @@ export class TestObjectManagementService implements IObjectManagementService {
|
||||
ObjectManagement.UserType.AADAuthentication,
|
||||
ObjectManagement.UserType.SqlAuthentication,
|
||||
ObjectManagement.UserType.NoLoginAccess
|
||||
]
|
||||
],
|
||||
supportedSecurableTypes: DatabaseLevelSecurableTypes
|
||||
};
|
||||
} else {
|
||||
viewInfo = {
|
||||
@@ -214,7 +340,8 @@ export class TestObjectManagementService implements IObjectManagementService {
|
||||
defaultLanguage: '<default>',
|
||||
loginName: 'sa',
|
||||
ownedSchemas: ['dbo'],
|
||||
databaseRoles: ['dbmanager', 'bulkadmin']
|
||||
databaseRoles: ['dbmanager', 'bulkadmin'],
|
||||
securablePermissions: DatabaseLevelPermissions
|
||||
},
|
||||
languages: languages,
|
||||
schemas: schemas,
|
||||
@@ -225,7 +352,8 @@ export class TestObjectManagementService implements IObjectManagementService {
|
||||
ObjectManagement.UserType.AADAuthentication,
|
||||
ObjectManagement.UserType.SqlAuthentication,
|
||||
ObjectManagement.UserType.NoLoginAccess
|
||||
]
|
||||
],
|
||||
supportedSecurableTypes: DatabaseLevelSecurableTypes
|
||||
};
|
||||
}
|
||||
return viewInfo;
|
||||
@@ -237,19 +365,23 @@ export class TestObjectManagementService implements IObjectManagementService {
|
||||
name: '',
|
||||
members: [],
|
||||
owner: '',
|
||||
memberships: []
|
||||
memberships: [],
|
||||
securablePermissions: []
|
||||
},
|
||||
isFixedRole: false,
|
||||
serverRoles: ['ServerLevelServerRole 1', 'ServerLevelServerRole 2', 'ServerLevelServerRole 3', 'ServerLevelServerRole 4'],
|
||||
supportedSecurableTypes: ServerLevelSecurableTypes
|
||||
} : <ObjectManagement.ServerRoleViewInfo>{
|
||||
objectInfo: {
|
||||
name: 'ServerLevelServerRole 1',
|
||||
members: ['ServerLevelLogin 1', 'ServerLevelServerRole 2'],
|
||||
owner: 'ServerLevelLogin 2',
|
||||
memberships: ['ServerLevelServerRole 3', 'ServerLevelServerRole 4']
|
||||
memberships: ['ServerLevelServerRole 3', 'ServerLevelServerRole 4'],
|
||||
securablePermissions: ServerLevelPermissions
|
||||
},
|
||||
isFixedRole: false,
|
||||
serverRoles: ['ServerLevelServerRole 2', 'ServerLevelServerRole 3', 'ServerLevelServerRole 4']
|
||||
serverRoles: ['ServerLevelServerRole 2', 'ServerLevelServerRole 3', 'ServerLevelServerRole 4'],
|
||||
supportedSecurableTypes: ServerLevelSecurableTypes
|
||||
};
|
||||
}
|
||||
|
||||
@@ -259,16 +391,20 @@ export class TestObjectManagementService implements IObjectManagementService {
|
||||
name: '',
|
||||
defaultSchema: 'dbo',
|
||||
ownedSchemas: [],
|
||||
securablePermissions: []
|
||||
},
|
||||
schemas: ['dbo', 'sys', 'admin']
|
||||
schemas: ['dbo', 'sys', 'admin'],
|
||||
supportedSecurableTypes: []
|
||||
} : <ObjectManagement.ApplicationRoleViewInfo>{
|
||||
objectInfo: {
|
||||
name: 'app role1',
|
||||
password: '******************',
|
||||
defaultSchema: 'dbo',
|
||||
ownedSchemas: ['dbo'],
|
||||
securablePermissions: DatabaseLevelPermissions
|
||||
},
|
||||
schemas: ['dbo', 'sys', 'admin']
|
||||
schemas: ['dbo', 'sys', 'admin'],
|
||||
supportedSecurableTypes: DatabaseLevelSecurableTypes
|
||||
};
|
||||
}
|
||||
|
||||
@@ -278,17 +414,21 @@ export class TestObjectManagementService implements IObjectManagementService {
|
||||
name: '',
|
||||
owner: '',
|
||||
members: [],
|
||||
ownedSchemas: []
|
||||
ownedSchemas: [],
|
||||
securablePermissions: []
|
||||
},
|
||||
schemas: ['dbo', 'sys', 'admin']
|
||||
schemas: ['dbo', 'sys', 'admin'],
|
||||
supportedSecurableTypes: DatabaseLevelSecurableTypes
|
||||
} : <ObjectManagement.DatabaseRoleViewInfo>{
|
||||
objectInfo: {
|
||||
name: 'db role1',
|
||||
owner: '',
|
||||
members: [],
|
||||
ownedSchemas: ['dbo']
|
||||
ownedSchemas: ['dbo'],
|
||||
securablePermissions: DatabaseLevelPermissions
|
||||
},
|
||||
schemas: ['dbo', 'sys', 'admin']
|
||||
schemas: ['dbo', 'sys', 'admin'],
|
||||
supportedSecurableTypes: DatabaseLevelSecurableTypes
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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