fix: fix superadmin impersonation

This commit is contained in:
Abhishek Kumar 2026-07-11 15:51:36 +05:30
parent d1339970a5
commit e405457676
16 changed files with 593 additions and 172 deletions

View file

@ -1,8 +1,17 @@
import os
from typing import Any
import aiohttp
class StackAuthUserSearchError(Exception):
"""Raised when Stack Auth user search fails unexpectedly."""
class StackAuthSessionError(Exception):
"""Raised when Stack Auth cannot create an impersonation session."""
class StackAuth:
def __init__(self):
self.project_id = os.environ.get("STACK_AUTH_PROJECT_ID")
@ -56,10 +65,56 @@ class StackAuth:
"is_impersonation": True,
}
async with aiohttp.ClientSession() as session:
async with session.post(url, headers=headers, json=data) as response:
response = await response.json()
return response
try:
async with aiohttp.ClientSession() as session:
async with session.post(url, headers=headers, json=data) as response:
if response.status >= 400:
raise StackAuthSessionError(
"Stack Auth session creation failed"
)
return await response.json()
except (aiohttp.ClientError, ValueError) as exc:
raise StackAuthSessionError("Stack Auth session creation failed") from exc
async def find_users_by_email(self, email: str) -> list[dict[str, Any]]:
"""Return Stack Auth users whose primary email exactly matches."""
normalized_email = email.strip().lower()
url = os.environ.get("STACK_AUTH_API_URL") + "/api/v1/users"
headers = {
"x-stack-access-type": "server",
"x-stack-project-id": self.project_id,
"x-stack-secret-server-key": self.secret_server_key,
}
params = {
"query": normalized_email,
"limit": "10",
}
try:
async with aiohttp.ClientSession() as session:
async with session.get(url, headers=headers, params=params) as response:
if response.status >= 400:
raise StackAuthUserSearchError("Stack Auth user search failed")
payload = await response.json()
except (aiohttp.ClientError, ValueError) as exc:
raise StackAuthUserSearchError("Stack Auth user search failed") from exc
users = payload.get("items", []) if isinstance(payload, dict) else []
if not isinstance(users, list):
return []
return [
user
for user in users
if isinstance(user, dict)
and self._stack_user_has_email(user, normalized_email)
]
def _stack_user_has_email(self, user: dict[str, Any], email: str) -> bool:
primary_email = user.get("primary_email")
return isinstance(primary_email, str) and primary_email.lower() == email
# ------------------------------------------------------------------
# Team & user management helpers

View file

@ -329,7 +329,7 @@ class CustomToolManager:
return handler, timeout_secs
def _transfer_handler_timeout_secs(self, tool: Any) -> float:
config = ((tool.definition or {}).get("config", {}) or {})
config = (tool.definition or {}).get("config", {}) or {}
try:
transfer_timeout = int(config.get("timeout", 30))
except (TypeError, ValueError):
@ -338,9 +338,8 @@ class CustomToolManager:
resolver_timeout = 0.0
resolver = config.get("resolver")
if (
config.get("destination_source", "static") == "dynamic"
and isinstance(resolver, dict)
if config.get("destination_source", "static") == "dynamic" and isinstance(
resolver, dict
):
try:
resolver_timeout = float(resolver.get("timeout_ms", 3000)) / 1000.0
@ -602,10 +601,9 @@ class CustomToolManager:
return
resolver = config.get("resolver") if isinstance(config, dict) else None
is_dynamic_transfer = (
config.get("destination_source", "static") == "dynamic"
and isinstance(resolver, dict)
)
is_dynamic_transfer = config.get(
"destination_source", "static"
) == "dynamic" and isinstance(resolver, dict)
resolver_phase_muted = False
def clear_transfer_setup_mute_state() -> None: