ForcePilot/backend/package/yuxi/channel/extensions/googlechat/security.py
Kris c4e301a5e0 feat(googlechat): 新增Google Chat渠道完整实现
新增Google Chat插件的所有核心功能模块,包括账户配置、消息收发、权限控制、媒体处理、会话管理等完整能力
2026-05-21 10:49:33 +08:00

87 lines
3.0 KiB
Python

from __future__ import annotations
import logging
import re
logger = logging.getLogger(__name__)
_EMAIL_PATTERN = re.compile(r".+@.+\..+")
class GoogleChatSecurityAdapter:
def __init__(self):
self._dm_policy: str = "pairing"
def resolve_dm_policy(self) -> dict:
return {"mode": self._dm_policy, "allow_from": []}
def check_allow_entry(self, entry: str) -> bool:
if not entry:
return False
if entry.startswith("googlechat:"):
entry = entry[len("googlechat:"):]
if entry.startswith("users/") and _EMAIL_PATTERN.match(entry[6:]):
return False
return bool(entry)
def normalize_allow_entry(self, entry: str) -> str:
if not entry:
return entry
for prefix in ("googlechat:", "google-chat:", "gchat:"):
if entry.lower().startswith(prefix):
entry = entry[len(prefix):]
break
if entry.startswith("user:users/"):
entry = entry[5:]
elif entry.startswith("space:spaces/"):
entry = entry[6:]
return entry.lower()
def collect_warnings(self, config: dict, account_id: str | None = None) -> list[str]:
warnings = []
gc_config = config.get("channels", {}).get("googlechat", {})
group_policy = gc_config.get("groupPolicy", "allowlist")
if group_policy == "open":
warnings.append(
"Group policy is 'open' - any Google Chat Space can trigger the bot. Consider using 'allowlist' for production."
)
dm_config = gc_config.get("dm", {})
dm_policy = dm_config.get("policy", "pairing")
if dm_policy == "open":
warnings.append(
"DM policy is 'open' - anyone can DM the bot. Consider using 'pairing' or 'allowlist' for production."
)
allow_from = dm_config.get("allowFrom", [])
for entry in allow_from:
normalized = self.normalize_allow_entry(entry)
if normalized.startswith("users/") and "@" in normalized:
warnings.append(
f"DM allowFrom entry '{entry}' uses email format. Email addresses are mutable; use users/<numeric_id> instead."
)
groups = gc_config.get("groups", {})
for group_key in groups:
if not group_key.startswith("spaces/") and group_key != "*":
warnings.append(
f"Group key '{group_key}' is not a stable spaces/<id> name. This may break routing."
)
return warnings
def check_dm_allowlist(
self, sender_name: str, allow_from: list[str], dangerously_allow_name_matching: bool = False
) -> bool:
if "*" in allow_from:
return True
for entry in allow_from:
if "@" in entry and not dangerously_allow_name_matching:
continue
normalized = self.normalize_allow_entry(entry)
if normalized == self.normalize_allow_entry(sender_name):
return True
return False