feat: magic-link auth with access requests and RBAC

This commit is contained in:
Jonas Miederer 2026-09-11 17:27:58 +02:00
parent c2125f063b
commit c8280f7078
13 changed files with 1058 additions and 2 deletions

View file

@ -13,7 +13,9 @@ from fastapi.responses import StreamingResponse
from sqlmodel import Session
from app.db import get_engine, get_session
from app.models import User
from app.settings import settings
from app.auth.deps import get_optional_user, resolve_tenant
from . import runner, store
from .deploy import import_from_langflow
@ -225,7 +227,11 @@ async def run_flow(
stream: bool = Query(default=False),
session: Session = Depends(get_db),
lfx: LfxClient = Depends(get_lfx),
user: User | None = Depends(get_optional_user),
):
# Tenant comes from the session; the body param stays as a deprecated
# fallback for unauthenticated callers.
data = data.model_copy(update={"tenant_id": resolve_tenant(user, data.tenant_id)})
try:
version = _resolve_version(session, flow_id, data.version_no)
except store.NotFoundError as exc:
@ -316,7 +322,12 @@ def list_runs(
flow_id: UUID,
tenant_id: str | None = Query(default=None),
session: Session = Depends(get_db),
user: User | None = Depends(get_optional_user),
):
# Authenticated callers are scoped to their session tenant unless they
# pass an explicit tenant (deprecated fallback for unauthenticated use).
if user is not None and tenant_id is None:
tenant_id = user.tenant_id
try:
return [_read_run(r) for r in store.list_runs(session, flow_id, tenant_id)]
except store.NotFoundError as exc:
@ -380,8 +391,11 @@ def get_run(
run_id: UUID,
tenant_id: str | None = Query(default=None),
session: Session = Depends(get_db),
user: User | None = Depends(get_optional_user),
):
"""Read a run. With tenant_id, cross-tenant reads 404 (v1 scoping)."""
if user is not None and tenant_id is None:
tenant_id = user.tenant_id
try:
run = store.get_run(session, run_id)
except store.NotFoundError as exc:
@ -408,7 +422,10 @@ def create_approval(data: ApprovalCreate, session: Session = Depends(get_db)):
def list_approvals(
tenant_id: str | None = Query(default=None),
session: Session = Depends(get_db),
user: User | None = Depends(get_optional_user),
):
if user is not None and tenant_id is None:
tenant_id = user.tenant_id
return [_read_task(t) for t in store.list_approval_tasks(session, tenant_id)]