mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Fixed the UI related issues reported during testing for Shared Storage in Server Mode. #5014
This commit is contained in:
parent
8b815d4aac
commit
e4eeba2aa7
@ -53,12 +53,14 @@ Shared Storage
|
||||
:alt: Other options
|
||||
:align: center
|
||||
|
||||
In shared storage the ``My Storage`` is the user's storage directory, and other directories are shared storage set by
|
||||
the admin user. Using this shared storage users can share the files with other users through pgAdmin.
|
||||
Admin users can mark the shared storage as restricted to restrict non-admin users from deleting, uploading,
|
||||
adding, and renaming files/folders in shared storage by setting the restricted_access flag in config.
|
||||
|
||||
*NOTE: You must ensure the directories specified are writeable by the user that the web server processes will be running as, e.g. apache or www-data.*
|
||||
In the storage manager, ``My Storage`` is the pgAdmin user’s storage directory, and other listed directories are shared
|
||||
storages set by the pgAdmin server administrator. Using these, pgAdmin users can have common storages to share files.
|
||||
pgAdmin server administrator can configure the shared storages using the <link>config file</link>. Storages can be
|
||||
marked as restricted to give read-only access to non-admin pgAdmin users.
|
||||
|
||||
|
||||
.. note:: You must ensure the directories specified are writeable by the user that the web server processes will be running as, e.g. apache or www-data.*
|
||||
|
||||
|
||||
Other Options
|
||||
|
@ -559,8 +559,11 @@ def get_shared_storage_list():
|
||||
|
||||
config.SHARED_STORAGE = shared_storage_config
|
||||
shared_storage_list = [sdir['name'] for sdir in shared_storage_config]
|
||||
restricted_shared_storage_list = [sdir['name'] for sdir in
|
||||
shared_storage_config if
|
||||
sdir['restricted_access']]
|
||||
|
||||
return shared_storage_list
|
||||
return shared_storage_list, restricted_shared_storage_list
|
||||
|
||||
|
||||
@blueprint.route("/js/utils.js")
|
||||
@ -630,7 +633,8 @@ def utils():
|
||||
auth_source = session['auth_source_manager'][
|
||||
'source_friendly_name']
|
||||
|
||||
shared_storage_list = get_shared_storage_list()
|
||||
shared_storage_list, \
|
||||
restricted_shared_storage_list = get_shared_storage_list()
|
||||
|
||||
return make_response(
|
||||
render_template(
|
||||
@ -665,6 +669,8 @@ def utils():
|
||||
password_length_min=config.PASSWORD_LENGTH_MIN,
|
||||
current_ui_lock=current_ui_lock,
|
||||
shared_storage_list=shared_storage_list,
|
||||
restricted_shared_storage_list=[] if current_user.has_role(
|
||||
"Administrator") else restricted_shared_storage_list,
|
||||
),
|
||||
200, {'Content-Type': MIMETYPE_APP_JS})
|
||||
|
||||
|
@ -57,6 +57,7 @@ define('pgadmin.browser.utils',
|
||||
/* GET PSQL Tool related config */
|
||||
pgAdmin['enable_psql'] = '{{enable_psql}}' == 'True';
|
||||
pgAdmin['shared_storage'] = {{shared_storage_list}}
|
||||
pgAdmin['restricted_shared_storage'] = {{restricted_shared_storage_list}}
|
||||
pgAdmin['platform'] = '{{platform}}';
|
||||
pgAdmin['qt_default_placeholder'] = '{{qt_default_placeholder}}'
|
||||
pgAdmin['vw_edt_default_placeholder'] = '{{vw_edt_default_placeholder}}'
|
||||
|
@ -17,6 +17,7 @@ import time
|
||||
from urllib.parse import unquote
|
||||
from sys import platform as _platform
|
||||
from flask_security import current_user
|
||||
from pgadmin.utils.constants import ACCESS_DENIED_MESSAGE
|
||||
import config
|
||||
import codecs
|
||||
import pathlib
|
||||
@ -792,11 +793,10 @@ class Filemanager():
|
||||
if selectedDir[
|
||||
'restricted_access'] and not current_user.has_role(
|
||||
"Administrator"):
|
||||
raise PermissionError(gettext(
|
||||
"Access denied: This shared folder has restricted "
|
||||
"access, you are not allowed to rename, delete, "
|
||||
"or upload any files/folders. Please contact "
|
||||
"Administrator to gain access."))
|
||||
return make_json_response(success=0,
|
||||
errormsg=ACCESS_DENIED_MESSAGE,
|
||||
info='ACCESS_DENIED',
|
||||
status=403)
|
||||
|
||||
def rename(self, old=None, new=None):
|
||||
"""
|
||||
|
@ -75,6 +75,7 @@ export default class FileManagerModule {
|
||||
onCancel={onCancel}
|
||||
onOK={onOK}
|
||||
sharedStorages={this.pgAdmin.server_mode == 'True' ? this.pgAdmin.shared_storage: []}
|
||||
restrictedSharedStorage={this.pgAdmin.server_mode == 'True' ? this.pgAdmin.restricted_shared_storage: []}
|
||||
/>
|
||||
);
|
||||
}, {
|
||||
|
@ -402,7 +402,7 @@ ConfirmFile.propTypes = {
|
||||
onNo: PropTypes.func
|
||||
};
|
||||
|
||||
export default function FileManager({params, closeModal, onOK, onCancel, sharedStorages=[]}) {
|
||||
export default function FileManager({params, closeModal, onOK, onCancel, sharedStorages=[], restrictedSharedStorage=[]}) {
|
||||
const classes = useStyles();
|
||||
const modalClasses = useModalStyles();
|
||||
const apiObj = useMemo(()=>getApiInstance(), []);
|
||||
@ -462,7 +462,7 @@ export default function FileManager({params, closeModal, onOK, onCancel, sharedS
|
||||
let newItems = await fmUtilsObj.getFolder(dirPath || fmUtilsObj.currPath, changeStoragePath);
|
||||
setItems(newItems);
|
||||
setPath(fmUtilsObj.currPath);
|
||||
params.dialog_type == 'storage_dialog' && fmUtilsObj.setLastVisitedDir(fmUtilsObj.currPath, selectedSS);
|
||||
setTimeout(()=>{fmUtilsObj.setLastVisitedDir(dirPath || fmUtilsObj.currPath, changeStoragePath);}, 100);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
setErrorMsg(parseApiError(error));
|
||||
@ -726,7 +726,7 @@ export default function FileManager({params, closeModal, onOK, onCancel, sharedS
|
||||
{params.dialog_type == 'storage_dialog' &&
|
||||
<PgIconButton title={gettext('Download')} icon={<GetAppRoundedIcon />}
|
||||
onClick={onDownload} disabled={showUploader || isNoneSelected || selectedRow?.file_type == 'dir' || selectedRow?.file_type == 'drive'} />}
|
||||
{fmUtilsObj.hasCapability('create') && <PgIconButton title={gettext('New Folder')} icon={<CreateNewFolderRoundedIcon />}
|
||||
{fmUtilsObj.hasCapability('create') && !restrictedSharedStorage.includes(selectedSS) && <PgIconButton title={gettext('New Folder')} icon={<CreateNewFolderRoundedIcon />}
|
||||
onClick={onAddFolder} disabled={showUploader} />}
|
||||
</PgButtonGroup>
|
||||
<PgButtonGroup size="small" style={{marginLeft: '4px'}}>
|
||||
@ -739,13 +739,13 @@ export default function FileManager({params, closeModal, onOK, onCancel, sharedS
|
||||
onClose={onMenuClose}
|
||||
label={gettext('Options')}
|
||||
>
|
||||
{fmUtilsObj.hasCapability('rename') && <PgMenuItem hasCheck onClick={renameSelectedItem} disabled={isNoneSelected}>
|
||||
{fmUtilsObj.hasCapability('rename') && !restrictedSharedStorage.includes(selectedSS) && <PgMenuItem hasCheck onClick={renameSelectedItem} disabled={isNoneSelected}>
|
||||
{gettext('Rename')}
|
||||
</PgMenuItem>}
|
||||
{fmUtilsObj.hasCapability('delete') && <PgMenuItem hasCheck onClick={deleteSelectedItem} disabled={isNoneSelected}>
|
||||
{fmUtilsObj.hasCapability('delete') && !restrictedSharedStorage.includes(selectedSS) && <PgMenuItem hasCheck onClick={deleteSelectedItem} disabled={isNoneSelected}>
|
||||
{gettext('Delete')}
|
||||
</PgMenuItem>}
|
||||
{fmUtilsObj.hasCapability('upload') && <>
|
||||
{fmUtilsObj.hasCapability('upload') && !restrictedSharedStorage.includes(selectedSS) && <>
|
||||
<PgMenuDivider />
|
||||
<PgMenuItem hasCheck onClick={(e)=>{
|
||||
e.keepOpen = false;
|
||||
@ -820,7 +820,7 @@ export default function FileManager({params, closeModal, onOK, onCancel, sharedS
|
||||
onChange={(e)=>{
|
||||
let val = e.target.value;
|
||||
fmUtilsObj.setFileType(val);
|
||||
openDir(fmUtilsObj.currPath);
|
||||
openDir(fmUtilsObj.currPath, selectedSS);
|
||||
setFileType(val);
|
||||
}}
|
||||
options={fmUtilsObj.allowedFileTypes?.map((type)=>({
|
||||
@ -852,4 +852,5 @@ FileManager.propTypes = {
|
||||
onOK: PropTypes.func,
|
||||
onCancel: PropTypes.func,
|
||||
sharedStorages: PropTypes.array,
|
||||
restrictedSharedStorage: PropTypes.array,
|
||||
};
|
||||
|
@ -17,7 +17,7 @@ from threading import Lock
|
||||
|
||||
import json
|
||||
from config import PG_DEFAULT_DRIVER, ON_DEMAND_RECORD_COUNT,\
|
||||
ALLOW_SAVE_PASSWORD
|
||||
ALLOW_SAVE_PASSWORD, SHARED_STORAGE
|
||||
from werkzeug.user_agent import UserAgent
|
||||
from flask import Response, url_for, render_template, session, current_app
|
||||
from flask import request
|
||||
@ -52,7 +52,7 @@ from pgadmin.tools.sqleditor.utils.macros import get_macros,\
|
||||
get_user_macros, set_macros
|
||||
from pgadmin.utils.constants import MIMETYPE_APP_JS, \
|
||||
SERVER_CONNECTION_CLOSED, ERROR_MSG_TRANS_ID_NOT_FOUND, \
|
||||
ERROR_FETCHING_DATA, MY_STORAGE
|
||||
ERROR_FETCHING_DATA, MY_STORAGE, ACCESS_DENIED_MESSAGE
|
||||
from pgadmin.model import Server, ServerGroup
|
||||
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
|
||||
from pgadmin.settings import get_setting
|
||||
@ -1856,6 +1856,18 @@ def save_file():
|
||||
last_storage = Preferences.module('file_manager').preference(
|
||||
'last_storage').get()
|
||||
if last_storage != MY_STORAGE:
|
||||
selectedDirList = [sdir for sdir in SHARED_STORAGE if
|
||||
sdir['name'] == last_storage]
|
||||
selectedDir = selectedDirList[0] if len(
|
||||
selectedDirList) == 1 else None
|
||||
|
||||
if selectedDir:
|
||||
if selectedDir['restricted_access'] and \
|
||||
not current_user.has_role("Administrator"):
|
||||
return make_json_response(success=0,
|
||||
errormsg=ACCESS_DENIED_MESSAGE,
|
||||
info='ACCESS_DENIED',
|
||||
status=403)
|
||||
storage_manager_path = get_storage_directory(
|
||||
shared_storage=last_storage)
|
||||
else:
|
||||
|
@ -422,7 +422,9 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
|
||||
}).catch((err)=>{
|
||||
eventBus.fireEvent(QUERY_TOOL_EVENTS.HANDLE_API_ERROR, err);
|
||||
});
|
||||
} else {
|
||||
} else if(error.response?.status == 403 && error.response?.data.info == 'ACCESS_DENIED') {
|
||||
Notifier.error(error.response.data.errormsg);
|
||||
}else {
|
||||
let msg = parseApiError(error);
|
||||
eventBus.current.fireEvent(QUERY_TOOL_EVENTS.SET_MESSAGE, msg, true);
|
||||
eventBus.current.fireEvent(QUERY_TOOL_EVENTS.FOCUS_PANEL, PANELS.MESSAGES);
|
||||
|
@ -122,3 +122,6 @@ PSYCOPG3 = 'psycopg3'
|
||||
|
||||
# Shared storage
|
||||
MY_STORAGE = 'my_storage'
|
||||
ACCESS_DENIED_MESSAGE = gettext(
|
||||
"Access denied: You’re having limited access. You’re not allowed to "
|
||||
"Rename, Delete or Create any files/folders")
|
||||
|
@ -42,7 +42,6 @@ const files = [
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const transId = 140391;
|
||||
const configData = {
|
||||
'transId': transId,
|
||||
@ -87,6 +86,7 @@ const configData = {
|
||||
};
|
||||
|
||||
const sharedStorageConfig = ['Shared Storage'];
|
||||
const restrictedSharedStorage = [];
|
||||
|
||||
const params={
|
||||
dialog_type: 'select_file',
|
||||
@ -127,6 +127,7 @@ describe('FileManger', ()=>{
|
||||
onOK={onOK}
|
||||
onCancel={onCancel}
|
||||
sharedStorages={sharedStorageConfig}
|
||||
restrictedSharedStorage={restrictedSharedStorage}
|
||||
{...props}
|
||||
/>
|
||||
</Theme>);
|
||||
@ -134,6 +135,7 @@ describe('FileManger', ()=>{
|
||||
|
||||
it('init', (done)=>{
|
||||
networkMock.onPost('/file_manager/init').reply(200, {'data': configData});
|
||||
networkMock.onPost(`/file_manager/save_last_dir/${transId}`).reply(200, {'success':1,'errormsg':'','info':'','result':null,'data':null});
|
||||
let ctrl = ctrlMount({});
|
||||
setTimeout(()=>{
|
||||
ctrl.update();
|
||||
|
Loading…
Reference in New Issue
Block a user