feat(tasks): add skill-templated task graph execution
This commit is contained in:
@ -9,6 +9,7 @@ from typing import Any
|
||||
import httpx
|
||||
from fastapi import Depends, FastAPI, Header, HTTPException, Query, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
from pydantic import ValidationError
|
||||
|
||||
from app.json_store import JsonStore
|
||||
from app.models import (
|
||||
@ -823,7 +824,10 @@ async def get_internal_channel_settings(backend_id: str, channel_id: str) -> dic
|
||||
async def oauth_token(request: Request) -> OAuthTokenResponse:
|
||||
content_type = request.headers.get("content-type", "")
|
||||
if "application/json" in content_type:
|
||||
payload = OAuthTokenRequest.model_validate(await request.json())
|
||||
try:
|
||||
payload = OAuthTokenRequest.model_validate(await request.json())
|
||||
except ValidationError as exc:
|
||||
raise HTTPException(status_code=422, detail=exc.errors()) from exc
|
||||
else:
|
||||
form = await request.form()
|
||||
payload = _parse_token_request_from_form(dict(form))
|
||||
|
||||
@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from pydantic import AliasChoices, BaseModel, Field
|
||||
|
||||
|
||||
def utcnow_iso() -> str:
|
||||
@ -173,7 +173,7 @@ class OAuthTokenRequest(BaseModel):
|
||||
grant_type: str = "client_credentials"
|
||||
client_id: str
|
||||
client_secret: str
|
||||
aud: str
|
||||
aud: str = Field(validation_alias=AliasChoices("aud", "audience"))
|
||||
scopes: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
|
||||
61
authz-service/src/tests/test_oauth_token_validation.py
Normal file
61
authz-service/src/tests/test_oauth_token_validation.py
Normal file
@ -0,0 +1,61 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
|
||||
def _client(tmp_path, monkeypatch) -> TestClient:
|
||||
monkeypatch.setenv("AUTHZ_DATA_DIR", str(tmp_path))
|
||||
monkeypatch.setenv("AUTHZ_PRIVATE_KEY_PATH", str(tmp_path / "signing_key.pem"))
|
||||
monkeypatch.setenv("AUTHZ_INTERNAL_TOKEN", "test-internal-token")
|
||||
import app.main as main
|
||||
|
||||
main = importlib.reload(main)
|
||||
return TestClient(main.app, raise_server_exceptions=False)
|
||||
|
||||
|
||||
def _register_backend(client: TestClient) -> dict:
|
||||
response = client.post(
|
||||
"/backends/register",
|
||||
json={"backend_id": "alice", "name": "Alice", "base_url": "http://alice.local"},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
return response.json()
|
||||
|
||||
|
||||
def test_json_token_request_accepts_audience_alias(tmp_path, monkeypatch) -> None:
|
||||
with _client(tmp_path, monkeypatch) as client:
|
||||
backend = _register_backend(client)
|
||||
response = client.post(
|
||||
"/oauth/token",
|
||||
json={
|
||||
"grant_type": "client_credentials",
|
||||
"client_id": backend["client_id"],
|
||||
"client_secret": backend["client_secret"],
|
||||
"audience": "mcp:outlook_mcp",
|
||||
"scopes": ["list_tools", "tool:auth_status"],
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
body = response.json()
|
||||
assert body["access_token"]
|
||||
assert body["token_type"] == "bearer"
|
||||
|
||||
|
||||
def test_json_token_request_validation_errors_return_422(tmp_path, monkeypatch) -> None:
|
||||
with _client(tmp_path, monkeypatch) as client:
|
||||
backend = _register_backend(client)
|
||||
response = client.post(
|
||||
"/oauth/token",
|
||||
json={
|
||||
"grant_type": "client_credentials",
|
||||
"client_id": backend["client_id"],
|
||||
"client_secret": backend["client_secret"],
|
||||
"scopes": ["list_tools"],
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 422
|
||||
assert response.json()["detail"]
|
||||
Reference in New Issue
Block a user