feat: rebuild as multi-user web agent
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from agent_platform.gateway.app import create_app
|
||||
from agent_platform.gateway.provider import normalize_workspace_path, workspace_ref
|
||||
from agent_platform.gateway.schemas import ToolResult, WorkspaceStatus
|
||||
|
||||
|
||||
def test_workspace_paths_cannot_escape() -> None:
|
||||
assert str(normalize_workspace_path("src/app.py")) == "src/app.py"
|
||||
assert str(normalize_workspace_path("/workspace/src/app.py")) == "src/app.py"
|
||||
for value in ("../secret", "/etc/passwd", "a/../../b", "bad\x00name"):
|
||||
with pytest.raises(ValueError):
|
||||
normalize_workspace_path(value)
|
||||
|
||||
|
||||
def test_workspace_identity_is_stable_and_separate() -> None:
|
||||
first = workspace_ref("user-1")
|
||||
assert first == workspace_ref("user-1")
|
||||
assert first != workspace_ref("user-2")
|
||||
assert "user-1" not in first.container_name
|
||||
assert "user-1" not in first.network_name
|
||||
|
||||
|
||||
class FakeProvider:
|
||||
provider_name = "local-docker"
|
||||
|
||||
async def status(self, user_id: str) -> WorkspaceStatus:
|
||||
return WorkspaceStatus(
|
||||
workspace_id=workspace_ref(user_id).workspace_id,
|
||||
provider="local-docker",
|
||||
container_name=workspace_ref(user_id).container_name,
|
||||
state="running",
|
||||
)
|
||||
|
||||
async def list_files(self, user_id, path, max_depth, limit):
|
||||
return ToolResult(ok=True, output=f"{user_id}:{path}")
|
||||
|
||||
async def read_file(self, *args, **kwargs):
|
||||
return ToolResult(ok=True)
|
||||
|
||||
async def write_file(self, *args, **kwargs):
|
||||
return ToolResult(ok=True)
|
||||
|
||||
async def search_files(self, *args, **kwargs):
|
||||
return ToolResult(ok=True)
|
||||
|
||||
async def exec(self, *args, **kwargs):
|
||||
return ToolResult(ok=True)
|
||||
|
||||
async def apply_patch(self, *args, **kwargs):
|
||||
return ToolResult(ok=True)
|
||||
|
||||
async def start_process(self, *args, **kwargs):
|
||||
return ToolResult(ok=True)
|
||||
|
||||
async def poll_process(self, *args, **kwargs):
|
||||
return ToolResult(ok=True)
|
||||
|
||||
async def cancel_process(self, *args, **kwargs):
|
||||
return ToolResult(ok=True)
|
||||
|
||||
|
||||
def test_gateway_requires_service_key_and_signed_identity(settings, identity_jwt) -> None:
|
||||
app = create_app(settings, provider=FakeProvider())
|
||||
with TestClient(app) as client:
|
||||
assert client.post("/v1/tools/list_files", json={}).status_code == 401
|
||||
headers = {
|
||||
"Authorization": f"Bearer {settings.internal_gateway_key}",
|
||||
"X-OpenWebUI-User-Jwt": identity_jwt,
|
||||
}
|
||||
response = client.post("/v1/tools/list_files", headers=headers, json={"path": "src"})
|
||||
assert response.status_code == 200
|
||||
assert response.json()["output"] == "user-123:src"
|
||||
|
||||
|
||||
def test_gateway_openapi_does_not_expose_auth_headers(settings) -> None:
|
||||
app = create_app(settings, provider=FakeProvider())
|
||||
with TestClient(app) as client:
|
||||
spec = client.get("/openapi.json").json()
|
||||
operation = spec["paths"]["/v1/tools/list_files"]["post"]
|
||||
assert all(
|
||||
parameter["name"] not in {"Authorization", "X-OpenWebUI-User-Jwt"}
|
||||
for parameter in operation.get("parameters", [])
|
||||
)
|
||||
Reference in New Issue
Block a user