From 256c693f76eb545179c1beb6bed1169764e54840 Mon Sep 17 00:00:00 2001 From: PC-Admin Date: Sun, 13 Aug 2023 23:32:06 +0800 Subject: [PATCH] add commentary to modules --- redlight_client_module.py | 25 ++++++++++++++++++------- redlight_server_module.py | 38 +++++++++++++++++++++++++------------- 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/redlight_client_module.py b/redlight_client_module.py index 1b04230..7edd805 100755 --- a/redlight_client_module.py +++ b/redlight_client_module.py @@ -12,20 +12,20 @@ from twisted.internet import defer from twisted.web.iweb import IBodyProducer from zope.interface import implementer -# Define a handler and set its level and format +# Setting up logging: file_handler = logging.FileHandler('/var/log/matrix-synapse/redlight.log') file_handler.setLevel(logging.INFO) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') file_handler.setFormatter(formatter) -# Get your logger logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) logger.addHandler(file_handler) -# Ensure that this logger's messages don't propagate to the root logger +# Prevent logger's messages from propagating to the root logger. logger.propagate = False +# Define a custom producer to convert our JSON data for HTTP requests. @implementer(IBodyProducer) class _JsonProducer: def __init__(self, data): @@ -46,17 +46,20 @@ class _JsonProducer: class RedlightClientModule: def __init__(self, config: dict, api: ModuleApi): self._api = api + # URL where we'll check if the room/user combination is allowed. self._redlight_url = config.get("redlight_url", "https://duckdomain.xyz/_matrix/loj/v1/abuse_lookup") - self._agent = Agent(reactor) + self._agent = Agent(reactor) # Twisted agent for making HTTP requests. logger.info("RedLightClientModule initialized.") + # Register the user_may_join_room function to be called by Synapse before a user joins a room. api.register_spam_checker_callbacks( user_may_join_room=self.user_may_join_room ) @staticmethod def double_hash_sha256(data: str) -> str: + """Double-hash the data with SHA256 for added security.""" first_hash = hashlib.sha256(data.encode()).digest() double_hashed = hashlib.sha256(first_hash).hexdigest() return double_hashed @@ -67,14 +70,17 @@ class RedlightClientModule: logger.info(f"User {user} is attempting to join room {room}. Invitation status: {is_invited}.") + # Double-hash the room and user IDs. hashed_room_id = self.double_hash_sha256(room) hashed_user_id = self.double_hash_sha256(user) + # Prepare the HTTP body. body = _JsonProducer({ "room_id_hash": hashed_room_id, "user_id_hash": hashed_user_id }) + # Make the HTTP request to our redlight server. response = await self._agent.request( b"PUT", self._redlight_url.encode(), @@ -82,25 +88,30 @@ class RedlightClientModule: body ) + # Extract the response body. response_body_bytes = await readBody(response) response_body = response_body_bytes.decode("utf-8") try: + # Try to parse the response body as JSON. response_json = json.loads(response_body) except json.JSONDecodeError: logger.error(f"Failed to decode response body: {response_body}") - #return NOT_SPAM # default to allowing if there's an error + # Handle the response based on its HTTP status code. if response.code == 200: raise AuthError(403, "User not allowed to join this room") elif response.code == 204: - return NOT_SPAM + return NOT_SPAM # Allow the user to join. else: + # Handle unexpected responses by logging them and allowing the user to join as a fallback. logger.error(f"Unexpected response code {response.code} with body: {response_body}") - return NOT_SPAM # default to allowing if there's an unexpected response + return NOT_SPAM +# Function to parse the module's configuration. def parse_config(config: dict) -> dict: return config +# Factory function to create an instance of the RedlightClientModule. def create_module(api: ModuleApi, config: dict) -> RedlightClientModule: return RedlightClientModule(config, api) diff --git a/redlight_server_module.py b/redlight_server_module.py index aed9e33..c52c44e 100755 --- a/redlight_server_module.py +++ b/redlight_server_module.py @@ -7,25 +7,28 @@ from twisted.internet.defer import inlineCallbacks from twisted.web.server import NOT_DONE_YET from twisted.web.http import OK, NO_CONTENT -# Define a handler and set its level and format +# Setting up logging specifically for this module: +# 1. Create a file handler to write logs to a specific file. file_handler = logging.FileHandler('/var/log/matrix-synapse/redlight.log') file_handler.setLevel(logging.INFO) +# 2. Define the format for the logs. formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') file_handler.setFormatter(formatter) -# Get your logger +# 3. Initialize the logger for this module and set its level. logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) +# 4. Attach the file handler to the logger. logger.addHandler(file_handler) -# Ensure that this logger's messages don't propagate to the root logger +# Prevent this logger's messages from being passed to the root logger or other handlers. logger.propagate = False class RedlightServerModule: def __init__(self, config: dict, api: ModuleApi): self._api = api - # Register the abuse_lookup endpoint + # Register a new web endpoint "/_matrix/loj/v1/abuse_lookup" which will be handled by RedlightServerResource. api.register_web_resource( "/_matrix/loj/v1/abuse_lookup", RedlightServerResource(self) @@ -34,16 +37,20 @@ class RedlightServerModule: logger.info("RedlightServerModule initialized.") class RedlightServerResource: - + # This flag helps Twisted identify this as a final resource and not look for children. isLeaf = True def __init__(self, module): self._module = module + # Handle incoming HTTP requests to the registered endpoint. def render(self, request): + # Extract HTTP method (GET, PUT, POST, etc.) from the request. method = request.method.decode('ascii') + # Based on the method, try to find the respective handler function. handler = getattr(self, f"on_{method}", None) + # If a handler is found, process the request with it. if handler: def _respond(result): request.write(result) @@ -57,28 +64,30 @@ class RedlightServerResource: d = handler(request) d.addCallbacks(_respond, _error) - return NOT_DONE_YET + return NOT_DONE_YET # indicates asynchronous processing else: + # If no handler is found for the method, return "Method Not Allowed". return self.method_not_allowed(request) + # Handle PUT requests to the endpoint. @inlineCallbacks def on_PUT(self, request): try: - # Extract body from the request + # Read and decode the request body. body = yield request.content.read() content = body.decode("utf-8") - - # Log the request to Synapse's log logger.info(f"Received abuse lookup request: {content}") - # Extract room_id_hash and user_id_hash from the content + # Extract specific data points from the request content. data = json.loads(content) room_id_hash = data["room_id_hash"] user_id_hash = data["user_id_hash"] - # Check the room_id_hash against your list/database or hardcoded value + # Placeholder check for abuse based on the room_id_hash. + # In a real-world scenario, you'd likely check against a database or a list. is_abuse = room_id_hash == "ee180279a57f716e5801335a2914e228667f363e460ccabcc49e8fd879e1be4a" + # Respond based on whether the request is identified as abusive or not. if is_abuse: request.setResponseCode(http.OK) defer.returnValue(json.dumps({ @@ -94,20 +103,23 @@ class RedlightServerResource: request.setResponseCode(400) defer.returnValue(json.dumps({"error": "Bad Request"}).encode("utf-8")) + # Handle GET requests (by disallowing them). def on_GET(self, request): return self.method_not_allowed(request) + # Handle POST requests (by disallowing them). def on_POST(self, request): return self.method_not_allowed(request) - # And similarly for other methods you want to block like DELETE, HEAD, etc. - + # General method to respond with "Method Not Allowed" for disallowed or unrecognized HTTP methods. def method_not_allowed(self, request): request.setResponseCode(405) return json.dumps({"error": "Method Not Allowed"}).encode("utf-8") +# Function to parse the configuration for this module. def parse_config(config: dict) -> dict: return config +# Factory function to create and return an instance of the RedlightServerModule. def create_module(api: ModuleApi, config: dict) -> RedlightServerModule: return RedlightServerModule(config, api)