fix(gateway): trust adapter-owned access policy over env default-deny (#34515)

Config-driven platform policies (dm_policy / group_policy / allow_from /
group_allow_from) for WeCom, Weixin, Yuanbao, and QQBot now work without
also setting a PLATFORM_ALLOWED_USERS env var.

These adapters enforce their access policy at intake — a message is dropped
inside the adapter and never dispatched unless it already passed the policy.
The gateway's env-based check (_is_user_authorized) ran afterward and, with
no env allowlist set, fell through to an env-only default-deny — silently
rejecting `dm_policy: open` and config-only allowlists the adapter had
already authorized.

Rather than re-implement each adapter's policy a second time in run.py
(which would drift), adapters that own their gate now declare it via a new
BasePlatformAdapter.enforces_own_access_policy property (default False). The
gateway trusts that flag and skips the env-only default-deny for those
platforms. Env allowlists still take precedence when set.

Also resolves unauthorized DM behavior from config dm_policy so allowlist /
disabled policies drop unauthorized DMs silently instead of leaking pairing
codes, while an explicit pairing policy opts back in.

Co-authored-by: Frowtek <frowte3k@gmail.com>
This commit is contained in:
teknium1
2026-05-29 04:22:41 -07:00
committed by Teknium
co-authored by Frowtek
parent ddaf2f6712
commit fd09b2c55e
8 changed files with 320 additions and 0 deletions
+23
View File
@@ -1655,6 +1655,29 @@ class BasePlatformAdapter(ABC):
"""
return len
@property
def enforces_own_access_policy(self) -> bool:
"""Whether this adapter gates inbound access before dispatch.
Some adapters (WeCom, Weixin, Yuanbao, QQBot) implement a documented
config-driven access surface — ``dm_policy`` / ``group_policy`` /
``allow_from`` / ``group_allow_from`` in ``PlatformConfig.extra`` — and
enforce it at intake: a message is dropped inside the adapter and never
reaches the gateway unless it already passed that policy.
The gateway's env-based allowlist check runs *after* the adapter, so for
these platforms a message arriving at ``_is_user_authorized`` has, by
definition, already been authorized by the adapter. Without this flag the
gateway would then deny it again (no env allowlist → default deny),
silently breaking ``dm_policy: open`` and config-only allowlists.
Adapters that own their access policy override this to return ``True``.
The gateway treats that as "already authorized at intake" and skips the
env-allowlist default-deny. Adapters that delegate access control to the
gateway leave it ``False`` (the default).
"""
return False
def supports_draft_streaming(
self,
chat_type: Optional[str] = None,