mirror of
https://github.com/pyMC-dev/pyMC_Repeater.git
synced 2026-08-07 17:33:16 +02:00
fix: add blank-password read-only guests to the room ACL
A blank-password login replied success (read-only guest) without ever creating an ACL entry or storing the ECDH shared secret. The client app believed it was logged in, but the room server's text handler and sync loop only see ACL members: the client's posts were dropped without a delivery ACK (send shows failed) and posts were never pushed to it. Rooms with only an admin password configured were fully affected since every guest login is blank-password. Add the guest to the ACL with guest permissions, the shared secret, and sync_since (mirroring the password path), reject when the ACL is full, and refresh activity timestamps on repeat blank logins. Refs #286
This commit is contained in:
@@ -106,13 +106,27 @@ class ACL:
|
||||
if not password:
|
||||
client = self.clients.get(pub_key)
|
||||
if client is None:
|
||||
if self.allow_read_only:
|
||||
logger.info("Blank password, allowing read-only guest access")
|
||||
return True, PERM_ACL_GUEST
|
||||
else:
|
||||
if not self.allow_read_only:
|
||||
logger.info("Blank password, sender not in ACL and read-only disabled")
|
||||
return False, 0
|
||||
logger.info(f"ACL-based login for {pub_key[:6].hex()}...")
|
||||
if len(self.clients) >= self.max_clients:
|
||||
logger.warning("ACL full, cannot add read-only guest")
|
||||
return False, 0
|
||||
# Read-only guests must still land in the ACL: the room server's
|
||||
# text handler and sync loop only see ACL members, so an absent
|
||||
# entry means the client's posts are silently dropped (never
|
||||
# decrypted or ACKed) and posts are never pushed to it.
|
||||
client = ClientInfo(client_identity, PERM_ACL_GUEST)
|
||||
self.clients[pub_key] = client
|
||||
logger.info(f"Blank password, added read-only guest {pub_key[:6].hex()}...")
|
||||
else:
|
||||
logger.info(f"ACL-based login for {pub_key[:6].hex()}...")
|
||||
client.last_activity = int(time.time())
|
||||
client.last_login_success = int(time.time())
|
||||
client.shared_secret = shared_secret
|
||||
if sync_since is not None:
|
||||
client.sync_since = sync_since
|
||||
logger.debug(f"Stored sync_since={sync_since} for client")
|
||||
return True, client.permissions
|
||||
|
||||
permissions = 0
|
||||
|
||||
@@ -37,10 +37,42 @@ def test_acl_blank_password_guest_rules_and_room_server_password_requirements():
|
||||
shared_secret=b"secret",
|
||||
password="",
|
||||
timestamp=10,
|
||||
sync_since=42,
|
||||
)
|
||||
assert ok is True
|
||||
assert perms == PERM_ACL_GUEST
|
||||
|
||||
# The read-only guest must land in the ACL with its shared secret: the
|
||||
# room server's text handler and sync loop only see ACL members, so an
|
||||
# absent entry means posts are dropped un-ACKed and pushes never happen.
|
||||
guest = acl.get_client(b"A" * 32)
|
||||
assert guest is not None
|
||||
assert guest.is_guest() is True
|
||||
assert guest.shared_secret == b"secret"
|
||||
assert guest.sync_since == 42
|
||||
|
||||
# A repeat blank-password login reuses the existing entry.
|
||||
ok_again, perms_again = acl.authenticate_client(
|
||||
client_identity=identity,
|
||||
shared_secret=b"secret",
|
||||
password="",
|
||||
timestamp=11,
|
||||
)
|
||||
assert ok_again is True
|
||||
assert perms_again == PERM_ACL_GUEST
|
||||
assert acl.get_num_clients() == 1
|
||||
|
||||
# A full ACL rejects new blank-password guests.
|
||||
acl_full = ACL(max_clients=0, allow_read_only=True)
|
||||
full_ok, full_perms = acl_full.authenticate_client(
|
||||
client_identity=identity,
|
||||
shared_secret=b"secret",
|
||||
password="",
|
||||
timestamp=10,
|
||||
)
|
||||
assert full_ok is False
|
||||
assert full_perms == 0
|
||||
|
||||
acl_ro_disabled = ACL(allow_read_only=False)
|
||||
ok2, perms2 = acl_ro_disabled.authenticate_client(
|
||||
client_identity=identity,
|
||||
|
||||
Reference in New Issue
Block a user