"""Offline bootstrap authorization: no anonymous or client-selected routing.""" import importlib.util import json from pathlib import Path from unittest.mock import patch import httpx from fastapi.testclient import TestClient spec = importlib.util.spec_from_file_location('hope_token', Path(__file__).parents[2] / 'web/token_server.py') module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) def test_anonymous_and_cookie_origin_denied(): with patch.multiple(module, CONTROL_SECRET='x'*32, API_KEY='key', API_SECRET='secret'): client = TestClient(module.app) assert client.post('/token').status_code == 401 assert client.post('/token', headers={'Cookie':'hope_session='+'y'*40,'Origin':'https://evil.invalid'}).status_code == 403 def test_authenticated_route_is_server_derived(): seen=[] uid='a'*32 def handler(request): body=json.loads(request.content) seen.append((request.url.path,body)) assert request.headers['X-Control-Secret']=='x'*32 if request.url.path.endswith('resolve-session'): return httpx.Response(200,json={'user':{'id':uid,'display_name':'John Doe'},'hermes_url':'http://127.0.0.1:1234','hermes_api_key':'private'}) assert body['session_token']=='y'*40 assert body['user_id']==uid assert body['room_name'].startswith('voice-'+uid+'-') return httpx.Response(201,json={}) original=httpx.AsyncClient def factory(**kwargs): return original(transport=httpx.MockTransport(handler),**kwargs) with patch.multiple(module,CONTROL_SECRET='x'*32,API_KEY='key',API_SECRET='secret'),patch.object(module.httpx,'AsyncClient',factory): response=TestClient(module.app).post('/token',headers={'Authorization':'Bearer '+'y'*40},json={'user_id':'victim','room':'victim'}) assert response.status_code==200 body=response.json() assert body['user']['id']==uid assert 'hermes_api_key' not in body import base64 claims=json.loads(base64.urlsafe_b64decode(body['token'].split('.')[1]+'==')) assert claims['sub']=='user:'+uid assert claims['video']['canPublishSources']==['microphone'] assert claims['exp']-claims['nbf']==305 assert response.headers['Cache-Control']=='no-store'