From 92c2af926a140be85692028bab6fc9a0e87a99c8 Mon Sep 17 00:00:00 2001 From: luukas Date: Sun, 27 Feb 2022 19:26:54 +0200 Subject: [PATCH 1/3] Reduced the amount of injection in JavaScript. * Moved file endpoints to file_handler.py * Added a global `serverId` for some pages * Fixed fi_FI translations * Some other stuff I don't remember --- app/classes/web/ajax_handler.py | 257 +--- app/classes/web/file_handler.py | 410 ++++++ app/classes/web/tornado_handler.py | 3 + app/frontend/templates/base.html | 14 +- app/frontend/templates/footer.html | 4 +- app/frontend/templates/panel/dashboard.html | 90 +- .../templates/panel/parts/details_stats.html | 4 +- .../templates/panel/server_backup.html | 226 ++-- .../templates/panel/server_config.html | 20 +- .../templates/panel/server_files.html | 1193 +++++++++-------- app/frontend/templates/panel/server_logs.html | 62 +- .../templates/panel/server_schedule_edit.html | 4 +- .../templates/panel/server_schedules.html | 8 +- app/frontend/templates/panel/server_term.html | 71 +- app/frontend/templates/server/wizard.html | 8 +- app/translations/fi_FI.json | 111 +- 16 files changed, 1386 insertions(+), 1099 deletions(-) create mode 100644 app/classes/web/file_handler.py diff --git a/app/classes/web/ajax_handler.py b/app/classes/web/ajax_handler.py index 5d739c9a..e847cc61 100644 --- a/app/classes/web/ajax_handler.py +++ b/app/classes/web/ajax_handler.py @@ -1,5 +1,4 @@ import os -import shutil import html import re import logging @@ -85,53 +84,8 @@ class AjaxHandler(BaseHandler): page_data['notify_data'] = data self.render_page('ajax/notify.html', page_data) - elif page == "get_file": - file_path = helper.get_os_understandable_path(self.get_argument('file_path', None)) - server_id = self.get_argument('id', None) - - if not self.check_server_id(server_id, 'get_file'): - return - else: - server_id = bleach.clean(server_id) - - if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), file_path)\ - or not helper.check_file_exists(os.path.abspath(file_path)): - logger.warning(f"Invalid path in get_file ajax call ({file_path})") - console.warning(f"Invalid path in get_file ajax call ({file_path})") - return - - - error = None - - try: - with open(file_path, encoding='utf-8') as file: - file_contents = file.read() - except UnicodeDecodeError: - file_contents = '' - error = 'UnicodeDecodeError' - - self.write({ - 'content': file_contents, - 'error': error - }) - self.finish() - - elif page == "get_tree": - server_id = self.get_argument('id', None) - path = self.get_argument('path', None) - - if not self.check_server_id(server_id, 'get_tree'): - return - else: - server_id = bleach.clean(server_id) - - if helper.validate_traversal(self.controller.servers.get_server_data_by_id(server_id)['path'], path): - self.write(helper.get_os_understandable_path(path) + '\n' + - helper.generate_tree(path)) - self.finish() elif page == "get_zip_tree": - server_id = self.get_argument('id', None) path = self.get_argument('path', None) self.write(helper.get_os_understandable_path(path) + '\n' + @@ -139,26 +93,12 @@ class AjaxHandler(BaseHandler): self.finish() elif page == "get_zip_dir": - server_id = self.get_argument('id', None) path = self.get_argument('path', None) self.write(helper.get_os_understandable_path(path) + '\n' + helper.generate_zip_dir(path)) self.finish() - elif page == "get_dir": - server_id = self.get_argument('id', None) - path = self.get_argument('path', None) - - if not self.check_server_id(server_id, 'get_tree'): - return - else: - server_id = bleach.clean(server_id) - - if helper.validate_traversal(self.controller.servers.get_server_data_by_id(server_id)['path'], path): - self.write(helper.get_os_understandable_path(path) + '\n' + - helper.generate_dir(path)) - self.finish() @tornado.web.authenticated def post(self, page): @@ -210,69 +150,10 @@ class AjaxHandler(BaseHandler): server_id, self.get_remote_ip()) - elif page == "create_file": - if not permissions['Files'] in user_perms: - if not superuser: - self.redirect("/panel/error?error=Unauthorized access to Files") - return - file_parent = helper.get_os_understandable_path(self.get_body_argument('file_parent', default=None, strip=True)) - file_name = self.get_body_argument('file_name', default=None, strip=True) - file_path = os.path.join(file_parent, file_name) - server_id = self.get_argument('id', None) - - if not self.check_server_id(server_id, 'create_file'): - return - else: - server_id = bleach.clean(server_id) - - if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), file_path) \ - or helper.check_file_exists(os.path.abspath(file_path)): - logger.warning(f"Invalid path in create_file ajax call ({file_path})") - console.warning(f"Invalid path in create_file ajax call ({file_path})") - return - - # Create the file by opening it - with open(file_path, 'w', encoding='utf-8') as file_object: - file_object.close() - - elif page == "create_dir": - if not permissions['Files'] in user_perms: - if not superuser: - self.redirect("/panel/error?error=Unauthorized access to Files") - return - dir_parent = helper.get_os_understandable_path(self.get_body_argument('dir_parent', default=None, strip=True)) - dir_name = self.get_body_argument('dir_name', default=None, strip=True) - dir_path = os.path.join(dir_parent, dir_name) - server_id = self.get_argument('id', None) - - if not self.check_server_id(server_id, 'create_dir'): - return - else: - server_id = bleach.clean(server_id) - - if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), dir_path) \ - or helper.check_path_exists(os.path.abspath(dir_path)): - logger.warning(f"Invalid path in create_dir ajax call ({dir_path})") - console.warning(f"Invalid path in create_dir ajax call ({dir_path})") - return - # Create the directory - os.mkdir(dir_path) - elif page == "send_order": self.controller.users.update_server_order(exec_user['user_id'], bleach.clean(self.get_argument('order'))) return - elif page == "unzip_file": - if not permissions['Files'] in user_perms: - if not superuser: - self.redirect("/panel/error?error=Unauthorized access to Files") - return - server_id = self.get_argument('id', None) - path = helper.get_os_understandable_path(self.get_argument('path', None)) - helper.unzipFile(path) - self.redirect(f"/panel/server_detail?id={server_id}&subpage=files") - return - elif page == "kill": if not permissions['Commands'] in user_perms: if not superuser: @@ -341,31 +222,6 @@ class AjaxHandler(BaseHandler): 'Players': Enum_Permissions_Server.Players, } user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id) - if page == "del_file": - if not permissions['Files'] in user_perms: - if not superuser: - self.redirect("/panel/error?error=Unauthorized access to Files") - return - file_path = helper.get_os_understandable_path(self.get_body_argument('file_path', default=None, strip=True)) - server_id = self.get_argument('id', None) - - console.warning(f"Delete {file_path} for server {server_id}") - - if not self.check_server_id(server_id, 'del_file'): - return - else: server_id = bleach.clean(server_id) - - server_info = self.controller.servers.get_server_data_by_id(server_id) - if not (helper.in_path(helper.get_os_understandable_path(server_info['path']), file_path) \ - or helper.in_path(helper.get_os_understandable_path(server_info['backup_path']), file_path)) \ - or not helper.check_file_exists(os.path.abspath(file_path)): - logger.warning(f"Invalid path in del_file ajax call ({file_path})") - console.warning(f"Invalid path in del_file ajax call ({file_path})") - return - - # Delete the file - os.remove(file_path) - if page == "del_task": if not permissions['Schedule'] in user_perms: self.redirect("/panel/error?error=Unauthorized access to Tasks") @@ -383,7 +239,7 @@ class AjaxHandler(BaseHandler): console.warning(f"Delete {file_path} for server {server_id}") - if not self.check_server_id(server_id, 'del_file'): + if not self.check_server_id(server_id, 'del_backup'): return else: server_id = bleach.clean(server_id) @@ -391,41 +247,14 @@ class AjaxHandler(BaseHandler): if not (helper.in_path(helper.get_os_understandable_path(server_info['path']), file_path) \ or helper.in_path(helper.get_os_understandable_path(server_info['backup_path']), file_path)) \ or not helper.check_file_exists(os.path.abspath(file_path)): - logger.warning(f"Invalid path in del_file ajax call ({file_path})") - console.warning(f"Invalid path in del_file ajax call ({file_path})") + logger.warning(f"Invalid path in del_backup ajax call ({file_path})") + console.warning(f"Invalid path in del_backup ajax call ({file_path})") return # Delete the file if helper.validate_traversal(helper.get_os_understandable_path(server_info['backup_path']), file_path): os.remove(file_path) - elif page == "del_dir": - if not permissions['Files'] in user_perms: - if not superuser: - self.redirect("/panel/error?error=Unauthorized access to Files") - return - dir_path = helper.get_os_understandable_path(self.get_body_argument('dir_path', default=None, strip=True)) - server_id = self.get_argument('id', None) - - console.warning(f"Delete {dir_path} for server {server_id}") - - if not self.check_server_id(server_id, 'del_dir'): - return - else: - server_id = bleach.clean(server_id) - - server_info = self.controller.servers.get_server_data_by_id(server_id) - if not helper.in_path(helper.get_os_understandable_path(server_info['path']), dir_path) \ - or not helper.check_path_exists(os.path.abspath(dir_path)): - logger.warning(f"Invalid path in del_file ajax call ({dir_path})") - console.warning(f"Invalid path in del_file ajax call ({dir_path})") - return - - # Delete the directory - # os.rmdir(dir_path) # Would only remove empty directories - if helper.validate_traversal(helper.get_os_understandable_path(server_info['path']), dir_path): - shutil.rmtree(dir_path) # Removes also when there are contents - elif page == "delete_server": if not permissions['Config'] in user_perms: if not superuser: @@ -464,86 +293,6 @@ class AjaxHandler(BaseHandler): self.tasks_manager.remove_all_server_tasks(server_id) self.controller.remove_server(server_id, True) - @tornado.web.authenticated - def put(self, page): - api_key, _, exec_user = self.current_user - superuser = exec_user['superuser'] - if api_key is not None: - superuser = superuser and api_key.superuser - - server_id = self.get_argument('id', None) - permissions = { - 'Commands': Enum_Permissions_Server.Commands, - 'Terminal': Enum_Permissions_Server.Terminal, - 'Logs': Enum_Permissions_Server.Logs, - 'Schedule': Enum_Permissions_Server.Schedule, - 'Backup': Enum_Permissions_Server.Backup, - 'Files': Enum_Permissions_Server.Files, - 'Config': Enum_Permissions_Server.Config, - 'Players': Enum_Permissions_Server.Players, - } - user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id) - if page == "save_file": - if not permissions['Files'] in user_perms: - if not superuser: - self.redirect("/panel/error?error=Unauthorized access to Files") - return - file_contents = self.get_body_argument('file_contents', default=None, strip=True) - file_path = helper.get_os_understandable_path(self.get_body_argument('file_path', default=None, strip=True)) - server_id = self.get_argument('id', None) - - if not self.check_server_id(server_id, 'save_file'): - return - else: - server_id = bleach.clean(server_id) - - if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), file_path)\ - or not helper.check_file_exists(os.path.abspath(file_path)): - logger.warning(f"Invalid path in save_file ajax call ({file_path})") - console.warning(f"Invalid path in save_file ajax call ({file_path})") - return - - # Open the file in write mode and store the content in file_object - with open(file_path, 'w', encoding='utf-8') as file_object: - file_object.write(file_contents) - - elif page == "rename_item": - if not permissions['Files'] in user_perms: - if not superuser: - self.redirect("/panel/error?error=Unauthorized access to Files") - return - item_path = helper.get_os_understandable_path(self.get_body_argument('item_path', default=None, strip=True)) - new_item_name = self.get_body_argument('new_item_name', default=None, strip=True) - server_id = self.get_argument('id', None) - - if not self.check_server_id(server_id, 'rename_item'): - return - else: - server_id = bleach.clean(server_id) - - if item_path is None or new_item_name is None: - logger.warning("Invalid path(s) in rename_item ajax call") - console.warning("Invalid path(s) in rename_item ajax call") - return - - if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), item_path) \ - or not helper.check_path_exists(os.path.abspath(item_path)): - logger.warning(f"Invalid old name path in rename_item ajax call ({server_id})") - console.warning(f"Invalid old name path in rename_item ajax call ({server_id})") - return - - new_item_path = os.path.join(os.path.split(item_path)[0], new_item_name) - - if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), - new_item_path) \ - or helper.check_path_exists(os.path.abspath(new_item_path)): - logger.warning(f"Invalid new name path in rename_item ajax call ({server_id})") - console.warning(f"Invalid new name path in rename_item ajax call ({server_id})") - return - - # RENAME - os.rename(item_path, new_item_path) - def check_server_id(self, server_id, page_name): if server_id is None: logger.warning(f"Server ID not defined in {page_name} ajax call ({server_id})") diff --git a/app/classes/web/file_handler.py b/app/classes/web/file_handler.py new file mode 100644 index 00000000..45b56ff1 --- /dev/null +++ b/app/classes/web/file_handler.py @@ -0,0 +1,410 @@ +import os +import shutil +import logging +import tornado.web +import tornado.escape +import bleach + +from app.classes.shared.console import console +from app.classes.shared.helpers import helper + +from app.classes.web.base_handler import BaseHandler +from app.classes.models.server_permissions import Enum_Permissions_Server + +logger = logging.getLogger(__name__) + + +class FileHandler(BaseHandler): + + def render_page(self, template, page_data): + self.render( + template, + data=page_data, + translate=self.translator.translate, + ) + + @tornado.web.authenticated + def get(self, page): + api_key, _, exec_user = self.current_user + superuser = exec_user['superuser'] + if api_key is not None: + superuser = superuser and api_key.superuser + + server_id = self.get_argument('id', None) + + permissions = { + 'Commands': Enum_Permissions_Server.Commands, + 'Terminal': Enum_Permissions_Server.Terminal, + 'Logs': Enum_Permissions_Server.Logs, + 'Schedule': Enum_Permissions_Server.Schedule, + 'Backup': Enum_Permissions_Server.Backup, + 'Files': Enum_Permissions_Server.Files, + 'Config': Enum_Permissions_Server.Config, + 'Players': Enum_Permissions_Server.Players, + } + user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id) + + if page == "get_file": + if not permissions['Files'] in user_perms: + if not superuser: + self.redirect("/panel/error?error=Unauthorized access to Files") + return + file_path = helper.get_os_understandable_path(self.get_argument('file_path', None)) + + if not self.check_server_id(server_id, 'get_file'): + return + else: + server_id = bleach.clean(server_id) + + if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), file_path)\ + or not helper.check_file_exists(os.path.abspath(file_path)): + logger.warning(f"Invalid path in get_file file file ajax call ({file_path})") + console.warning(f"Invalid path in get_file file file ajax call ({file_path})") + return + + + error = None + + try: + with open(file_path, encoding='utf-8') as file: + file_contents = file.read() + except UnicodeDecodeError: + file_contents = '' + error = 'UnicodeDecodeError' + + self.write({ + 'content': file_contents, + 'error': error + }) + self.finish() + + elif page == "get_tree": + if not permissions['Files'] in user_perms: + if not superuser: + self.redirect("/panel/error?error=Unauthorized access to Files") + return + path = self.get_argument('path', None) + + if not self.check_server_id(server_id, 'get_tree'): + return + else: + server_id = bleach.clean(server_id) + + if helper.validate_traversal(self.controller.servers.get_server_data_by_id(server_id)['path'], path): + self.write(helper.get_os_understandable_path(path) + '\n' + + helper.generate_tree(path)) + self.finish() + + elif page == "get_dir": + if not permissions['Files'] in user_perms: + if not superuser: + self.redirect("/panel/error?error=Unauthorized access to Files") + return + path = self.get_argument('path', None) + + if not self.check_server_id(server_id, 'get_tree'): + return + else: + server_id = bleach.clean(server_id) + + if helper.validate_traversal(self.controller.servers.get_server_data_by_id(server_id)['path'], path): + self.write(helper.get_os_understandable_path(path) + '\n' + + helper.generate_dir(path)) + self.finish() + + @tornado.web.authenticated + def post(self, page): + api_key, _, exec_user = self.current_user + superuser = exec_user['superuser'] + if api_key is not None: + superuser = superuser and api_key.superuser + + server_id = self.get_argument('id', None) + + permissions = { + 'Commands': Enum_Permissions_Server.Commands, + 'Terminal': Enum_Permissions_Server.Terminal, + 'Logs': Enum_Permissions_Server.Logs, + 'Schedule': Enum_Permissions_Server.Schedule, + 'Backup': Enum_Permissions_Server.Backup, + 'Files': Enum_Permissions_Server.Files, + 'Config': Enum_Permissions_Server.Config, + 'Players': Enum_Permissions_Server.Players, + } + user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id) + + if page == "create_file": + if not permissions['Files'] in user_perms: + if not superuser: + self.redirect("/panel/error?error=Unauthorized access to Files") + return + file_parent = helper.get_os_understandable_path(self.get_body_argument('file_parent', default=None, strip=True)) + file_name = self.get_body_argument('file_name', default=None, strip=True) + file_path = os.path.join(file_parent, file_name) + + if not self.check_server_id(server_id, 'create_file'): + return + else: + server_id = bleach.clean(server_id) + + if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), file_path) \ + or helper.check_file_exists(os.path.abspath(file_path)): + logger.warning(f"Invalid path in create_file file ajax call ({file_path})") + console.warning(f"Invalid path in create_file file ajax call ({file_path})") + return + + # Create the file by opening it + with open(file_path, 'w', encoding='utf-8') as file_object: + file_object.close() + + elif page == "create_dir": + if not permissions['Files'] in user_perms: + if not superuser: + self.redirect("/panel/error?error=Unauthorized access to Files") + return + dir_parent = helper.get_os_understandable_path(self.get_body_argument('dir_parent', default=None, strip=True)) + dir_name = self.get_body_argument('dir_name', default=None, strip=True) + dir_path = os.path.join(dir_parent, dir_name) + + if not self.check_server_id(server_id, 'create_dir'): + return + else: + server_id = bleach.clean(server_id) + + if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), dir_path) \ + or helper.check_path_exists(os.path.abspath(dir_path)): + logger.warning(f"Invalid path in create_dir file ajax call ({dir_path})") + console.warning(f"Invalid path in create_dir file ajax call ({dir_path})") + return + # Create the directory + os.mkdir(dir_path) + + elif page == "unzip_file": + if not permissions['Files'] in user_perms: + if not superuser: + self.redirect("/panel/error?error=Unauthorized access to Files") + return + path = helper.get_os_understandable_path(self.get_argument('path', None)) + helper.unzipFile(path) + self.redirect(f"/panel/server_detail?id={server_id}&subpage=files") + return + + + @tornado.web.authenticated + def delete(self, page): + api_key, _, exec_user = self.current_user + superuser = exec_user['superuser'] + if api_key is not None: + superuser = superuser and api_key.superuser + + server_id = self.get_argument('id', None) + + permissions = { + 'Commands': Enum_Permissions_Server.Commands, + 'Terminal': Enum_Permissions_Server.Terminal, + 'Logs': Enum_Permissions_Server.Logs, + 'Schedule': Enum_Permissions_Server.Schedule, + 'Backup': Enum_Permissions_Server.Backup, + 'Files': Enum_Permissions_Server.Files, + 'Config': Enum_Permissions_Server.Config, + 'Players': Enum_Permissions_Server.Players, + } + user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id) + if page == "del_file": + if not permissions['Files'] in user_perms: + if not superuser: + self.redirect("/panel/error?error=Unauthorized access to Files") + return + file_path = helper.get_os_understandable_path(self.get_body_argument('file_path', default=None, strip=True)) + + console.warning(f"Delete {file_path} for server {server_id}") + + if not self.check_server_id(server_id, 'del_file'): + return + else: server_id = bleach.clean(server_id) + + server_info = self.controller.servers.get_server_data_by_id(server_id) + if not (helper.in_path(helper.get_os_understandable_path(server_info['path']), file_path) \ + or helper.in_path(helper.get_os_understandable_path(server_info['backup_path']), file_path)) \ + or not helper.check_file_exists(os.path.abspath(file_path)): + logger.warning(f"Invalid path in del_file file ajax call ({file_path})") + console.warning(f"Invalid path in del_file file ajax call ({file_path})") + return + + # Delete the file + os.remove(file_path) + + elif page == "del_dir": + if not permissions['Files'] in user_perms: + if not superuser: + self.redirect("/panel/error?error=Unauthorized access to Files") + return + dir_path = helper.get_os_understandable_path(self.get_body_argument('dir_path', default=None, strip=True)) + + console.warning(f"Delete {dir_path} for server {server_id}") + + if not self.check_server_id(server_id, 'del_dir'): + return + else: + server_id = bleach.clean(server_id) + + server_info = self.controller.servers.get_server_data_by_id(server_id) + if not helper.in_path(helper.get_os_understandable_path(server_info['path']), dir_path) \ + or not helper.check_path_exists(os.path.abspath(dir_path)): + logger.warning(f"Invalid path in del_file file ajax call ({dir_path})") + console.warning(f"Invalid path in del_file file ajax call ({dir_path})") + return + + # Delete the directory + # os.rmdir(dir_path) # Would only remove empty directories + if helper.validate_traversal(helper.get_os_understandable_path(server_info['path']), dir_path): + shutil.rmtree(dir_path) # Removes also when there are contents + + @tornado.web.authenticated + def put(self, page): + api_key, _, exec_user = self.current_user + superuser = exec_user['superuser'] + if api_key is not None: + superuser = superuser and api_key.superuser + + server_id = self.get_argument('id', None) + permissions = { + 'Commands': Enum_Permissions_Server.Commands, + 'Terminal': Enum_Permissions_Server.Terminal, + 'Logs': Enum_Permissions_Server.Logs, + 'Schedule': Enum_Permissions_Server.Schedule, + 'Backup': Enum_Permissions_Server.Backup, + 'Files': Enum_Permissions_Server.Files, + 'Config': Enum_Permissions_Server.Config, + 'Players': Enum_Permissions_Server.Players, + } + user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id) + if page == "save_file": + if not permissions['Files'] in user_perms: + if not superuser: + self.redirect("/panel/error?error=Unauthorized access to Files") + return + file_contents = self.get_body_argument('file_contents', default=None, strip=True) + file_path = helper.get_os_understandable_path(self.get_body_argument('file_path', default=None, strip=True)) + + if not self.check_server_id(server_id, 'save_file'): + return + else: + server_id = bleach.clean(server_id) + + if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), file_path)\ + or not helper.check_file_exists(os.path.abspath(file_path)): + logger.warning(f"Invalid path in save_file file ajax call ({file_path})") + console.warning(f"Invalid path in save_file file ajax call ({file_path})") + return + + # Open the file in write mode and store the content in file_object + with open(file_path, 'w', encoding='utf-8') as file_object: + file_object.write(file_contents) + + elif page == "rename_file": + if not permissions['Files'] in user_perms: + if not superuser: + self.redirect("/panel/error?error=Unauthorized access to Files") + return + item_path = helper.get_os_understandable_path(self.get_body_argument('item_path', default=None, strip=True)) + new_item_name = self.get_body_argument('new_item_name', default=None, strip=True) + + if not self.check_server_id(server_id, 'rename_file'): + return + else: + server_id = bleach.clean(server_id) + + if item_path is None or new_item_name is None: + logger.warning("Invalid path(s) in rename_file file ajax call") + console.warning("Invalid path(s) in rename_file file ajax call") + return + + if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), item_path) \ + or not helper.check_path_exists(os.path.abspath(item_path)): + logger.warning(f"Invalid old name path in rename_file file ajax call ({server_id})") + console.warning(f"Invalid old name path in rename_file file ajax call ({server_id})") + return + + new_item_path = os.path.join(os.path.split(item_path)[0], new_item_name) + + if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), + new_item_path) \ + or helper.check_path_exists(os.path.abspath(new_item_path)): + logger.warning(f"Invalid new name path in rename_file file ajax call ({server_id})") + console.warning(f"Invalid new name path in rename_file file ajax call ({server_id})") + return + + # RENAME + os.rename(item_path, new_item_path) + + + @tornado.web.authenticated + def patch(self, page): + api_key, _, exec_user = self.current_user + superuser = exec_user['superuser'] + if api_key is not None: + superuser = superuser and api_key.superuser + + server_id = self.get_argument('id', None) + permissions = { + 'Commands': Enum_Permissions_Server.Commands, + 'Terminal': Enum_Permissions_Server.Terminal, + 'Logs': Enum_Permissions_Server.Logs, + 'Schedule': Enum_Permissions_Server.Schedule, + 'Backup': Enum_Permissions_Server.Backup, + 'Files': Enum_Permissions_Server.Files, + 'Config': Enum_Permissions_Server.Config, + 'Players': Enum_Permissions_Server.Players, + } + user_perms = self.controller.server_perms.get_user_id_permissions_list(exec_user['user_id'], server_id) + if page == "rename_file": + if not permissions['Files'] in user_perms: + if not superuser: + self.redirect("/panel/error?error=Unauthorized access to Files") + return + item_path = helper.get_os_understandable_path(self.get_body_argument('item_path', default=None, strip=True)) + new_item_name = self.get_body_argument('new_item_name', default=None, strip=True) + + if not self.check_server_id(server_id, 'rename_file'): + return + else: + server_id = bleach.clean(server_id) + + if item_path is None or new_item_name is None: + logger.warning("Invalid path(s) in rename_file file ajax call") + console.warning("Invalid path(s) in rename_file file ajax call") + return + + if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), item_path) \ + or not helper.check_path_exists(os.path.abspath(item_path)): + logger.warning(f"Invalid old name path in rename_file file ajax call ({server_id})") + console.warning(f"Invalid old name path in rename_file file ajax call ({server_id})") + return + + new_item_path = os.path.join(os.path.split(item_path)[0], new_item_name) + + if not helper.in_path(helper.get_os_understandable_path(self.controller.servers.get_server_data_by_id(server_id)['path']), + new_item_path) \ + or helper.check_path_exists(os.path.abspath(new_item_path)): + logger.warning(f"Invalid new name path in rename_file file ajax call ({server_id})") + console.warning(f"Invalid new name path in rename_file file ajax call ({server_id})") + return + + # RENAME + os.rename(item_path, new_item_path) + + def check_server_id(self, server_id, page_name): + if server_id is None: + logger.warning(f"Server ID not defined in {page_name} file ajax call ({server_id})") + console.warning(f"Server ID not defined in {page_name} file ajax call ({server_id})") + return + else: + server_id = bleach.clean(server_id) + + # does this server id exist? + if not self.controller.servers.server_id_exists(server_id): + logger.warning(f"Server ID not found in {page_name} file ajax call ({server_id})") + console.warning(f"Server ID not found in {page_name} file ajax call ({server_id})") + return + return True diff --git a/app/classes/web/tornado_handler.py b/app/classes/web/tornado_handler.py index 85e7b7c1..2bd1240f 100644 --- a/app/classes/web/tornado_handler.py +++ b/app/classes/web/tornado_handler.py @@ -4,6 +4,8 @@ import json import asyncio import logging +from app.classes.web.file_handler import FileHandler + logger = logging.getLogger(__name__) try: @@ -122,6 +124,7 @@ class Webserver: (r'/panel/(.*)', PanelHandler, handler_args), (r'/server/(.*)', ServerHandler, handler_args), (r'/ajax/(.*)', AjaxHandler, handler_args), + (r'/files/(.*)', FileHandler, handler_args), (r'/api/stats/servers', ServersStats, handler_args), (r'/api/stats/node', NodeStats, handler_args), (r'/ws', SocketHandler, handler_args), diff --git a/app/frontend/templates/base.html b/app/frontend/templates/base.html index 05c8e457..8f86cedf 100644 --- a/app/frontend/templates/base.html +++ b/app/frontend/templates/base.html @@ -34,6 +34,10 @@ + + + + @@ -375,7 +379,7 @@ } function notify(message) { - console.log(`notify(${message}})`); + console.log(`notify(${message})`); var paragraphEl = document.createElement('p'); var closeEl = document.createElement('span'); @@ -410,7 +414,15 @@ } webSocket.on('notification', notify); + document.addEventListener('alpine:init', () => { + console.log('%c[Crafty Controller] %cAlpine.js pre-initialization!', 'font-weight: 900; color: #800080;', 'color: #eee;'); + }) + document.addEventListener('alpine:initialized', () => { + console.log('%c[Crafty Controller] %cAlpine.js initialized!', 'font-weight: 900; color: #800080;', 'color: #eee;'); + }) + $(document).ready(function () { + console.log('%c[Crafty Controller] %cReady for JS!', 'font-weight: 900; color: #800080;', 'font-weight: 900; color: #eee;'); $('#support_logs').click(function () { var dialog = bootbox.dialog({ message: '

{{ translate('notify', 'preparingLogs', data['lang']) }}

', diff --git a/app/frontend/templates/footer.html b/app/frontend/templates/footer.html index 175c6c12..fec76eba 100644 --- a/app/frontend/templates/footer.html +++ b/app/frontend/templates/footer.html @@ -2,11 +2,11 @@ - \ No newline at end of file + diff --git a/app/frontend/templates/panel/dashboard.html b/app/frontend/templates/panel/dashboard.html index 183b0b63..5bb655f8 100644 --- a/app/frontend/templates/panel/dashboard.html +++ b/app/frontend/templates/panel/dashboard.html @@ -134,29 +134,43 @@ {% if server['user_command_permission'] %} {% if server['stats']['running'] %} -   - -   -   + + +   + + + +   + + + +   + {% elif server['stats']['updating']%} + {{ translate('serverTerm', 'updating', data['lang']) }} {% elif server['stats']['waiting_start']%} + {{ translate('dashboard', 'starting', data['lang']) }} {% else %} -   -   -   + + +   + + +   + + +   {% end %} {% end %} @@ -439,9 +453,9 @@ send_command(server_id, 'start_server'); bootbox.alert({ backdrop: true, - title: '{% raw translate("dashboard", "sendingCommand", data['lang']) %}', - message: '
  {% raw translate("dashboard", "bePatientStart", data['lang']) %}
' - }); + title: '{% raw translate("dashboard", "sendingCommand", data["lang"]) %}', + message: '
  {% raw translate("dashboard", "bePatientStart", data["lang"]) %}
' + }); }); $(".stop_button").click(function () { @@ -450,9 +464,9 @@ send_command(server_id, 'stop_server'); bootbox.alert({ backdrop: true, - title: '{% raw translate("dashboard", "sendingCommand", data['lang']) %}', - message: '
  {% raw translate("dashboard", "bePatientStop", data['lang']) %}
' - }); + title: '{% raw translate("dashboard", "sendingCommand", data["lang"]) %}', + message: '
  {% raw translate("dashboard", "bePatientStop", data["lang"]) %}
' + }); }); $(".restart_button").click(function () { @@ -460,9 +474,9 @@ send_command(server_id, 'restart_server'); bootbox.alert({ backdrop: true, - title: '{% raw translate("dashboard", "sendingCommand", data['lang']) %}', - message: '
  {% raw translate("dashboard", "bePatientRestart", data['lang']) %}
' - }); + title: '{% raw translate("dashboard", "sendingCommand", data["lang"]) %}', + message: '
  {% raw translate("dashboard", "bePatientRestart", data["lang"]) %}
' + }); }); $(".kill_button").click(function () { server_id = $(this).attr("data-id"); @@ -470,11 +484,11 @@ message: "This will kill the server process and all it's subprocesses. Killing a process can potentially corrupt files. Only do this in extreme circumstances. Are you sure you would like to continue?", buttons: { confirm: { - label: '{% raw translate("dashboard", "kill", data['lang']) %}', + label: '{% raw translate("dashboard", "kill", data["lang"]) %}', className: 'btn-danger' }, cancel: { - label: '{% raw translate("panelConfig", "cancel", data['lang']) %}', + label: '{% raw translate("panelConfig", "cancel", data["lang"]) %}', className: 'btn-secondary' } }, @@ -482,7 +496,7 @@ if (result) { send_kill(server_id); var dialog = bootbox.dialog({ - title: '{% raw translate("dashboard", "killing", data['lang']) %}', + title: '{% raw translate("dashboard", "killing", data["lang"]) %}', message: '

Loading...

' }); @@ -524,13 +538,13 @@ if (webSocket) { webSocket.on('update_button_status', function (updateButton) { - var id = 'controls'; - var dataId = updateButton.server_id; - var string = updateButton.string - var id = id.concat(updateButton.server_id); - if (updateButton.isUpdating) { - console.log(updateButton.isUpdating) - document.getElementById(id).innerHTML = string; + let serverId = updateButton.server_id; + let message = updateButton.string; + let updating = updateButton.isUpdating; + let id = 'controls' + serverId; + if (updating) { + console.log(updating) + document.getElementById(id).innerHTML = message; } else { window.location.reload() @@ -547,9 +561,9 @@ send_command(server_id, 'clone_server'); bootbox.alert({ backdrop: true, - title: '{% raw translate("dashboard", "sendingCommand", data['lang']) %}', - message: '
  {% raw translate("dashboard", "bePatientClone", data['lang']) %}
' - }); + title: '{% raw translate("dashboard", "sendingCommand", data["lang"]) %}', + message: '
  {% raw translate("dashboard", "bePatientClone", data["lang"]) %}
' + }); }); }); @@ -605,4 +619,4 @@ -{% end %} \ No newline at end of file +{% end %} diff --git a/app/frontend/templates/panel/parts/details_stats.html b/app/frontend/templates/panel/parts/details_stats.html index 019050f8..f05c4336 100644 --- a/app/frontend/templates/panel/parts/details_stats.html +++ b/app/frontend/templates/panel/parts/details_stats.html @@ -91,7 +91,7 @@ document.body.onload = (() => { console.log('calculateTime'); - startedUTC = '{{ data['server_stats']['started'] }}'; + startedUTC = "{{ data['server_stats']['started'] }}"; if (startedUTC != 'False') { console.log('started utc:', startedUTC); startedUTC = moment.utc(startedUTC, 'YYYY-MM-DD HH:mm:ss'); @@ -220,4 +220,4 @@ webSocket.on('update_server_details', update_server_details); //} }); - \ No newline at end of file + diff --git a/app/frontend/templates/panel/server_backup.html b/app/frontend/templates/panel/server_backup.html index e71522c2..482b287c 100644 --- a/app/frontend/templates/panel/server_backup.html +++ b/app/frontend/templates/panel/server_backup.html @@ -123,136 +123,138 @@ {% block js %} -{% end %} \ No newline at end of file +{% end %} diff --git a/app/frontend/templates/panel/server_config.html b/app/frontend/templates/panel/server_config.html index 7cdf3c2a..74216884 100644 --- a/app/frontend/templates/panel/server_config.html +++ b/app/frontend/templates/panel/server_config.html @@ -32,7 +32,7 @@
- {% include "parts/server_controls_list.html %} + {% include "parts/server_controls_list.html" %}
@@ -188,14 +188,14 @@
{% if data['server_stats']['running'] %} - {{ translate('serverConfig', 'deleteServer', data['lang']) }}
{{ translate('serverConfig', 'stopBeforeDeleting', data['lang']) }} {% else %} - - - + + +
{% end %} {% end %} @@ -93,7 +93,9 @@ {% block js %} -{% end %} \ No newline at end of file +{% end %} diff --git a/app/frontend/templates/server/wizard.html b/app/frontend/templates/server/wizard.html index a36fe068..46b50a22 100644 --- a/app/frontend/templates/server/wizard.html +++ b/app/frontend/templates/server/wizard.html @@ -426,7 +426,7 @@ document.getElementById("root_files_button").addEventListener("click", function(){ if(document.forms["zip"]["server_path"].value != ""){ if(document.getElementById('root_files_button').classList.contains('clicked')){ - document.getElementById('main-tree-div').innerHTML = '{{ translate('serverFiles', 'files', data['lang']) }}' + document.getElementById('main-tree-div').innerHTML = '{{ translate("serverFiles", "files", data["lang"]) }}' }else{ document.getElementById('root_files_button').classList.add('clicked') } @@ -482,8 +482,8 @@ function hide(event) { function wait_msg(importing){ bootbox.alert({ - title: importing ? '{% raw translate("serverWizard", "importing", data['lang']) %}' : '{% raw translate("serverWizard", "downloading", data['lang']) %}', - message: ' {% raw translate("serverWizard", "bePatient", data['lang']) %}', + title: importing ? '{% raw translate("serverWizard", "importing", data["lang"]) %}' : '{% raw translate("serverWizard", "downloading", data["lang"]) %}', + message: ' {% raw translate("serverWizard", "bePatient", data["lang"]) %}', }); } @@ -647,4 +647,4 @@ function hide(event) { } //]]> -{% end %} \ No newline at end of file +{% end %} diff --git a/app/translations/fi_FI.json b/app/translations/fi_FI.json index b61a46cb..8b19ef0f 100644 --- a/app/translations/fi_FI.json +++ b/app/translations/fi_FI.json @@ -76,10 +76,67 @@ "clickRoot": "Napsauta tästä valitaksesi juurihakemiston", "explainRoot": "Napsauta alla olevaa painiketta valitaksesi palvelimesi juurihakemiston arkistosta" }, - "usersConfig":{ + "userConfig":{ + "pageTitle": "Muokkaa käyttäjää", + "pageTitleNew": "Luo käyttäjä", + "config": "Asetukset", + "apiKey": "API-avaimet", + "userSettings": "Käyttäjäasetukset", + "userName": "Käyttäjätunnus", + "userNameDesc": "Millä nimellä haluat kutsua tätä käyttäjää?", + "password": "Salasana", + "repeat": "Toista salasana", + "leaveBlank": "Jos haluat muokata käyttäjää vaihtamatta salasanaa, jätä se tyhjäksi.", + "craftyPermDesc": "Craftyn oikeuksia, joita tällä käyttäjällä on", + "gravEmail": "Gravatar™ Sähköposti", + "gravDesc": "Tämä shäköposti on vain Gravatar™-palvelun käyttöön. Crafty ei missään olosuhteissa käytä tätä sähköpostia muuhun kuin Gravatar™-tietojesi etsimiseen", + "userLang": "Käyttäjän kieli", + "userRoles": "Käyttäjän roolit", + "userRolesDesc": "Roolit, joissa tämä käyttäjä on", + "roleName": "Roolin nimi", + "member": "Jäsen?", + "craftyPerms": "Craftyn oikeudet: ", + "permName":"Oikeuden nimi", + "auth": "Valtuutettu? ", + "uses": "Sallittujen käyttäkertojen määtä (-1 == Ei rajaa)", + "super": "Järjestelmänvalvoja", + "enabled": "Päällä", + "configArea": "Käyttäjän Asetukset", + "configAreaDesc": "Tässä voi muokata kaikkia käyttäjän asetuksia", + "created": "Luotu: ", + "lastLogin": "Viimeinen Sisäänkirjautuminen: ", + "lastUpdate": "Viimeinen Muokkaus: ", + "lastIP": "Viimeinen IP: ", + "deleteUserB": "Poista käyttäjä", + "notExist": "Et voi poistaa mitään, joka ei ole olemassa!", + "delSuper": "Et voi poistaa järjestelmänvalvojaa!", "deleteUser": "Poista käyttäjä: ", "confirmDelete": "Oletko varma, että haluat poistaa tämän käyttäjän? Tätä ei voi peruuttaa." }, + "rolesConfig": { + "pageTitle": "Muokkaa roolia", + "pageTitleNew": "Luo rooli", + "config": "Asetukset", + "roleTitle": "Roolin asetukset", + "roleName": "Roolin nimi: ", + "roleDesc": "Millä nimellä haluaisit kutsua tätä roolia?", + "roleServers": "Sallitut palvelimet", + "serversDesc": "Palvelimet, joihin tällä roolilla on pääsy", + "serverName": "Server Name", + "serverAccess": "Pääsy?", + "rolePerms": "Roolin käyttöoikeudet", + "permsServer": "Tämän roolin käyttöoikeudet näille määritetyille palvelimille", + "permName": "Oikeuden nimi", + "permAccess": "Pääsy?", + "roleUsers": "Roolin käyttäjät: ", + "roleUserName": "Käyttäjätunnus", + "roleConfigArea": "Roolin asetukset", + "configDesc": "Täällä voit muuttaa roolin asetuksia", + "created": "Luotu: ", + "configUpdate": "Viimeinen muokkaus: ", + "delRole": "Poista rooli", + "doesNotExist": "Et voi poistaa mitään, joka ei ole olemassa!" + }, "dashboard": { "dashboard": "Kojelauta", "memUsage": "Muistin käyttö", @@ -113,16 +170,17 @@ "cpuCurFreq": "Nykyinen kellotaajuus", "cpuMaxFreq": "Maksimi kellotaajuus", "cpuCores": "Suorittimen ytimet", - "start": "Alkaa", - "stop": "Lopettaa", - "clone": "Klooni", + "start": "Käynnistä", + "stop": "Pysäytä", + "clone": "Kloonaa", "kill": "Tapa prosessi", "restart": "Uudelleenkäynnistää", - "killing": "Tappamisprosessi ...", - "starting": "Myöhästynyt lähtö", - "delay-explained": "Palvelu/agentti on äskettäin aloittanut ja viivästyttää minecraft -palvelimen ilmentymän alkua", + "killing": "Tapetaan prosessia...", + "starting": "Myöhästynyt käynnistys", + "delay-explained": "Palvelu/agentti on äskettäin aloittanut ja viivästyttää palvelimen käynnistämistä", "no-servers": "Palvelimia ei tällä hetkellä ole. Aloita napsauttamalla", - "welcome": "Tervetuloa Crafty Controller" + "welcome": "Tervetuloa Crafty Controlleriin", + "crashed": "Kaatui" }, "accessDenied": { "accessDenied": "Käyttö estetty", @@ -133,7 +191,7 @@ "serverStats": { "online": "Päällä", "offline": "Pois päältä", - "starting": "Myöhästynyt lähtö", + "starting": "Myöhästynyt aloitus", "serverStatus": "Palvelimen tila", "serverStarted": "Palvelin käynnistyi", "serverUptime": "Palvelimen käyttöaika", @@ -165,7 +223,7 @@ "restart": "Uudelleen­käynnistä", "stop": "Sammuta", "updating": "Updating", - "starting": "Myöhästynyt lähtö", + "starting": "Myöhästynyt aloitus", "delay-explained": "Palvelu/agentti on äskettäin aloittanut ja viivästyttää minecraft -palvelimen ilmentymän alkua" }, "serverPlayerManagement": { @@ -277,7 +335,9 @@ "noDeleteFiles": "Ei, poista vain paneelista", "sendingDelete": "Poistetaan palvelinta", "bePatientDelete": "Ole kärsivällinen, kun poistamme palvelimesi Crafty-paneelista. Tämä näyttö sulkeutuu hetken kuluttua.", - "bePatientDeleteFiles" : "Ole kärsivällinen, kun poistamme palvelimesi Crafty-paneelista ja poistamme kaikki tiedostot. Tämä näyttö sulkeutuu hetken kuluttua." + "bePatientDeleteFiles" : "Ole kärsivällinen, kun poistamme palvelimesi Crafty-paneelista ja poistamme kaikki tiedostot. Tämä näyttö sulkeutuu hetken kuluttua.", + "crashTime": "Kaatumisen aikakatkaisu", + "crashTimeDesc": "Kuinka kauan meidän pitäisi odottaa, ennen kuin harkitsemme palvelimesi kaatuneen?" }, "serverConfigHelp": { "title": "Palvelimen asetukset", @@ -297,6 +357,18 @@ ] }, "panelConfig": { + "pageTitle": "Paneelin asetukset", + "users": "Käyttäjät", + "roles": "Roolit", + "newUser": "Luo uusi käyttäjä", + "newRole": "Luo uusi rooli", + "user": "Käyttäjä", + "enabled": "Käytössä", + "allowedServers": "Sallitut palvelimet", + "assignedRoles": "Määrätyt roolit", + "edit": "Muokkaa", + "role": "Rooli", + "roleUsers": "Roolin käyttäjät", "save": "Tallenna", "cancel": "Peruuta", "delete": "Poista", @@ -395,6 +467,23 @@ "doesNotWorkWithoutJavascript": "Varoitus: Crafty ei toimi kunnolla ilman JavaScriptiä!" }, "apiKeys": { + "pageTitle": "Muokkaa käyttäjän API-avaimia", + "config": "Asetukset", + "apiKeys": "API-avaimet", + "name": "Nimi", + "created": "Luotu", + "perms": "Käyttöoikeudet", + "buttons": "Painikkeet", + "yes": "Kyllä", + "no": "Ei", + "server": "Palvelin: ", + "crafty": "Crafty: ", + "getToken": "Hanki API-avain", + "createNew": "Luo uusi API-avain", + "nameDesc": "Millä nimellä haluat kutsua tätä API-avainta? ", + "permName": "Oikeuden nimi", + "auth":"Valtuutettu? ", + "superUser": "Järjestelmänvalvoja", "deleteKeyConfirmation": "Haluatko varmasti poistaa tämän API-avaimen? Tämä on peruuttamaton toimenpide!", "deleteKeyConfirmationTitle": "Poistetaanko API-avain ${keyId}?" } From 154b99792fbb4a019dd87beb03cc60dc1abdbb2d Mon Sep 17 00:00:00 2001 From: luukas Date: Sun, 27 Feb 2022 19:45:32 +0200 Subject: [PATCH 2/3] Fix code quality :) --- app/classes/web/tornado_handler.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/classes/web/tornado_handler.py b/app/classes/web/tornado_handler.py index 2bd1240f..42313ead 100644 --- a/app/classes/web/tornado_handler.py +++ b/app/classes/web/tornado_handler.py @@ -4,8 +4,6 @@ import json import asyncio import logging -from app.classes.web.file_handler import FileHandler - logger = logging.getLogger(__name__) try: @@ -16,6 +14,7 @@ try: import tornado.escape import tornado.locale import tornado.httpserver + from app.classes.web.file_handler import FileHandler from app.classes.web.public_handler import PublicHandler from app.classes.web.panel_handler import PanelHandler from app.classes.web.default_handler import DefaultHandler From 775c4d5c9fec4562b426369843038319953a5f22 Mon Sep 17 00:00:00 2001 From: luukas Date: Sun, 27 Feb 2022 20:14:14 +0200 Subject: [PATCH 3/3] Fix this one little stupid bug --- app/frontend/templates/panel/server_files.html | 1 - 1 file changed, 1 deletion(-) diff --git a/app/frontend/templates/panel/server_files.html b/app/frontend/templates/panel/server_files.html index 370d0ed2..bc439193 100644 --- a/app/frontend/templates/panel/server_files.html +++ b/app/frontend/templates/panel/server_files.html @@ -569,7 +569,6 @@ path = event.target.parentElement.getAttribute('data-path'); console.log("PATH: " + path); $(function () { - serverId = parseInt(serverId); var uploadHtml = "
" + '
'+"