fix: repair vaultwarden admin session

This commit is contained in:
Brad Stein 2026-01-20 00:06:50 -03:00
parent ce66744a9e
commit f0f5164942
2 changed files with 58 additions and 7 deletions

View File

@ -20,7 +20,7 @@ class VaultwardenInvite:
class VaultwardenService: class VaultwardenService:
def __init__(self) -> None: def __init__(self) -> None:
self._admin_lock = threading.Lock() 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_expires_at: float = 0.0
self._admin_session_base_url: str = "" self._admin_session_base_url: str = ""
self._rate_limited_until: float = 0.0 self._rate_limited_until: float = 0.0
@ -84,15 +84,15 @@ class VaultwardenService:
with self._admin_lock: with self._admin_lock:
if self._rate_limited_until and now < self._rate_limited_until: if self._rate_limited_until and now < self._rate_limited_until:
raise RuntimeError("vaultwarden rate limited") raise RuntimeError("vaultwarden rate limited")
if self._admin_session and now < self._admin_session_expires_at and self._admin_session_base_url == base_url: if self._admin_client and now < self._admin_session_expires_at and self._admin_session_base_url == base_url:
return self._admin_session return self._admin_client
if self._admin_session: if self._admin_client:
try: try:
self._admin_session.close() self._admin_client.close()
except Exception: except Exception:
pass pass
self._admin_session = None self._admin_client = None
token = get_secret_value( token = get_secret_value(
settings.vaultwarden_namespace, settings.vaultwarden_namespace,
@ -112,7 +112,7 @@ class VaultwardenService:
raise RuntimeError("vaultwarden rate limited") raise RuntimeError("vaultwarden rate limited")
resp.raise_for_status() resp.raise_for_status()
self._admin_session = client self._admin_client = client
self._admin_session_base_url = base_url self._admin_session_base_url = base_url
self._admin_session_expires_at = now + float(settings.vaultwarden_admin_session_ttl_sec) self._admin_session_expires_at = now + float(settings.vaultwarden_admin_session_ttl_sec)
return client return client

View File

@ -8,6 +8,7 @@ from ariadne.services.firefly import FireflyService
from ariadne.services.mailu import MailuService from ariadne.services.mailu import MailuService
from ariadne.services.nextcloud import NextcloudService from ariadne.services.nextcloud import NextcloudService
from ariadne.services.wger import WgerService from ariadne.services.wger import WgerService
from ariadne.services.vaultwarden import VaultwardenService
class DummySpawner: class DummySpawner:
@ -43,6 +44,27 @@ class DummyClient:
return types.SimpleNamespace(status_code=self.status_code) 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: def test_nextcloud_sync_mail_builds_env(monkeypatch) -> None:
dummy = types.SimpleNamespace( dummy = types.SimpleNamespace(
nextcloud_namespace="nextcloud", nextcloud_namespace="nextcloud",
@ -130,6 +152,35 @@ def test_mailu_sync_includes_force(monkeypatch) -> None:
assert client.payload["force"] is True 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: def test_nextcloud_missing_config(monkeypatch) -> None:
dummy = types.SimpleNamespace( dummy = types.SimpleNamespace(
nextcloud_namespace="", nextcloud_namespace="",