Changes grok suggested.
This commit is contained in:
@@ -38,61 +38,47 @@ class HttpUser(Persistent):
|
|||||||
self._enabled = bool(value)
|
self._enabled = bool(value)
|
||||||
self._p_changed = True
|
self._p_changed = True
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
#
|
||||||
# rf_enabled property – tied directly to the main server's blacklist
|
# rf enabled checks..
|
||||||
# ------------------------------------------------------------------
|
#
|
||||||
@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.setter
|
def is_rf_enabled(self, connection) -> bool:
|
||||||
def rf_enabled(self, allow: 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.
|
Only allows enabling if the username is a valid AX.25 callsign.
|
||||||
"""
|
"""
|
||||||
import transaction
|
from packetserver.utils import is_valid_ax25_callsign # our validator
|
||||||
from packetserver.common.util import is_valid_ax25_callsign # assuming you have this helper
|
|
||||||
|
|
||||||
root = transaction.get().db().root()
|
root = connection.root()
|
||||||
config = root.setdefault('config', PersistentMapping())
|
config = root.setdefault('config', PersistentMapping())
|
||||||
blacklist = config.setdefault('blacklist', PersistentList())
|
blacklist = config.setdefault('blacklist', PersistentList())
|
||||||
|
|
||||||
upper_name = self.username
|
upper_name = self.username
|
||||||
|
|
||||||
if allow:
|
if allow:
|
||||||
# Trying to enable RF access
|
|
||||||
if not is_valid_ax25_callsign(upper_name):
|
if not is_valid_ax25_callsign(upper_name):
|
||||||
raise ValueError(f"{upper_name} is not a valid AX.25 callsign – cannot enable RF access")
|
raise ValueError(f"{upper_name} is not a valid AX.25 callsign – cannot enable RF access")
|
||||||
|
|
||||||
if upper_name in blacklist:
|
if upper_name in blacklist:
|
||||||
blacklist.remove(upper_name)
|
blacklist.remove(upper_name)
|
||||||
config['blacklist'] = blacklist
|
blacklist._p_changed = True
|
||||||
self._p_changed = True
|
|
||||||
else:
|
else:
|
||||||
# Disable RF access
|
|
||||||
if upper_name not in blacklist:
|
if upper_name not in blacklist:
|
||||||
blacklist.append(upper_name)
|
blacklist.append(upper_name)
|
||||||
config['blacklist'] = blacklist
|
blacklist._p_changed = True
|
||||||
self._p_changed = True
|
|
||||||
|
|
||||||
# Ensure changes are marked
|
|
||||||
root._p_changed = True
|
|
||||||
config._p_changed = True
|
config._p_changed = True
|
||||||
|
root._p_changed = True
|
||||||
|
# Caller should commit the transaction
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# Password handling (unchanged)
|
# Password handling (unchanged)
|
||||||
|
|||||||
@@ -19,11 +19,12 @@ async def profile(current_user: HttpUser = Depends(get_current_http_user)):
|
|||||||
main_users = root.get('users', {})
|
main_users = root.get('users', {})
|
||||||
bbs_user = main_users.get(username)
|
bbs_user = main_users.get(username)
|
||||||
safe_profile = bbs_user.to_safe_dict() if bbs_user else {}
|
safe_profile = bbs_user.to_safe_dict() if bbs_user else {}
|
||||||
|
rf_enabled = current_user.is_rf_enabled(conn)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
**safe_profile,
|
**safe_profile,
|
||||||
"http_enabled": current_user.enabled,
|
"http_enabled": current_user.enabled,
|
||||||
"rf_enabled": current_user.rf_enabled,
|
"rf_enabled": rf_enabled,
|
||||||
"http_created_at": current_user.created_at,
|
"http_created_at": current_user.created_at,
|
||||||
"http_last_login": current_user.last_login,
|
"http_last_login": current_user.last_login,
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
from fastapi import APIRouter, Depends, HTTPException, status
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
from pydantic import BaseModel, Field, validator
|
from pydantic import BaseModel, Field, validator
|
||||||
from typing import List
|
from typing import List
|
||||||
|
from persistent.list import PersistentList
|
||||||
|
from persistent.mapping import PersistentMapping
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
@@ -32,7 +34,9 @@ async def send_message(
|
|||||||
payload: SendMessageRequest,
|
payload: SendMessageRequest,
|
||||||
current_user: HttpUser = Depends(get_current_http_user)
|
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(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_403_FORBIDDEN,
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
detail="RF gateway access required to send messages"
|
detail="RF gateway access required to send messages"
|
||||||
@@ -54,8 +58,6 @@ async def send_message(
|
|||||||
msg_from=username,
|
msg_from=username,
|
||||||
msg_to=to_tuple,
|
msg_to=to_tuple,
|
||||||
text=payload.text,
|
text=payload.text,
|
||||||
subject=payload.subject, # if Message supports it; otherwise drop
|
|
||||||
sent_at=datetime.utcnow(),
|
|
||||||
attachments=() # empty for now
|
attachments=() # empty for now
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ def main():
|
|||||||
print(f"Error: User {callsign} not found")
|
print(f"Error: User {callsign} not found")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
try:
|
try:
|
||||||
user.rf_enabled = True
|
user.set_rf_enabled(connection, True)
|
||||||
transaction.commit()
|
transaction.commit()
|
||||||
print(f"RF gateway enabled for {callsign}")
|
print(f"RF gateway enabled for {callsign}")
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
@@ -206,7 +206,7 @@ def main():
|
|||||||
if not user:
|
if not user:
|
||||||
print(f"Error: User {callsign} not found")
|
print(f"Error: User {callsign} not found")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
user.rf_enabled = False
|
user.set_rf_enabled(connection, False)
|
||||||
transaction.commit()
|
transaction.commit()
|
||||||
print(f"RF gateway disabled for {callsign}")
|
print(f"RF gateway disabled for {callsign}")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user