From 698c5683c82da26dcca51abaf940bbde7859868b Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 21 Jan 2026 13:36:04 -0300 Subject: [PATCH] vaultwarden: skip sync when no pending work --- ariadne/services/vaultwarden_sync.py | 31 +++++++++++++++++++++++++++- tests/test_vaultwarden_sync.py | 30 +++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/ariadne/services/vaultwarden_sync.py b/ariadne/services/vaultwarden_sync.py index de63158..2044e03 100644 --- a/ariadne/services/vaultwarden_sync.py +++ b/ariadne/services/vaultwarden_sync.py @@ -150,6 +150,27 @@ def _cooldown_active(status: str, synced_ts: float | None) -> bool: return time.time() - synced_ts < settings.vaultwarden_retry_cooldown_sec +def _has_pending_failures(users: list[dict[str, Any]]) -> bool: + for user in users: + username = (user.get("username") if isinstance(user.get("username"), str) else "") or "" + username = username.strip() + if not username or user.get("enabled") is False: + continue + if user.get("serviceAccountClientId") or username.startswith("service-account-"): + continue + attrs = user.get("attributes") if isinstance(user.get("attributes"), dict) else {} + status = _extract_attr(attrs, VAULTWARDEN_STATUS_ATTR) + synced_at = _extract_attr(attrs, VAULTWARDEN_SYNCED_AT_ATTR) + synced_ts = _parse_synced_at(synced_at) + if not status: + return True + if status in {"invited", "already_present"} and not synced_at: + return True + if status in {"error", "rate_limited"} and not _cooldown_active(status, synced_ts): + return True + return False + + def _set_sync_status(username: str, status: str) -> None: try: _set_user_attribute(username, VAULTWARDEN_STATUS_ATTR, status) @@ -234,7 +255,15 @@ def run_vaultwarden_sync() -> VaultwardenSyncSummary: ) return summary - users = keycloak_admin.iter_users(page_size=200, brief=False) + users = list(keycloak_admin.iter_users(page_size=200, brief=False)) + if not _has_pending_failures(users): + summary = counters.summary(detail="no pending failures") + logger.info( + "vaultwarden sync skipped", + extra={"event": "vaultwarden_sync", "status": "skip", "detail": summary.detail}, + ) + return summary + for user in users: status, ok = _sync_user(user, counters) if status is None: diff --git a/tests/test_vaultwarden_sync.py b/tests/test_vaultwarden_sync.py index 957f841..37da8d8 100644 --- a/tests/test_vaultwarden_sync.py +++ b/tests/test_vaultwarden_sync.py @@ -39,6 +39,32 @@ def test_vaultwarden_sync_requires_admin(monkeypatch) -> None: assert summary.detail == "keycloak admin not configured" +def test_vaultwarden_sync_skips_without_pending_failures(monkeypatch) -> None: + now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") + dummy = DummyAdmin( + users=[ + { + "id": "1", + "username": "alice", + "enabled": True, + "attributes": {"vaultwarden_status": ["invited"], "vaultwarden_synced_at": [now]}, + } + ], + attrs={ + "1": { + "id": "1", + "username": "alice", + "attributes": {"vaultwarden_status": ["invited"], "vaultwarden_synced_at": [now]}, + } + }, + ) + monkeypatch.setattr(vaultwarden_sync, "keycloak_admin", dummy) + + summary = vaultwarden_sync.run_vaultwarden_sync() + assert summary.detail == "no pending failures" + assert summary.processed == 0 + + def test_vaultwarden_sync_skips_when_missing_mailbox(monkeypatch) -> None: dummy = DummyAdmin( users=[{"id": "1", "username": "alice", "enabled": True, "attributes": {"mailu_email": ["alice@bstein.dev"]}}], @@ -103,7 +129,7 @@ def test_vaultwarden_sync_respects_retry_cooldown(monkeypatch) -> None: monkeypatch.setattr(vaultwarden_sync.mailu, "mailbox_exists", lambda email: True) summary = vaultwarden_sync.run_vaultwarden_sync() - assert summary.skipped == 1 + assert summary.detail == "no pending failures" def test_vaultwarden_sync_bails_after_failures(monkeypatch) -> None: @@ -271,7 +297,7 @@ def test_vaultwarden_sync_skips_disabled_and_service_accounts(monkeypatch) -> No monkeypatch.setattr(vaultwarden_sync, "keycloak_admin", dummy) summary = vaultwarden_sync.run_vaultwarden_sync() - assert summary.skipped == 3 + assert summary.detail == "no pending failures" def test_vaultwarden_sync_get_user_failure(monkeypatch) -> None: