refactor(teams): extract card-action authorization gate

This commit is contained in:
Teknium
2026-09-02 22:41:25 -07:00
parent 47117f51ba
commit ec50968ae9

View File

@@ -573,10 +573,8 @@ class TeamsAdapter(BasePlatformAdapter):
@staticmethod
def _invoke_card(body: list) -> "InvokeResponse[AdaptiveCardActionMessageResponse]":
return InvokeResponse(
status=200,
body=AdaptiveCardActionCardResponse(value=AdaptiveCard().with_version("1.4").with_body(body)),
)
card = AdaptiveCard().with_version("1.4").with_body(body)
return InvokeResponse(status=200, body=AdaptiveCardActionCardResponse(value=card))
async def _on_card_action(
self, ctx: "ActivityContext[AdaptiveCardInvokeActivity]"
@@ -589,23 +587,9 @@ class TeamsAdapter(BasePlatformAdapter):
session_key = data.get("session_key", "")
if not hermes_action or not session_key:
return self._invoke_message("Unknown action.")
# Default-deny: approval clicks require TEAMS_ALLOWED_USERS or an explicit
# TEAMS_ALLOW_ALL_USERS=true opt-in, else anyone who can message the bot could approve.
allowed_csv = os.getenv("TEAMS_ALLOWED_USERS", "").strip()
allow_all = os.getenv("TEAMS_ALLOW_ALL_USERS", "").strip().lower() in {"1", "true", "yes"}
if not allow_all:
if not allowed_csv:
logger.warning(
"[teams] card action rejected: TEAMS_ALLOWED_USERS not configured "
"and TEAMS_ALLOW_ALL_USERS not set — default deny")
return self._invoke_message("⛔ Approval buttons require TEAMS_ALLOWED_USERS to be configured.")
from_account = ctx.activity.from_
clicker_id = getattr(from_account, "aad_object_id", None) or getattr(from_account, "id", "")
allowed_ids = {uid.strip() for uid in allowed_csv.split(",") if uid.strip()}
if "*" not in allowed_ids and clicker_id not in allowed_ids:
logger.warning("[teams] Unauthorized card action by %s — ignoring", clicker_id)
return self._invoke_message("⛔ Not authorized.")
denied = self._card_action_denied(ctx.activity.from_)
if denied:
return self._invoke_message(denied)
choice = _APPROVAL_CHOICES.get(hermes_action)
if not choice:
return self._invoke_message("Unknown action.")
@@ -616,6 +600,26 @@ class TeamsAdapter(BasePlatformAdapter):
body.append(TextBlock(text=_APPROVAL_LABELS[choice], wrap=True, weight="Bolder"))
return self._invoke_card(body)
@staticmethod
def _card_action_denied(from_account: Any) -> Optional[str]:
"""Default-deny gate for approval clicks: require TEAMS_ALLOWED_USERS or an explicit
TEAMS_ALLOW_ALL_USERS=true opt-in, else anyone who can message the bot could approve.
Returns the user-facing denial text, or ``None`` when allowed."""
if os.getenv("TEAMS_ALLOW_ALL_USERS", "").strip().lower() in {"1", "true", "yes"}:
return None
allowed_csv = os.getenv("TEAMS_ALLOWED_USERS", "").strip()
if not allowed_csv:
logger.warning(
"[teams] card action rejected: TEAMS_ALLOWED_USERS not configured "
"and TEAMS_ALLOW_ALL_USERS not set — default deny")
return "⛔ Approval buttons require TEAMS_ALLOWED_USERS to be configured."
clicker_id = getattr(from_account, "aad_object_id", None) or getattr(from_account, "id", "")
allowed_ids = {uid.strip() for uid in allowed_csv.split(",") if uid.strip()}
if "*" not in allowed_ids and clicker_id not in allowed_ids:
logger.warning("[teams] Unauthorized card action by %s — ignoring", clicker_id)
return "⛔ Not authorized."
return None
async def send_exec_approval(
self, chat_id: str, command: str, session_key: str, description: str = "dangerous command",
metadata: Optional[Dict[str, Any]] = None, allow_permanent: bool = True, allow_session: bool = True,
@@ -628,8 +632,7 @@ class TeamsAdapter(BasePlatformAdapter):
def _action(title: str, hermes_action: str, **kw) -> "ExecuteAction":
return ExecuteAction(
title=title, verb="hermes_approve", data={**btn_data_base, "hermes_action": hermes_action}, **kw
)
title=title, verb="hermes_approve", data={**btn_data_base, "hermes_action": hermes_action}, **kw)
actions = [_action("Allow Once", "approve_once", style="positive")]
if not smart_denied and allow_session: