loomio: sanitize usernames instead of falling back to another invalid one
The username fix rejected a dot-containing OAuth uid and then substituted the
email local part, which carries the same dots — so Loomio answered 422
("Usernames must contain lower case letters, numbers, underscores, and internal
hyphens only") on every sweep, for the one member whose Cloudron username has a
dot. Normalize instead: disallowed characters become underscores, and leading
and trailing hyphens/underscores are stripped. Also log the Loomio user id
rather than the member's email.
This commit is contained in:
1 parent
e24c272e2e
commit
178a4aa360
1 file changed
+21
-11
+21
-11
@@ -995,6 +995,18 @@ def set_member_matrix_mxid(email: str, mxid: str) -> None:
|
|||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
def _loomio_safe_username(raw) -> str | None:
|
||||||
|
"""Sanitize a candidate into a Loomio-legal username.
|
||||||
|
|
||||||
|
Loomio accepts lowercase letters, numbers, underscores and INTERNAL hyphens
|
||||||
|
only, so a dot-containing candidate — an OAuth uid or an email local part
|
||||||
|
such as "joshua.z.tan" — must be normalized rather than passed through.
|
||||||
|
Passing it through is what made this fix fail with HTTP 422 on every sweep.
|
||||||
|
"""
|
||||||
|
u = re.sub(r"[^a-z0-9_-]", "_", (raw or "").lower()).strip("-_")
|
||||||
|
return u if len(u) >= 2 else None
|
||||||
|
|
||||||
|
|
||||||
async def loomio_fix_usernames(client: httpx.AsyncClient) -> dict:
|
async def loomio_fix_usernames(client: httpx.AsyncClient) -> dict:
|
||||||
"""Rename Loomio users with auto-generated handles to their Cloudron username.
|
"""Rename Loomio users with auto-generated handles to their Cloudron username.
|
||||||
|
|
||||||
@@ -1014,16 +1026,13 @@ async def loomio_fix_usernames(client: httpx.AsyncClient) -> dict:
|
|||||||
target = None
|
target = None
|
||||||
for ident in u.get("identities") or []:
|
for ident in u.get("identities") or []:
|
||||||
if ident.get("identity_type") == "oauth" and ident.get("uid"):
|
if ident.get("identity_type") == "oauth" and ident.get("uid"):
|
||||||
uid = ident["uid"].lower()
|
target = _loomio_safe_username(ident["uid"])
|
||||||
# Loomio usernames allow lowercase letters, numbers, underscores and hyphens.
|
if not target:
|
||||||
if uid.replace("_", "").replace("-", "").isalnum() and len(uid) >= 2:
|
# uid unusable even after sanitizing — fall back to the email local part
|
||||||
target = uid
|
target = _loomio_safe_username((u.get("email") or "").split("@")[0])
|
||||||
break
|
break
|
||||||
# uid unusable (dots etc) — use email local-part instead
|
if not target:
|
||||||
target = (u.get("email") or "").split("@")[0].lower() or None
|
target = _loomio_safe_username((u.get("email") or "").split("@")[0])
|
||||||
break
|
|
||||||
if not target and u.get("email") and "@" in u["email"]:
|
|
||||||
target = u["email"].split("@")[0].lower()
|
|
||||||
if not target or u.get("username") == target:
|
if not target or u.get("username") == target:
|
||||||
skipped += 1
|
skipped += 1
|
||||||
continue
|
continue
|
||||||
@@ -1034,8 +1043,9 @@ async def loomio_fix_usernames(client: httpx.AsyncClient) -> dict:
|
|||||||
if rr.status_code == 200:
|
if rr.status_code == 200:
|
||||||
fixed += 1
|
fixed += 1
|
||||||
else:
|
else:
|
||||||
logger.warning("Loomio username fix for %s failed: %s %s",
|
# log the Loomio user id, never the member's email
|
||||||
u.get("email"), rr.status_code, rr.text[:120])
|
logger.warning("Loomio username fix for user %s failed: %s %s",
|
||||||
|
u.get("id"), rr.status_code, rr.text[:120])
|
||||||
if fixed:
|
if fixed:
|
||||||
logger.info("Loomio usernames fixed: %s", fixed)
|
logger.info("Loomio usernames fixed: %s", fixed)
|
||||||
return {"fixed": fixed, "skipped": skipped}
|
return {"fixed": fixed, "skipped": skipped}
|
||||||
|
|||||||
Reference in new issue
Block a user