redlight/redlight_server_module.py

131 lines
5.4 KiB
Python
Raw Normal View History

import logging
import json
from synapse.module_api import ModuleApi
from twisted.web import http
from twisted.internet import defer
from twisted.internet.defer import inlineCallbacks
from twisted.web.server import NOT_DONE_YET
from twisted.web.http import OK, NO_CONTENT
2023-08-13 11:32:06 -04:00
# 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)
2023-08-13 11:32:06 -04:00
# 2. Define the format for the logs.
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
2023-08-13 11:32:06 -04:00
# 3. Initialize the logger for this module and set its level.
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
2023-08-13 11:32:06 -04:00
# 4. Attach the file handler to the logger.
logger.addHandler(file_handler)
2023-08-13 11:32:06 -04:00
# 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
2023-08-13 11:32:06 -04:00
# 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)
)
logger.info("RedlightServerModule initialized.")
class RedlightServerResource:
2023-08-13 11:32:06 -04:00
# This flag helps Twisted identify this as a final resource and not look for children.
isLeaf = True
def __init__(self, module):
self._module = module
2023-08-13 11:32:06 -04:00
# Handle incoming HTTP requests to the registered endpoint.
def render(self, request):
2023-08-13 11:32:06 -04:00
# Extract HTTP method (GET, PUT, POST, etc.) from the request.
method = request.method.decode('ascii')
2023-08-13 11:32:06 -04:00
# Based on the method, try to find the respective handler function.
handler = getattr(self, f"on_{method}", None)
2023-08-13 11:32:06 -04:00
# If a handler is found, process the request with it.
if handler:
def _respond(result):
request.write(result)
request.finish()
def _error(failure):
logger.error(f"Error processing abuse lookup request: {failure}")
request.setResponseCode(500)
request.write(json.dumps({"error": "Internal Server Error"}).encode("utf-8"))
request.finish()
d = handler(request)
d.addCallbacks(_respond, _error)
2023-08-13 11:32:06 -04:00
return NOT_DONE_YET # indicates asynchronous processing
else:
2023-08-13 13:05:16 -04:00
logger.warning(f"Received a request with unsupported method: {method}")
2023-08-13 11:32:06 -04:00
# If no handler is found for the method, return "Method Not Allowed".
return self.method_not_allowed(request)
2023-08-13 11:32:06 -04:00
# Handle PUT requests to the endpoint.
@inlineCallbacks
def on_PUT(self, request):
2023-08-13 13:05:16 -04:00
logger.info(f"Processing PUT request from {request.getClientIP()}.")
try:
2023-08-13 11:32:06 -04:00
# Read and decode the request body.
body = yield request.content.read()
content = body.decode("utf-8")
logger.info(f"Received abuse lookup request: {content}")
2023-08-13 11:32:06 -04:00
# 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"]
2023-08-13 11:32:06 -04:00
# 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"
2023-08-13 11:32:06 -04:00
# Respond based on whether the request is identified as abusive or not.
if is_abuse:
2023-08-13 13:05:16 -04:00
logger.warning(f"Abuse detected from {request.getClientIP()}, user_id_hash: {user_id_hash} room_id_hash: {room_id_hash}.")
request.setResponseCode(http.OK)
defer.returnValue(json.dumps({
"error": None,
"report_id": "b973d82a-6932-4cad-ac9f-f647a3a9d204",
}).encode("utf-8"))
else:
2023-08-13 13:05:16 -04:00
logger.info(f"No abuse detected for request from {request.getClientIP()}.")
request.setResponseCode(http.NO_CONTENT)
defer.returnValue(b"")
except Exception as e:
2023-08-13 13:05:16 -04:00
logger.error(f"Error processing abuse lookup PUT request from {request.getClientIP()}: {e}")
request.setResponseCode(400)
defer.returnValue(json.dumps({"error": "Bad Request"}).encode("utf-8"))
2023-08-13 11:32:06 -04:00
# Handle GET requests (by disallowing them).
def on_GET(self, request):
return self.method_not_allowed(request)
2023-08-13 11:32:06 -04:00
# Handle POST requests (by disallowing them).
def on_POST(self, request):
return self.method_not_allowed(request)
2023-08-13 11:32:06 -04:00
# General method to respond with "Method Not Allowed" for disallowed or unrecognized HTTP methods.
def method_not_allowed(self, request):
2023-08-13 13:05:16 -04:00
logger.warning(f"Method Not Allowed: {request.method.decode('ascii')} from {request.getClientIP()}.")
request.setResponseCode(405)
return json.dumps({"error": "Method Not Allowed"}).encode("utf-8")
2023-08-13 11:32:06 -04:00
# Function to parse the configuration for this module.
def parse_config(config: dict) -> dict:
return config
2023-08-13 11:32:06 -04:00
# Factory function to create and return an instance of the RedlightServerModule.
def create_module(api: ModuleApi, config: dict) -> RedlightServerModule:
return RedlightServerModule(config, api)