Made some changes. Figured out the rf_enable thing.
This commit is contained in:
@@ -10,6 +10,14 @@ import string
|
|||||||
from persistent.mapping import PersistentMapping
|
from persistent.mapping import PersistentMapping
|
||||||
from persistent.list import PersistentList
|
from persistent.list import PersistentList
|
||||||
|
|
||||||
|
# Pre-compiled regex for performance
|
||||||
|
_AX25_CALLSIGN_REGEX = re.compile(
|
||||||
|
r'^[A-Z0-9]{1,6}(-[0-9]{1,2})?$'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Regex for base callsign only (no SSID)
|
||||||
|
_BASE_CALLSIGN_REGEX = re.compile(r'^[A-Z][A-Z0-9]{0,5}$')
|
||||||
|
|
||||||
def email_valid(email: str) -> bool:
|
def email_valid(email: str) -> bool:
|
||||||
"""Taken from https://www.geeksforgeeks.org/check-if-email-address-valid-or-not-in-python/"""
|
"""Taken from https://www.geeksforgeeks.org/check-if-email-address-valid-or-not-in-python/"""
|
||||||
regex = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,7}\b'
|
regex = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,7}\b'
|
||||||
@@ -172,4 +180,70 @@ def convert_from_persistent(data):
|
|||||||
elif isinstance(data, PersistentList):
|
elif isinstance(data, PersistentList):
|
||||||
return [convert_from_persistent(item) for item in data]
|
return [convert_from_persistent(item) for item in data]
|
||||||
else:
|
else:
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def is_valid_ax25_callsign(callsign: str) -> bool:
|
||||||
|
"""
|
||||||
|
Validate an AX.25 callsign (with optional SSID).
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
- Base: 1-6 uppercase alphanumeric, must start with letter (A-Z)
|
||||||
|
- Optional: single '-' followed by 1-2 digits (0-15)
|
||||||
|
- No spaces, lowercase, or other characters
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
W1AW → True
|
||||||
|
W1AW-1 → True
|
||||||
|
W1AW-15 → True
|
||||||
|
1A2BCD → False (starts with digit)
|
||||||
|
w1aw → False (lowercase)
|
||||||
|
W1AW- → False (no SSID digits)
|
||||||
|
W1AW--1 → False (double dash)
|
||||||
|
W1AW-16 → False (SSID >15)
|
||||||
|
"""
|
||||||
|
if not callsign:
|
||||||
|
return False
|
||||||
|
callsign = callsign.strip().upper()
|
||||||
|
if not _AX25_CALLSIGN_REGEX.match(callsign):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Extra check: base must start with letter (not digit)
|
||||||
|
base = callsign.split('-')[0]
|
||||||
|
if not base[0].isalpha():
|
||||||
|
return False
|
||||||
|
|
||||||
|
# If SSID present, ensure 0-15
|
||||||
|
if '-' in callsign:
|
||||||
|
ssid_str = callsign.split('-')[1]
|
||||||
|
if int(ssid_str) > 15:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def is_valid_base_ax25_callsign(base_call: str) -> bool:
|
||||||
|
"""
|
||||||
|
Validate an AX.25 **base** callsign (without SSID).
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
- 1–6 characters total
|
||||||
|
- Must start with an uppercase letter (A–Z)
|
||||||
|
- Remaining characters: uppercase letters A–Z or digits 0–9
|
||||||
|
- No hyphen, no SSID, no other characters
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
W1AW → True
|
||||||
|
M0XYZ → True
|
||||||
|
K9ABC → True (6 chars)
|
||||||
|
1ABC → False (starts with digit)
|
||||||
|
W1AW- → False (has hyphen)
|
||||||
|
W1AW-1 → False (has SSID)
|
||||||
|
w1aw → False (lowercase)
|
||||||
|
A → True (single letter)
|
||||||
|
ABC1234 → False (7 chars)
|
||||||
|
|
||||||
|
Use this when you specifically need the base portion (e.g., before adding SSID).
|
||||||
|
"""
|
||||||
|
if not base_call:
|
||||||
|
return False
|
||||||
|
base_call = base_call.strip().upper()
|
||||||
|
return bool(_BASE_CALLSIGN_REGEX.match(base_call))
|
||||||
@@ -3,6 +3,8 @@ from persistent import Persistent
|
|||||||
from argon2 import PasswordHasher
|
from argon2 import PasswordHasher
|
||||||
from argon2.exceptions import VerifyMismatchError
|
from argon2.exceptions import VerifyMismatchError
|
||||||
import time
|
import time
|
||||||
|
from persistent.mapping import PersistentMapping
|
||||||
|
from persistent.list import PersistentList
|
||||||
|
|
||||||
ph = PasswordHasher()
|
ph = PasswordHasher()
|
||||||
|
|
||||||
@@ -64,7 +66,7 @@ class HttpUser(Persistent):
|
|||||||
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
|
import transaction
|
||||||
from packetserver.utils import is_valid_ax25_callsign # assuming you have this helper
|
from packetserver.common.util import is_valid_ax25_callsign # assuming you have this helper
|
||||||
|
|
||||||
root = transaction.get().db().root()
|
root = transaction.get().db().root()
|
||||||
config = root.setdefault('config', PersistentMapping())
|
config = root.setdefault('config', PersistentMapping())
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import argparse
|
|||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from getpass import getpass
|
from getpass import getpass
|
||||||
|
import ax25
|
||||||
|
|
||||||
import ZODB.FileStorage
|
import ZODB.FileStorage
|
||||||
import ZODB.DB
|
import ZODB.DB
|
||||||
@@ -102,7 +103,14 @@ def main():
|
|||||||
upper_callsign = lambda c: c.upper()
|
upper_callsign = lambda c: c.upper()
|
||||||
|
|
||||||
if args.command == "add":
|
if args.command == "add":
|
||||||
|
from packetserver.common.util import is_valid_ax25_callsign
|
||||||
callsign = upper_callsign(args.callsign)
|
callsign = upper_callsign(args.callsign)
|
||||||
|
if is_valid_ax25_callsign(callsign):
|
||||||
|
base = ax25.Address(callsign).call.upper()
|
||||||
|
if base != callsign:
|
||||||
|
print(f"Error: Trying to add valid callsign + ssid. Remove -<num> and add again.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
if callsign in users_mapping:
|
if callsign in users_mapping:
|
||||||
print(f"Error: HTTP user {callsign} already exists")
|
print(f"Error: HTTP user {callsign} already exists")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@@ -121,7 +129,8 @@ def main():
|
|||||||
|
|
||||||
main_users = root.setdefault('users', PersistentMapping())
|
main_users = root.setdefault('users', PersistentMapping())
|
||||||
if callsign not in main_users:
|
if callsign not in main_users:
|
||||||
User.write_new(main_users, args.callsign)
|
new_user = User(callsign)
|
||||||
|
new_user.write_new(root)
|
||||||
print(f" → Also created regular BBS user {callsign} (with UUID)")
|
print(f" → Also created regular BBS user {callsign} (with UUID)")
|
||||||
else:
|
else:
|
||||||
print(f" → Regular BBS user {callsign} already exists")
|
print(f" → Regular BBS user {callsign} already exists")
|
||||||
|
|||||||
Reference in New Issue
Block a user