vaultwarden: skip sync when no pending work
This commit is contained in:
parent
50561b7957
commit
698c5683c8
@ -150,6 +150,27 @@ def _cooldown_active(status: str, synced_ts: float | None) -> bool:
|
|||||||
return time.time() - synced_ts < settings.vaultwarden_retry_cooldown_sec
|
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:
|
def _set_sync_status(username: str, status: str) -> None:
|
||||||
try:
|
try:
|
||||||
_set_user_attribute(username, VAULTWARDEN_STATUS_ATTR, status)
|
_set_user_attribute(username, VAULTWARDEN_STATUS_ATTR, status)
|
||||||
@ -234,7 +255,15 @@ def run_vaultwarden_sync() -> VaultwardenSyncSummary:
|
|||||||
)
|
)
|
||||||
return summary
|
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:
|
for user in users:
|
||||||
status, ok = _sync_user(user, counters)
|
status, ok = _sync_user(user, counters)
|
||||||
if status is None:
|
if status is None:
|
||||||
|
|||||||
@ -39,6 +39,32 @@ def test_vaultwarden_sync_requires_admin(monkeypatch) -> None:
|
|||||||
assert summary.detail == "keycloak admin not configured"
|
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:
|
def test_vaultwarden_sync_skips_when_missing_mailbox(monkeypatch) -> None:
|
||||||
dummy = DummyAdmin(
|
dummy = DummyAdmin(
|
||||||
users=[{"id": "1", "username": "alice", "enabled": True, "attributes": {"mailu_email": ["alice@bstein.dev"]}}],
|
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)
|
monkeypatch.setattr(vaultwarden_sync.mailu, "mailbox_exists", lambda email: True)
|
||||||
|
|
||||||
summary = vaultwarden_sync.run_vaultwarden_sync()
|
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:
|
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)
|
monkeypatch.setattr(vaultwarden_sync, "keycloak_admin", dummy)
|
||||||
|
|
||||||
summary = vaultwarden_sync.run_vaultwarden_sync()
|
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:
|
def test_vaultwarden_sync_get_user_failure(monkeypatch) -> None:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user