From b671e49e69c1aba629ed9c8ee58488d56cdce7c9 Mon Sep 17 00:00:00 2001 From: Michael Woods Date: Sun, 21 Dec 2025 21:10:11 -0500 Subject: [PATCH] Changes grok suggested. --- packetserver/http/auth.py | 56 +++++++++-------------- packetserver/http/routers/profile.py | 3 +- packetserver/http/routers/send.py | 8 ++-- packetserver/runners/http_user_manager.py | 4 +- 4 files changed, 30 insertions(+), 41 deletions(-) diff --git a/packetserver/http/auth.py b/packetserver/http/auth.py index 30c99f1..59356b2 100644 --- a/packetserver/http/auth.py +++ b/packetserver/http/auth.py @@ -38,61 +38,47 @@ class HttpUser(Persistent): self._enabled = bool(value) self._p_changed = True - # ------------------------------------------------------------------ - # rf_enabled property – tied directly to the main server's blacklist - # ------------------------------------------------------------------ - @property - def rf_enabled(self) -> bool: - """ - True if the callsign is NOT in the global blacklist. - This allows HTTP users to act as RF gateways only if explicitly allowed. - """ - from ZODB import DB # deferred import to avoid circular issues - # We'll get the db from the transaction in most contexts - # But for safety, we'll reach into the current connection's root - import transaction - try: - root = transaction.get().db().root() - blacklist = root.get('config', {}).get('blacklist', []) - return self.username not in blacklist - except Exception: - # If we're outside a transaction (e.g. during tests), default safe - return False + # + # rf enabled checks.. + # - @rf_enabled.setter - def rf_enabled(self, allow: bool): + def is_rf_enabled(self, connection) -> bool: """ - Enable/disable RF gateway capability by adding/removing from the global blacklist. + Check if RF gateway is enabled (i.e., callsign NOT in global blacklist). + Requires an open ZODB connection. + """ + root = connection.root() + blacklist = root.get('config', {}).get('blacklist', []) + return self.username not in blacklist + + def set_rf_enabled(self, connection, allow: bool): + """ + Enable/disable RF gateway by adding/removing from global blacklist. + Requires an open ZODB connection (inside a transaction). Only allows enabling if the username is a valid AX.25 callsign. """ - import transaction - from packetserver.common.util import is_valid_ax25_callsign # assuming you have this helper + from packetserver.utils import is_valid_ax25_callsign # our validator - root = transaction.get().db().root() + root = connection.root() config = root.setdefault('config', PersistentMapping()) blacklist = config.setdefault('blacklist', PersistentList()) upper_name = self.username if allow: - # Trying to enable RF access if not is_valid_ax25_callsign(upper_name): raise ValueError(f"{upper_name} is not a valid AX.25 callsign – cannot enable RF access") - if upper_name in blacklist: blacklist.remove(upper_name) - config['blacklist'] = blacklist - self._p_changed = True + blacklist._p_changed = True else: - # Disable RF access if upper_name not in blacklist: blacklist.append(upper_name) - config['blacklist'] = blacklist - self._p_changed = True + blacklist._p_changed = True - # Ensure changes are marked - root._p_changed = True config._p_changed = True + root._p_changed = True + # Caller should commit the transaction # ------------------------------------------------------------------ # Password handling (unchanged) diff --git a/packetserver/http/routers/profile.py b/packetserver/http/routers/profile.py index 654edea..f9543de 100644 --- a/packetserver/http/routers/profile.py +++ b/packetserver/http/routers/profile.py @@ -19,11 +19,12 @@ async def profile(current_user: HttpUser = Depends(get_current_http_user)): main_users = root.get('users', {}) bbs_user = main_users.get(username) safe_profile = bbs_user.to_safe_dict() if bbs_user else {} + rf_enabled = current_user.is_rf_enabled(conn) return { **safe_profile, "http_enabled": current_user.enabled, - "rf_enabled": current_user.rf_enabled, + "rf_enabled": rf_enabled, "http_created_at": current_user.created_at, "http_last_login": current_user.last_login, } \ No newline at end of file diff --git a/packetserver/http/routers/send.py b/packetserver/http/routers/send.py index c2da188..c37a032 100644 --- a/packetserver/http/routers/send.py +++ b/packetserver/http/routers/send.py @@ -2,6 +2,8 @@ from fastapi import APIRouter, Depends, HTTPException, status from pydantic import BaseModel, Field, validator from typing import List +from persistent.list import PersistentList +from persistent.mapping import PersistentMapping from datetime import datetime import uuid @@ -32,7 +34,9 @@ async def send_message( payload: SendMessageRequest, current_user: HttpUser = Depends(get_current_http_user) ): - if not current_user.rf_enabled: + from packetserver.runners.http_server import get_db_connection + conn = get_db_connection() + if not current_user.is_rf_enabled(conn): raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="RF gateway access required to send messages" @@ -54,8 +58,6 @@ async def send_message( msg_from=username, msg_to=to_tuple, text=payload.text, - subject=payload.subject, # if Message supports it; otherwise drop - sent_at=datetime.utcnow(), attachments=() # empty for now ) diff --git a/packetserver/runners/http_user_manager.py b/packetserver/runners/http_user_manager.py index b84e50f..e679859 100644 --- a/packetserver/runners/http_user_manager.py +++ b/packetserver/runners/http_user_manager.py @@ -193,7 +193,7 @@ def main(): print(f"Error: User {callsign} not found") sys.exit(1) try: - user.rf_enabled = True + user.set_rf_enabled(connection, True) transaction.commit() print(f"RF gateway enabled for {callsign}") except ValueError as e: @@ -206,7 +206,7 @@ def main(): if not user: print(f"Error: User {callsign} not found") sys.exit(1) - user.rf_enabled = False + user.set_rf_enabled(connection, False) transaction.commit() print(f"RF gateway disabled for {callsign}")