vaultwarden: make cred sync idempotent

This commit is contained in:
Brad Stein 2026-01-03 18:18:31 -03:00
parent 12348258fa
commit 51a733096f

View File

@ -27,7 +27,9 @@ def _iter_keycloak_users(page_size: int = 200) -> Iterable[dict[str, Any]]:
first = 0
while True:
headers = client.headers()
params = {"first": str(first), "max": str(page_size)}
# We need attributes for idempotency (vaultwarden_status/vaultwarden_email). Keycloak defaults to a
# brief representation which may omit these.
params = {"first": str(first), "max": str(page_size), "briefRepresentation": "false"}
with httpx.Client(timeout=settings.HTTP_CHECK_TIMEOUT_SEC) as http:
resp = http.get(url, params=params, headers=headers)
resp.raise_for_status()
@ -79,7 +81,9 @@ def _vaultwarden_email_for_user(user: dict[str, Any]) -> str:
if email and email.lower().endswith(f"@{settings.MAILU_DOMAIN.lower()}"):
return email
return f"{username}@{settings.MAILU_DOMAIN}"
# Don't guess an internal mailbox address until Mailu sync has run and stored mailu_email.
# This avoids spamming Vaultwarden invites that can never be delivered (unknown recipient).
return ""
def _set_user_attribute_if_missing(username: str, user: dict[str, Any], key: str, value: str) -> None:
@ -121,6 +125,7 @@ def main() -> int:
skipped += 1
continue
current_status = _extract_attr(user.get("attributes"), VAULTWARDEN_STATUS_ATTR)
email = _vaultwarden_email_for_user(user)
if not email:
print(f"skip {username}: missing email", file=sys.stderr)
@ -132,6 +137,12 @@ def main() -> int:
except Exception:
pass
# If we've already successfully invited or confirmed presence, do not re-invite on every cron run.
# Vaultwarden returns 409 for "already exists", which is idempotent but noisy and can trigger rate limits.
if current_status in {"invited", "already_present"}:
skipped += 1
continue
processed += 1
result = invite_user(email)
if result.ok: