diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..5008ddfc Binary files /dev/null and b/.DS_Store differ diff --git a/app/classes/shared/controller.py b/app/classes/shared/controller.py index cd568504..ce021d37 100644 --- a/app/classes/shared/controller.py +++ b/app/classes/shared/controller.py @@ -4,6 +4,8 @@ import logging import sys import yaml import asyncio +import shutil +import tempfile import zipfile from distutils import dir_util @@ -110,10 +112,15 @@ class Controller: @staticmethod def list_authorized_servers(userId): - #servers = db_helper.get_authorized_servers(userId) - servers = db_helper.get_authorized_servers_from_roles(userId) + servers = db_helper.get_authorized_servers(userId) + server_list = [] + for item in servers: + server_list.append(item) + role_servers = db_helper.get_authorized_servers_from_roles(userId) + for item in role_servers: + server_list.append(item) logger.debug("servers list = {}".format(servers)) - return servers + return server_list def get_server_data(self, server_id): for s in self.servers_list: @@ -276,8 +283,32 @@ class Controller: if helper.check_file_perms(zip_path): helper.ensure_dir_exists(new_server_dir) helper.ensure_dir_exists(backup_path) + tempDir = tempfile.mkdtemp() with zipfile.ZipFile(zip_path, 'r') as zip_ref: - zip_ref.extractall(new_server_dir) + zip_ref.extractall(tempDir) + test = zip_ref.filelist[1].filename + 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]) + + full_root_path = os.path.join(tempDir, root_path) + + 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() else: return "false" @@ -313,7 +344,7 @@ class Controller: return new_id - def remove_server(self, server_id): + def remove_server(self, server_id, files): counter = 0 for s in self.servers_list: @@ -330,7 +361,8 @@ class Controller: if running: self.stop_server(server_id) - + if files: + shutil.rmtree(db_helper.get_server_data_by_id(server_id)['path']) # remove the server from the DB db_helper.remove_server(server_id) diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index 0091a647..5d124d32 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -12,6 +12,7 @@ import logging import html import zipfile import pathlib +import shutil from datetime import datetime from socket import gethostname @@ -553,4 +554,39 @@ class Helpers: return json.loads(content) -helper = Helpers() \ No newline at end of file + @staticmethod + def zip_directory(file, path, compression=zipfile.ZIP_LZMA): + with zipfile.ZipFile(file, 'w', compression) as zf: + for root, dirs, files in os.walk(path): + for file in files: + zf.write(os.path.join(root, file), + os.path.relpath(os.path.join(root, file), + os.path.join(path, '..'))) + @staticmethod + def copy_files(source, dest): + if os.path.isfile(source): + shutil.copyfile(source, dest) + logger.info("Copying jar %s to %s", source, dest) + else: + logger.info("Source jar does not exist.") + + @staticmethod + def download_file(executable_url, jar_path): + try: + r = requests.get(executable_url, timeout=5) + except Exception as ex: + logger.error("Could not download executable: %s", ex) + return False + if r.status_code != 200: + logger.error("Unable to download file from %s", executable_url) + return False + + try: + open(jar_path, "wb").write(r.content) + except Exception as e: + logger.error("Unable to finish executable download. Error: %s", e) + return False + return True + + +helper = Helpers() diff --git a/app/classes/shared/models.py b/app/classes/shared/models.py index b53c2903..5d9924e0 100644 --- a/app/classes/shared/models.py +++ b/app/classes/shared/models.py @@ -122,6 +122,7 @@ class Servers(BaseModel): auto_start_delay = IntegerField(default=10) crash_detection = BooleanField(default=0) stop_command = CharField(default="stop") + executable_update_url = CharField(default="") server_ip = CharField(default="127.0.0.1") server_port = IntegerField(default=25565) logs_delete_after = IntegerField(default=0) @@ -129,6 +130,16 @@ class Servers(BaseModel): class Meta: table_name = "servers" + +class User_Servers(BaseModel): + user_id = ForeignKeyField(Users, backref='user_server') + server_id = ForeignKeyField(Servers, backref='user_server') + + class Meta: + table_name = 'user_servers' + primary_key = CompositeKey('user_id', 'server_id') + + class Role_Servers(BaseModel): role_id = ForeignKeyField(Roles, backref='role_server') server_id = ForeignKeyField(Servers, backref='role_server') @@ -156,6 +167,7 @@ class Server_Stats(BaseModel): players = CharField(default="") desc = CharField(default="Unable to Connect") version = CharField(default="") + updating = BooleanField(default=False) class Meta: @@ -220,6 +232,7 @@ class db_builder: Users, Roles, User_Roles, + User_Servers, Host_Stats, Webhooks, Servers, @@ -334,6 +347,7 @@ class db_shortcuts: def remove_server(server_id): with database.atomic(): Role_Servers.delete().where(Role_Servers.server_id == server_id).execute() + User_Servers.delete().where(User_Servers.server_id == server_id).execute() Servers.delete().where(Servers.server_id == server_id).execute() @staticmethod @@ -358,7 +372,37 @@ class db_shortcuts: server_data.append(db_helper.get_server_data_by_id(u.server_id)) return server_data - + + @staticmethod + def get_all_authorized_servers(user_id): + user_servers = User_Servers.select().where(User_Servers.user_id == user_id) + user_roles = User_Roles.select().where(User_Roles.user_id == user_id) + server_data = [] + roles_list = [] + role_server = [] + server_data = [] + + for u in user_servers: + server_data.append(db_helper.get_server_data_by_id(u.server_id)) + + for u in user_roles: + roles_list.append(db_helper.get_role(u.role_id)) + + for r in roles_list: + role_test = Role_Servers.select().where(Role_Servers.role_id == r.get('role_id')) + for t in role_test: + role_server.append(t) + + for s in role_server: + found = False + for item in user_servers: + if s.server_id == item.server_id: + found = True + if not found: + server_data.append(db_helper.get_server_data_by_id(s.server_id)) + + return server_data + @staticmethod def get_authorized_servers_from_roles(user_id): user_roles = User_Roles.select().where(User_Roles.user_id == user_id) @@ -394,16 +438,44 @@ class db_shortcuts: user_servers = User_Servers.select().where(User_Servers.user_id == user_id) authorized_servers = [] server_data = [] + user_roles = User_Roles.select().where(User_Roles.user_id == user_id) + roles_list = [] + role_server = [] for u in user_servers: authorized_servers.append(db_helper.get_server_data_by_id(u.server_id)) + for u in user_roles: + roles_list.append(db_helper.get_role(u.role_id)) + + for r in roles_list: + role_test = Role_Servers.select().where(Role_Servers.role_id == r.get('role_id')) + for t in role_test: + role_server.append(t) + + for s in role_server: + found = False + for item in user_servers: + if s.server_id == item.server_id: + found = True + if not found: + authorized_servers.append(db_helper.get_server_data_by_id(s.server_id)) + for s in authorized_servers: - latest = Server_Stats.select().where(Server_Stats.server_id == s.get('server_id')).order_by(Server_Stats.created.desc()).limit(1) - server_data.append({'server_data': s, "stats": db_helper.return_rows(latest)}) + latest = Server_Stats.select().where(Server_Stats.server_id == s.get('server_id')).order_by( + Server_Stats.created.desc()).limit(1) + server_data.append({'server_data': s, "stats": db_helper.return_rows(latest)[0]}) return server_data - + @staticmethod + def get_user_roles_names(user_id): + roles_list = [] + roles = User_Roles.select().where(User_Roles.user_id == user_id) + for r in roles: + roles_list.append(db_helper.get_role(r.role_id)['role_name']) + return roles_list + + @staticmethod def get_authorized_servers_stats_from_roles(user_id): user_roles = User_Roles.select().where(User_Roles.user_id == user_id) @@ -529,16 +601,34 @@ class db_shortcuts: roles = set() for r in roles_query: roles.add(r.role_id.role_id) - #servers_query = User_Servers.select().join(Servers, JOIN.INNER).where(User_Servers.user_id == user_id) + servers_query = User_Servers.select().join(Servers, JOIN.INNER).where(User_Servers.user_id == user_id) ## TODO: this query needs to be narrower servers = set() - #for s in servers_query: - # servers.add(s.server_id.server_id) + for s in servers_query: + servers.add(s.server_id.server_id) user['roles'] = roles - #user['servers'] = servers + user['servers'] = servers #logger.debug("user: ({}) {}".format(user_id, user)) return user + @staticmethod + def add_user_server(server_id, user_id): + servers = User_Servers.insert({User_Servers.server_id: server_id, User_Servers.user_id: user_id}).execute() + return servers + + + @staticmethod + def user_query(user_id): + user_query = Users.select().where(Users.user_id == user_id) + return user_query + + @staticmethod + def user_role_query(user_id): + user_query = User_Roles.select().where(User_Roles.user_id == user_id) + query = Roles.select().where(Roles.role_id == -1) + for u in user_query: + query = Roles.select().where(Roles.role_id == u.role_id) + return query @staticmethod def get_user(user_id): @@ -555,7 +645,7 @@ class db_shortcuts: superuser: False, api_token: None, roles: [], - servers: [] + servers: [], } user = model_to_dict(Users.get(Users.user_id == user_id)) @@ -581,9 +671,9 @@ class db_shortcuts: elif key == "roles": added_roles = user_data['roles'].difference(base_data['roles']) removed_roles = base_data['roles'].difference(user_data['roles']) - #elif key == "servers": - # added_servers = user_data['servers'].difference(base_data['servers']) - # removed_servers = base_data['servers'].difference(user_data['servers']) + elif key == "servers": + added_servers = user_data['servers'].difference(base_data['servers']) + removed_servers = base_data['servers'].difference(user_data['servers']) elif key == "regen_api": if user_data['regen_api']: up_data['api_token'] = db_shortcuts.new_api_token() @@ -600,10 +690,10 @@ class db_shortcuts: # TODO: This is horribly inefficient and we should be using bulk queries but im going for functionality at this point User_Roles.delete().where(User_Roles.user_id == user_id).where(User_Roles.role_id.in_(removed_roles)).execute() - #for server in added_servers: - # User_Servers.get_or_create(user_id=user_id, server_id=server) + for server in added_servers: + User_Servers.get_or_create(user_id=user_id, server_id=server) # # TODO: This is horribly inefficient and we should be using bulk queries but im going for functionality at this point - #User_Servers.delete().where(User_Servers.user_id == user_id).where(User_Servers.server_id.in_(removed_servers)).execute() + User_Servers.delete().where(User_Servers.user_id == user_id).where(User_Servers.server_id.in_(removed_servers)).execute() if up_data: Users.update(up_data).where(Users.user_id == user_id).execute() @@ -842,6 +932,15 @@ class db_shortcuts: } return conf + @staticmethod + def set_update(server_id, value): + try: + row = Server_Stats.select().where(Server_Stats.server_id == server_id) + except Exception as ex: + logger.error("Database entry not found. ".format(ex)) + with database.atomic(): + Server_Stats.update(updating=value).where(Server_Stats.server_id == server_id).execute() + @staticmethod def set_backup_config(server_id: int, backup_path: str = None, max_backups: int = None, auto_enabled: bool = True): logger.debug("Updating server {} backup config with {}".format(server_id, locals())) diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index e08e9843..2266b256 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -10,13 +10,13 @@ import threading import schedule import logging.config import zipfile -import shutil -import zlib +from threading import Thread from app.classes.shared.helpers import helper from app.classes.shared.console import console from app.classes.shared.models import db_helper, Servers +from app.classes.web.websocket_helper import websocket_helper logger = logging.getLogger(__name__) @@ -44,11 +44,14 @@ class Server: self.settings = None self.updating = False self.server_id = None + self.jar_update_url = None self.name = None self.is_crashed = False self.restart_count = 0 self.crash_watcher_schedule = None self.stats = stats + self.backup_thread = threading.Thread(target=self.a_backup_server, daemon=True, name="backup") + self.is_backingup = False def reload_server_settings(self): server_data = db_helper.get_server_data_by_id(self.server_id) @@ -113,11 +116,16 @@ class Server: def start_server(self): + logger.info("Start command detected. Reloading settings from DB for server {}".format(self.name)) + self.setup_server_run_command() # fail safe in case we try to start something already running if self.check_running(): logger.error("Server is already running - Cancelling Startup") console.error("Server is already running - Cancelling Startup") return False + if self.check_update(): + logger.error("Server is updating. Terminating startup.") + return False logger.info("Launching Server {} with command {}".format(self.name, self.server_command)) console.info("Launching Server {} with command {}".format(self.name, self.server_command)) @@ -129,7 +137,17 @@ class Server: logger.info("Linux Detected") logger.info("Starting server in {p} with command: {c}".format(p=self.server_path, c=self.server_command)) - self.process = pexpect.spawn(self.server_command, cwd=self.server_path, timeout=None, encoding=None) + try: + self.process = pexpect.spawn(self.server_command, cwd=self.server_path, timeout=None, encoding=None) + except Exception as ex: + msg = "Server {} failed to start with error code: {}".format(self.name, ex) + logger.error(msg) + websocket_helper.broadcast('send_start_error', { + 'error': msg + }) + return False + websocket_helper.broadcast('send_start_reload', { + }) self.is_crashed = False self.start_time = str(datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')) @@ -320,21 +338,50 @@ class Server: console.info("Removing old crash detection watcher thread") schedule.clear(self.name) + def is_backup_running(self): + if self.is_backingup: + return True + else: + return False + def backup_server(self): + backup_thread = threading.Thread(target=self.a_backup_server, daemon=True, name="backup") + logger.info("Starting Backup Thread for server {}.".format(self.settings['server_name'])) + #checks if the backup thread is currently alive for this server + if not self.is_backingup: + try: + backup_thread.start() + except Exception as ex: + logger.error("Failed to start backup: {}".format(ex)) + return False + else: + logger.error("Backup is already being processed for server {}. Canceling backup request".format(self.settings['server_name'])) + return False + logger.info("Backup Thread started for server {}.".format(self.settings['server_name'])) + + def a_backup_server(self): logger.info("Starting server {} (ID {}) backup".format(self.name, self.server_id)) + self.is_backingup = True conf = db_helper.get_backup_config(self.server_id) try: - backup_filename = "{}/{}".format(conf['backup_path'], datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')) + backup_filename = "{}/{}.zip".format(self.settings['backup_path'], datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')) logger.info("Creating backup of server '{}' (ID#{}) at '{}'".format(self.settings['server_name'], self.server_id, backup_filename)) - shutil.make_archive(backup_filename, 'zip', self.server_path) + helper.zip_directory(backup_filename, self.server_path) while len(self.list_backups()) > conf["max_backups"]: backup_list = self.list_backups() oldfile = backup_list[0] - oldfile_path = "{}/{}".format(conf['backup_path'], oldfile['path']) - logger.info("Removing old backup '{}'".format(oldfile['path'])) - os.remove(oldfile_path) + backup_path = self.settings['backup_path'] + old_file_name = oldfile['path'] + back_old_file = os.path.join(backup_path, old_file_name) + logger.info("Removing old backup '{}'".format(oldfile)) + os.remove(back_old_file) + self.is_backingup = False + logger.info("Backup of server: {} completed".format(self.name)) + return except: logger.exception("Failed to create backup of server {} (ID {})".format(self.name, self.server_id)) + self.is_backingup = False + return def list_backups(self): conf = db_helper.get_backup_config(self.server_id) @@ -343,3 +390,89 @@ class Server: return [{"path": os.path.relpath(f['path'], start=conf['backup_path']), "size": f["size"]} for f in files] else: return [] + + def jar_update(self): + db_helper.set_update(self.server_id, True) + update_thread = threading.Thread(target=self.a_jar_update, daemon=True, name="exe_update") + update_thread.start() + + def check_update(self): + server_stats = db_helper.get_server_stats_by_id(self.server_id) + if server_stats['updating']: + return True + else: + return False + + def a_jar_update(self): + error = False + wasStarted = "-1" + self.backup_server() + #checks if server is running. Calls shutdown if it is running. + if self.check_running(): + wasStarted = True + logger.info("Server with PID {} is running. Sending shutdown command".format(self.PID)) + self.stop_threaded_server() + else: + wasStarted = False + if len(websocket_helper.clients) > 0: + # There are clients + self.check_update() + message = ' UPDATING...' + websocket_helper.broadcast('update_button_status', { + 'isUpdating': self.check_update(), + 'server_id': self.server_id, + 'wasRunning': wasStarted, + 'string': message + }) + backup_dir = os.path.join(self.settings['path'], 'crafty_executable_backups') + #checks if backup directory already exists + if os.path.isdir(backup_dir): + backup_executable = os.path.join(backup_dir, 'old_server.jar') + else: + logger.info("Executable backup directory not found for Server: {}. Creating one.".format(self.name)) + os.mkdir(backup_dir) + backup_executable = os.path.join(backup_dir, 'old_server.jar') + + if os.path.isfile(backup_executable): + #removes old backup + logger.info("Old backup found for server: {}. Removing...".format(self.name)) + os.remove(backup_executable) + logger.info("Old backup removed for server: {}.".format(self.name)) + else: + logger.info("No old backups found for server: {}".format(self.name)) + + current_executable = os.path.join(self.settings['path'], self.settings['executable']) + + #copies to backup dir + helper.copy_files(current_executable, backup_executable) + + #boolean returns true for false for success + downloaded = helper.download_file(self.settings['executable_update_url'], current_executable) + + while db_helper.get_server_stats_by_id(self.server_id)['updating']: + if downloaded and not self.is_backingup: + logger.info("Executable updated successfully. Starting Server") + + db_helper.set_update(self.server_id, False) + if len(websocket_helper.clients) > 0: + # There are clients + self.check_update() + websocket_helper.broadcast('notification', "Executable update finished for " + self.name) + time.sleep(3) + websocket_helper.broadcast('update_button_status', { + 'isUpdating': self.check_update(), + 'server_id': self.server_id, + 'wasRunning': wasStarted + }) + websocket_helper.broadcast('notification', "Executable update finished for "+self.name) + + db_helper.add_to_audit_log_raw('Alert', '-1', self.server_id, "Executable update finished for "+self.name, self.settings['server_ip']) + if wasStarted: + self.start_server() + elif not downloaded and not self.is_backingup: + time.sleep(5) + db_helper.set_update(self.server_id, False) + websocket_helper.broadcast('notification', + "Executable update failed for " + self.name + ". Check log file for details.") + logger.error("Executable download failed.") + pass diff --git a/app/classes/shared/tasks.py b/app/classes/shared/tasks.py index 660af321..20b0ffa4 100644 --- a/app/classes/shared/tasks.py +++ b/app/classes/shared/tasks.py @@ -5,6 +5,7 @@ import time import logging import threading import asyncio +import shutil from app.classes.shared.helpers import helper from app.classes.shared.console import console @@ -110,6 +111,18 @@ class TasksManager: elif command == "backup_server": svr.backup_server() + elif command == "update_executable": + svr.jar_update() + elif command == "delete_server": + logger.info( + "Removing server from panel for server: {}".format(c['server_id']['server_name'])) + self.controller.remove_server(c['server_id']['server_id'], False) + + elif command == "delete_server_files": + logger.info( + "Removing server and all associated files for server: {}".format(c['server_id']['server_name'])) + self.controller.remove_server(c['server_id']['server_id'], True) + db_helper.mark_command_complete(c.get('command_id', None)) time.sleep(1) diff --git a/app/classes/web/ajax_handler.py b/app/classes/web/ajax_handler.py index 0796527e..700994b6 100644 --- a/app/classes/web/ajax_handler.py +++ b/app/classes/web/ajax_handler.py @@ -210,7 +210,7 @@ class AjaxHandler(BaseHandler): server_id = self.get_argument('id', None) print(server_id) - console.warning("delete {} for server {}".format(file_path, server_id)) + console.warning("delete {} for server {}".format(dir_path, server_id)) if not self.check_server_id(server_id, 'del_dir'): return False else: server_id = bleach.clean(server_id) diff --git a/app/classes/web/panel_handler.py b/app/classes/web/panel_handler.py index 98a0b4a1..ebb2dfd9 100644 --- a/app/classes/web/panel_handler.py +++ b/app/classes/web/panel_handler.py @@ -7,6 +7,8 @@ import time import datetime import os +from tornado import iostream + from app.classes.shared.console import console from app.classes.shared.models import Users, installer from app.classes.web.base_handler import BaseHandler @@ -36,11 +38,11 @@ class PanelHandler(BaseHandler): defined_servers = self.controller.list_defined_servers() exec_user_role.add("Super User") else: - defined_servers = self.controller.list_authorized_servers(exec_user_id) logger.debug(exec_user['roles']) for r in exec_user['roles']: role = db_helper.get_role(r) exec_user_role.add(role['role_name']) + defined_servers = db_helper.get_all_authorized_servers(exec_user_id) page_data = { # todo: make this actually pull and compare version data @@ -109,10 +111,9 @@ class PanelHandler(BaseHandler): if exec_user['superuser'] == 1: page_data['servers'] = db_helper.get_all_servers_stats() else: - #page_data['servers'] = db_helper.get_authorized_servers_stats(exec_user_id) - ras = db_helper.get_authorized_servers_stats_from_roles(exec_user_id) - logger.debug("ASFR: {}".format(ras)) - page_data['servers'] = ras + user_auth = db_helper.get_authorized_servers_stats(exec_user_id) + logger.debug("ASFR: {}".format(user_auth)) + page_data['servers'] = user_auth for s in page_data['servers']: try: @@ -137,10 +138,10 @@ class PanelHandler(BaseHandler): return if exec_user['superuser'] != 1: - #if not db_helper.server_id_authorized(server_id, exec_user_id): - if not db_helper.server_id_authorized_from_roles(int(server_id), exec_user_id): - self.redirect("/panel/error?error=Invalid Server ID") - return False + if not db_helper.server_id_authorized(server_id, exec_user_id): + if not db_helper.server_id_authorized_from_roles(int(server_id), exec_user_id): + self.redirect("/panel/error?error=Invalid Server ID") + return False valid_subpages = ['term', 'logs', 'backup', 'config', 'files', 'admin_controls'] @@ -255,8 +256,43 @@ class PanelHandler(BaseHandler): self.redirect("/panel/server_detail?id={}&subpage=backup".format(server_id)) elif page == 'panel_config': - page_data['users'] = db_helper.get_all_users() - page_data['roles'] = db_helper.get_all_roles() + auth_servers = {} + auth_role_servers = {} + users_list = [] + role_users = {} + roles = db_helper.get_all_roles() + role_servers = [] + user_roles = {} + for user in db_helper.get_all_users(): + user_roles_list = db_helper.get_user_roles_names(user.user_id) + user_servers = db_helper.get_all_authorized_servers(user.user_id) + servers = [] + for server in user_servers: + servers.append(server['server_name']) + new_item = {user.user_id: servers} + auth_servers.update(new_item) + data = {user.user_id: user_roles_list} + user_roles.update(data) + for role in roles: + role_servers = [] + role = db_helper.get_role(role.role_id) + for serv_id in role['servers']: + role_servers.append(db_helper.get_server_data_by_id(serv_id)['server_name']) + data = {role['role_id']: role_servers} + auth_role_servers.update(data) + + + page_data['auth-servers'] = auth_servers + page_data['role-servers'] = auth_role_servers + page_data['user-roles'] = user_roles + + if exec_user['superuser'] == 1: + page_data['users'] = db_helper.get_all_users() + page_data['roles'] = db_helper.get_all_roles() + else: + page_data['users'] = db_helper.user_query(exec_user['user_id']) + page_data['roles'] = db_helper.user_role_query(exec_user['user_id']) + for user in page_data['users']: if user.user_id != exec_user['user_id']: user.api_token = "********" @@ -283,22 +319,39 @@ class PanelHandler(BaseHandler): page_data['roles_all'] = db_helper.get_all_roles() page_data['servers'] = [] page_data['servers_all'] = self.controller.list_defined_servers() + page_data['role-servers'] = [] template = "panel/panel_edit_user.html" elif page == "edit_user": - page_data['new_user'] = False user_id = self.get_argument('id', None) + user_servers = db_helper.get_authorized_servers(user_id) + role_servers = db_helper.get_authorized_servers_from_roles(user_id) + page_role_servers = [] + servers = set() + for server in user_servers: + flag = False + for rserver in role_servers: + if rserver['server_id'] == server['server_id']: + flag = True + if not flag: + servers.add(server['server_id']) + for server in role_servers: + page_role_servers.append(server['server_id']) + page_data['new_user'] = False page_data['user'] = db_helper.get_user(user_id) - page_data['servers'] = db_helper.get_authorized_servers_stats_from_roles(user_id) + page_data['servers'] = servers + page_data['role-servers'] = page_role_servers page_data['roles_all'] = db_helper.get_all_roles() page_data['servers_all'] = self.controller.list_defined_servers() - if not exec_user['superuser']: - self.redirect("/panel/error?error=Unauthorized access: not superuser") - return - elif user_id is None: + if user_id is None: self.redirect("/panel/error?error=Invalid User ID") return + elif not exec_user['superuser']: + page_data['servers'] = [] + page_data['role-servers'] = [] + page_data['roles_all'] = [] + page_data['servers_all'] = [] if exec_user['user_id'] != page_data['user']['user_id']: page_data['user']['api_token'] = "********" @@ -332,6 +385,12 @@ class PanelHandler(BaseHandler): self.redirect("/panel/panel_config") elif page == "add_role": + user_roles = {} + for user in db_helper.get_all_users(): + user_roles_list = db_helper.get_user_roles_names(user.user_id) + user_servers = db_helper.get_all_authorized_servers(user.user_id) + data = {user.user_id: user_roles_list} + user_roles.update(data) page_data['new_role'] = True page_data['role'] = {} page_data['role']['role_name'] = "" @@ -339,6 +398,8 @@ class PanelHandler(BaseHandler): page_data['role']['created'] = "N/A" page_data['role']['last_update'] = "N/A" page_data['role']['servers'] = set() + page_data['user-roles'] = user_roles + page_data['users'] = db_helper.get_all_users() if not exec_user['superuser']: self.redirect("/panel/error?error=Unauthorized access: not superuser") @@ -348,10 +409,20 @@ class PanelHandler(BaseHandler): template = "panel/panel_edit_role.html" elif page == "edit_role": + auth_servers = {} + + user_roles = {} + for user in db_helper.get_all_users(): + user_roles_list = db_helper.get_user_roles_names(user.user_id) + user_servers = db_helper.get_all_authorized_servers(user.user_id) + data = {user.user_id: user_roles_list} + user_roles.update(data) page_data['new_role'] = False role_id = self.get_argument('id', None) page_data['role'] = db_helper.get_role(role_id) page_data['servers_all'] = self.controller.list_defined_servers() + page_data['user-roles'] = user_roles + page_data['users'] = db_helper.get_all_users() if not exec_user['superuser']: self.redirect("/panel/error?error=Unauthorized access: not superuser") @@ -426,14 +497,17 @@ class PanelHandler(BaseHandler): auto_start_delay = self.get_argument('auto_start_delay', '10') server_ip = self.get_argument('server_ip', None) server_port = self.get_argument('server_port', None) + executable_update_url = self.get_argument('executable_update_url', None) auto_start = int(float(self.get_argument('auto_start', '0'))) crash_detection = int(float(self.get_argument('crash_detection', '0'))) logs_delete_after = int(float(self.get_argument('logs_delete_after', '0'))) subpage = self.get_argument('subpage', None) if not exec_user['superuser']: - self.redirect("/panel/error?error=Unauthorized access: not superuser") - return + if not db_helper.server_id_authorized(server_id, exec_user_id): + if not db_helper.server_id_authorized_from_roles(server_id, exec_user_id): + self.redirect("/panel/error?error=Unauthorized access: invalid server id") + return elif server_id is None: self.redirect("/panel/error?error=Invalid Server ID") return @@ -454,6 +528,7 @@ class PanelHandler(BaseHandler): Servers.server_ip: server_ip, Servers.server_port: server_port, Servers.auto_start: auto_start, + Servers.executable_update_url: executable_update_url, Servers.crash_detection: crash_detection, Servers.logs_delete_after: logs_delete_after, }).where(Servers.server_id == server_id).execute() @@ -508,7 +583,18 @@ class PanelHandler(BaseHandler): regen_api = int(float(self.get_argument('regen_api', '0'))) if not exec_user['superuser']: - self.redirect("/panel/error?error=Unauthorized access: not superuser") + user_data = { + "username": username, + "password": password0, + } + db_helper.update_user(user_id, user_data=user_data) + + db_helper.add_to_audit_log(exec_user['user_id'], + "Edited user {} (UID:{}) password".format(username, + user_id), + server_id=0, + source_ip=self.get_remote_ip()) + self.redirect("/panel/panel_config") return elif username is None or username == "": self.redirect("/panel/error?error=Invalid username") @@ -536,17 +622,28 @@ class PanelHandler(BaseHandler): if argument: roles.add(role.role_id) + servers = set() + for server in self.controller.list_defined_servers(): + argument = int(float( + bleach.clean( + self.get_argument('server_{}_access'.format(server['server_id']), '0') + ) + )) + if argument: + servers.add(server['server_id']) + user_data = { "username": username, "password": password0, "enabled": enabled, "regen_api": regen_api, "roles": roles, + "servers": servers, } db_helper.update_user(user_id, user_data=user_data) db_helper.add_to_audit_log(exec_user['user_id'], - "Edited user {} (UID:{}) with roles {}".format(username, user_id, roles), + "Edited user {} (UID:{}) with roles {} and servers {}".format(username, user_id, roles, servers), server_id=0, source_ip=self.get_remote_ip()) self.redirect("/panel/panel_config") @@ -595,7 +692,11 @@ class PanelHandler(BaseHandler): servers.add(server['server_id']) user_id = db_helper.add_user(username, password=password0, enabled=enabled) - db_helper.update_user(user_id, {"roles":roles}) + user_data = { + "roles": roles, + "servers": servers, + } + db_helper.update_user(user_id, user_data) db_helper.add_to_audit_log(exec_user['user_id'], "Added user {} (UID:{})".format(username, user_id), diff --git a/app/classes/web/server_handler.py b/app/classes/web/server_handler.py index 8f1cbba2..bbdbbdae 100644 --- a/app/classes/web/server_handler.py +++ b/app/classes/web/server_handler.py @@ -199,6 +199,7 @@ class ServerHandler(BaseHandler): server_type, server_version = server_parts # todo: add server type check here and call the correct server add functions if not a jar new_server_id = self.controller.create_jar_server(server_type, server_version, server_name, min_mem, max_mem, port) + db_helper.add_user_server(new_server_id, exec_user_id) db_helper.add_to_audit_log(exec_user_data['user_id'], "created a {} {} server named \"{}\"".format(server_version, str(server_type).capitalize(), server_name), # Example: Admin created a 1.16.5 Bukkit server named "survival" new_server_id, diff --git a/app/frontend/templates/base.html b/app/frontend/templates/base.html index 2a780c64..1a8e9bc1 100644 --- a/app/frontend/templates/base.html +++ b/app/frontend/templates/base.html @@ -219,6 +219,25 @@ var webSocket; // {% end%} +if (webSocket) { + webSocket.on('send_start_error', function (start_error) { + var x = document.querySelector('.bootbox'); + if(x){ + x.remove()} + var x = document.querySelector('.modal-backdrop'); + if(x){ + x.remove()} + bootbox.alert(start_error.error); + }); + } + + if (webSocket) { + webSocket.on('send_start_reload', function (start_error) { + location.reload() + }); + } + + function warn(message) { var closeEl = document.createElement('span'); var strongEL = document.createElement('strong'); diff --git a/app/frontend/templates/panel/dashboard.html b/app/frontend/templates/panel/dashboard.html index a3ee9085..5aed2ffc 100644 --- a/app/frontend/templates/panel/dashboard.html +++ b/app/frontend/templates/panel/dashboard.html @@ -123,14 +123,15 @@ -