mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-07-27 00:17:53 +00:00
63 lines
2.2 KiB
Python
63 lines
2.2 KiB
Python
"""Principal builder — the single sanctioned way to construct a Principal.
|
|
|
|
Both Layer 1 (tool assembly) and Layer 2 (GuardrailAuthorizationAdapter) must
|
|
use this builder so identity semantics stay consistent. It is a pure function:
|
|
no global config reads, no caching, no input mutation.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Mapping
|
|
from typing import Any
|
|
|
|
from deerflow.authz.provider import Principal
|
|
|
|
|
|
def normalize_authz_attributes(raw: Any) -> dict[str, Any]:
|
|
"""Validate and copy ``authz_attributes`` into a fresh dict.
|
|
|
|
This is the single normalization point shared by the Principal builder and
|
|
all propagation sites (middleware, executor, task_tool). Keeping it in one
|
|
place ensures every in-process consumption boundary raises ``TypeError``
|
|
for non-Mapping values rather than silently coercing.
|
|
|
|
Raises:
|
|
TypeError: If *raw* is not ``None`` and not a ``Mapping``.
|
|
"""
|
|
if raw is None:
|
|
return {}
|
|
if isinstance(raw, Mapping):
|
|
return dict(raw)
|
|
raise TypeError(f"authz_attributes must be a Mapping, got {type(raw).__name__}")
|
|
|
|
|
|
def build_principal_from_context(
|
|
context: Mapping[str, Any],
|
|
*,
|
|
default_role: str,
|
|
) -> Principal:
|
|
"""Build a :class:`Principal` from a runtime context mapping.
|
|
|
|
Args:
|
|
context: The runtime context (``config["context"]`` or a dict assembled
|
|
from a :class:`~deerflow.guardrails.provider.GuardrailRequest`).
|
|
default_role: Role used when ``user_role`` is ``None`` or empty string.
|
|
Unknown but non-empty roles are **not** replaced — only missing ones.
|
|
|
|
Raises:
|
|
TypeError: If ``authz_attributes`` is present but not a ``Mapping``.
|
|
"""
|
|
resolved_role = context.get("user_role")
|
|
if resolved_role is None or resolved_role == "":
|
|
resolved_role = default_role
|
|
|
|
return Principal(
|
|
user_id=context.get("user_id"),
|
|
role=resolved_role,
|
|
oauth_provider=context.get("oauth_provider"),
|
|
oauth_id=context.get("oauth_id"),
|
|
channel_user_id=context.get("channel_user_id"),
|
|
is_internal=context.get("is_internal") is True,
|
|
attributes=normalize_authz_attributes(context.get("authz_attributes")),
|
|
)
|