loomio: only rename auto-generated handles, never one a human chose
Testing the previous commit against the live Loomio user list showed it would also have renamed a member whose handle was chosen by hand (a working name would have become the sanitized OIDC uid) — the uid is domain-shaped, so the sanitizer produced a different, wrong target. The function is scoped to Loomio's own auto-generated handles: the email local part with non-alphanumerics stripped plus a random suffix. Gate on that shape. Verified against the live user list (70 users) before building: exactly one handle changes — the member whose username fix 422'd on every sweep — no computed target violates Loomio's rule, and the hand-chosen handle is left alone.
This commit is contained in:
1 parent
178a4aa360
commit
492019088e
1 file changed
+19
+19
@@ -1007,6 +1007,20 @@ def _loomio_safe_username(raw) -> str | None:
|
||||
return u if len(u) >= 2 else None
|
||||
|
||||
|
||||
def _loomio_is_autogenerated(current: str, email: str) -> bool:
|
||||
"""Is this handle one Loomio generated for us?
|
||||
|
||||
Loomio derives a handle from the email local part with non-alphanumerics
|
||||
stripped, plus a random suffix ("joshuaztan" + "xjan5rzb8j27" for
|
||||
joshua.z.tan@...). Anything that is not that shape — "amarino", say — was
|
||||
chosen by a person, and renaming it would be an unrequested member-visible
|
||||
change. Only auto-generated handles are in scope for this fix.
|
||||
"""
|
||||
local = re.sub(r"[^a-z0-9]", "", (email or "").split("@")[0].lower())
|
||||
cur = (current or "").lower()
|
||||
return bool(local) and cur.startswith(local) and len(cur) > len(local)
|
||||
|
||||
|
||||
async def loomio_fix_usernames(client: httpx.AsyncClient) -> dict:
|
||||
"""Rename Loomio users with auto-generated handles to their Cloudron username.
|
||||
|
||||
@@ -1023,6 +1037,11 @@ async def loomio_fix_usernames(client: httpx.AsyncClient) -> dict:
|
||||
|
||||
fixed, skipped = 0, 0
|
||||
for u in users:
|
||||
# Only Loomio's own auto-generated handles are in scope: a name a human
|
||||
# chose stays, even when it differs from the Cloudron username.
|
||||
if not _loomio_is_autogenerated(u.get("username") or "", u.get("email") or ""):
|
||||
skipped += 1
|
||||
continue
|
||||
target = None
|
||||
for ident in u.get("identities") or []:
|
||||
if ident.get("identity_type") == "oauth" and ident.get("uid"):
|
||||
|
||||
Reference in new issue
Block a user