Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
"""WhatsApp DM/group allowlist must resolve phone↔LID aliases at intake.
|
||||
|
||||
Regression for #14486: WhatsApp now delivers inbound DM senders in LID form
|
||||
(``<id>@lid``) while operators configure the allowlist with phone numbers.
|
||||
The adapter-level gate (``_is_dm_allowed`` / ``_is_group_allowed`` →
|
||||
``_should_process_message``) did a raw set-membership check with no LID
|
||||
resolution, so every DM from an allowed user was silently dropped before the
|
||||
gateway authz layer ever ran.
|
||||
|
||||
The fix routes the adapter gate through the shared
|
||||
``gateway.whatsapp_identity.expand_whatsapp_aliases`` helper, which reads the
|
||||
bridge's ``lid-mapping-*.json`` session files (the same source the gateway
|
||||
authz and session-key paths already use).
|
||||
"""
|
||||
|
||||
import json
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from gateway.config import Platform, PlatformConfig
|
||||
from hermes_constants import get_hermes_home
|
||||
|
||||
|
||||
PHONE = "351912345678"
|
||||
LID = "77214955630717"
|
||||
|
||||
|
||||
def _make_adapter(dm_policy=None, allow_from=None, group_policy=None, group_allow_from=None):
|
||||
from plugins.platforms.whatsapp.adapter import WhatsAppAdapter
|
||||
|
||||
extra = {}
|
||||
if dm_policy is not None:
|
||||
extra["dm_policy"] = dm_policy
|
||||
if allow_from is not None:
|
||||
extra["allow_from"] = allow_from
|
||||
if group_policy is not None:
|
||||
extra["group_policy"] = group_policy
|
||||
if group_allow_from is not None:
|
||||
extra["group_allow_from"] = group_allow_from
|
||||
|
||||
adapter = object.__new__(WhatsAppAdapter)
|
||||
adapter.platform = Platform.WHATSAPP
|
||||
adapter.config = PlatformConfig(enabled=True, extra=extra)
|
||||
adapter._message_handler = AsyncMock()
|
||||
adapter._dm_policy = str(extra.get("dm_policy", "open")).strip().lower()
|
||||
adapter._allow_from = WhatsAppAdapter._coerce_allow_list(extra.get("allow_from"))
|
||||
adapter._group_policy = str(extra.get("group_policy", "open")).strip().lower()
|
||||
adapter._group_allow_from = WhatsAppAdapter._coerce_allow_list(
|
||||
extra.get("group_allow_from")
|
||||
)
|
||||
return adapter
|
||||
|
||||
|
||||
def _write_lid_mapping(phone=PHONE, lid=LID):
|
||||
"""Mirror what the JS bridge writes: phone→lid and lid→phone (reverse)."""
|
||||
session_dir = get_hermes_home() / "whatsapp" / "session"
|
||||
session_dir.mkdir(parents=True, exist_ok=True)
|
||||
(session_dir / f"lid-mapping-{phone}.json").write_text(json.dumps(lid), encoding="utf-8")
|
||||
(session_dir / f"lid-mapping-{lid}_reverse.json").write_text(
|
||||
json.dumps(phone), encoding="utf-8"
|
||||
)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------- DM gate
|
||||
|
||||
def test_dm_phone_allowlist_matches_lid_sender():
|
||||
"""allow_from has the phone number; inbound sender arrives as @lid (the bug)."""
|
||||
_write_lid_mapping()
|
||||
adapter = _make_adapter(dm_policy="allowlist", allow_from=[PHONE])
|
||||
|
||||
assert adapter._is_dm_allowed(f"{LID}@lid") is True
|
||||
|
||||
|
||||
def test_dm_phone_with_plus_allowlist_matches_lid_sender():
|
||||
"""A ``+``-prefixed phone allowlist entry still resolves to the LID sender."""
|
||||
_write_lid_mapping()
|
||||
adapter = _make_adapter(dm_policy="allowlist", allow_from=[f"+{PHONE}"])
|
||||
|
||||
assert adapter._is_dm_allowed(f"{LID}@lid") is True
|
||||
|
||||
|
||||
# ------------------------------------------------------------------ group gate
|
||||
|
||||
def test_group_jid_exact_match_still_works():
|
||||
"""Group allowlists use full ``@g.us`` JIDs — exact match must pass through."""
|
||||
adapter = _make_adapter(
|
||||
group_policy="allowlist", group_allow_from=["120363001234567890@g.us"]
|
||||
)
|
||||
|
||||
assert adapter._is_group_allowed("120363001234567890@g.us") is True
|
||||
|
||||
|
||||
def test_group_unlisted_jid_blocked():
|
||||
adapter = _make_adapter(
|
||||
group_policy="allowlist", group_allow_from=["120363001234567890@g.us"]
|
||||
)
|
||||
|
||||
assert adapter._is_group_allowed("120363009999999999@g.us") is False
|
||||
|
||||
|
||||
# ------------------------------------------------------ end-to-end intake gate
|
||||
|
||||
def test_should_process_message_dm_phone_allowlist_lid_sender():
|
||||
"""Full intake path: a DM from a phone-allowlisted contact arriving as @lid."""
|
||||
_write_lid_mapping()
|
||||
adapter = _make_adapter(dm_policy="allowlist", allow_from=[PHONE])
|
||||
|
||||
data = {
|
||||
"isGroup": False,
|
||||
"body": "hello",
|
||||
"senderId": f"{LID}@lid",
|
||||
"from": f"{LID}@lid",
|
||||
"botIds": [],
|
||||
"mentionedIds": [],
|
||||
}
|
||||
assert adapter._should_process_message(data) is True
|
||||
Reference in New Issue
Block a user