From f0f5164942a4d07434a268d7746e7bf4d4d6549c Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Tue, 20 Jan 2026 00:06:50 -0300 Subject: [PATCH] fix: repair vaultwarden admin session --- ariadne/services/vaultwarden.py | 14 ++++----- tests/test_services.py | 51 +++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/ariadne/services/vaultwarden.py b/ariadne/services/vaultwarden.py index 263d4ef..b89e131 100644 --- a/ariadne/services/vaultwarden.py +++ b/ariadne/services/vaultwarden.py @@ -20,7 +20,7 @@ class VaultwardenInvite: class VaultwardenService: def __init__(self) -> None: self._admin_lock = threading.Lock() - self._admin_session: httpx.Client | None = None + self._admin_client: httpx.Client | None = None self._admin_session_expires_at: float = 0.0 self._admin_session_base_url: str = "" self._rate_limited_until: float = 0.0 @@ -84,15 +84,15 @@ class VaultwardenService: with self._admin_lock: if self._rate_limited_until and now < self._rate_limited_until: raise RuntimeError("vaultwarden rate limited") - if self._admin_session and now < self._admin_session_expires_at and self._admin_session_base_url == base_url: - return self._admin_session + if self._admin_client and now < self._admin_session_expires_at and self._admin_session_base_url == base_url: + return self._admin_client - if self._admin_session: + if self._admin_client: try: - self._admin_session.close() + self._admin_client.close() except Exception: pass - self._admin_session = None + self._admin_client = None token = get_secret_value( settings.vaultwarden_namespace, @@ -112,7 +112,7 @@ class VaultwardenService: raise RuntimeError("vaultwarden rate limited") resp.raise_for_status() - self._admin_session = client + self._admin_client = client self._admin_session_base_url = base_url self._admin_session_expires_at = now + float(settings.vaultwarden_admin_session_ttl_sec) return client diff --git a/tests/test_services.py b/tests/test_services.py index 5acef9a..a8bd9ea 100644 --- a/tests/test_services.py +++ b/tests/test_services.py @@ -8,6 +8,7 @@ from ariadne.services.firefly import FireflyService from ariadne.services.mailu import MailuService from ariadne.services.nextcloud import NextcloudService from ariadne.services.wger import WgerService +from ariadne.services.vaultwarden import VaultwardenService class DummySpawner: @@ -43,6 +44,27 @@ class DummyClient: return types.SimpleNamespace(status_code=self.status_code) +class DummyResponse: + def __init__(self, status_code=200, text=""): + self.status_code = status_code + self.text = text + + def raise_for_status(self): + return None + + +class DummyVaultwardenClient: + def __init__(self): + self.calls = [] + + def post(self, path, json=None, data=None): + self.calls.append((path, json, data)) + return DummyResponse(200, "") + + def close(self): + return None + + def test_nextcloud_sync_mail_builds_env(monkeypatch) -> None: dummy = types.SimpleNamespace( nextcloud_namespace="nextcloud", @@ -130,6 +152,35 @@ def test_mailu_sync_includes_force(monkeypatch) -> None: assert client.payload["force"] is True +def test_vaultwarden_invite_uses_admin_session(monkeypatch) -> None: + dummy_settings = types.SimpleNamespace( + vaultwarden_namespace="vaultwarden", + vaultwarden_admin_secret_name="vaultwarden-admin", + vaultwarden_admin_secret_key="ADMIN_TOKEN", + vaultwarden_admin_rate_limit_backoff_sec=600, + vaultwarden_admin_session_ttl_sec=900, + vaultwarden_service_host="vaultwarden-service.vaultwarden.svc.cluster.local", + vaultwarden_pod_label="app=vaultwarden", + vaultwarden_pod_port=80, + ) + client = DummyVaultwardenClient() + + monkeypatch.setattr("ariadne.services.vaultwarden.settings", dummy_settings) + monkeypatch.setattr("ariadne.services.vaultwarden.get_secret_value", lambda *args, **kwargs: "token") + monkeypatch.setattr("ariadne.services.vaultwarden.httpx.Client", lambda *args, **kwargs: client) + monkeypatch.setattr( + "ariadne.services.vaultwarden.VaultwardenService._find_pod_ip", + staticmethod(lambda *args, **kwargs: "127.0.0.1"), + ) + + svc = VaultwardenService() + result = svc.invite_user("alice@bstein.dev") + + assert result.ok is True + assert any(call[0] == "/admin" for call in client.calls) + assert any(call[0] == "/admin/invite" for call in client.calls) + + def test_nextcloud_missing_config(monkeypatch) -> None: dummy = types.SimpleNamespace( nextcloud_namespace="",