117 lines
4.6 KiB
Python
117 lines
4.6 KiB
Python
from unittest.mock import MagicMock
|
|
|
|
from gateway.platforms.api_server import APIServerAdapter
|
|
from gateway.config import PlatformConfig
|
|
|
|
|
|
def _make_adapter(api_key: str = "sk-secret") -> APIServerAdapter:
|
|
extra = {"key": api_key} if api_key else {}
|
|
return APIServerAdapter(PlatformConfig(enabled=True, extra=extra))
|
|
|
|
|
|
def _request(headers=None):
|
|
req = MagicMock(headers=headers or {})
|
|
req.query = {}
|
|
return req
|
|
|
|
|
|
class TestSessionOwnerHelpers:
|
|
def test_request_identity_fields_prefer_headers_over_metadata(self):
|
|
adapter = _make_adapter()
|
|
req = _request(
|
|
{
|
|
"X-Hermes-User-Id": "header-user",
|
|
"X-Hermes-User-Email": "header@bremen.com.tw",
|
|
"X-Hermes-Session-Key": "header-key",
|
|
}
|
|
)
|
|
user_id, email, session_key = adapter._request_identity_fields(
|
|
req,
|
|
{
|
|
"metadata": {
|
|
"user_id": "meta-user",
|
|
"user_email": "meta@bremen.com.tw",
|
|
}
|
|
},
|
|
)
|
|
assert user_id == "header-user"
|
|
assert email == "header@bremen.com.tw"
|
|
assert session_key == "header-key"
|
|
|
|
def test_authorize_session_owner_allows_unowned_session(self):
|
|
adapter = _make_adapter()
|
|
assert adapter._authorize_session_owner(_request(), {"id": "s1", "user_id": None}) is None
|
|
|
|
def test_authorize_session_owner_allows_matching_session_key(self):
|
|
adapter = _make_adapter()
|
|
req = _request({"X-Hermes-Session-Key": "webui:user-42"})
|
|
assert adapter._authorize_session_owner(req, {"id": "s1", "user_id": "key:webui:user-42"}) is None
|
|
|
|
def test_authorize_session_owner_rejects_mismatch(self):
|
|
adapter = _make_adapter()
|
|
req = _request({"X-Hermes-Session-Key": "webui:user-7"})
|
|
resp = adapter._authorize_session_owner(req, {"id": "s1", "user_id": "key:webui:user-42"})
|
|
assert resp is not None
|
|
assert resp.status == 403
|
|
|
|
def test_owner_candidates_prefer_principal_then_email_then_user_then_key(self, monkeypatch):
|
|
adapter = _make_adapter()
|
|
|
|
class _Store:
|
|
def get_principal_context(self, source):
|
|
return {"principal_id": "p-123"}
|
|
|
|
def get_verified_email_for_source(self, source):
|
|
return "dk96@bremen.com.tw"
|
|
|
|
monkeypatch.setattr("gateway.platforms.api_server.GatewayUserStore", lambda: _Store())
|
|
monkeypatch.setattr(adapter, "_verified_email_target_if_bound", lambda chat_id, email, explicit_user_requested=False: email)
|
|
candidates = adapter._owner_candidates_from_identity(
|
|
user_id="openwebui-42",
|
|
email="DK96@bremen.com.tw",
|
|
session_key="webui:user-42",
|
|
)
|
|
assert candidates[0] == "principal:p-123"
|
|
assert "email:dk96@bremen.com.tw" in candidates
|
|
assert "user:openwebui-42" in candidates
|
|
assert "key:webui:user-42" in candidates
|
|
|
|
|
|
class TestSessionListScope:
|
|
def test_list_sessions_uses_resolved_owner_scope(self, monkeypatch):
|
|
adapter = _make_adapter()
|
|
db = MagicMock()
|
|
db.list_sessions_rich.return_value = []
|
|
adapter._session_db = db
|
|
req = _request({"Authorization": "Bearer sk-secret", "X-Hermes-Session-Key": "webui:user-42", "X-Hermes-User-Id": "openwebui-42"})
|
|
monkeypatch.setattr(adapter, "_session_owner_from_request_body", lambda request, body=None: "principal:p-123")
|
|
|
|
import asyncio
|
|
|
|
resp = asyncio.run(adapter._handle_list_sessions(req))
|
|
assert resp.status == 200
|
|
kwargs = db.list_sessions_rich.call_args.kwargs
|
|
assert kwargs["user_id"] == "principal:p-123"
|
|
|
|
|
|
class TestSessionCreateOwner:
|
|
def test_create_session_persists_owner_from_resolved_identity(self, monkeypatch):
|
|
adapter = _make_adapter()
|
|
db = MagicMock()
|
|
db.get_session.side_effect = [None, {"id": "sess-1", "source": "api_server", "user_id": "principal:p-123"}]
|
|
adapter._session_db = db
|
|
req = _request({"Authorization": "Bearer sk-secret", "X-Hermes-Session-Key": "webui:user-42", "X-Hermes-User-Id": "openwebui-42"})
|
|
|
|
async def _json():
|
|
return {"id": "sess-1", "model": "hermes-agent", "metadata": {"user_id": "openwebui-42", "user_email": "dk96@bremen.com.tw"}}
|
|
|
|
req.json = _json
|
|
monkeypatch.setattr(adapter, "_session_owner_from_request_body", lambda request, body=None: "principal:p-123")
|
|
|
|
import asyncio
|
|
|
|
resp = asyncio.run(adapter._handle_create_session(req))
|
|
assert resp.status == 201
|
|
kwargs = db.create_session.call_args.kwargs
|
|
assert kwargs["user_id"] == "principal:p-123"
|