feat: add OIDC browser sessions with live access enforcement
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a0e747-8f27-7242-8df8-8bc44f88c929
This commit is contained in:
parent
fdea2f5192
commit
a15fe032b0
14 changed files with 807 additions and 31 deletions
|
|
@ -179,8 +179,12 @@ class AccessBoundary:
|
|||
routers. Routes added without catalog admission remain denied.
|
||||
"""
|
||||
def __init__(self, app, *, host, controller: AccessController | None,
|
||||
catalog: dict[str, str] | None = None):
|
||||
catalog: dict[str, str] | None = None, body_timeout: float = 10, browser=None):
|
||||
if not 0 < body_timeout <= 10:
|
||||
raise ValueError("body timeout must be positive and at most ten seconds")
|
||||
self.app, self.host, self.controller = app, host, controller
|
||||
self.body_timeout = body_timeout
|
||||
self.browser = browser
|
||||
self.catalog = catalog if catalog is not None else json.loads(
|
||||
files("hub_core.security").joinpath("routes.json").read_text()
|
||||
)["routes"]
|
||||
|
|
@ -196,15 +200,31 @@ class AccessBoundary:
|
|||
if scope["method"] == "GET" and scope["path"] == "/healthz":
|
||||
await JSONResponse({"status": "ok"})(scope, receive, send)
|
||||
return
|
||||
if self.browser is not None:
|
||||
from hub_core.security.browser import BROWSER_ROUTES
|
||||
if (scope["method"], scope["path"]) in BROWSER_ROUTES:
|
||||
response = await self.browser.handle(Request(scope, receive))
|
||||
await response(scope, receive, send)
|
||||
return
|
||||
correlation = str(uuid4())
|
||||
context = None
|
||||
cookie_auth = False
|
||||
action = None
|
||||
digest = None
|
||||
try:
|
||||
headers = Request(scope).headers.getlist("authorization")
|
||||
if len(headers) != 1 or not headers[0].startswith("Bearer "):
|
||||
raise AccessFailure(401, "bearer_required")
|
||||
token = headers[0][7:]
|
||||
request = Request(scope, receive)
|
||||
headers = request.headers.getlist("authorization")
|
||||
if self.browser is not None and not headers:
|
||||
token = self.browser.access_token(request)
|
||||
cookie_auth = True
|
||||
else:
|
||||
if self.browser is not None:
|
||||
from hub_core.security.browser import SESSION_COOKIE
|
||||
if SESSION_COOKIE in request.cookies:
|
||||
raise AccessFailure(400, "mixed_browser_credentials")
|
||||
if len(headers) != 1 or not headers[0].startswith("Bearer "):
|
||||
raise AccessFailure(401, "bearer_required")
|
||||
token = headers[0][7:]
|
||||
if not token or len(token) > 16384 or any(c.isspace() for c in token):
|
||||
raise AccessFailure(401, "invalid_access_token")
|
||||
if self.controller is None:
|
||||
|
|
@ -218,11 +238,15 @@ class AccessBoundary:
|
|||
# Bind policy to the exact request without exposing content to PDP/audit.
|
||||
request = Request(scope, receive)
|
||||
chunks, size = [], 0
|
||||
async for chunk in request.stream():
|
||||
size += len(chunk)
|
||||
if size > 1024 * 1024:
|
||||
raise AccessFailure(413, "request_too_large")
|
||||
chunks.append(chunk)
|
||||
try:
|
||||
async with asyncio.timeout(self.body_timeout):
|
||||
async for chunk in request.stream():
|
||||
size += len(chunk)
|
||||
if size > 1024 * 1024:
|
||||
raise AccessFailure(413, "request_too_large")
|
||||
chunks.append(chunk)
|
||||
except TimeoutError as exc:
|
||||
raise AccessFailure(408, "request_body_timeout") from exc
|
||||
body = b"".join(chunks)
|
||||
digest = hashlib.sha256(b"\0".join([
|
||||
scope["method"].encode(), scope["path"].encode(),
|
||||
|
|
@ -241,6 +265,8 @@ class AccessBoundary:
|
|||
for field in ("from_address", "from_agent", "author"):
|
||||
if field in payload and payload[field] not in context.facts.producer_addresses:
|
||||
raise AccessFailure(403, "producer_identity_mismatch")
|
||||
if cookie_auth:
|
||||
self.browser.access_token(request) # Recheck expiry/logout after owner awaits.
|
||||
scope.setdefault("state", {})["hub_access"] = context
|
||||
except Exception as exc:
|
||||
failure = exc if isinstance(exc, AccessFailure) else AccessFailure(503, "access_unavailable")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue