diff --git a/app/classes/controllers/server_perms_controller.py b/app/classes/controllers/server_perms_controller.py index 1a15f3c3..314f9999 100644 --- a/app/classes/controllers/server_perms_controller.py +++ b/app/classes/controllers/server_perms_controller.py @@ -51,6 +51,17 @@ class Server_Perms_Controller: def add_role_server(server_id, role_id, rs_permissions="00000000"): return server_permissions.add_role_server(server_id, role_id, rs_permissions) + @staticmethod + def get_server_roles(server_id): + return server_permissions.get_server_roles(server_id) + + @staticmethod + def backup_role_swap(old_server_id, new_server_id): + role_list = server_permissions.get_server_roles(old_server_id) + for role in role_list: + server_permissions.add_role_server(new_server_id, role.role_id, server_permissions.get_permissions_mask(int(role.role_id), int(old_server_id))) + #server_permissions.add_role_server(new_server_id, role.role_id, '00001000') + #************************************************************************************************ # Servers Permissions Methods #************************************************************************************************ diff --git a/app/classes/models/management.py b/app/classes/models/management.py index 1146068b..d3d938d2 100644 --- a/app/classes/models/management.py +++ b/app/classes/models/management.py @@ -227,6 +227,10 @@ class helpers_management: def update_scheduled_task(schedule_id, updates): Schedules.update(updates).where(Schedules.schedule_id == schedule_id).execute() + @staticmethod + def delete_scheduled_task_by_server(server_id): + Schedules.delete().where(Schedules.server_id == int(server_id)).execute() + @staticmethod def get_scheduled_task(schedule_id): return model_to_dict(Schedules.get(Schedules.schedule_id == schedule_id)).execute() diff --git a/app/classes/models/server_permissions.py b/app/classes/models/server_permissions.py index 43f50b96..ccf81a38 100644 --- a/app/classes/models/server_permissions.py +++ b/app/classes/models/server_permissions.py @@ -118,10 +118,18 @@ class Permissions_Servers: @staticmethod def get_permissions_mask(role_id, server_id): permissions_mask = '' - role_server = Role_Servers.select().where(Role_Servers.role_id == role_id).where(Role_Servers.server_id == server_id).execute() + role_server = Role_Servers.select().where(Role_Servers.role_id == role_id).where(Role_Servers.server_id == server_id).get() permissions_mask = role_server.permissions return permissions_mask + @staticmethod + def get_server_roles(server_id): + role_list = [] + roles = Role_Servers.select().where(Role_Servers.server_id == server_id).execute() + for role in roles: + role_list.append(role.role_id) + return role_list + @staticmethod def get_role_permissions_list(role_id): permissions_mask = '00000000' diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index f9a1293e..cf3330b3 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -338,6 +338,12 @@ class Helpers: logger.critical("Unable to write to {} - Error: {}".format(path, e)) return False + def checkRoot(self): + if os.geteuid() == 0: + return True + else: + return False + def unzipFile(self, zip_path): new_dir_list = zip_path.split('/') new_dir = '' diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index 83165672..be9a526f 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -1,13 +1,17 @@ import os +import pathlib import time import logging import sys +from peewee import DoesNotExist +import schedule import yaml import asyncio import shutil import tempfile import zipfile from distutils import dir_util +from app.classes.models.management import helpers_management from app.classes.shared.helpers import helper from app.classes.shared.console import console @@ -285,34 +289,55 @@ class Controller: helper.ensure_dir_exists(new_server_dir) helper.ensure_dir_exists(backup_path) tempDir = tempfile.mkdtemp() + has_properties = False with zipfile.ZipFile(zip_path, 'r') as zip_ref: + #extracts archive to temp directory zip_ref.extractall(tempDir) - for i in range(len(zip_ref.filelist)): - if len(zip_ref.filelist) > 1 or not zip_ref.filelist[i].filename.endswith('/'): - test = zip_ref.filelist[i].filename - break - path_list = test.split('/') - root_path = path_list[0] - if len(path_list) > 1: - for i in range(len(path_list)-2): - root_path = os.path.join(root_path, path_list[i+1]) + if len(zip_ref.filelist) > 1: + for item in os.listdir(tempDir): + if str(item) == 'server.properties': + has_properties = True + try: + shutil.move(os.path.join(tempDir, item), os.path.join(new_server_dir, item)) + except Exception as ex: + logger.error('ERROR IN ZIP IMPORT: {}'.format(ex)) + if not has_properties: + logger.info("No server.properties found on zip file import. Creating one with port selection of {}".format(str(port))) + with open(os.path.join(new_server_dir, "server.properties"), "w") as f: + f.write("server-port={}".format(port)) + f.close() + zip_ref.close() + else: - full_root_path = os.path.join(tempDir, root_path) + #iterates list of files + for i in range(len(zip_ref.filelist)): + #checks if the list of files inside of a dir is greater than 1 or if it's not a directory. + if len(zip_ref.filelist) > 1 or not zip_ref.filelist[i].is_dir(): + #sets local variable to be that filename and we break out of the loop since we found our root dir. + test = zip_ref.filelist[i-1].filename + break + path_list = test.split('/') + root_path = path_list[0] + if len(path_list) > 1: + for i in range(len(path_list)-2): + root_path = os.path.join(root_path, path_list[i+1]) - has_properties = False - for item in os.listdir(full_root_path): - if str(item) == 'server.properties': - has_properties = True - try: - shutil.move(os.path.join(full_root_path, item), os.path.join(new_server_dir, item)) - except Exception as ex: - logger.error('ERROR IN ZIP IMPORT: {}'.format(ex)) - if not has_properties: - logger.info("No server.properties found on zip file import. Creating one with port selection of {}".format(str(port))) - with open(os.path.join(new_server_dir, "server.properties"), "w") as f: - f.write("server-port={}".format(port)) - f.close() - zip_ref.close() + full_root_path = os.path.join(tempDir, root_path) + + + for item in os.listdir(full_root_path): + if str(item) == 'server.properties': + has_properties = True + try: + shutil.move(os.path.join(full_root_path, item), os.path.join(new_server_dir, item)) + except Exception as ex: + logger.error('ERROR IN ZIP IMPORT: {}'.format(ex)) + if not has_properties: + logger.info("No server.properties found on zip file import. Creating one with port selection of {}".format(str(port))) + with open(os.path.join(new_server_dir, "server.properties"), "w") as f: + f.write("server-port={}".format(port)) + f.close() + zip_ref.close() else: return "false" @@ -328,20 +353,30 @@ class Controller: server_log_file, server_stop, port) return new_id + def rename_backup_dir(self, old_server_id, new_server_id, new_uuid): + server_data = self.servers.get_server_data_by_id(old_server_id) + old_bu_path = server_data['backup_path'] + Server_Perms_Controller.backup_role_swap(old_server_id, new_server_id) + backup_path = helper.validate_traversal(helper.backup_path, old_bu_path) + backup_path_components = list(backup_path.parts) + backup_path_components[-1] = new_uuid + new_bu_path = pathlib.PurePath(os.path.join(*backup_path_components)) + backup_path.rename(new_bu_path) + def register_server(self, name: str, server_uuid: str, server_dir: str, backup_path: str, server_command: str, server_file: str, server_log_file: str, server_stop: str, server_port: int): # put data in the db new_id = self.servers.create_server(name, server_uuid, server_dir, backup_path, server_command, server_file, server_log_file, server_stop, server_port) + if not helper.check_file_exists(os.path.join(server_dir, "crafty_managed.txt")): + try: + # place a file in the dir saying it's owned by crafty + with open(os.path.join(server_dir, "crafty_managed.txt"), 'w') as f: + f.write( + "The server is managed by Crafty Controller.\n Leave this directory/files alone please") + f.close() - try: - # place a file in the dir saying it's owned by crafty - with open(os.path.join(server_dir, "crafty_managed.txt"), 'w') as f: - f.write( - "The server is managed by Crafty Controller.\n Leave this directory/files alone please") - f.close() - - except Exception as e: - logger.error("Unable to create required server files due to :{}".format(e)) - return False + except Exception as e: + logger.error("Unable to create required server files due to :{}".format(e)) + return False # let's re-init all servers self.init_all_servers() @@ -356,6 +391,7 @@ class Controller: if int(s['server_id']) == int(server_id): server_data = self.get_server_data(server_id) server_name = server_data['server_name'] + backup_dir = self.servers.get_server_data_by_id(server_id)['backup_path'] logger.info("Deleting Server: ID {} | Name: {} ".format(server_id, server_name)) console.info("Deleting Server: ID {} | Name: {} ".format(server_id, server_name)) @@ -366,7 +402,19 @@ class Controller: if running: self.stop_server(server_id) if files: - shutil.rmtree(helper.get_os_understandable_path(self.servers.get_server_data_by_id(server_id)['path'])) + try: + shutil.rmtree(helper.get_os_understandable_path(self.servers.get_server_data_by_id(server_id)['path'])) + except Exception as e: + logger.error("Unable to delete server files for server with ID: {} with error logged: {}".format(server_id, e)) + if helper.check_path_exists(self.servers.get_server_data_by_id(server_id)['backup_path']): + shutil.rmtree(helper.get_os_understandable_path(self.servers.get_server_data_by_id(server_id)['backup_path'])) + + + #Cleanup scheduled tasks + try: + helpers_management.delete_scheduled_task_by_server(server_id) + except DoesNotExist: + logger.info("No scheduled jobs exist. Continuing.") # remove the server from the DB self.servers.remove_server(server_id) @@ -374,3 +422,4 @@ class Controller: self.servers_list.pop(counter) counter += 1 + return diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index 5193dd59..623f22d1 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -243,7 +243,16 @@ class Server: try: self.process = subprocess.Popen(self.server_command, cwd=self.server_path, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) except Exception as ex: - msg = "Server {} failed to start with error code: {}".format(self.name, ex) + #Checks for java on initial fail + if os.system("java -version") == 32512: + msg = "Server {} failed to start with error code: {}".format(self.name, "Java not found. Please install Java then try again.") + if user_id: + websocket_helper.broadcast_user(user_id, 'send_start_error',{ + 'error': translation.translate('error', 'noJava', user_lang).format(self.name) + }) + return False + else: + msg = "Server {} failed to start with error code: {}".format(self.name, ex) logger.error(msg) if user_id: websocket_helper.broadcast_user(user_id, 'send_start_error',{ @@ -512,14 +521,14 @@ class Server: return def list_backups(self): - conf = management_helper.get_backup_config(self.server_id) if self.settings['backup_path']: if helper.check_path_exists(helper.get_os_understandable_path(self.settings['backup_path'])): files = helper.get_human_readable_files_sizes(helper.list_dir_by_date(helper.get_os_understandable_path(self.settings['backup_path']))) - return [{"path": os.path.relpath(f['path'], start=helper.get_os_understandable_path(conf['backup_path'])), "size": f["size"]} for f in files] + return [{"path": os.path.relpath(f['path'], start=helper.get_os_understandable_path(self.settings['backup_path'])), "size": f["size"]} for f in files] else: return [] else: + logger.info("Error putting backup file list for server with ID: {}".format(self.server_id)) return[] def jar_update(self): diff --git a/app/classes/web/ajax_handler.py b/app/classes/web/ajax_handler.py index 0e4c5faa..a9247ace 100644 --- a/app/classes/web/ajax_handler.py +++ b/app/classes/web/ajax_handler.py @@ -212,6 +212,21 @@ class AjaxHandler(BaseHandler): svr = self.controller.get_server_obj(server_id) svr.agree_eula(user_data['user_id']) + elif page == "restore_backup": + server_id = bleach.clean(self.get_argument('id', None)) + zip_name = bleach.clean(self.get_argument('zip_file', None)) + svr_obj = self.controller.servers.get_server_obj(server_id) + server_data = self.controller.servers.get_server_data_by_id(server_id) + backup_path = svr_obj.backup_path + if helper.validate_traversal(backup_path, zip_name): + new_server = self.controller.import_zip_server(svr_obj.server_name, os.path.join(backup_path, zip_name), server_data['executable'], '1', '2', server_data['server_port']) + new_server_id = new_server + new_server = self.controller.get_server_data(new_server) + self.controller.rename_backup_dir(server_id, new_server_id, new_server['server_uuid']) + self.controller.remove_server(server_id, True) + self.redirect('/panel/dashboard') + + @tornado.web.authenticated def delete(self, page): if page == "del_file": diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index 210794e0..f1056efb 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -121,14 +121,23 @@ class PanelHandler(BaseHandler): elif page == 'dashboard': if exec_user['superuser'] == 1: - page_data['servers'] = self.controller.servers.get_all_servers_stats() + try: + page_data['servers'] = self.controller.servers.get_all_servers_stats() + except IndexError: + self.controller.stats.record_stats() + page_data['servers'] = self.controller.servers.get_all_servers_stats() + for data in page_data['servers']: try: data['stats']['waiting_start'] = self.controller.servers.get_waiting_start(int(data['stats']['server_id']['server_id'])) except: data['stats']['waiting_start'] = False else: - user_auth = self.controller.servers.get_authorized_servers_stats(exec_user_id) + try: + user_auth = self.controller.servers.get_authorized_servers_stats(exec_user_id) + except IndexError: + self.controller.stats.record_stats() + user_auth = self.controller.servers.get_authorized_servers_stats(exec_user_id) logger.debug("ASFR: {}".format(user_auth)) page_data['servers'] = user_auth page_data['server_stats']['running'] = 0 @@ -208,9 +217,12 @@ class PanelHandler(BaseHandler): if subpage == "backup": server_info = self.controller.servers.get_server_data_by_id(server_id) page_data['backup_config'] = self.controller.management.get_backup_config(server_id) - page_data['backup_list'] = server.list_backups() + self.controller.refresh_server_settings(server_id) + try: + page_data['backup_list'] = server.list_backups() + except: + page_data['backup_list'] = [] page_data['backup_path'] = helper.wtol_path(server_info["backup_path"]) - print(page_data['backup_path']) def get_banned_players_html(): banned_players = self.controller.servers.get_banned_players(server_id) @@ -416,8 +428,6 @@ class PanelHandler(BaseHandler): return elif Enum_Permissions_Crafty.User_Config not in exec_user_crafty_permissions: if str(user_id) != str(exec_user_id): - print("USER ID ", user_id) - print("EXEC ID ", exec_user_id) self.redirect("/panel/error?error=Unauthorized access: not a user editor") return diff --git a/app/frontend/templates/panel/server_backup.html b/app/frontend/templates/panel/server_backup.html index 8650aee9..11fb1acf 100644 --- a/app/frontend/templates/panel/server_backup.html +++ b/app/frontend/templates/panel/server_backup.html @@ -45,8 +45,10 @@ {{ translate('serverBackups', 'backupNow', data['lang']) }}