add commentary to modules
This commit is contained in:
parent
028dc2d7b5
commit
256c693f76
@ -12,20 +12,20 @@ from twisted.internet import defer
|
|||||||
from twisted.web.iweb import IBodyProducer
|
from twisted.web.iweb import IBodyProducer
|
||||||
from zope.interface import implementer
|
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 = logging.FileHandler('/var/log/matrix-synapse/redlight.log')
|
||||||
file_handler.setLevel(logging.INFO)
|
file_handler.setLevel(logging.INFO)
|
||||||
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||||
file_handler.setFormatter(formatter)
|
file_handler.setFormatter(formatter)
|
||||||
|
|
||||||
# Get your logger
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
logger.setLevel(logging.INFO)
|
logger.setLevel(logging.INFO)
|
||||||
logger.addHandler(file_handler)
|
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
|
logger.propagate = False
|
||||||
|
|
||||||
|
# Define a custom producer to convert our JSON data for HTTP requests.
|
||||||
@implementer(IBodyProducer)
|
@implementer(IBodyProducer)
|
||||||
class _JsonProducer:
|
class _JsonProducer:
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
@ -46,17 +46,20 @@ class _JsonProducer:
|
|||||||
class RedlightClientModule:
|
class RedlightClientModule:
|
||||||
def __init__(self, config: dict, api: ModuleApi):
|
def __init__(self, config: dict, api: ModuleApi):
|
||||||
self._api = api
|
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._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.")
|
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(
|
api.register_spam_checker_callbacks(
|
||||||
user_may_join_room=self.user_may_join_room
|
user_may_join_room=self.user_may_join_room
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def double_hash_sha256(data: str) -> str:
|
def double_hash_sha256(data: str) -> str:
|
||||||
|
"""Double-hash the data with SHA256 for added security."""
|
||||||
first_hash = hashlib.sha256(data.encode()).digest()
|
first_hash = hashlib.sha256(data.encode()).digest()
|
||||||
double_hashed = hashlib.sha256(first_hash).hexdigest()
|
double_hashed = hashlib.sha256(first_hash).hexdigest()
|
||||||
return double_hashed
|
return double_hashed
|
||||||
@ -67,14 +70,17 @@ class RedlightClientModule:
|
|||||||
|
|
||||||
logger.info(f"User {user} is attempting to join room {room}. Invitation status: {is_invited}.")
|
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_room_id = self.double_hash_sha256(room)
|
||||||
hashed_user_id = self.double_hash_sha256(user)
|
hashed_user_id = self.double_hash_sha256(user)
|
||||||
|
|
||||||
|
# Prepare the HTTP body.
|
||||||
body = _JsonProducer({
|
body = _JsonProducer({
|
||||||
"room_id_hash": hashed_room_id,
|
"room_id_hash": hashed_room_id,
|
||||||
"user_id_hash": hashed_user_id
|
"user_id_hash": hashed_user_id
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# Make the HTTP request to our redlight server.
|
||||||
response = await self._agent.request(
|
response = await self._agent.request(
|
||||||
b"PUT",
|
b"PUT",
|
||||||
self._redlight_url.encode(),
|
self._redlight_url.encode(),
|
||||||
@ -82,25 +88,30 @@ class RedlightClientModule:
|
|||||||
body
|
body
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Extract the response body.
|
||||||
response_body_bytes = await readBody(response)
|
response_body_bytes = await readBody(response)
|
||||||
response_body = response_body_bytes.decode("utf-8")
|
response_body = response_body_bytes.decode("utf-8")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# Try to parse the response body as JSON.
|
||||||
response_json = json.loads(response_body)
|
response_json = json.loads(response_body)
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
logger.error(f"Failed to decode response body: {response_body}")
|
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:
|
if response.code == 200:
|
||||||
raise AuthError(403, "User not allowed to join this room")
|
raise AuthError(403, "User not allowed to join this room")
|
||||||
elif response.code == 204:
|
elif response.code == 204:
|
||||||
return NOT_SPAM
|
return NOT_SPAM # Allow the user to join.
|
||||||
else:
|
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}")
|
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:
|
def parse_config(config: dict) -> dict:
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
# Factory function to create an instance of the RedlightClientModule.
|
||||||
def create_module(api: ModuleApi, config: dict) -> RedlightClientModule:
|
def create_module(api: ModuleApi, config: dict) -> RedlightClientModule:
|
||||||
return RedlightClientModule(config, api)
|
return RedlightClientModule(config, api)
|
||||||
|
@ -7,25 +7,28 @@ from twisted.internet.defer import inlineCallbacks
|
|||||||
from twisted.web.server import NOT_DONE_YET
|
from twisted.web.server import NOT_DONE_YET
|
||||||
from twisted.web.http import OK, NO_CONTENT
|
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 = logging.FileHandler('/var/log/matrix-synapse/redlight.log')
|
||||||
file_handler.setLevel(logging.INFO)
|
file_handler.setLevel(logging.INFO)
|
||||||
|
# 2. Define the format for the logs.
|
||||||
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||||
file_handler.setFormatter(formatter)
|
file_handler.setFormatter(formatter)
|
||||||
|
|
||||||
# Get your logger
|
# 3. Initialize the logger for this module and set its level.
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
logger.setLevel(logging.INFO)
|
logger.setLevel(logging.INFO)
|
||||||
|
# 4. Attach the file handler to the logger.
|
||||||
logger.addHandler(file_handler)
|
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
|
logger.propagate = False
|
||||||
|
|
||||||
class RedlightServerModule:
|
class RedlightServerModule:
|
||||||
def __init__(self, config: dict, api: ModuleApi):
|
def __init__(self, config: dict, api: ModuleApi):
|
||||||
self._api = api
|
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(
|
api.register_web_resource(
|
||||||
"/_matrix/loj/v1/abuse_lookup",
|
"/_matrix/loj/v1/abuse_lookup",
|
||||||
RedlightServerResource(self)
|
RedlightServerResource(self)
|
||||||
@ -34,16 +37,20 @@ class RedlightServerModule:
|
|||||||
logger.info("RedlightServerModule initialized.")
|
logger.info("RedlightServerModule initialized.")
|
||||||
|
|
||||||
class RedlightServerResource:
|
class RedlightServerResource:
|
||||||
|
# This flag helps Twisted identify this as a final resource and not look for children.
|
||||||
isLeaf = True
|
isLeaf = True
|
||||||
|
|
||||||
def __init__(self, module):
|
def __init__(self, module):
|
||||||
self._module = module
|
self._module = module
|
||||||
|
|
||||||
|
# Handle incoming HTTP requests to the registered endpoint.
|
||||||
def render(self, request):
|
def render(self, request):
|
||||||
|
# Extract HTTP method (GET, PUT, POST, etc.) from the request.
|
||||||
method = request.method.decode('ascii')
|
method = request.method.decode('ascii')
|
||||||
|
# Based on the method, try to find the respective handler function.
|
||||||
handler = getattr(self, f"on_{method}", None)
|
handler = getattr(self, f"on_{method}", None)
|
||||||
|
|
||||||
|
# If a handler is found, process the request with it.
|
||||||
if handler:
|
if handler:
|
||||||
def _respond(result):
|
def _respond(result):
|
||||||
request.write(result)
|
request.write(result)
|
||||||
@ -57,28 +64,30 @@ class RedlightServerResource:
|
|||||||
|
|
||||||
d = handler(request)
|
d = handler(request)
|
||||||
d.addCallbacks(_respond, _error)
|
d.addCallbacks(_respond, _error)
|
||||||
return NOT_DONE_YET
|
return NOT_DONE_YET # indicates asynchronous processing
|
||||||
else:
|
else:
|
||||||
|
# If no handler is found for the method, return "Method Not Allowed".
|
||||||
return self.method_not_allowed(request)
|
return self.method_not_allowed(request)
|
||||||
|
|
||||||
|
# Handle PUT requests to the endpoint.
|
||||||
@inlineCallbacks
|
@inlineCallbacks
|
||||||
def on_PUT(self, request):
|
def on_PUT(self, request):
|
||||||
try:
|
try:
|
||||||
# Extract body from the request
|
# Read and decode the request body.
|
||||||
body = yield request.content.read()
|
body = yield request.content.read()
|
||||||
content = body.decode("utf-8")
|
content = body.decode("utf-8")
|
||||||
|
|
||||||
# Log the request to Synapse's log
|
|
||||||
logger.info(f"Received abuse lookup request: {content}")
|
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)
|
data = json.loads(content)
|
||||||
room_id_hash = data["room_id_hash"]
|
room_id_hash = data["room_id_hash"]
|
||||||
user_id_hash = data["user_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"
|
is_abuse = room_id_hash == "ee180279a57f716e5801335a2914e228667f363e460ccabcc49e8fd879e1be4a"
|
||||||
|
|
||||||
|
# Respond based on whether the request is identified as abusive or not.
|
||||||
if is_abuse:
|
if is_abuse:
|
||||||
request.setResponseCode(http.OK)
|
request.setResponseCode(http.OK)
|
||||||
defer.returnValue(json.dumps({
|
defer.returnValue(json.dumps({
|
||||||
@ -94,20 +103,23 @@ class RedlightServerResource:
|
|||||||
request.setResponseCode(400)
|
request.setResponseCode(400)
|
||||||
defer.returnValue(json.dumps({"error": "Bad Request"}).encode("utf-8"))
|
defer.returnValue(json.dumps({"error": "Bad Request"}).encode("utf-8"))
|
||||||
|
|
||||||
|
# Handle GET requests (by disallowing them).
|
||||||
def on_GET(self, request):
|
def on_GET(self, request):
|
||||||
return self.method_not_allowed(request)
|
return self.method_not_allowed(request)
|
||||||
|
|
||||||
|
# Handle POST requests (by disallowing them).
|
||||||
def on_POST(self, request):
|
def on_POST(self, request):
|
||||||
return self.method_not_allowed(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):
|
def method_not_allowed(self, request):
|
||||||
request.setResponseCode(405)
|
request.setResponseCode(405)
|
||||||
return json.dumps({"error": "Method Not Allowed"}).encode("utf-8")
|
return json.dumps({"error": "Method Not Allowed"}).encode("utf-8")
|
||||||
|
|
||||||
|
# Function to parse the configuration for this module.
|
||||||
def parse_config(config: dict) -> dict:
|
def parse_config(config: dict) -> dict:
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
# Factory function to create and return an instance of the RedlightServerModule.
|
||||||
def create_module(api: ModuleApi, config: dict) -> RedlightServerModule:
|
def create_module(api: ModuleApi, config: dict) -> RedlightServerModule:
|
||||||
return RedlightServerModule(config, api)
|
return RedlightServerModule(config, api)
|
||||||
|
Loading…
Reference in New Issue
Block a user