Arch: Finalize V3 Auth modularization and Unified Agent spec.
- Integrated zero-dependency Auth models and dependencies_v3.py. - Successfully resolved circular dependency boot loops. - Verified site_domain search exception via verify_v3_exceptions.py. - Refined Unified Agent Architecture with Storage Layer and API-driven access details. - Updated project roadmap and milestones in GEMINI.md.
This commit is contained in:
104
app/routers/dependencies_v3.py
Normal file
104
app/routers/dependencies_v3.py
Normal file
@@ -0,0 +1,104 @@
|
||||
from fastapi import Depends, Header, HTTPException, Query, Response, status
|
||||
from typing import Optional, Union
|
||||
import logging
|
||||
import asyncio
|
||||
|
||||
from app.models.auth_models import AccountContext
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# --- Account Context Dependencies ---
|
||||
|
||||
def get_account_context_optional(
|
||||
x_account_id: Optional[str] = Header(None, min_length=11, max_length=22),
|
||||
x_no_account_id: Optional[str] = Header(None, min_length=3, max_length=100),
|
||||
x_no_account_id_token: Optional[str] = Query(None, min_length=11, max_length=22),
|
||||
) -> AccountContext:
|
||||
"""
|
||||
Resolves the account context but does not raise 403 on failure.
|
||||
Uses DEFERRED imports to prevent circular dependency at startup.
|
||||
"""
|
||||
from app.db_sql import redis_lookup_id_random
|
||||
|
||||
resolved_account_id = None
|
||||
resolved_account_id_random = None
|
||||
auth_method = 'guest'
|
||||
|
||||
if x_account_id:
|
||||
resolved_account_id_random = x_account_id
|
||||
if looked_up_id := redis_lookup_id_random(table_name='account', record_id_random=x_account_id):
|
||||
resolved_account_id = looked_up_id
|
||||
auth_method = 'legacy_header'
|
||||
elif x_no_account_id_token:
|
||||
resolved_account_id_random = x_no_account_id_token
|
||||
if looked_up_id := redis_lookup_id_random(table_name='account', record_id_random=x_no_account_id_token):
|
||||
resolved_account_id = looked_up_id
|
||||
auth_method = 'token_query'
|
||||
elif x_no_account_id:
|
||||
resolved_account_id = None
|
||||
resolved_account_id_random = '--- NO ACCOUNT ---'
|
||||
auth_method = 'bypass'
|
||||
|
||||
return AccountContext(
|
||||
account_id=resolved_account_id,
|
||||
account_id_random=resolved_account_id_random,
|
||||
auth_method=auth_method,
|
||||
administrator=(auth_method == 'bypass'),
|
||||
manager=(auth_method == 'bypass'),
|
||||
super=(auth_method == 'bypass')
|
||||
)
|
||||
|
||||
def get_account_context(
|
||||
x_account_id: Optional[str] = Header(None, min_length=11, max_length=22),
|
||||
x_no_account_id: Optional[str] = Header(None, min_length=3, max_length=100),
|
||||
x_no_account_id_token: Optional[str] = Query(None, min_length=11, max_length=22),
|
||||
) -> AccountContext:
|
||||
"""Strict version of account context resolution."""
|
||||
ctx = get_account_context_optional(x_account_id, x_no_account_id, x_no_account_id_token)
|
||||
if ctx.auth_method == 'guest':
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail='Account context required.')
|
||||
return ctx
|
||||
|
||||
|
||||
# --- Shared Pagination & Status Dependencies ---
|
||||
|
||||
class PaginationParams:
|
||||
def __init__(
|
||||
self,
|
||||
limit: int = Query(100, ge=0),
|
||||
offset: int = Query(0, ge=0),
|
||||
):
|
||||
self.limit = limit
|
||||
self.offset = offset
|
||||
|
||||
class StatusFilterParams:
|
||||
def __init__(
|
||||
self,
|
||||
enabled: str = Query('enabled'),
|
||||
hidden: str = Query('not_hidden'),
|
||||
):
|
||||
self.enabled = enabled
|
||||
self.hidden = hidden
|
||||
|
||||
class SerializationParams:
|
||||
def __init__(
|
||||
self,
|
||||
by_alias: bool = Query(True),
|
||||
exclude_unset: bool = Query(False),
|
||||
exclude_defaults: bool = Query(False),
|
||||
exclude_none: bool = Query(False),
|
||||
):
|
||||
self.by_alias = by_alias
|
||||
self.exclude_unset = exclude_unset
|
||||
self.exclude_defaults = exclude_defaults
|
||||
self.exclude_none = exclude_none
|
||||
|
||||
class DelayParams:
|
||||
def __init__(
|
||||
self,
|
||||
x_delay_ms: Optional[int] = Header(0, alias='X-Delay-ms'),
|
||||
delay_ms: Optional[int] = Query(0),
|
||||
):
|
||||
val = max(x_delay_ms or 0, delay_ms or 0)
|
||||
self.sleep_time_ms = val
|
||||
self.sleep_time_s = val / 1000.0
|
||||
Reference in New Issue
Block a user