///////////////////////////////////////////////////////////// // // 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 _ from 'lodash'; import url_for from 'sources/url_for'; import React from 'react'; import { Box } from '@material-ui/core'; import { makeStyles } from '@material-ui/core/styles'; import Wizard from '../helpers/wizard/Wizard'; import WizardStep from '../helpers/wizard/WizardStep'; import PgTable from 'sources/components/PgTable'; import { getNodePrivilegeRoleSchema } from '../../../browser/server_groups/servers/static/js/privilege.ui.js'; import { InputSQL, InputText, FormFooterMessage, MESSAGE_TYPE } from './FormComponents'; import getApiInstance from '../api_instance'; import SchemaView from '../SchemaView'; import clsx from 'clsx'; import Loader from 'sources/components/Loader'; import Alertify from 'pgadmin.alertifyjs'; import PropTypes from 'prop-types'; import PrivilegeSchema from '../../../tools/grant_wizard/static/js/privilege_schema.ui'; const useStyles = makeStyles(() => ({ grantWizardStep: { height: '100%' }, grantWizardTitle: { top: '0 !important', opacity: '1 !important', borderRadius: '6px 6px 0px 0px !important', margin: '0 !important', width: '100%', height: '6%' }, grantWizardContent: { height: '94% !important' }, stepPanelCss: { height: 500, overflow: 'hidden' }, objectSelection: { display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden', marginBottom: '1em' }, searchBox: { marginBottom: '1em', display: 'flex', }, searchPadding: { flex: 2.5 }, searchInput: { flex: 1, marginTop: 2, borderLeft: 'none', paddingLeft: 5 }, grantWizardPanelContent: { paddingTop: '0.9em !important', overflow: 'hidden' }, grantWizardSql: { height: '90% !important', width: '100%' }, privilegeStep: { height: '100%', } }), ); export default function GrantWizard({ sid, did, nodeInfo, nodeData }) { const classes = useStyles(); var columns = [ { Header: 'Object Type', accessor: 'object_type', sortble: true, resizable: false, disableGlobalFilter: true }, { Header: 'Schema', accessor: 'nspname', sortble: true, resizable: false, disableGlobalFilter: true }, { Header: 'Name', accessor: 'name_with_args', sortble: true, resizable: true, disableGlobalFilter: false, minWidth: 280 }, { Header: 'parameters', accessor: 'proargs', sortble: false, resizable: false, disableGlobalFilter: false, minWidth: 280, isVisible: false }, { Header: 'Name', accessor: 'name', sortble: false, resizable: false, disableGlobalFilter: false, minWidth: 280, isVisible: false }, { Header: 'ID', accessor: 'oid', sortble: false, resizable: false, disableGlobalFilter: false, minWidth: 280, isVisible: false } ]; var steps = ['Object Selection', 'Privilege Selection', 'Review Selection']; const [selectedObject, setSelectedObject] = React.useState([]); const [selectedAcl, setSelectedAcl] = React.useState({}); const [msqlData, setSQL] = React.useState(''); const [stepType, setStepType] = React.useState(''); const [searchVal, setSearchVal] = React.useState(''); const [loaderText, setLoaderText] = React.useState(''); const [tablebData, setTableData] = React.useState([]); const [privOptions, setPrivOptions] = React.useState({}); const [privileges, setPrivileges] = React.useState([]); const [privSchemaInstance, setPrivSchemaInstance] = React.useState(); const [errMsg, setErrMsg] = React.useState(''); const api = getApiInstance(); const validatePrivilege = () => { var isValid = true; selectedAcl.privilege.forEach((priv) => { if ((_.isUndefined(priv.grantee) || _.isUndefined(priv.privileges) || priv.privileges.length === 0) && isValid) { isValid = false; } }); return !isValid; }; React.useEffect(() => { privSchemaInstance?.privilegeRoleSchema.updateSupportedPrivs(privileges); }, [privileges]); React.useEffect(() => { const privSchema = new PrivilegeSchema((privileges) => getNodePrivilegeRoleSchema('', nodeInfo, nodeData, privileges)); setPrivSchemaInstance(privSchema); setLoaderText('Loading...'); api.get(url_for( 'grant_wizard.acl', { 'sid': encodeURI(sid), 'did': encodeURI(did), } )).then(res => { setPrivOptions(res.data); }); var node_type = nodeData._type.replace('coll-', '').replace( 'materialized_', '' ); var _url = url_for( 'grant_wizard.objects', { 'sid': encodeURI(sid), 'did': encodeURI(did), 'node_id': encodeURI(nodeData._id), 'node_type': encodeURI(node_type), }); api.get(_url) .then(res => { var data = res.data.result; data.forEach(element => { if (element.icon) element['icon'] = { 'object_type': element.icon }; if(element.object_type === 'Function') { element.name_with_args = element.name + '(' + (typeof(element.proargs) != 'undefined' ? element.proargs : '') + ')'; } else { element.name_with_args = element.name; } }); setTableData(data); setLoaderText(''); }) .catch(() => { Alertify.error(gettext('Error while fetching grant wizard data.')); setLoaderText(''); }); }, [nodeData]); const wizardStepChange = (data) => { switch (data.currentStep) { case 0: setStepType('object_type'); break; case 1: setStepType('privileges'); break; case 2: setLoaderText('Loading SQL ...'); var msql_url = url_for( 'grant_wizard.modified_sql', { 'sid': encodeURI(sid), 'did': encodeURI(did), }); var post_data = { acl: selectedAcl.privilege, objects: selectedObject }; api.post(msql_url, post_data) .then(res => { setSQL(res.data.data); setLoaderText(''); }) .catch(() => { Alertify.error(gettext('Error while fetching SQL.')); }); break; default: setStepType(''); } }; const onSave = () => { setLoaderText('Saving...'); var _url = url_for( 'grant_wizard.apply', { 'sid': encodeURI(sid), 'did': encodeURI(did), }); const post_data = { acl: selectedAcl.privilege, objects: selectedObject }; api.post(_url, post_data) .then(() => { setLoaderText(''); Alertify.wizardDialog().close(); }) .catch((error) => { setLoaderText(''); Alertify.error(gettext(`Error while saving grant wizard data: ${error.response.data.errormsg}`)); }); }; const disableNextCheck = () => { return selectedObject.length > 0 && stepType === 'object_type' ? false : selectedAcl?.privilege?.length > 0 && stepType === 'privileges' ? validatePrivilege() : true; }; const onDialogHelp= () => { window.open(url_for('help.static', { 'filename': 'grant_wizard.html' }), 'pgadmin_help'); }; const getTableSelectedRows = (selRows) => { var selObj = []; var objectTypes = new Set(); if (selRows.length > 0) { selRows.forEach((row) => { var object_type = ''; switch (row.values.object_type) { case 'Function': object_type = 'function'; break; case 'Trigger Function': object_type = 'function'; break; case 'Procedure': object_type = 'procedure'; break; case 'Table': object_type = 'table'; break; case 'Sequence': object_type = 'sequence'; break; case 'View': object_type = 'table'; break; case 'Materialized View': object_type = 'table'; break; case 'Foreign Table': object_type = 'foreign_table'; break; case 'Package': object_type = 'package'; break; default: break; } objectTypes.add(object_type); selObj.push(row.values); }); } var privileges = new Set(); objectTypes.forEach((objType) => { privOptions[objType]?.acl.forEach((priv) => { privileges.add(priv); }); }); setPrivileges(Array.from(privileges)); setSelectedObject(selObj); setErrMsg(selObj.length === 0 ? gettext('Please select any database object.') : ''); }; const onErrClose = React.useCallback(()=>{ setErrMsg(''); }); return ( <> {gettext('Grant Wizard')} { setSearchVal(val);} }> {privSchemaInstance && { }} viewHelperProps={{ mode: 'create' }} schema={privSchemaInstance} showFooter={false} isTabView={false} onDataChange={(isChanged, changedData) => { setSelectedAcl(changedData); }} /> } {gettext('The SQL below will be executed on the database server to grant the selected privileges. Please click on Finish to complete the process.')} ); } GrantWizard.propTypes = { sid: PropTypes.string, did: PropTypes.number, nodeInfo: PropTypes.object, nodeData: PropTypes.object, };