fix(users): shorten the platform administrator to a name Discourse accepts

526cb7e212 dropped the hyphen so Keycloak and Mastodon would take the
name, and left it one character too long for the third consumer. The
Discourse job of run 37799529673 fails while applying the credential:

    TASK [web-app-discourse : 🔑 Ensure the administrator account carries the current password]
    Validation failed: Username must be no more than 20 characters (ActiveRecord::RecordInvalid)

platform-administrator is 22 characters and platformadministrator is 21, so
removing the separator cleared the character rule without clearing the
length one, and the second consumer only reported once the first was
satisfied. The name is now platformadmin, 13 characters, checked against
all four constraints at once: the Keycloak pattern, the Discourse ceiling,
the 304 reserved usernames and the names already taken.

The role does not configure max_username_length, so Discourse's default of
20 governs. Raising that setting would also have made the deploy pass, but
it widens an application's identity policy to fit an account name we
choose, which is the wrong direction.

The lint gains the ceiling behind the character rule it already enforced,
so the next compound name is rejected locally rather than by a Rails
validation mid-deploy.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kevinveenbirkenbachandClaude Opus 5 committed 2026-10-08 21:25:47 +02:00
1 parent 9bea4c3bb6
commit feccf1c281
2 files changed
+25 -9

No files matched your search

+1 -1
View File
@@ -3,7 +3,7 @@ platform_administrator:
description: Application administrator for {{ ORGANIZATION }} on {{ DOMAIN_PRIMARY }}
firstname: Platform
lastname: Administrator
username: platformadministrator
username: platformadmin
authorized_keys: []
accounts: [mailbox, identity]
@@ -8,6 +8,7 @@ from utils.cache.users import get_user_defaults
from . import PROJECT_ROOT
IDENTITY_USERNAME = re.compile(r"^[a-z0-9]+$")
IDENTITY_USERNAME_MAX = 20
class TestIdentityUsernameCharset(unittest.TestCase):
@@ -29,20 +30,35 @@ class TestIdentityUsernameCharset(unittest.TestCase):
for a compound account name and every other hyphenated user in the
repository is exempt by having no ``identity`` account, so nothing
catches it until a realm import or a tootctl call runs.
The length ceiling is Discourse's, which answers a longer name with
``Validation failed: Username must be no more than 20 characters
(ActiveRecord::RecordInvalid)``. It sits behind the character rule:
dropping a hyphen shortens a name by one, so a compound name that
was too long stays too long, and the second consumer only speaks up
once the first is satisfied.
"""
users = get_user_defaults(roles_dir=str(PROJECT_ROOT / "roles"))
offenders = sorted(
f"{key}: {entry['username']}"
for key, entry in users.items()
if "identity" in (entry.get("accounts") or [])
and not IDENTITY_USERNAME.match(str(entry.get("username", "")))
)
offenders: list[str] = []
for key, entry in sorted(users.items()):
if "identity" not in (entry.get("accounts") or []):
continue
username = str(entry.get("username", ""))
if not IDENTITY_USERNAME.match(username):
offenders.append(
f"{key}: {username!r} is not lowercase alphanumeric"
)
elif len(username) > IDENTITY_USERNAME_MAX:
offenders.append(
f"{key}: {username!r} is {len(username)} characters, "
f"over the {IDENTITY_USERNAME_MAX} Discourse allows"
)
if offenders:
self.fail(
"These users are registered as an identity but carry a "
"username Keycloak and Mastodon both refuse (lowercase "
"letters and digits only):\n - " + "\n - ".join(offenders)
"username one of Keycloak, Mastodon or Discourse refuses:\n"
" - " + "\n - ".join(offenders)
)