fix(signal): consult pairing store in is_allowed

BaseChannel.is_allowed ORs is_approved (the pairing store) into the
allow decision; the signal override dropped that step and only looked
at config.allow_from. With the new DM-pairing flow in place, an
approved-via-pairing sender's next message would have failed the
allow check and triggered another pairing code in a loop.

OR in a normalized check against the pairing store: walk each part of
the pipe-joined sender_id through _normalize_signal_id and call
is_approved for each variant, so an approval stored under one form
(phone with/without "+", UUID/ACI) still matches when the next inbound
uses a different form. Mirrors how slack.py:643 handles it.

Also tightens the empty-allowlist warning to only fire when nothing
else granted access, since pairing-store hits are now a valid path.

Not part of the original review, but Comments 2 and 3 turn this latent
gap into a broken round-trip — included so the pairing UX actually
works.
This commit is contained in:
Kaloyan Tenchov
2026-05-21 01:00:36 +08:00
committed by Xubin Ren
parent 82dfe8c1f7
commit b3d0d24a52
2 changed files with 37 additions and 4 deletions
+16
View File
@@ -681,6 +681,22 @@ class TestHandleDataMessageDM:
assert handled[0]["is_dm"] is True
assert handled[0]["chat_id"] == "+19995550002"
@pytest.mark.asyncio
async def test_dm_paired_sender_allowed_without_allowlist_entry(self, monkeypatch):
# Once a sender completes pairing they should pass is_allowed on every
# subsequent message — otherwise the pairing reply loops forever.
approved = {"+19995550002"}
monkeypatch.setattr(
"nanobot.channels.signal.is_approved",
lambda channel, sender_id: sender_id in approved,
)
ch = _make_channel(dm_enabled=True, dm_policy="allowlist", dm_allow_from=[])
assert ch.is_allowed("+19995550002") is True
# Variant forms (with/without "+") must still match a stored approval.
assert ch.is_allowed("19995550002") is True
# Unpaired sender stays denied.
assert ch.is_allowed("+19995559999") is False
@pytest.mark.asyncio
async def test_dm_allowlist_matches_without_plus_prefix(self):
"""An allowlist entry without '+' must match a sender that carries '+'."""