From 675724759694938dcaeb22d0bf1c0b0aa83135a4 Mon Sep 17 00:00:00 2001 From: Marat Fayzullin Date: Wed, 15 Nov 2023 23:43:52 -0500 Subject: [PATCH] Banning unbanning users now works. --- htdocs/lib/UI.js | 8 +++-- htdocs/lib/settings/ClientList.js | 17 +++++++++ htdocs/settings.html | 12 +++---- htdocs/settings.js | 3 +- owrx/controllers/assets.py | 1 + owrx/controllers/settings/__init__.py | 33 +++++++++++------ owrx/http.py | 17 ++++++--- owrx/websocket.py | 51 +++++++++++++++++++++++---- 8 files changed, 111 insertions(+), 31 deletions(-) create mode 100644 htdocs/lib/settings/ClientList.js diff --git a/htdocs/lib/UI.js b/htdocs/lib/UI.js index 2bea4b3a..63d6ec23 100644 --- a/htdocs/lib/UI.js +++ b/htdocs/lib/UI.js @@ -171,8 +171,12 @@ UI.toggleFrame = function(on) { this.frame = on; LS.save('ui_frame', on); $('#openwebrx-frame-checkbox').attr('checked', on); - $('#openwebrx-panel-receiver').css( 'border', on ? '2px solid white' : '2px solid transparent'); - $('#openwebrx-dialog-bookmark').css('border', on ? '2px solid white' : '2px solid transparent'); + + var border = on ? '2px solid white' : '2px solid transparent'; + $('#openwebrx-panel-receiver').css( 'border', border); + $('#openwebrx-dialog-bookmark').css('border', border); +// $('#openwebrx-digimode-canvas-container').css('border', border); +// $('.openwebrx-message-panel').css('border', border); } }; diff --git a/htdocs/lib/settings/ClientList.js b/htdocs/lib/settings/ClientList.js new file mode 100644 index 00000000..b4abc438 --- /dev/null +++ b/htdocs/lib/settings/ClientList.js @@ -0,0 +1,17 @@ +$.fn.clientList = function() { + this.each(function() { + $(this).on('click', '.client-ban', function(e) { + $.ajax(document.location.href + "/ban/" + this.value).done(function() { + document.location.reload(); + }); + return false; + }); + + $(this).on('click', '.client-unban', function(e) { + $.ajax(document.location.href + "/unban/" + this.value).done(function() { + document.location.reload(); + }); + return false; + }); + }); +} diff --git a/htdocs/settings.html b/htdocs/settings.html index 303d374a..b16304e3 100644 --- a/htdocs/settings.html +++ b/htdocs/settings.html @@ -11,12 +11,6 @@ ${header}
-
-

Clients

-
-
- ${clients} -

Settings

@@ -43,5 +37,11 @@ ${header} Feature report
+
+

Clients

+
+
+ ${clients} +
diff --git a/htdocs/settings.js b/htdocs/settings.js index b1bc7361..c222bde0 100644 --- a/htdocs/settings.js +++ b/htdocs/settings.js @@ -9,4 +9,5 @@ $(function(){ $('#scheduler').schedulerInput(); $('.exponential-input').exponentialInput(); $('.device-log-messages').logMessages(); -}); \ No newline at end of file + $('.client-list').clientList(); +}); diff --git a/owrx/controllers/assets.py b/owrx/controllers/assets.py index e5da4a25..37b36880 100644 --- a/owrx/controllers/assets.py +++ b/owrx/controllers/assets.py @@ -181,6 +181,7 @@ class CompiledAssetsController(GzipMixin, ModificationAwareController): "lib/settings/SchedulerInput.js", "lib/settings/ExponentialInput.js", "lib/settings/LogMessages.js", + "lib/settings/ClientList.js", "settings.js", ], } diff --git a/owrx/controllers/settings/__init__.py b/owrx/controllers/settings/__init__.py index 34d09027..baa97776 100644 --- a/owrx/controllers/settings/__init__.py +++ b/owrx/controllers/settings/__init__.py @@ -21,16 +21,13 @@ class SettingsController(AuthorizationMixin, WebpageController): variables["clients"] = self.renderClients() return variables -#
-#
- def renderClients(self): return """ - + {clients} @@ -40,11 +37,12 @@ class SettingsController(AuthorizationMixin, WebpageController): ) def renderClient(self, c): - return "".format( - c["ts"].strftime('%Y-%m-%d %H:%M:%S'), + return "".format( self.renderIp(c["ip"]), - c["sdr"] + " " + c["band"] if "sdr" in c else "n/a", - self.renderButtons(c["ip"]) + "banned" if c["ban"] else c["sdr"] + " " + c["band"] if "sdr" in c else "n/a", + "until" if c["ban"] else "since", + c["ts"].strftime('%H:%M:%S'), + self.renderButtons(c) ) def renderIp(self, ip): @@ -53,10 +51,23 @@ class SettingsController(AuthorizationMixin, WebpageController): {1} """.format(ip, ip) - def renderButtons(self, ip): + def renderButtons(self, c): + action = "unban" if c["ban"] else "ban" return """ - - """.format(ip) + + """.format(action, c["ip"], action) + + def ban(self): + ip = self.request.matches.group(1) + logger.info("Banning {0} for {1} minutes".format(ip, 15)) + WebSocketConnection.banIp(ip, 15) + self.send_response("{}", content_type="application/json", code=200) + + def unban(self): + ip = self.request.matches.group(1) + logger.info("Unbanning {0}".format(ip)) + WebSocketConnection.unbanIp(ip) + self.send_response("{}", content_type="application/json", code=200) class SettingsFormController(AuthorizationMixin, BreadcrumbMixin, WebpageController, metaclass=ABCMeta): diff --git a/owrx/http.py b/owrx/http.py index 12995cac..c2ab9ce7 100644 --- a/owrx/http.py +++ b/owrx/http.py @@ -23,12 +23,14 @@ from owrx.controllers.session import SessionController from owrx.controllers.profile import ProfileController from owrx.controllers.imageupload import ImageUploadController from owrx.controllers.robots import RobotsController +from owrx.websocket import WebSocketConnection from owrx.storage import Storage from http.server import BaseHTTPRequestHandler from urllib.parse import urlparse, parse_qs import re from abc import ABC, abstractmethod from http.cookies import SimpleCookie +from datetime import datetime import logging @@ -104,6 +106,8 @@ class Router(object): StaticRoute("/metrics", MetricsController, options={"action": "prometheusAction"}), StaticRoute("/metrics.json", MetricsController), StaticRoute("/settings", SettingsController), + RegexRoute("^/settings/ban/(.+)$", SettingsController, options={"action": "ban"}), + RegexRoute("^/settings/unban/(.+)$", SettingsController, options={"action": "unban"}), StaticRoute("/settings/general", GeneralSettingsController), StaticRoute( "/settings/general", GeneralSettingsController, method="POST", options={"action": "processFormData"} @@ -173,12 +177,15 @@ class Router(object): return r def route(self, handler, request): - route = self.find_route(request) - if route is not None: - controller = route.controller - controller(handler, request, route.controllerOptions).handle_request() - else: + if WebSocketConnection.isIpBanned(handler.client_address[0]): handler.send_error(404, "Not Found", "The page you requested could not be found.") + else: + route = self.find_route(request) + if route is not None: + controller = route.controller + controller(handler, request, route.controllerOptions).handle_request() + else: + handler.send_error(404, "Not Found", "The page you requested could not be found.") class RequestHandler(BaseHTTPRequestHandler): diff --git a/owrx/websocket.py b/owrx/websocket.py index 69dc047a..6708fbe0 100644 --- a/owrx/websocket.py +++ b/owrx/websocket.py @@ -6,7 +6,7 @@ from multiprocessing import Pipe import select import threading from abc import ABC, abstractmethod -from datetime import datetime +from datetime import datetime, timedelta import logging @@ -47,6 +47,7 @@ class Handler(ABC): class WebSocketConnection(object): connections = [] + bans = {} @staticmethod def closeAll(): @@ -59,20 +60,58 @@ class WebSocketConnection(object): @staticmethod def listAll(): result = [] - for x in WebSocketConnection.connections: + for c in WebSocketConnection.connections: entry = { - "ts" : x.startTime, - "ip" : x.handler.client_address[0] + "ts" : c.startTime, + "ip" : c.handler.client_address[0], + "ban" : False } - rx = x.messageHandler + rx = c.messageHandler if hasattr(rx, "sdr"): entry["sdr"] = rx.sdr.getName() entry["band"] = rx.sdr.getProfileName() result.append(entry) + WebSocketConnection.cleanBans() + for ip in WebSocketConnection.bans: + result.append({ + "ts" : WebSocketConnection.bans[ip], + "ip" : ip, + "ban" : True + }) return result + @staticmethod + def banIp(ip: str, minutes: int): + WebSocketConnection.cleanBans() + WebSocketConnection.bans[ip] = datetime.now() + timedelta(minutes=minutes) + banned = [] + for c in WebSocketConnection.connections: + if ip == c.handler.client_address[0]: + banned.append(c) + for c in banned: + try: + c.close() + except: + logger.exception("exception while banning %s" % ip) + + @staticmethod + def unbanIp(ip: str): + if ip in WebSocketConnection.bans: + del WebSocketConnection.bans[ip] + + @staticmethod + def isIpBanned(ip: str): + return ip in WebSocketConnection.bans and datetime.now() < WebSocketConnection.bans[ip] + + @staticmethod + def cleanBans(): + now = datetime.now() + old = [ip for ip in WebSocketConnection.bans if now >= WebSocketConnection.bans[ip]] + for ip in old: + del WebSocketConnection.bans[ip] + def __init__(self, handler, messageHandler: Handler): - self.startTime = datetime.utcnow() + self.startTime = datetime.now() self.handler = handler self.handler.connection.setblocking(0) self.messageHandler = None
Connection Time IP Address SDR ProfileLocal Time Actions
{0}{1}{2}{3}
{0}{1}{2} {3}{4}