Add authentication to WS, notify user when an activity log gets logged, and more

This commit is contained in:
luukas 2021-03-01 02:54:20 +02:00
parent a965af5491
commit 9c62099f32
7 changed files with 65 additions and 18 deletions

View File

@ -6,6 +6,7 @@ import datetime
from app.classes.shared.helpers import helper from app.classes.shared.helpers import helper
from app.classes.shared.console import console from app.classes.shared.console import console
from app.classes.minecraft.server_props import ServerProps from app.classes.minecraft.server_props import ServerProps
from app.classes.web.websocket_helper import websocket_helper
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -254,14 +255,14 @@ class db_shortcuts:
def get_server_friendly_name(self, server_id): def get_server_friendly_name(self, server_id):
server_data = self.get_server_data_by_id(server_id) server_data = self.get_server_data_by_id(server_id)
friendly_name = "{}-{}".format(server_data.get('server_id', 0), server_data.get('server_name', None)) friendly_name = "{} with ID: {}".format(server_data.get('server_name', None), server_data.get('server_id', 0))
return friendly_name return friendly_name
def send_command(self, user_id, server_id, remote_ip, command): def send_command(self, user_id, server_id, remote_ip, command):
server_name = self.get_server_friendly_name(server_id) server_name = self.get_server_friendly_name(server_id)
self.add_to_audit_log(user_id, "Issued Command {} for Server: {}".format(command, server_name), self.add_to_audit_log(user_id, "issued command {} for server {}".format(command, server_name),
server_id, remote_ip) server_id, remote_ip)
Commands.insert({ Commands.insert({
@ -294,6 +295,8 @@ class db_shortcuts:
audit_msg = "{} {}".format(str(user_data.username).capitalize(), log_msg) audit_msg = "{} {}".format(str(user_data.username).capitalize(), log_msg)
websocket_helper.broadcast('notification', audit_msg)
Audit_Log.insert({ Audit_Log.insert({
Audit_Log.user_name: user_data.username, Audit_Log.user_name: user_data.username,
Audit_Log.user_id: user_id, Audit_Log.user_id: user_id,
@ -302,6 +305,16 @@ class db_shortcuts:
Audit_Log.source_ip: source_ip Audit_Log.source_ip: source_ip
}).execute() }).execute()
@staticmethod
def add_to_audit_log_raw(user_name, user_id, server_id, log_msg, source_ip):
Audit_Log.insert({
Audit_Log.user_name: user_name,
Audit_Log.user_id: user_id,
Audit_Log.server_id: server_id,
Audit_Log.log_msg: log_msg,
Audit_Log.source_ip: source_ip
}).execute()

View File

@ -142,7 +142,6 @@ class TasksManager:
loop = asyncio.new_event_loop() loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop) asyncio.set_event_loop(loop)
console.debug('realtime start')
host_stats = db_helper.get_latest_hosts_stats() host_stats = db_helper.get_latest_hosts_stats()
while True: while True:
@ -153,11 +152,9 @@ class TasksManager:
db_helper.get_latest_hosts_stats().get('mem_percent'): db_helper.get_latest_hosts_stats().get('mem_percent'):
# Stats are different # Stats are different
console.debug('realtime 1')
host_stats = db_helper.get_latest_hosts_stats() host_stats = db_helper.get_latest_hosts_stats()
if len(websocket_helper.clients) > 0: if len(websocket_helper.clients) > 0:
# There are clients # There are clients
console.debug('realtime 11')
websocket_helper.broadcast('update_host_stats', { websocket_helper.broadcast('update_host_stats', {
'cpu_usage': host_stats.get('cpu_usage'), 'cpu_usage': host_stats.get('cpu_usage'),
'cpu_cores': host_stats.get('cpu_cores'), 'cpu_cores': host_stats.get('cpu_cores'),
@ -169,7 +166,6 @@ class TasksManager:
time.sleep(4) time.sleep(4)
else: else:
# Stats are same # Stats are same
console.debug('realtime 0')
time.sleep(8) time.sleep(8)

View File

@ -113,7 +113,7 @@ class ServerHandler(BaseHandler):
if new_server_id: if new_server_id:
db_helper.add_to_audit_log(user_data['user_id'], db_helper.add_to_audit_log(user_data['user_id'],
"Created server {} named {}".format(server, server_name), "created a {} {} server named \"{}\"".format(server_parts[1], str(server_parts[0]).capitalize(), server_name), # Example: Admin created a 1.16.5 Bukkit server named "survival"
new_server_id, new_server_id,
self.get_remote_ip()) self.get_remote_ip())
else: else:

View File

@ -24,7 +24,7 @@ try:
from app.classes.web.server_handler import ServerHandler from app.classes.web.server_handler import ServerHandler
from app.classes.web.ajax_handler import AjaxHandler from app.classes.web.ajax_handler import AjaxHandler
from app.classes.web.api_handler import ServersStats, NodeStats from app.classes.web.api_handler import ServersStats, NodeStats
from app.classes.web.websocket_handler import WebSocketHandler from app.classes.web.websocket_handler import SocketHandler
except ModuleNotFoundError as e: except ModuleNotFoundError as e:
logger.critical("Import Error: Unable to load {} module".format(e, e.name)) logger.critical("Import Error: Unable to load {} module".format(e, e.name))
@ -126,7 +126,7 @@ class webserver:
(r'/ajax/(.*)', AjaxHandler), (r'/ajax/(.*)', AjaxHandler),
(r'/api/stats/servers', ServersStats), (r'/api/stats/servers', ServersStats),
(r'/api/stats/node', NodeStats), (r'/api/stats/node', NodeStats),
(r'/ws', WebSocketHandler), (r'/ws', SocketHandler),
] ]
app = tornado.web.Application( app = tornado.web.Application(

View File

@ -2,15 +2,44 @@ import json
import tornado.websocket import tornado.websocket
from app.classes.shared.console import console from app.classes.shared.console import console
from app.classes.shared.models import Users, db_helper
from app.classes.web.websocket_helper import websocket_helper from app.classes.web.websocket_helper import websocket_helper
class WebSocketHandler(tornado.websocket.WebSocketHandler): class SocketHandler(tornado.websocket.WebSocketHandler):
def get_remote_ip(self):
remote_ip = self.request.headers.get("X-Real-IP") or \
self.request.headers.get("X-Forwarded-For") or \
self.request.remote_ip
return remote_ip
def check_auth(self):
user_data_cookie_raw = self.get_secure_cookie('user_data')
if user_data_cookie_raw and user_data_cookie_raw.decode('utf-8'):
user_data_cookie = user_data_cookie_raw.decode('utf-8')
user_id = json.loads(user_data_cookie)['user_id']
query = Users.select().where(Users.user_id == user_id)
if query.exists():
return True
return False
def open(self): def open(self):
if self.check_auth():
self.handle()
else:
websocket_helper.send_message(self, 'notification', 'Not authenticated for WebSocket connection')
self.close()
db_helper.add_to_audit_log_raw('unknown', 0, 0, 'Someone tried to connect via WebSocket without proper authentication', self.get_remote_ip())
websocket_helper.broadcast('notification', 'Someone tried to connect via WebSocket without proper authentication')
def handle(self):
websocket_helper.addClient(self) websocket_helper.addClient(self)
console.debug('Opened WebSocket connection') console.debug('Opened WebSocket connection')
websocket_helper.broadcast('notification', 'New client connected') # websocket_helper.broadcast('notification', 'New client connected')
def on_message(self, rawMessage): def on_message(self, rawMessage):
@ -21,5 +50,5 @@ class WebSocketHandler(tornado.websocket.WebSocketHandler):
def on_close(self): def on_close(self):
websocket_helper.removeClient(self) websocket_helper.removeClient(self)
console.debug('Closed WebSocket connection') console.debug('Closed WebSocket connection')
websocket_helper.broadcast('notification', 'Client disconnected') # websocket_helper.broadcast('notification', 'Client disconnected')

View File

@ -11,12 +11,15 @@ class WebSocketHelper:
def removeClient(self, client): def removeClient(self, client):
self.clients.add(client) self.clients.add(client)
def broadcast(self, message_type: str, data): def send_message(self, client, event_type, data):
console.debug('Sending: ' + str(json.dumps({'type': message_type, 'data': data}))) message = str(json.dumps({'event': event_type, 'data': data}))
message = str(json.dumps({'event': message_type, 'data': data})) client.write_message(message)
def broadcast(self, event_type, data):
console.debug('Sending: ' + str(json.dumps({'event': event_type, 'data': data})))
for client in self.clients: for client in self.clients:
try: try:
client.write_message(message) self.send_message(client, event_type, data)
except: except:
pass pass

View File

@ -184,7 +184,13 @@
listenEvents listenEvents
.filter(listenedEvent => listenedEvent.event == message.event) .filter(listenedEvent => listenedEvent.event == message.event)
.forEach(listenedEvent => listenedEvent.callback(message.data)) .forEach(listenedEvent => listenedEvent.callback(message.data))
} };
wsInternal.onerror = function (errorEvent) {
console.error('WebSocket Error', errorEvent);
};
wsInternal.onclose = function (closeEvent) {
console.log('Closed WebSocket', closeEvent);
};
webSocket = { webSocket = {