69 lines
2.4 KiB
Python
69 lines
2.4 KiB
Python
from __future__ import annotations
|
|
|
|
from fastapi import Request
|
|
from fastapi.responses import JSONResponse
|
|
|
|
from server.auth.context import bind_identity, reset_identity
|
|
from server.auth.licenses import get_license_provider, is_desktop_request
|
|
from server.auth.providers import AuthError, get_auth_provider
|
|
|
|
|
|
PUBLIC_API_PATHS = {
|
|
"/api/health",
|
|
"/api/auth/login",
|
|
"/api/auth/refresh",
|
|
"/api/auth/logout",
|
|
"/api/auth/license/activate",
|
|
}
|
|
|
|
SELF_AUTHORIZED_WRITE_PREFIXES = (
|
|
"/api/workspace",
|
|
"/api/projects",
|
|
"/api/sessions",
|
|
"/api/project-files",
|
|
)
|
|
|
|
|
|
class AuthenticationMiddleware:
|
|
"""ASGI middleware keeps identity bound for the full streaming response lifetime."""
|
|
|
|
def __init__(self, app) -> None:
|
|
self.app = app
|
|
|
|
async def __call__(self, scope, receive, send) -> None:
|
|
if scope.get("type") != "http":
|
|
await self.app(scope, receive, send)
|
|
return
|
|
path = scope.get("path") or ""
|
|
if scope.get("method") == "OPTIONS" or not path.startswith("/api/") or path in PUBLIC_API_PATHS:
|
|
await self.app(scope, receive, send)
|
|
return
|
|
request = Request(scope, receive=receive)
|
|
try:
|
|
provider = get_license_provider() if is_desktop_request(request) else get_auth_provider()
|
|
identity = await provider.authenticate(request)
|
|
except AuthError as exc:
|
|
response = JSONResponse(
|
|
{"error": {"code": exc.code, "message": exc.message}},
|
|
status_code=exc.status_code,
|
|
)
|
|
await response(scope, receive, send)
|
|
return
|
|
token = bind_identity(identity)
|
|
try:
|
|
method = scope.get("method") or "GET"
|
|
if method not in {"GET", "HEAD", "OPTIONS"} and not path.startswith(SELF_AUTHORIZED_WRITE_PREFIXES):
|
|
from server.state.projects import get_project_store
|
|
try:
|
|
get_project_store().require_active_write()
|
|
except PermissionError as exc:
|
|
response = JSONResponse(
|
|
{"error": {"code": "PROJECT_READ_ONLY", "message": str(exc)}},
|
|
status_code=403,
|
|
)
|
|
await response(scope, receive, send)
|
|
return
|
|
await self.app(scope, receive, send)
|
|
finally:
|
|
reset_identity(token)
|