mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-13 17:22:15 -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:
@@ -199,7 +199,8 @@ export function createViewContext(): ViewTestContext {
|
||||
data: [] as any[][],
|
||||
columns: [] as string[],
|
||||
onRowSelected: onClick.event,
|
||||
appendData: (data: any[][]) => undefined,
|
||||
appendData: (_data: any[][]) => undefined,
|
||||
setActiveCell: (_row: number, _column: number) => undefined
|
||||
});
|
||||
|
||||
let loadingComponent: () => azdata.LoadingComponent = () => Object.assign({}, componentBase, {
|
||||
|
||||
@@ -301,6 +301,9 @@ export class MockTableComponent extends MockUIComponent implements azdata.TableC
|
||||
appendData(data: any[][]): Thenable<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
setActiveCell(row: number, column: number): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
export class MockDeclarativeTableComponent extends MockUIComponent implements azdata.DeclarativeTableComponent {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
||||
"version": "4.8.0.5",
|
||||
"version": "4.8.0.7",
|
||||
"downloadFileNames": {
|
||||
"Windows_86": "win-x86-net7.0.zip",
|
||||
"Windows_64": "win-x64-net7.0.zip",
|
||||
|
||||
111
extensions/mssql/src/mssql.d.ts
vendored
111
extensions/mssql/src/mssql.d.ts
vendored
@@ -917,7 +917,14 @@ declare module 'mssql' {
|
||||
}
|
||||
|
||||
/**
|
||||
* Base interface for the object view information
|
||||
* Base interface for all the security principal objects. e.g. Login, Server Role, Database Role...
|
||||
*/
|
||||
export interface SecurityPrincipalObject extends SqlObject {
|
||||
securablePermissions: SecurablePermissions[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Base interface for the object view information.
|
||||
*/
|
||||
export interface ObjectViewInfo<T extends SqlObject> {
|
||||
/**
|
||||
@@ -926,10 +933,52 @@ declare module 'mssql' {
|
||||
objectInfo: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Securable type metadata.
|
||||
*/
|
||||
export interface SecurableTypeMetadata {
|
||||
/**
|
||||
* Name of the securable type.
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* Display name of the securable type.
|
||||
*/
|
||||
displayName: string;
|
||||
/**
|
||||
* Permissions supported by the securable type.
|
||||
*/
|
||||
permissions: PermissionMetadata[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Permission metadata.
|
||||
*/
|
||||
export interface PermissionMetadata {
|
||||
/**
|
||||
* Name of the permission.
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* Display name of the permission.
|
||||
*/
|
||||
displayName: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base interface for security principal object's view information.
|
||||
*/
|
||||
export interface SecurityPrincipalViewInfo<T extends SecurityPrincipalObject> extends ObjectViewInfo<T> {
|
||||
/**
|
||||
* The securable types that the security principal object can be granted permissions on.
|
||||
*/
|
||||
supportedSecurableTypes: SecurableTypeMetadata[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Server level login.
|
||||
*/
|
||||
export interface Login extends SqlObject {
|
||||
export interface Login extends SecurityPrincipalObject {
|
||||
/**
|
||||
* Authentication type.
|
||||
*/
|
||||
@@ -1025,7 +1074,7 @@ declare module 'mssql' {
|
||||
/**
|
||||
* The information required to render the login view.
|
||||
*/
|
||||
export interface LoginViewInfo extends ObjectViewInfo<Login> {
|
||||
export interface LoginViewInfo extends SecurityPrincipalViewInfo<Login> {
|
||||
/**
|
||||
* The authentication types supported by the server.
|
||||
*/
|
||||
@@ -1062,20 +1111,24 @@ declare module 'mssql' {
|
||||
/**
|
||||
* The permission information a principal has on a securable.
|
||||
*/
|
||||
export interface Permission {
|
||||
export interface SecurablePermissionItem {
|
||||
/**
|
||||
* Name of the permission.
|
||||
* name of the permission.
|
||||
*/
|
||||
name: string;
|
||||
permission: string;
|
||||
/**
|
||||
* Whether the permission is granted or denied.
|
||||
* Name of the grantor.
|
||||
*/
|
||||
grant: boolean;
|
||||
grantor: string;
|
||||
/**
|
||||
* Whether the permission is granted or denied. Undefined means not specified.
|
||||
*/
|
||||
grant?: boolean;
|
||||
/**
|
||||
* Whether the pincipal can grant this permission to other principals.
|
||||
* The value will be ignored if the grant property is set to false.
|
||||
*/
|
||||
withGrant: boolean;
|
||||
withGrant?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1083,13 +1136,25 @@ declare module 'mssql' {
|
||||
*/
|
||||
export interface SecurablePermissions {
|
||||
/**
|
||||
* The securable.
|
||||
* The securable name.
|
||||
*/
|
||||
securable: SqlObject;
|
||||
name: string;
|
||||
/**
|
||||
* The Permissions.
|
||||
* The securable type.
|
||||
*/
|
||||
permissions: Permission[];
|
||||
type: string;
|
||||
/**
|
||||
* The schema name of the object if applicable.
|
||||
*/
|
||||
schema?: string;
|
||||
/**
|
||||
* The permissions.
|
||||
*/
|
||||
permissions: SecurablePermissionItem[];
|
||||
/**
|
||||
* The effective permissions. Includes all permissions granted to the principal, including those granted through role memberships.
|
||||
*/
|
||||
effectivePermissions: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1135,7 +1200,7 @@ declare module 'mssql' {
|
||||
/**
|
||||
* Database user.
|
||||
*/
|
||||
export interface User extends SqlObject {
|
||||
export interface User extends SecurityPrincipalObject {
|
||||
/**
|
||||
* Type of the user.
|
||||
*/
|
||||
@@ -1172,7 +1237,7 @@ declare module 'mssql' {
|
||||
/**
|
||||
* The information required to render the user view.
|
||||
*/
|
||||
export interface UserViewInfo extends ObjectViewInfo<User> {
|
||||
export interface UserViewInfo extends SecurityPrincipalViewInfo<User> {
|
||||
/**
|
||||
* All user types supported by the database.
|
||||
*/
|
||||
@@ -1198,7 +1263,7 @@ declare module 'mssql' {
|
||||
/**
|
||||
* Interface representing the server role object.
|
||||
*/
|
||||
export interface ServerRoleInfo extends SqlObject {
|
||||
export interface ServerRoleInfo extends SecurityPrincipalObject {
|
||||
/**
|
||||
* Name of the server principal that owns the server role.
|
||||
*/
|
||||
@@ -1216,7 +1281,7 @@ declare module 'mssql' {
|
||||
/**
|
||||
* Interface representing the information required to render the server role view.
|
||||
*/
|
||||
export interface ServerRoleViewInfo extends ObjectViewInfo<ServerRoleInfo> {
|
||||
export interface ServerRoleViewInfo extends SecurityPrincipalViewInfo<ServerRoleInfo> {
|
||||
/**
|
||||
* Whether the server role is a fixed role.
|
||||
*/
|
||||
@@ -1230,7 +1295,7 @@ declare module 'mssql' {
|
||||
/**
|
||||
* Interface representing the application role object.
|
||||
*/
|
||||
export interface ApplicationRoleInfo extends SqlObject {
|
||||
export interface ApplicationRoleInfo extends SecurityPrincipalObject {
|
||||
/**
|
||||
* Default schema of the application role.
|
||||
*/
|
||||
@@ -1248,7 +1313,7 @@ declare module 'mssql' {
|
||||
/**
|
||||
* Interface representing the information required to render the application role view.
|
||||
*/
|
||||
export interface ApplicationRoleViewInfo extends ObjectViewInfo<ApplicationRoleInfo> {
|
||||
export interface ApplicationRoleViewInfo extends SecurityPrincipalViewInfo<ApplicationRoleInfo> {
|
||||
/**
|
||||
* List of all the schemas in the database.
|
||||
*/
|
||||
@@ -1258,7 +1323,7 @@ declare module 'mssql' {
|
||||
/**
|
||||
* Interface representing the database role object.
|
||||
*/
|
||||
export interface DatabaseRoleInfo extends SqlObject {
|
||||
export interface DatabaseRoleInfo extends SecurityPrincipalObject {
|
||||
/**
|
||||
* Name of the database principal that owns the database role.
|
||||
*/
|
||||
@@ -1276,7 +1341,7 @@ declare module 'mssql' {
|
||||
/**
|
||||
* Interface representing the information required to render the database role view.
|
||||
*/
|
||||
export interface DatabaseRoleViewInfo extends ObjectViewInfo<DatabaseRoleInfo> {
|
||||
export interface DatabaseRoleViewInfo extends SecurityPrincipalViewInfo<DatabaseRoleInfo> {
|
||||
/**
|
||||
* List of all the schemas in the database.
|
||||
*/
|
||||
@@ -1294,7 +1359,7 @@ declare module 'mssql' {
|
||||
/**
|
||||
* type of the object.
|
||||
*/
|
||||
type: NodeType;
|
||||
type: string;
|
||||
/**
|
||||
* schema of the object.
|
||||
*/
|
||||
@@ -1369,7 +1434,7 @@ declare module 'mssql' {
|
||||
* @param searchText Search text.
|
||||
* @param schema Schema to search in.
|
||||
*/
|
||||
search(contextId: string, objectTypes: ObjectManagement.NodeType[], searchText?: string, schema?: string): Thenable<ObjectManagement.SearchResultItem[]>;
|
||||
search(contextId: string, objectTypes: string[], searchText?: string, schema?: string): Thenable<ObjectManagement.SearchResultItem[]>;
|
||||
}
|
||||
// Object Management - End.
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -11,13 +11,13 @@ import * as uiLoc from '../ui/localizedConstants';
|
||||
export const DefaultLabelWidth = 150;
|
||||
export const DefaultInputWidth = 300;
|
||||
export const DefaultTableWidth = DefaultInputWidth + DefaultLabelWidth;
|
||||
export const DefaultMaxTableHeight = 400;
|
||||
export const DefaultMaxTableRowCount = 10;
|
||||
export const DefaultMinTableRowCount = 1;
|
||||
export const TableRowHeight = 25;
|
||||
export const TableColumnHeaderHeight = 30;
|
||||
const TableRowHeight = 25;
|
||||
const TableColumnHeaderHeight = 30;
|
||||
|
||||
export function getTableHeight(rowCount: number, minRowCount: number = DefaultMinTableRowCount, maxHeight: number = DefaultMaxTableHeight): number {
|
||||
return Math.min(Math.max(rowCount, minRowCount) * TableRowHeight + TableColumnHeaderHeight, maxHeight);
|
||||
export function getTableHeight(rowCount: number, minRowCount: number = DefaultMinTableRowCount, maxRowCount: number = DefaultMaxTableRowCount): number {
|
||||
return Math.min(Math.max(rowCount, minRowCount), maxRowCount) * TableRowHeight + TableColumnHeaderHeight;
|
||||
}
|
||||
|
||||
export type TableListItemEnabledStateGetter<T> = (item: T) => boolean;
|
||||
@@ -168,7 +168,7 @@ export abstract class DialogBase<DialogResult> {
|
||||
columnNames: string[],
|
||||
allItems: T[],
|
||||
selectedItems: T[],
|
||||
maxHeight: number = DefaultMaxTableHeight,
|
||||
maxRowCount: number = DefaultMaxTableRowCount,
|
||||
enabledStateGetter: TableListItemEnabledStateGetter<T> = DefaultTableListItemEnabledStateGetter,
|
||||
rowValueGetter: TableListItemValueGetter<T> = DefaultTableListItemValueGetter,
|
||||
itemComparer: TableListItemComparer<T> = DefaultTableListItemComparer): azdata.TableComponent {
|
||||
@@ -179,7 +179,7 @@ export abstract class DialogBase<DialogResult> {
|
||||
data: data,
|
||||
columns: [
|
||||
{
|
||||
value: uiLoc.SelectedText,
|
||||
value: uiLoc.SelectText,
|
||||
type: azdata.ColumnType.checkBox,
|
||||
options: { actionOnCheckbox: azdata.ActionOnCellCheckboxCheck.customAction }
|
||||
}, ...columnNames.map(name => {
|
||||
@@ -187,7 +187,7 @@ export abstract class DialogBase<DialogResult> {
|
||||
})
|
||||
],
|
||||
width: DefaultTableWidth,
|
||||
height: getTableHeight(data.length, DefaultMinTableRowCount, maxHeight)
|
||||
height: getTableHeight(data.length, DefaultMinTableRowCount, maxRowCount)
|
||||
}
|
||||
).component();
|
||||
this.disposables.push(table.onCellAction!((arg: azdata.ICheckboxCellActionEventArgs) => {
|
||||
@@ -203,9 +203,11 @@ export abstract class DialogBase<DialogResult> {
|
||||
return table;
|
||||
}
|
||||
|
||||
protected setTableData(table: azdata.TableComponent, data: any[][], maxHeight: number = DefaultMaxTableHeight) {
|
||||
table.data = data;
|
||||
table.height = getTableHeight(data.length, DefaultMinTableRowCount, maxHeight);
|
||||
protected async setTableData(table: azdata.TableComponent, data: any[][], maxRowCount: number = DefaultMaxTableRowCount) {
|
||||
await table.updateProperties({
|
||||
data: data,
|
||||
height: getTableHeight(data.length, DefaultMinTableRowCount, maxRowCount)
|
||||
});
|
||||
}
|
||||
|
||||
protected getDataForTableList<T>(
|
||||
@@ -221,14 +223,14 @@ export abstract class DialogBase<DialogResult> {
|
||||
});
|
||||
}
|
||||
|
||||
protected createTable(ariaLabel: string, columns: azdata.TableColumn[], data: any[][], maxHeight: number = DefaultMaxTableHeight): azdata.TableComponent {
|
||||
protected createTable(ariaLabel: string, columns: string[], data: any[][], maxRowCount: number = DefaultMaxTableRowCount): azdata.TableComponent {
|
||||
const table = this.modelView.modelBuilder.table().withProps(
|
||||
{
|
||||
ariaLabel: ariaLabel,
|
||||
data: data,
|
||||
columns: columns,
|
||||
width: DefaultTableWidth,
|
||||
height: getTableHeight(data.length, DefaultMinTableRowCount, maxHeight)
|
||||
height: getTableHeight(data.length, DefaultMinTableRowCount, maxRowCount)
|
||||
}
|
||||
).component();
|
||||
return table;
|
||||
@@ -238,7 +240,8 @@ export abstract class DialogBase<DialogResult> {
|
||||
let addButton: azdata.ButtonComponent;
|
||||
let removeButton: azdata.ButtonComponent;
|
||||
const updateButtons = () => {
|
||||
removeButton.enabled = table.selectedRows.length > 0;
|
||||
this.onFormFieldChange();
|
||||
removeButton.enabled = table.selectedRows?.length === 1 && table.selectedRows[0] !== -1 && table.selectedRows[0] < table.data.length;
|
||||
}
|
||||
addButton = this.createButton(uiLoc.AddText, addButtonAriaLabel, async () => {
|
||||
await addHandler();
|
||||
@@ -246,6 +249,9 @@ export abstract class DialogBase<DialogResult> {
|
||||
});
|
||||
removeButton = this.createButton(uiLoc.RemoveText, removeButtonAriaLabel, async () => {
|
||||
await removeHandler();
|
||||
if (table.selectedRows.length === 1 && table.selectedRows[0] >= table.data.length) {
|
||||
table.selectedRows = [table.data.length - 1];
|
||||
}
|
||||
updateButtons();
|
||||
}, false);
|
||||
this.disposables.push(table.onRowSelected(() => {
|
||||
@@ -308,12 +314,12 @@ export abstract class DialogBase<DialogResult> {
|
||||
}
|
||||
}
|
||||
|
||||
protected addItem(container: azdata.DivContainer | azdata.FlexContainer, item: azdata.Component, index?: number): void {
|
||||
protected addItem(container: azdata.DivContainer | azdata.FlexContainer, item: azdata.Component, itemLayout?: azdata.FlexItemLayout, index?: number): void {
|
||||
if (container.items.indexOf(item) === -1) {
|
||||
if (index === undefined) {
|
||||
container.addItem(item);
|
||||
container.addItem(item, itemLayout);
|
||||
} else {
|
||||
container.insertItem(item, index);
|
||||
container.insertItem(item, index, itemLayout);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -327,6 +333,10 @@ export abstract class DialogBase<DialogResult> {
|
||||
private createFormContainer(items: azdata.Component[]): azdata.DivContainer {
|
||||
return this.modelView.modelBuilder.divContainer().withLayout({ width: 'calc(100% - 20px)', height: 'calc(100% - 20px)' }).withProps({
|
||||
CSSStyles: { 'padding': '10px' }
|
||||
}).withItems(items, { CSSStyles: { 'margin-block-end': '10px' } }).component();
|
||||
}).withItems(items, this.getSectionItemLayout()).component();
|
||||
}
|
||||
|
||||
protected getSectionItemLayout(): azdata.FlexItemLayout {
|
||||
return { CSSStyles: { 'margin-block-end': '5px' } };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export const YesText: string = localize('mssql.ui.yesText', "Yes");
|
||||
export const OkText: string = localize('mssql.ui.OkText', "OK");
|
||||
export const LoadingDialogText: string = localize('mssql.ui.loadingDialog', "Loading dialog...");
|
||||
export const ScriptText: string = localize('mssql.ui.scriptText', "Script");
|
||||
export const SelectedText = localize('objectManagement.selectedLabel', "Selected");
|
||||
export const SelectText = localize('objectManagement.selectLabel', "Select");
|
||||
export const AddText = localize('objectManagement.addText', "Add…");
|
||||
export const RemoveText = localize('objectManagement.removeText', "Remove");
|
||||
export const NoActionScriptedMessage: string = localize('mssql.ui.noActionScriptedMessage', "There is no action to be scripted.");
|
||||
|
||||
@@ -295,7 +295,8 @@ export function createViewContext(): ViewTestContext {
|
||||
columns: [] as string[],
|
||||
onRowSelected: onClick.event,
|
||||
onCellAction: onClick.event,
|
||||
appendData: (_data: any[][]) => undefined
|
||||
appendData: (_data: any[][]) => undefined,
|
||||
setActiveCell: (_row: number, _column: number) => undefined
|
||||
});
|
||||
let tableBuilder: azdata.ComponentBuilder<azdata.TableComponent, azdata.TableComponentProperties> = {
|
||||
component: () => table(),
|
||||
|
||||
7
src/sql/azdata.proposed.d.ts
vendored
7
src/sql/azdata.proposed.d.ts
vendored
@@ -1947,4 +1947,11 @@ declare module 'azdata' {
|
||||
isPrimary: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
export interface TableComponent {
|
||||
/**
|
||||
* Set active cell.
|
||||
*/
|
||||
setActiveCell(row: number, column: number): void;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@ export enum ComponentEventType {
|
||||
export enum ModelViewAction {
|
||||
SelectTab = 'selectTab',
|
||||
AppendData = 'appendData',
|
||||
Filter = 'filter'
|
||||
Filter = 'filter',
|
||||
SetActiveCell = 'setActiveCell'
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1503,6 +1503,10 @@ class TableComponentWrapper extends ComponentWrapper implements azdata.TableComp
|
||||
public appendData(v: any[][]): Thenable<void> {
|
||||
return this.doAction(ModelViewAction.AppendData, v);
|
||||
}
|
||||
|
||||
public setActiveCell(row: number, column: number): void {
|
||||
this.doAction(ModelViewAction.SetActiveCell, row, column);
|
||||
}
|
||||
}
|
||||
|
||||
class DropDownWrapper extends ComponentWrapper implements azdata.DropDownComponent {
|
||||
|
||||
@@ -186,7 +186,8 @@ export enum ModelComponentTypes {
|
||||
export enum ModelViewAction {
|
||||
SelectTab = 'selectTab',
|
||||
AppendData = 'appendData',
|
||||
Filter = 'filter'
|
||||
Filter = 'filter',
|
||||
SetActiveCell = 'setActiveCell'
|
||||
}
|
||||
|
||||
export enum ColumnSizingMode {
|
||||
|
||||
@@ -40,6 +40,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
||||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { ITableService } from 'sql/workbench/services/table/browser/tableService';
|
||||
import { deepClone, equals } from 'vs/base/common/objects';
|
||||
|
||||
export enum ColumnSizingMode {
|
||||
ForceFit = 0, // all columns will be sized to fit in viewable space, no horiz scroll bar
|
||||
@@ -358,24 +359,27 @@ export default class TableComponent extends ComponentBase<azdata.TableComponentP
|
||||
}
|
||||
|
||||
public override setProperties(properties: { [key: string]: any; }): void {
|
||||
const oldColumns = deepClone(this.columns);
|
||||
super.setProperties(properties);
|
||||
this._tableData.clear();
|
||||
this._tableData.push(this.transformData(this.data, this.columns));
|
||||
this._tableColumns = this.transformColumns(this.columns);
|
||||
this._table.columns = this._tableColumns;
|
||||
if (!equals(oldColumns, this.columns)) {
|
||||
this._tableColumns = this.transformColumns(this.columns);
|
||||
this._table.columns = this._tableColumns;
|
||||
this._checkboxColumns.forEach((column, columnName) => { this.registerPlugins(columnName, column); })
|
||||
Object.keys(this._buttonColumns).forEach(col => this.registerPlugins(col, this._buttonColumns[col]));
|
||||
Object.keys(this._hyperlinkColumns).forEach(col => this.registerPlugins(col, this._hyperlinkColumns[col]));
|
||||
Object.keys(this._contextMenuColumns).forEach(col => this.registerPlugins(col, this._contextMenuColumns[col]));
|
||||
|
||||
this._table.columns = this._tableColumns;
|
||||
this._table.autosizeColumns();
|
||||
}
|
||||
this._table.setData(this._tableData);
|
||||
this._table.setTableTitle(this.title);
|
||||
if (this.selectedRows) {
|
||||
this._table.setSelectedRows(this.selectedRows);
|
||||
}
|
||||
|
||||
this._checkboxColumns.forEach((column, columnName) => {
|
||||
this.registerPlugins(columnName, column);
|
||||
})
|
||||
Object.keys(this._buttonColumns).forEach(col => this.registerPlugins(col, this._buttonColumns[col]));
|
||||
Object.keys(this._hyperlinkColumns).forEach(col => this.registerPlugins(col, this._hyperlinkColumns[col]));
|
||||
Object.keys(this._contextMenuColumns).forEach(col => this.registerPlugins(col, this._contextMenuColumns[col]));
|
||||
|
||||
if (this.headerFilter === true) {
|
||||
this.registerFilterPlugin();
|
||||
this._tableData.clearFilter();
|
||||
@@ -433,7 +437,8 @@ export default class TableComponent extends ComponentBase<azdata.TableComponentP
|
||||
width: col.width,
|
||||
cssClass: col.cssClass,
|
||||
headerCssClass: col.headerCssClass,
|
||||
actionOnCheck: checkboxAction
|
||||
actionOnCheck: checkboxAction,
|
||||
columnId: `checkbox-column-${index}`,
|
||||
}, index));
|
||||
|
||||
this._register(this._checkboxColumns.get(col.value).onChange((state) => {
|
||||
@@ -589,7 +594,6 @@ export default class TableComponent extends ComponentBase<azdata.TableComponentP
|
||||
|
||||
|
||||
private registerPlugins(col: string, plugin: CheckboxSelectColumn<{}> | ButtonColumn<{}> | HyperlinkColumn<{}> | ContextMenuColumn<{}>): void {
|
||||
|
||||
const index = 'index' in plugin ? plugin.index : this.columns?.findIndex(x => x === col || ('value' in x && x['value'] === col));
|
||||
if (index >= 0) {
|
||||
this._tableColumns.splice(index, 0, plugin.definition);
|
||||
@@ -598,10 +602,6 @@ export default class TableComponent extends ComponentBase<azdata.TableComponentP
|
||||
this._pluginsRegisterStatus[col] = true;
|
||||
}
|
||||
}
|
||||
|
||||
this._table.columns = this._tableColumns;
|
||||
this._table.autosizeColumns();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -723,6 +723,10 @@ export default class TableComponent extends ComponentBase<azdata.TableComponentP
|
||||
switch (action) {
|
||||
case ModelViewAction.AppendData:
|
||||
this.appendData(args[0]);
|
||||
break;
|
||||
case ModelViewAction.SetActiveCell:
|
||||
this._table.grid.setActiveCell(args[0], args[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user