feat(multi-tenant): resolve per-caller credentials in every tool
All 20 tools now drop the athlete_id/api_key parameters and instead resolve the authenticated caller's stored, enabled credentials via credentials.resolve_caller_credentials() (get_access_token().subject -> store). Security: there is no tool parameter a caller can pass to supply a key, so a disabled/unapproved user cannot bypass the admin-approval gate — each tool returns a helpful "not approved / set up your credentials" message instead. Gear resolution now uses the caller's athlete id rather than an env var. Tests: conftest autouse fixture runs tool tests as an enabled user; a parametrized test asserts every tool refuses when unauthorized; existing tool tests updated (no more athlete_id/api_key kwargs). 221 passing at 91.5%. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -51,6 +51,20 @@ def test_no_token_falls_back_to_env_config(monkeypatch):
|
||||
assert asyncio.run(resolve_caller_credentials()) == ("i999", "envkey")
|
||||
|
||||
|
||||
def test_get_access_token_raising_is_treated_as_no_context(monkeypatch):
|
||||
# outside a request the SDK accessor may raise; that must fall back to env config
|
||||
def _boom():
|
||||
raise RuntimeError("no request context")
|
||||
|
||||
monkeypatch.setattr(credentials, "get_access_token", _boom)
|
||||
monkeypatch.setattr(
|
||||
credentials,
|
||||
"get_config",
|
||||
lambda: Config(api_key="envkey", athlete_id="i999", intervals_api_base_url="x", user_agent="t"),
|
||||
)
|
||||
assert asyncio.run(resolve_caller_credentials()) == ("i999", "envkey")
|
||||
|
||||
|
||||
def test_no_token_no_env_raises(monkeypatch):
|
||||
monkeypatch.setattr(credentials, "get_access_token", lambda: None)
|
||||
monkeypatch.setattr(
|
||||
|
||||
Reference in New Issue
Block a user