132 lines
5.0 KiB
Python
132 lines
5.0 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
from yuxi.channel.extensions.feishu.utils import parse_feishu_allow_entry
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class FeishuSecurityAdapter:
|
|
|
|
def __init__(self):
|
|
self._dm_allowlist: set[str] = set()
|
|
self._group_allowlist: set[str] = set()
|
|
|
|
def resolve_dm_policy(self) -> dict:
|
|
return {"mode": "pairing", "allow_from": []}
|
|
|
|
def resolve_dm_policy_for_account(self, account: dict) -> dict:
|
|
mode = account.get("dm_policy", "pairing")
|
|
return {"mode": mode, "allow_from": account.get("allow_from", [])}
|
|
|
|
def resolve_group_policy_for_account(self, account: dict) -> dict:
|
|
mode = account.get("group_policy", "allowlist")
|
|
return {"mode": mode, "allow_from": account.get("group_allow_from", [])}
|
|
|
|
async def check_allowlist(self, peer_id: str, channel_type: str) -> bool:
|
|
if channel_type == "direct":
|
|
return peer_id in self._dm_allowlist
|
|
return peer_id in self._group_allowlist
|
|
|
|
def load_allowlists(self, account: dict) -> None:
|
|
self._dm_allowlist.clear()
|
|
self._group_allowlist.clear()
|
|
|
|
for entry in account.get("allow_from", []):
|
|
entry_type, entry_id = parse_feishu_allow_entry(str(entry))
|
|
if entry_type == "user":
|
|
self._dm_allowlist.add(entry_id)
|
|
elif entry_type == "wildcard":
|
|
pass
|
|
|
|
for entry in account.get("group_allow_from", []):
|
|
entry_type, entry_id = parse_feishu_allow_entry(str(entry))
|
|
if entry_type == "chat":
|
|
self._group_allowlist.add(entry_id)
|
|
elif entry_type == "wildcard":
|
|
pass
|
|
|
|
def add_dm_allow(self, peer_id: str) -> None:
|
|
self._dm_allowlist.add(peer_id)
|
|
|
|
def add_group_allow(self, chat_id: str) -> None:
|
|
self._group_allowlist.add(chat_id)
|
|
|
|
def check_dm_access(self, account: dict, sender_id: str) -> tuple[bool, str]:
|
|
dm_policy = account.get("dm_policy", "pairing")
|
|
allow_from = account.get("allow_from", [])
|
|
|
|
if dm_policy == "disabled":
|
|
return False, "DM is disabled"
|
|
|
|
if dm_policy == "open":
|
|
if "*" in allow_from:
|
|
return True, "open"
|
|
return False, "open DM requires '*' in allowFrom"
|
|
|
|
if dm_policy == "allowlist":
|
|
if sender_id in allow_from or any(
|
|
entry.strip() == sender_id for entry in allow_from
|
|
):
|
|
return True, "allowlist"
|
|
return False, f"sender {sender_id} not in allowlist"
|
|
|
|
if dm_policy == "pairing":
|
|
return True, "pairing"
|
|
|
|
return False, f"unknown dm_policy: {dm_policy}"
|
|
|
|
def check_group_access(
|
|
self,
|
|
account: dict,
|
|
chat_id: str,
|
|
sender_id: str,
|
|
mentioned_bot: bool = False,
|
|
require_mention: bool = True,
|
|
) -> tuple[bool, str]:
|
|
group_policy = account.get("group_policy", "allowlist")
|
|
group_allow_from = account.get("group_allow_from", [])
|
|
groups = account.get("groups", {})
|
|
|
|
if group_policy == "disabled":
|
|
return False, "Group chat is disabled"
|
|
|
|
if require_mention and not mentioned_bot:
|
|
return False, "@mention required"
|
|
|
|
if group_policy == "open":
|
|
return True, "open"
|
|
|
|
if group_policy == "allowlist":
|
|
chat_entries = [e for e in group_allow_from if e.startswith("chat:") or e.startswith("oc_")]
|
|
if any(chat_id in e for e in chat_entries):
|
|
return True, "allowlist"
|
|
|
|
if chat_id in groups:
|
|
group_cfg = groups[chat_id]
|
|
if not group_cfg.get("enabled", True):
|
|
return False, "group disabled"
|
|
group_allow = group_cfg.get("allow_from", [])
|
|
if group_allow and sender_id not in group_allow:
|
|
return False, f"sender {sender_id} not in group allow list"
|
|
return True, "explicit group config"
|
|
|
|
return False, f"chat {chat_id} not in group allow list"
|
|
|
|
return False, f"unknown group_policy: {group_policy}"
|
|
|
|
def check_mention_required(self, account: dict, chat_id: str | None = None) -> bool:
|
|
if chat_id:
|
|
groups = account.get("groups", {})
|
|
group_cfg = groups.get(chat_id, {})
|
|
if "require_mention" in group_cfg:
|
|
return group_cfg["require_mention"]
|
|
return account.get("require_mention", True)
|
|
|
|
def collect_warnings(self, config: dict, account_id: str | None = None, account: dict | None = None) -> list[str]:
|
|
warnings = []
|
|
group_policy = account.get("group_policy", "") if account else config.get("channels", {}).get("feishu", {}).get("groupPolicy", "")
|
|
if group_policy == "open" and not config.get("channels", {}).get("feishu", {}).get("groups"):
|
|
warnings.append("groupPolicy is 'open' without groups allowlist - consider setting groupPolicy='allowlist'")
|
|
return warnings |