diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/mview.ui.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/mview.ui.js index f7a52d7ac..e8cbd8d52 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/mview.ui.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/views/static/js/mview.ui.js @@ -108,21 +108,6 @@ export default class MViewSchema extends BaseUISchema { type: 'nested-tab', group: gettext('Parameter'), mode: ['create', 'edit'], schema: this.getVacuumSettingsSchema(), }, - /*{ - type: 'nested', control: 'tab', id: 'materialization', - label: gettext('Parameter'), mode: ['edit', 'create'], - group: gettext('Parameter'), - schema: Backform.VacuumSettingsSchema, - }, - { - // Add Privilege Control - id: 'datacl', label: gettext('Privileges'), type: 'collection', - model: pgBrowser.Node.PrivilegeRoleModel.extend({ - privileges: ['a', 'r', 'w', 'd', 'D', 'x', 't'], - }), uniqueCol : ['grantee'], editable: false, - group: 'security', canAdd: true, canDelete: true, - mode: ['edit', 'create'], control: 'unique-col-collection', - },*/ { id: 'datacl', label: gettext('Privileges'), type: 'collection', schema: this.getPrivilegeRoleSchema(['U']), diff --git a/web/pgadmin/browser/server_groups/servers/roles/static/js/role.js b/web/pgadmin/browser/server_groups/servers/roles/static/js/role.js index dfbda46bc..a321416e6 100644 --- a/web/pgadmin/browser/server_groups/servers/roles/static/js/role.js +++ b/web/pgadmin/browser/server_groups/servers/roles/static/js/role.js @@ -6,6 +6,10 @@ // This software is released under the PostgreSQL Licence // ////////////////////////////////////////////////////////////// +import RoleSchema from './role.ui'; +import { getNodeVariableSchema } from '../../../static/js/variable.ui'; +import { getNodeListByName } from '../../../../../static/js/node_ajax'; +import { getMembershipSchema } from '../../../static/js/membership.ui'; define('pgadmin.node.role', [ 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', @@ -833,13 +837,22 @@ define('pgadmin.node.role', [ gettext('Reassign/Drop Owned - \'%s\'', _d.label) ).resizeTo(pgAdmin.Browser.stdW.md, pgAdmin.Browser.stdH.lg); }, + getSchema: function(treeNodeInfo, itemNodeData) { + return new RoleSchema( + ()=>getNodeVariableSchema(this, treeNodeInfo, itemNodeData, true, false), + ()=>getMembershipSchema(this, treeNodeInfo, itemNodeData), + { + role: ()=>getNodeListByName('role', treeNodeInfo, itemNodeData), + nodeInfo: treeNodeInfo + }, + ); + }, model: pgAdmin.Browser.Node.Model.extend({ idAttribute: 'oid', defaults: { oid: null, rolname: undefined, rolcanlogin: false, - rolpassword: null, rolconnlimit: -1, rolsuper: false, rolcreaterole: false, @@ -847,11 +860,7 @@ define('pgadmin.node.role', [ rolinherit: true, rolcatupdate: false, rolreplication: false, - rolmembership: [], - rolmembers: [], rolvaliduntil: null, - seclabels: [], - variables: [], }, schema: [{ id: 'rolname', label: gettext('Name'), type: 'text', @@ -859,19 +868,6 @@ define('pgadmin.node.role', [ },{ id: 'oid', label: gettext('OID'), cell: 'string', mode: ['properties'], editable: false, type: 'text', visible: true, - },{ - id: 'rolpassword', label: gettext('Password'), type: 'password', - group: gettext('Definition'), mode: ['edit', 'create'], - control: 'input', deps: ['rolcanlogin'], retype: true, - cell: 'string', disabled: function(m) { - if (!m.isNew()) { - var user = this.node_info.server.user; - - return (!(user.is_superuser || user.can_create_role) && - user.id != m.get('oid')); - } - return false; - }, },{ id: 'rolvaliduntil', readonly: 'readonly', type: 'text', group: gettext('Definition'), label: gettext('Account expires'), @@ -955,73 +951,6 @@ define('pgadmin.node.role', [ controlsClassName: 'pgadmin-controls pg-el-sm-8 pg-el-12', min_version: 90100, readonly: 'readonly', - }, - { - id: 'rolmembership', label: gettext('Member of'), - group: gettext('Membership'), type: 'collection', - cell: 'string', mode: ['properties', 'edit', 'create'], - readonly: 'readonly', - control: RoleMembersControl, model: pgBrowser.Node.Model.extend({ - keys: ['role'], - idAttribute: 'role', - defaults: { - role: undefined, - admin: false, - }, - validate: function() { - return null; - }, - }), - filter: function(d) { - return this.model.isNew() || (this.model.get('rolname') != d.label); - }, - helpMessage: function(m) { - if (m.has('read_only') && m.get('read_only') == false) { - return gettext('Select the checkbox for roles to include WITH ADMIN OPTION.'); - } else { - return gettext('Roles shown with a check mark have the WITH ADMIN OPTION set.'); - } - }, - }, - { - id: 'rolmembers', label: gettext('Members'), type: 'collection', group: gettext('Membership'), - mode: ['properties', 'edit', 'create'], cell: 'string', - readonly: 'readonly', - control: RoleMembersControl, model: pgBrowser.Node.Model.extend({ - keys: ['role'], - idAttribute: 'role', - defaults: { - role: undefined, - admin: false, - }, - validate: function() { - return null; - }, - }), - filter: function(d) { - return this.model.isNew() || (this.model.get('rolname') != d.label); - }, - helpMessage: function(m) { - if (m.has('read_only') && m.get('read_only') == false) { - return gettext('Select the checkbox for roles to include WITH ADMIN OPTION.'); - } else { - return gettext('Roles shown with a check mark have the WITH ADMIN OPTION set.'); - } - }, - }, - { - id: 'variables', label: '', type: 'collection', - group: gettext('Parameters'), hasDatabase: true, url: 'variables', - model: pgBrowser.Node.VariableModel.extend({keys:['name', 'database']}), - control: 'variable-collection', - mode: [ 'edit', 'create'], canAdd: true, canDelete: true, - readonly: 'readonly', - },{ - id: 'seclabels', label: gettext('Security labels'), - model: SecurityModel, editable: false, type: 'collection', - group: gettext('Security'), mode: ['edit', 'create'], - min_version: 90200, readonly: 'readonly', canAdd: true, - canEdit: false, canDelete: true, control: 'unique-col-collection', }], readonly: function(m) { if (!m.has('read_only')) { diff --git a/web/pgadmin/browser/server_groups/servers/roles/static/js/role.ui.js b/web/pgadmin/browser/server_groups/servers/roles/static/js/role.ui.js new file mode 100644 index 000000000..1b2dde9c1 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/roles/static/js/role.ui.js @@ -0,0 +1,231 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2021, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import gettext from 'sources/gettext'; +import BaseUISchema from 'sources/SchemaView/base_schema.ui'; +import SecLabelSchema from '../../../static/js/sec_label.ui'; + + +export default class RoleSchema extends BaseUISchema { + constructor(getVariableSchema, getMembershipSchema,fieldOptions={}) { + super({ + oid: null, + rolname: null, + rolcanlogin: false, + rolpassword: null, + rolconnlimit: -1, + rolsuper: false, + rolcreaterole: false, + rolcreatedb: false, + rolinherit: true, + rolcatupdate: false, + rolreplication: false, + rolmembership: [], + rolmembers: [], + rolvaliduntil: null, + seclabels: [], + variables: [], + }); + this.getVariableSchema = getVariableSchema; + this.getMembershipSchema = getMembershipSchema; + this.fieldOptions = { + role: [], + ...fieldOptions, + }; + + this.isReadOnly = null; + this.nodeInfo = this.fieldOptions.nodeInfo; + this.user = this.nodeInfo.server.user; + } + + get idAttribute() { + return 'oid'; + } + + readOnly(state) { + var user = this.nodeInfo.server.user; + this.oid = state.oid; + this.isReadOnly = !(user.is_superuser || user.can_create_role); + return (!(user.is_superuser || user.can_create_role) && user.id != state.oid); + } + + memberDataFormatter(rawData) { + var members = ''; + if(_.isObject(rawData)) { + var withAdmin = ''; + rawData.forEach(member => { + if(member.admin) { withAdmin = ' [WITH ADMIN]';} + + if (members.length > 0) { members += ', '; } + members = members + (member.role + withAdmin); + }); + } + return members; + } + + get baseFields() { + let obj = this; + return [ + { + id: 'rolname', label: gettext('Name'), type: 'text', noEmpty: true, + disabled: obj.readOnly, + },{ + id: 'oid', label: gettext('OID'), cell: 'string', mode: ['properties'], + editable: false, type: 'text', visible: true, + }, + { + id: 'is_sys_obj', label: gettext('System role?'), + cell:'boolean', type: 'switch', mode: ['properties'], + }, + { + id: 'description', label: gettext('Comments'), type: 'multiline', + mode: ['properties', 'edit', 'create'], + disabled: obj.readOnly, + }, + { + id: 'rolpassword', label: gettext('Password'), type: 'password', + group: gettext('Definition'), mode: ['edit', 'create'], + control: 'input', deps: ['rolcanlogin'], retype: true, + cell: 'text', disabled: obj.readOnly, + }, + { + id: 'rolvaliduntil', type: 'datetimepicker', + group: gettext('Definition'), label: gettext('Account expires'), + mode: ['properties', 'edit', 'create'], + deps: ['rolcanlogin'], + helpMessage: gettext('Please note that if you leave this field blank, then password will never expire.'), + controlProps: { format: 'YYYY-MM-DD HH:mm:ss Z', ampm: false, + placeholder: gettext('No Expiry'), autoOk: true, + }, + disabled: obj.readOnly, + }, + { + id: 'rolconnlimit', type: 'int', group: gettext('Definition'), + label: gettext('Connection limit'), cell: 'integer', min : -1, + mode: ['properties', 'edit', 'create'], + disabled: obj.readOnly, + }, + { + id: 'rolcanlogin', label: gettext('Can login?'), + type: 'switch', + controlLabelClassName: 'control-label pg-el-sm-4 pg-el-12', + controlsClassName: 'pgadmin-controls pg-el-sm-8 pg-el-12', + group: gettext('Privileges'), + disabled: obj.readOnly, + }, + { + id: 'rolsuper', label: gettext('Superuser?'), + type: 'switch', + group: gettext('Privileges'), + depChange: (state) => { + state.rolcatupdate = state.rolcreaterole = state.rolcreatedb = state.rolsuper; + }, + disabled: obj.readOnly, + }, + { + id: 'rolcreaterole', label: gettext('Create roles?'), + group: gettext('Privileges'), + type: 'switch', + controlLabelClassName: 'control-label pg-el-sm-4 pg-el-12', + controlsClassName: 'pgadmin-controls pg-el-sm-8 pg-el-12', + disabled: obj.readOnly, + }, + { + id: 'rolcreatedb', label: gettext('Create databases?'), + group: gettext('Privileges'), + type: 'switch', + disabled: obj.readOnly, + }, + { + id: 'rolcatupdate', label: gettext('Update catalog?'), + max_version: 90400, + group: gettext('Privileges'), + type: 'switch', + disabled: (state) => { + return !state.rolsuper; + }, + readonly: () => { + return !(obj.user.is_superuser || obj.user.can_create_role); + } + }, + { + id: 'rolinherit', group: gettext('Privileges'), + label: gettext('Inherit rights from the parent roles?'), + type: 'switch', + controlLabelClassName: 'control-label pg-el-sm-4 pg-el-12', + controlsClassName: 'pgadmin-controls pg-el-sm-8 pg-el-12', + disabled: obj.readOnly, + }, + { + id: 'rolreplication', group: gettext('Privileges'), + label: gettext('Can initiate streaming replication and backups?'), + type: 'switch', + controlLabelClassName: 'control-label pg-el-sm-4 pg-el-12', + controlsClassName: 'pgadmin-controls pg-el-sm-8 pg-el-12', + min_version: 90100, + disabled: obj.readOnly, + }, + { + id: 'rolmembership', label: gettext('Member of'), group: gettext('Membership'), + disabled: obj.readOnly, + mode: ['edit', 'create'], cell: 'text', + type: 'collection', + schema: new obj.getMembershipSchema(), + helpMessage: obj.isReadOnly ? gettext('Select the checkbox for roles to include WITH ADMIN OPTION.') : gettext('Roles shown with a check mark have the WITH ADMIN OPTION set.'), + }, + { + id: 'rolmembership', label: gettext('Member of'), group: gettext('Membership'), + disabled: obj.readOnly, + mode: ['properties'], cell: 'text', + type: 'text', + controlProps: { + formatter: { + fromRaw: obj.memberDataFormatter, + }, + } + }, + { + id: 'rolmembers', label: gettext('Members'), group: gettext('Membership'), + mode: ['edit', 'create'], cell: 'text', + type: 'collection', + schema: new obj.getMembershipSchema(), + disabled: obj.readOnly, + helpMessage: obj.isReadOnly ? gettext('Select the checkbox for roles to include WITH ADMIN OPTION.') : gettext('Roles shown with a check mark have the WITH ADMIN OPTION set.') , + }, + { + id: 'rolmembers', label: gettext('Members'), group: gettext('Membership'), + disabled: obj.readOnly, + mode: ['properties'], cell: 'text', + type: 'text', + controlProps: { + formatter: { + fromRaw: obj.memberDataFormatter, + }, + } + }, + { + id: 'variables', label: '', type: 'collection', + group: gettext('Parameters'), + schema: this.getVariableSchema(), + mode: [ 'edit', 'create'], canAdd: true, canDelete: true, + disabled: obj.readOnly, + }, + { + id: 'seclabels', label: gettext('Security labels'), type: 'collection', + schema: new SecLabelSchema(), + editable: false, group: gettext('Security'), + mode: ['edit', 'create'], + canAdd: true, canEdit: false, canDelete: true, + uniqueCol : ['provider'], + min_version: 90200, + disabled: obj.readOnly, + } + ]; + } +} diff --git a/web/pgadmin/browser/server_groups/servers/static/js/membership.ui.js b/web/pgadmin/browser/server_groups/servers/static/js/membership.ui.js new file mode 100644 index 000000000..62df3bae3 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/static/js/membership.ui.js @@ -0,0 +1,59 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2021, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import gettext from 'sources/gettext'; +import BaseUISchema from 'sources/SchemaView/base_schema.ui'; +import { getNodeListByName } from '../../../../static/js/node_ajax'; + + +export function getMembershipSchema(nodeObj, treeNodeInfo, itemNodeData) { + return new MembershipSchema( + ()=>getNodeListByName('role', treeNodeInfo, itemNodeData, {}, ()=>true), + ); +} + + +export default class MembershipSchema extends BaseUISchema { + constructor(roleMembersOptions) { + super({ + role: undefined, + admin: undefined + }); + this.roleMembersOptions = roleMembersOptions; + } + + get baseFields() { + return [{ + id: 'role', label: gettext('User/Role'), type:'text', + editable: true, + cell: ()=>({ + cell: 'select', options: this.roleMembersOptions, + controlProps: { + allowClear: false, + } + }), + noEmpty: true, + minWidth: 300 + }, + { + id: 'admin', label: gettext('WITH ADMIN'), + cell: 'checkbox', type: 'checkbox', + minWidth: 300, + deps: ['role'], + depChange: (state) => { + if(_.isUndefined(state.admin)) { + state.admin = false; + } + } + }, + ]; + } + + +} diff --git a/web/regression/javascript/schema_ui_files/membership.ui.spec.js b/web/regression/javascript/schema_ui_files/membership.ui.spec.js new file mode 100644 index 000000000..e07ea80e0 --- /dev/null +++ b/web/regression/javascript/schema_ui_files/membership.ui.spec.js @@ -0,0 +1,119 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2021, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import jasmineEnzyme from 'jasmine-enzyme'; +import React from 'react'; +import '../helper/enzyme.helper'; +import { createMount } from '@material-ui/core/test-utils'; +import pgAdmin from 'sources/pgadmin'; +import {messages} from '../fake_messages'; +import SchemaView from '../../../pgadmin/static/js/SchemaView'; +import MembershipSchema, {getMembershipSchema} from '../../../pgadmin/browser/server_groups/servers/static/js/membership.ui'; +import * as nodeAjax from '../../../pgadmin/browser/static/js/node_ajax'; + +describe('PrivilegeSchema', ()=>{ + let mount; + let schemaObj = new MembershipSchema( + ()=>[]); + let getInitData = ()=>Promise.resolve({}); + + /* Use createMount so that material ui components gets the required context */ + /* https://material-ui.com/guides/testing/#api */ + beforeAll(()=>{ + mount = createMount(); + }); + + afterAll(() => { + mount.cleanUp(); + }); + + beforeEach(()=>{ + jasmineEnzyme(); + /* messages used by validators */ + pgAdmin.Browser = pgAdmin.Browser || {}; + pgAdmin.Browser.messages = pgAdmin.Browser.messages || messages; + pgAdmin.Browser.utils = pgAdmin.Browser.utils || {}; + pgAdmin.Browser.utils.support_ssh_tunnel = true; + }); + + it('create', ()=>{ + mount({}} + onClose={()=>{}} + onHelp={()=>{}} + onEdit={()=>{}} + onDataChange={()=>{}} + confirmOnCloseReset={false} + hasSQL={false} + disableSqlHelp={false} + disableDialogHelp={false} + />); + }); + + it('edit', ()=>{ + mount({}} + onClose={()=>{}} + onHelp={()=>{}} + onEdit={()=>{}} + onDataChange={()=>{}} + confirmOnCloseReset={false} + hasSQL={false} + disableSqlHelp={false} + disableDialogHelp={false} + />); + }); + + it('properties', ()=>{ + mount({}} + onEdit={()=>{}} + />); + }); + + it('MembershipMemberSchema', ()=>{ + spyOn(nodeAjax, 'getNodeListByName').and.returnValue([]); + let memberObj = new getMembershipSchema({}, {server: {user: {name: 'postgres'}}}, {}); + let ctrl = mount({}} + onClose={()=>{}} + onHelp={()=>{}} + onEdit={()=>{}} + onDataChange={()=>{}} + confirmOnCloseReset={false} + hasSQL={false} + disableSqlHelp={false} + disableDialogHelp={false} + />); + /* Make sure you hit every corner */ + ctrl.find('DataGridView').at(0).find('PgIconButton[data-test="add-row"]').find('button').simulate('click'); + }); +}); diff --git a/web/regression/javascript/schema_ui_files/role.ui.spec.js b/web/regression/javascript/schema_ui_files/role.ui.spec.js new file mode 100644 index 000000000..d33bed7b6 --- /dev/null +++ b/web/regression/javascript/schema_ui_files/role.ui.spec.js @@ -0,0 +1,107 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2021, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import jasmineEnzyme from 'jasmine-enzyme'; +import React from 'react'; +import '../helper/enzyme.helper'; +import { createMount } from '@material-ui/core/test-utils'; +import pgAdmin from 'sources/pgadmin'; +import {messages} from '../fake_messages'; +import SchemaView from '../../../pgadmin/static/js/SchemaView'; +import BaseUISchema from 'sources/SchemaView/base_schema.ui'; +import RoleSchema from '../../../pgadmin/browser/server_groups/servers/roles/static/js/role.ui'; + + +class MockSchema extends BaseUISchema { + get baseFields() { + return []; + } +} + +describe('RoleSchema', ()=>{ + let mount; + let schemaObj = new RoleSchema( + ()=>new MockSchema(), + ()=>new MockSchema(), + { + role: ()=>[], + nodeInfo: {server: {user: {name:'postgres', id:0}}} + }, + ); + let getInitData = ()=>Promise.resolve({}); + + /* Use createMount so that material ui components gets the required context */ + /* https://material-ui.com/guides/testing/#api */ + beforeAll(()=>{ + mount = createMount(); + }); + + afterAll(() => { + mount.cleanUp(); + }); + + beforeEach(()=>{ + jasmineEnzyme(); + /* messages used by validators */ + pgAdmin.Browser = pgAdmin.Browser || {}; + pgAdmin.Browser.messages = pgAdmin.Browser.messages || messages; + pgAdmin.Browser.utils = pgAdmin.Browser.utils || {}; + }); + + it('create', ()=>{ + mount({}} + onClose={()=>{}} + onHelp={()=>{}} + onEdit={()=>{}} + onDataChange={()=>{}} + confirmOnCloseReset={false} + hasSQL={false} + disableSqlHelp={false} + />); + }); + + it('edit', ()=>{ + mount({}} + onClose={()=>{}} + onHelp={()=>{}} + onEdit={()=>{}} + onDataChange={()=>{}} + confirmOnCloseReset={false} + hasSQL={false} + disableSqlHelp={false} + />); + }); + + it('properties', ()=>{ + mount({}} + onEdit={()=>{}} + />); + }); +}); +