mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2025-01-20 02:05:30 +01:00
0d3fc43931
Add better messaging for support log prep. Add english translation for notify. Chnage dep to 4.0 for tzlocal
529 lines
25 KiB
Python
529 lines
25 KiB
Python
import json
|
|
import logging
|
|
import tempfile
|
|
import threading
|
|
from typing import Container
|
|
import zipfile
|
|
|
|
import tornado.web
|
|
import tornado.escape
|
|
import bleach
|
|
import os
|
|
import shutil
|
|
import html
|
|
import re
|
|
from app.classes.models.users import helper_users
|
|
|
|
from app.classes.shared.console import console
|
|
from app.classes.shared.main_models import Users, installer
|
|
from app.classes.web.base_handler import BaseHandler
|
|
from app.classes.shared.helpers import helper
|
|
from app.classes.shared.server import ServerOutBuf
|
|
from app.classes.models.server_permissions import Enum_Permissions_Server
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class AjaxHandler(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):
|
|
user_data = json.loads(self.get_secure_cookie("user_data"))
|
|
error = bleach.clean(self.get_argument('error', "WTF Error!"))
|
|
|
|
template = "panel/denied.html"
|
|
|
|
page_data = {
|
|
'user_data': user_data,
|
|
'error': error
|
|
}
|
|
|
|
if page == "error":
|
|
template = "public/error.html"
|
|
self.render_page(template, page_data)
|
|
|
|
elif page == 'server_log':
|
|
server_id = self.get_argument('id', None)
|
|
full_log = self.get_argument('full', False)
|
|
|
|
if server_id is None:
|
|
logger.warning("Server ID not found in server_log ajax call")
|
|
self.redirect("/panel/error?error=Server ID Not Found")
|
|
return
|
|
|
|
server_id = bleach.clean(server_id)
|
|
|
|
server_data = self.controller.servers.get_server_data_by_id(server_id)
|
|
if not server_data:
|
|
logger.warning("Server Data not found in server_log ajax call")
|
|
self.redirect("/panel/error?error=Server ID Not Found")
|
|
return
|
|
|
|
if not server_data['log_path']:
|
|
logger.warning("Log path not found in server_log ajax call ({})".format(server_id))
|
|
|
|
if full_log:
|
|
log_lines = helper.get_setting('max_log_lines')
|
|
data = helper.tail_file(helper.get_os_understandable_path(server_data['log_path']), log_lines)
|
|
else:
|
|
data = ServerOutBuf.lines.get(server_id, [])
|
|
|
|
|
|
for d in data:
|
|
try:
|
|
d = re.sub('(\033\\[(0;)?[0-9]*[A-z]?(;[0-9])?m?)|(> )', '', d)
|
|
d = re.sub('[A-z]{2}\b\b', '', d)
|
|
line = helper.log_colors(html.escape(d))
|
|
self.write('{}<br />'.format(line))
|
|
# self.write(d.encode("utf-8"))
|
|
|
|
except Exception as e:
|
|
logger.warning("Skipping Log Line due to error: {}".format(e))
|
|
pass
|
|
|
|
elif page == "announcements":
|
|
data = helper.get_announcements()
|
|
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("Invalid path in get_file ajax call ({})".format(file_path))
|
|
console.warning("Invalid path in get_file ajax call ({})".format(file_path))
|
|
return
|
|
|
|
|
|
error = None
|
|
|
|
try:
|
|
with open(file_path) 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' +
|
|
helper.generate_zip_tree(path))
|
|
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):
|
|
user_data = json.loads(self.get_secure_cookie("user_data"))
|
|
server_id = self.get_argument('id', None)
|
|
exec_user_id = user_data['user_id']
|
|
exec_user = helper_users.get_user(exec_user_id)
|
|
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_server_permissions_foruser(exec_user_id, server_id)
|
|
error = bleach.clean(self.get_argument('error', "WTF Error!"))
|
|
|
|
page_data = {
|
|
'user_data': user_data,
|
|
'error': error
|
|
}
|
|
|
|
if page == "send_command":
|
|
command = self.get_body_argument('command', default=None, strip=True)
|
|
server_id = self.get_argument('id')
|
|
|
|
if server_id is None:
|
|
logger.warning("Server ID not found in send_command ajax call")
|
|
console.warning("Server ID not found in send_command ajax call")
|
|
|
|
srv_obj = self.controller.get_server_obj(server_id)
|
|
|
|
if command:
|
|
if srv_obj.check_running():
|
|
srv_obj.send_command(command)
|
|
|
|
self.controller.management.add_to_audit_log(user_data['user_id'], "Sent command to {} terminal: {}".format(self.controller.servers.get_server_friendly_name(server_id), command), server_id, self.get_remote_ip())
|
|
|
|
elif page == "create_file":
|
|
if not permissions['Files'] in user_perms:
|
|
if not exec_user['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("Invalid path in create_file ajax call ({})".format(file_path))
|
|
console.warning("Invalid path in create_file ajax call ({})".format(file_path))
|
|
return
|
|
|
|
# Create the file by opening it
|
|
with open(file_path, 'w') as file_object:
|
|
file_object.close()
|
|
|
|
elif page == "create_dir":
|
|
if not permissions['Files'] in user_perms:
|
|
if not exec_user['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("Invalid path in create_dir ajax call ({})".format(dir_path))
|
|
console.warning("Invalid path in create_dir ajax call ({})".format(dir_path))
|
|
return
|
|
# Create the directory
|
|
os.mkdir(dir_path)
|
|
|
|
elif page == "unzip_file":
|
|
if not permissions['Files'] in user_perms:
|
|
if not exec_user['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("/panel/server_detail?id={}&subpage=files".format(server_id))
|
|
return
|
|
|
|
elif page == "kill":
|
|
if not permissions['Commands'] in user_perms:
|
|
if not exec_user['superuser']:
|
|
self.redirect("/panel/error?error=Unauthorized access to Commands")
|
|
return
|
|
server_id = self.get_argument('id', None)
|
|
svr = self.controller.get_server_obj(server_id)
|
|
try:
|
|
svr.kill()
|
|
except Exception as e:
|
|
logger.error("Could not find PID for requested termsig. Full error: {}".format(e))
|
|
return
|
|
elif page == "eula":
|
|
server_id = self.get_argument('id', None)
|
|
svr = self.controller.get_server_obj(server_id)
|
|
svr.agree_eula(user_data['user_id'])
|
|
|
|
elif page == "restore_backup":
|
|
if not permissions['Backup'] in user_perms:
|
|
if not exec_user['superuser']:
|
|
self.redirect("/panel/error?error=Unauthorized access to Backups")
|
|
return
|
|
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):
|
|
tempDir = helper.unzip_backup_archive(backup_path, zip_name)
|
|
new_server = self.controller.import_zip_server(svr_obj.server_name, tempDir, 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')
|
|
|
|
elif page == "unzip_server":
|
|
path = self.get_argument('path', None)
|
|
helper.unzipServer(path, exec_user_id)
|
|
return
|
|
|
|
|
|
@tornado.web.authenticated
|
|
def delete(self, page):
|
|
user_data = json.loads(self.get_secure_cookie("user_data"))
|
|
server_id = self.get_argument('id', None)
|
|
exec_user_id = user_data['user_id']
|
|
exec_user = helper_users.get_user(exec_user_id)
|
|
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_server_permissions_foruser(exec_user_id, server_id)
|
|
if page == "del_file":
|
|
if not permissions['Files'] in user_perms:
|
|
if not exec_user['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("delete {} for server {}".format(file_path, 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("Invalid path in del_file ajax call ({})".format(file_path))
|
|
console.warning("Invalid path in del_file ajax call ({})".format(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")
|
|
else:
|
|
sch_id = self.get_argument('schedule_id', '-404')
|
|
self.tasks_manager.remove_job(sch_id)
|
|
|
|
if page == "del_backup":
|
|
if not permissions['Backup'] in user_perms:
|
|
if not exec_user['superuser']:
|
|
self.redirect("/panel/error?error=Unauthorized access to Backups")
|
|
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("delete {} for server {}".format(file_path, 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("Invalid path in del_file ajax call ({})".format(file_path))
|
|
console.warning("Invalid path in del_file ajax call ({})".format(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 exec_user['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("delete {} for server {}".format(dir_path, 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("Invalid path in del_file ajax call ({})".format(dir_path))
|
|
console.warning("Invalid path in del_file ajax call ({})".format(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 exec_user['superuser']:
|
|
self.redirect("/panel/error?error=Unauthorized access to Config")
|
|
return
|
|
server_id = self.get_argument('id', None)
|
|
logger.info(
|
|
"Removing server from panel for server: {}".format(self.controller.servers.get_server_friendly_name(server_id)))
|
|
|
|
server_data = self.controller.get_server_data(server_id)
|
|
server_name = server_data['server_name']
|
|
|
|
self.controller.management.add_to_audit_log(exec_user_id,
|
|
"Deleted server {} named {}".format(server_id, server_name),
|
|
server_id,
|
|
self.get_remote_ip())
|
|
|
|
self.tasks_manager.remove_all_server_tasks(server_id)
|
|
self.controller.remove_server(server_id, False)
|
|
|
|
elif page == "delete_server_files":
|
|
if not permissions['Config'] in user_perms:
|
|
if not exec_user['superuser']:
|
|
self.redirect("/panel/error?error=Unauthorized access to Config")
|
|
return
|
|
server_id = self.get_argument('id', None)
|
|
logger.info(
|
|
"Removing server and all associated files for server: {}".format(self.controller.servers.get_server_friendly_name(server_id)))
|
|
|
|
server_data = self.controller.get_server_data(server_id)
|
|
server_name = server_data['server_name']
|
|
|
|
self.controller.management.add_to_audit_log(exec_user_id,
|
|
"Deleted server {} named {}".format(server_id, server_name),
|
|
server_id,
|
|
self.get_remote_ip())
|
|
|
|
self.tasks_manager.remove_all_server_tasks(server_id)
|
|
self.controller.remove_server(server_id, True)
|
|
|
|
@tornado.web.authenticated
|
|
def put(self, page):
|
|
user_data = json.loads(self.get_secure_cookie("user_data"))
|
|
server_id = self.get_argument('id', None)
|
|
exec_user_id = user_data['user_id']
|
|
exec_user = helper_users.get_user(exec_user_id)
|
|
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_server_permissions_foruser(exec_user_id, server_id)
|
|
if page == "save_file":
|
|
if not permissions['Files'] in user_perms:
|
|
if not exec_user['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("Invalid path in save_file ajax call ({})".format(file_path))
|
|
console.warning("Invalid path in save_file ajax call ({})".format(file_path))
|
|
return
|
|
|
|
# Open the file in write mode and store the content in file_object
|
|
with open(file_path, 'w') as file_object:
|
|
file_object.write(file_contents)
|
|
|
|
elif page == "rename_item":
|
|
if not permissions['Files'] in user_perms:
|
|
if not exec_user['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("Invalid old name path in rename_item ajax call ({})".format(server_id))
|
|
console.warning("Invalid old name path in rename_item ajax call ({})".format(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("Invalid new name path in rename_item ajax call ({})".format(server_id))
|
|
console.warning("Invalid new name path in rename_item ajax call ({})".format(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("Server ID not defined in {} ajax call ({})".format(page_name, server_id))
|
|
console.warning("Server ID not defined in {} ajax call ({})".format(page_name, 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("Server ID not found in {} ajax call ({})".format(page_name, server_id))
|
|
console.warning("Server ID not found in {} ajax call ({})".format(page_name, server_id))
|
|
return
|
|
return True
|