Skip to content

Commit

Permalink
Ensure that the authenticated users can't access each other's directo…
Browse files Browse the repository at this point in the history
…ries and files by providing relative paths. #5734
  • Loading branch information
adityatoshniwal authored and akshay-joshi committed Jan 17, 2023
1 parent 188bcbd commit 64d7289
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 126 deletions.
41 changes: 5 additions & 36 deletions web/pgadmin/tools/backup/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
from flask_security import login_required, current_user
from pgadmin.misc.bgprocess.processes import BatchProcess, IProcessDesc
from pgadmin.utils import PgAdminModule, get_storage_directory, html, \
fs_short_path, document_dir, does_utility_exist, get_server
from pgadmin.utils.ajax import make_json_response, bad_request
fs_short_path, document_dir, does_utility_exist, get_server, \
filename_with_file_manager_path
from pgadmin.utils.ajax import make_json_response, bad_request, unauthorized

from config import PG_DEFAULT_DRIVER
from pgadmin.model import Server, SharedServer
Expand Down Expand Up @@ -189,40 +190,6 @@ def script():
)


def filename_with_file_manager_path(_file, create_file=True):
"""
Args:
file: File name returned from client file manager
create_file: Set flag to False when file creation doesn't required
Returns:
Filename to use for backup with full path taken from preference
"""
# Set file manager directory from preference
storage_dir = get_storage_directory()
if storage_dir:
_file = os.path.join(storage_dir, _file.lstrip('/').lstrip('\\'))
elif not os.path.isabs(_file):
_file = os.path.join(document_dir(), _file)

def short_filepath():
short_path = fs_short_path(_file)
# fs_short_path() function may return empty path on Windows
# if directory doesn't exists. In that case we strip the last path
# component and get the short path.
if os.name == 'nt' and short_path == '':
base_name = os.path.basename(_file)
dir_name = os.path.dirname(_file)
short_path = fs_short_path(dir_name) + '\\' + base_name
return short_path

if create_file:
# Touch the file to get the short path of the file on windows.
with open(_file, 'a'):
return short_filepath()

return short_filepath()


def _get_args_params_values(data, conn, backup_obj_type, backup_file, server,
manager):
"""
Expand Down Expand Up @@ -367,6 +334,8 @@ def create_backup_objects_job(sid):
try:
backup_file = filename_with_file_manager_path(
data['file'], (data.get('format', '') != 'directory'))
except PermissionError as e:
return unauthorized(errormsg=str(e))
except Exception as e:
return bad_request(errormsg=str(e))

Expand Down
36 changes: 6 additions & 30 deletions web/pgadmin/tools/import_export/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
from flask_security import login_required, current_user
from pgadmin.misc.bgprocess.processes import BatchProcess, IProcessDesc
from pgadmin.utils import PgAdminModule, get_storage_directory, html, \
fs_short_path, document_dir, IS_WIN, does_utility_exist
from pgadmin.utils.ajax import make_json_response, bad_request
fs_short_path, document_dir, IS_WIN, does_utility_exist, \
filename_with_file_manager_path
from pgadmin.utils.ajax import make_json_response, bad_request, unauthorized

from config import PG_DEFAULT_DRIVER
from pgadmin.model import Server
Expand Down Expand Up @@ -145,33 +146,6 @@ def script():
)


def filename_with_file_manager_path(_file, _present=False):
"""
Args:
file: File name returned from client file manager
Returns:
Filename to use for backup with full path taken from preference
"""
# Set file manager directory from preference
storage_dir = get_storage_directory()

if storage_dir:
_file = os.path.join(storage_dir, _file.lstrip('/').lstrip('\\'))
elif not os.path.isabs(_file):
_file = os.path.join(document_dir(), _file)

if not _present:
# Touch the file to get the short path of the file on windows.
with open(_file, 'a'):
return fs_short_path(_file)
else:
if not os.path.isfile(_file):
return None

return fs_short_path(_file)


def _get_ignored_column_list(data, driver, conn):
"""
Get list of ignored columns for import/export.
Expand Down Expand Up @@ -297,7 +271,9 @@ def create_import_export_job(sid):
if 'filename' in data:
try:
_file = filename_with_file_manager_path(
data['filename'], data['is_import'])
data['filename'], not data['is_import'])
except PermissionError as e:
return unauthorized(errormsg=str(e))
except Exception as e:
return bad_request(errormsg=str(e))

Expand Down
13 changes: 11 additions & 2 deletions web/pgadmin/tools/import_export_servers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@
from pgadmin.utils import PgAdminModule
from pgadmin.utils.ajax import bad_request
from pgadmin.utils.constants import MIMETYPE_APP_JS
from pgadmin.utils.ajax import make_json_response, internal_server_error
from pgadmin.utils.ajax import make_json_response, internal_server_error, \
unauthorized
from pgadmin.model import ServerGroup, Server
from pgadmin.utils import clear_database_servers, dump_database_servers,\
load_database_servers, validate_json_data
load_database_servers, validate_json_data, filename_with_file_manager_path
from urllib.parse import unquote
from pgadmin.utils.paths import get_storage_directory

Expand Down Expand Up @@ -118,6 +119,14 @@ def load_servers():

# retrieve storage directory path
storage_manager_path = get_storage_directory()

try:
file_path = filename_with_file_manager_path(file_path)
except PermissionError as e:
return unauthorized(errormsg=str(e))
except Exception as e:
return bad_request(errormsg=str(e))

if storage_manager_path:
# generate full path of file
file_path = os.path.join(
Expand Down
30 changes: 5 additions & 25 deletions web/pgadmin/tools/restore/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
from flask_security import login_required, current_user
from pgadmin.misc.bgprocess.processes import BatchProcess, IProcessDesc
from pgadmin.utils import PgAdminModule, get_storage_directory, html, \
fs_short_path, document_dir, does_utility_exist, get_server
from pgadmin.utils.ajax import make_json_response, bad_request
fs_short_path, document_dir, does_utility_exist, get_server, \
filename_with_file_manager_path
from pgadmin.utils.ajax import make_json_response, bad_request, \
internal_server_error

from config import PG_DEFAULT_DRIVER
from pgadmin.model import Server, SharedServer
Expand Down Expand Up @@ -129,28 +131,6 @@ def script():
)


def filename_with_file_manager_path(_file):
"""
Args:
file: File name returned from client file manager
Returns:
Filename to use for backup with full path taken from preference
"""
# Set file manager directory from preference
storage_dir = get_storage_directory()

if storage_dir:
_file = os.path.join(storage_dir, _file.lstrip('/').lstrip('\\'))
elif not os.path.isabs(_file):
_file = os.path.join(document_dir(), _file)

if not os.path.isfile(_file) and not os.path.exists(_file):
return None

return fs_short_path(_file)


def _get_create_req_data():
"""
Get data from request for create restore job.
Expand All @@ -164,7 +144,7 @@ def _get_create_req_data():
try:
_file = filename_with_file_manager_path(data['file'])
except Exception as e:
return True, bad_request(errormsg=str(e)), data
return True, internal_server_error(errormsg=str(e)), data, None

if _file is None:
return True, make_json_response(
Expand Down
75 changes: 46 additions & 29 deletions web/pgadmin/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,45 @@ def get_complete_file_path(file, validate=True):
return file


def filename_with_file_manager_path(_file, create_file=False,
skip_permission_check=False):
"""
Args:
file: File name returned from client file manager
create_file: Set flag to False when file creation doesn't required
Returns:
Filename to use for backup with full path taken from preference
"""
# Set file manager directory from preference
storage_dir = get_storage_directory()

from pgadmin.misc.file_manager import Filemanager
Filemanager.check_access_permission(
storage_dir, _file, skip_permission_check)
if storage_dir:
_file = os.path.join(storage_dir, _file.lstrip('/').lstrip('\\'))
elif not os.path.isabs(_file):
_file = os.path.join(document_dir(), _file)

def short_filepath():
short_path = fs_short_path(_file)
# fs_short_path() function may return empty path on Windows
# if directory doesn't exists. In that case we strip the last path
# component and get the short path.
if os.name == 'nt' and short_path == '':
base_name = os.path.basename(_file)
dir_name = os.path.dirname(_file)
short_path = fs_short_path(dir_name) + '\\' + base_name
return short_path

if create_file:
# Touch the file to get the short path of the file on windows.
with open(_file, 'a'):
return short_filepath()

return short_filepath()


def does_utility_exist(file):
"""
This function will check the utility file exists on given path.
Expand Down Expand Up @@ -434,27 +473,12 @@ def dump_database_servers(output_file, selected_servers,

object_dict["Servers"] = server_dict

# retrieve storage directory path
storage_manager_path = None
if not from_setup:
storage_manager_path = get_storage_directory(user)

# generate full path of file
file_path = unquote(output_file)

from pgadmin.misc.file_manager import Filemanager
try:
Filemanager.check_access_permission(storage_manager_path, file_path,
from_setup)
file_path = filename_with_file_manager_path(
unquote(output_file), skip_permission_check=from_setup)
except Exception as e:
return _handle_error(str(e), from_setup)

if storage_manager_path is not None:
file_path = os.path.join(
storage_manager_path,
file_path.lstrip('/').lstrip('\\')
)

# write to file
file_content = json.dumps(object_dict, indent=4)
error_str = "Error: {0}"
Expand Down Expand Up @@ -548,19 +572,12 @@ def load_database_servers(input_file, selected_servers,
if user is None:
return False, USER_NOT_FOUND % load_user

# retrieve storage directory path
storage_manager_path = None
if not from_setup:
storage_manager_path = get_storage_directory(user)

# generate full path of file
file_path = unquote(input_file)
if storage_manager_path:
# generate full path of file
file_path = os.path.join(
storage_manager_path,
file_path.lstrip('/').lstrip('\\')
)
try:
file_path = filename_with_file_manager_path(
unquote(input_file), skip_permission_check=from_setup)
except Exception as e:
return _handle_error(str(e), from_setup)

try:
with open(file_path) as f:
Expand Down
8 changes: 4 additions & 4 deletions web/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8517,9 +8517,9 @@ react-checkbox-tree@^1.7.2:
nanoid "^3.0.0"
prop-types "^15.5.8"

"react-data-grid@git+https://github.com/EnterpriseDB/react-data-grid.git/#200d2f5e02de694e3e9ffbe177c279bc40240fb8":
"react-data-grid@git+https://github.com/pgadmin-org/react-data-grid.git/#200d2f5e02de694e3e9ffbe177c279bc40240fb8":
version "7.0.0-beta.14"
resolved "git+https://github.com/EnterpriseDB/react-data-grid.git/#200d2f5e02de694e3e9ffbe177c279bc40240fb8"
resolved "git+https://github.com/pgadmin-org/react-data-grid.git/#200d2f5e02de694e3e9ffbe177c279bc40240fb8"
dependencies:
clsx "^1.1.1"

Expand Down Expand Up @@ -10337,9 +10337,9 @@ watchpack@^2.4.0:
glob-to-regexp "^0.4.1"
graceful-fs "^4.1.2"

"webcabin-docker@git+https://github.com/EnterpriseDB/wcDocker/#3df8aac825ee2892f4d824de273b779cc6dbcad8":
"webcabin-docker@git+https://github.com/pgadmin-org/wcdocker/#3df8aac825ee2892f4d824de273b779cc6dbcad8":
version "2.2.5"
resolved "git+https://github.com/EnterpriseDB/wcDocker/#3df8aac825ee2892f4d824de273b779cc6dbcad8"
resolved "git+https://github.com/pgadmin-org/wcdocker/#3df8aac825ee2892f4d824de273b779cc6dbcad8"
dependencies:
"@fortawesome/fontawesome-free" "^5.14.0"
FileSaver "^0.10.0"
Expand Down

0 comments on commit 64d7289

Please sign in to comment.