146 lines
4.8 KiB
Python
146 lines
4.8 KiB
Python
from __future__ import annotations
|
|
|
|
import re
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
_GROUP_KEY_PATTERN = re.compile(r"^spaces/.+$")
|
|
|
|
|
|
def apply_inbound_access_policy(
|
|
space_name: str,
|
|
space_type: str,
|
|
sender_name: str,
|
|
is_dm: bool,
|
|
account: dict,
|
|
) -> dict:
|
|
"""
|
|
Returns {"allowed": bool, "reason": str, "requires_pairing": bool}
|
|
"""
|
|
if is_dm:
|
|
return _check_dm_policy(sender_name, account)
|
|
else:
|
|
return _check_group_policy(space_name, space_type, account)
|
|
|
|
|
|
def _check_dm_policy(sender_name: str, account: dict) -> dict:
|
|
dm_policy = account.get("dm_policy", "pairing")
|
|
dm_enabled = account.get("dm_enabled", True)
|
|
|
|
if not dm_enabled:
|
|
return {"allowed": False, "reason": "dm_disabled"}
|
|
|
|
if dm_policy == "open":
|
|
return {"allowed": True, "reason": "open_dm"}
|
|
|
|
if dm_policy == "allowlist":
|
|
allow_from = account.get("dm_allow_from", [])
|
|
if "*" in allow_from:
|
|
return {"allowed": True, "reason": "wildcard_allow"}
|
|
if sender_name in allow_from:
|
|
return {"allowed": True, "reason": "in_allowlist"}
|
|
normalized = _normalize_sender_for_allowlist_check(sender_name, allow_from)
|
|
if normalized:
|
|
return {"allowed": True, "reason": "in_allowlist"}
|
|
return {"allowed": False, "reason": "not_in_allowlist"}
|
|
|
|
if dm_policy == "pairing":
|
|
allow_from = account.get("dm_allow_from", [])
|
|
resolved = account.get("resolved")
|
|
if resolved and hasattr(resolved, "dm_allow_from"):
|
|
allow_from = resolved.dm_allow_from
|
|
if sender_name in allow_from:
|
|
return {"allowed": True, "reason": "already_paired"}
|
|
normalized = _normalize_sender_for_allowlist_check(sender_name, allow_from)
|
|
if normalized:
|
|
return {"allowed": True, "reason": "already_paired"}
|
|
return {"allowed": False, "reason": "pairing_required", "requires_pairing": True}
|
|
|
|
return {"allowed": False, "reason": f"unknown_policy: {dm_policy}"}
|
|
|
|
|
|
def _check_group_policy(space_name: str, space_type: str, account: dict) -> dict:
|
|
group_policy = account.get("group_policy", "allowlist")
|
|
groups = account.get("groups", {})
|
|
|
|
if group_policy == "disabled":
|
|
return {"allowed": False, "reason": "groups_disabled"}
|
|
|
|
if group_policy == "open":
|
|
return {"allowed": True, "reason": "open_group"}
|
|
|
|
if group_policy == "allowlist":
|
|
if "*" in groups:
|
|
return {"allowed": True, "reason": "wildcard_group"}
|
|
if space_name in groups:
|
|
entry = groups[space_name]
|
|
if entry.get("enabled") is False:
|
|
return {"allowed": False, "reason": "group_disabled"}
|
|
return {"allowed": True, "reason": "in_group_list"}
|
|
if not _GROUP_KEY_PATTERN.match(space_name):
|
|
return {"allowed": False, "reason": "deprecated_group_key"}
|
|
return {"allowed": False, "reason": "not_in_group_list"}
|
|
|
|
return {"allowed": False, "reason": f"unknown_policy: {group_policy}"}
|
|
|
|
|
|
def _normalize_sender_for_allowlist_check(sender_name: str, allow_from: list[str]) -> str | None:
|
|
for entry in allow_from:
|
|
if entry.startswith("googlechat:"):
|
|
if entry[len("googlechat:"):] == sender_name:
|
|
return sender_name
|
|
if entry.startswith("users/"):
|
|
if entry == sender_name:
|
|
return sender_name
|
|
return None
|
|
|
|
|
|
def is_control_command_message(text: str) -> bool:
|
|
text = text.strip()
|
|
return text.startswith("/config") or text.startswith("/status") or text.startswith("/admin")
|
|
|
|
|
|
def should_require_command_authorization(
|
|
is_group: bool, text: str, command_authorized: bool
|
|
) -> bool:
|
|
if not is_group:
|
|
return False
|
|
if not is_control_command_message(text):
|
|
return False
|
|
return not command_authorized
|
|
|
|
|
|
def detect_mention_for_access(annotations: list[dict]) -> bool:
|
|
if not annotations:
|
|
return False
|
|
for ann in annotations:
|
|
if ann.get("type") == "USER_MENTION":
|
|
um = ann.get("userMention", {})
|
|
if um.get("user", {}).get("name") == "users/app":
|
|
return True
|
|
return False
|
|
|
|
|
|
def resolve_group_require_mention(space_name: str, account: dict) -> bool:
|
|
groups = account.get("groups", {})
|
|
default_require = account.get("require_mention", True)
|
|
|
|
if "*" in groups:
|
|
entry = groups["*"]
|
|
if "requireMention" in entry:
|
|
return entry["requireMention"]
|
|
|
|
if space_name in groups:
|
|
entry = groups[space_name]
|
|
if "requireMention" in entry:
|
|
return entry["requireMention"]
|
|
|
|
return default_require
|
|
|
|
|
|
def should_bypass_mention_for_text_commands(text: str, account: dict) -> bool:
|
|
if not account.get("allow_text_commands", False):
|
|
return False
|
|
return is_control_command_message(text)
|