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:
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

View File

@ -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="",