mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-04-25 11:18:22 +00:00
Add new application structure: - app/main.py - application entry point - app/plugins/ - plugin system with auth plugin: - api/ - REST API endpoints and schemas - authorization/ - auth policies, providers, hooks - domain/ - business logic (service, models, jwt, password) - injection/ - route injection and guards - ops/ - operational utilities - runtime/ - runtime configuration - security/ - middleware, CSRF, dependencies - storage/ - user repositories and models - app/static/ - static assets (scalar.js for API docs) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
103 lines
3.7 KiB
Python
103 lines
3.7 KiB
Python
"""Runtime route guard backed by the auth plugin's route policy registry."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Awaitable, Callable
|
|
|
|
from fastapi import HTTPException, Request
|
|
|
|
from app.plugins.auth.authorization.authentication import (
|
|
authenticate_request,
|
|
get_auth_context,
|
|
set_auth_context,
|
|
)
|
|
from app.plugins.auth.authorization.authorization import ensure_authenticated
|
|
from app.plugins.auth.authorization.hooks import get_authz_hooks
|
|
from app.plugins.auth.authorization.policies import require_run_owner, require_thread_owner
|
|
from app.plugins.auth.injection.registry_loader import RoutePolicyRegistry, RoutePolicySpec
|
|
|
|
PolicyGuard = Callable[[Request, RoutePolicySpec], Awaitable[None]]
|
|
|
|
|
|
async def _check_capability(request: Request, spec: RoutePolicySpec) -> None:
|
|
if not spec.capability:
|
|
return
|
|
|
|
auth = get_auth_context(request)
|
|
if auth is None:
|
|
raise HTTPException(status_code=500, detail="Missing auth context")
|
|
|
|
if ":" not in spec.capability:
|
|
raise RuntimeError(f"Invalid capability format: {spec.capability}")
|
|
resource, action = spec.capability.split(":", 1)
|
|
if not auth.has_permission(resource, action):
|
|
raise HTTPException(status_code=403, detail=f"Permission denied: {spec.capability}")
|
|
|
|
|
|
async def _guard_thread_owner(request: Request, spec: RoutePolicySpec) -> None:
|
|
auth = get_auth_context(request)
|
|
if auth is None:
|
|
raise HTTPException(status_code=500, detail="Missing auth context")
|
|
thread_id = request.path_params.get("thread_id")
|
|
if not isinstance(thread_id, str):
|
|
raise RuntimeError("owner:thread policy requires thread_id path parameter")
|
|
await require_thread_owner(request, auth, thread_id=thread_id, require_existing=spec.require_existing)
|
|
|
|
|
|
async def _guard_run_owner(request: Request, spec: RoutePolicySpec) -> None:
|
|
auth = get_auth_context(request)
|
|
if auth is None:
|
|
raise HTTPException(status_code=500, detail="Missing auth context")
|
|
thread_id = request.path_params.get("thread_id")
|
|
run_id = request.path_params.get("run_id")
|
|
if not isinstance(thread_id, str) or not isinstance(run_id, str):
|
|
raise RuntimeError("owner:run policy requires thread_id and run_id path parameters")
|
|
await require_run_owner(
|
|
request,
|
|
auth,
|
|
thread_id=thread_id,
|
|
run_id=run_id,
|
|
require_existing=spec.require_existing,
|
|
)
|
|
|
|
|
|
_POLICY_GUARDS: dict[str, PolicyGuard] = {
|
|
"owner:thread": _guard_thread_owner,
|
|
"owner:run": _guard_run_owner,
|
|
}
|
|
|
|
|
|
async def enforce_route_policy(request: Request) -> None:
|
|
registry = getattr(request.app.state, "auth_route_policy_registry", None)
|
|
if not isinstance(registry, RoutePolicyRegistry):
|
|
raise RuntimeError("Auth route policy registry is not configured")
|
|
|
|
route = request.scope.get("route")
|
|
path_template = getattr(route, "path", None)
|
|
if not isinstance(path_template, str):
|
|
raise RuntimeError("Unable to resolve route path for authorization")
|
|
|
|
spec = registry.get(request.method, path_template)
|
|
if spec is None:
|
|
raise RuntimeError(f"Missing auth route policy for {request.method} {path_template}")
|
|
if spec.public:
|
|
return
|
|
|
|
auth = get_auth_context(request)
|
|
if auth is None:
|
|
hooks = get_authz_hooks(request)
|
|
auth = await authenticate_request(request, permission_provider=hooks.permission_provider)
|
|
set_auth_context(request, auth)
|
|
|
|
ensure_authenticated(auth)
|
|
await _check_capability(request, spec)
|
|
|
|
for policy_name in spec.policies:
|
|
guard = _POLICY_GUARDS.get(policy_name)
|
|
if guard is None:
|
|
raise RuntimeError(f"Unknown route policy guard: {policy_name}")
|
|
await guard(request, spec)
|
|
|
|
|
|
__all__ = ["enforce_route_policy"]
|