3 Commits

Author SHA1 Message Date
librelad
5b6ed924d2 fix(chat tools): wire list_users into the WebUI user-list modal
The Tools tab has an interactive modal: when a list_users task completes it
parses the task log for EZ_USER lines and renders one row per account with
reset / promote / delete buttons. All four new apps failed its contract in every
respect, so running List Users produced log text and nothing else.

- The marker is EZ_USER, tab-separated as email, username, roles. Matrix and
  Stoat emitted LP_USER in a different field order; Mattermost and Rocket.Chat
  emitted no marker at all.
- Matrix and Stoat then consumed their own marker lines in the formatting loop
  and printed only the pretty version, so nothing reached the log to parse.
- The row buttons look up tools by id: reset_password, set_admin, delete_user.
  The deactivate tools were named deactivate_user / disable_user, so no delete
  button rendered.
- Prefill only fills a field named email or username. Rocket.Chat's and Stoat's
  identifier field was called user, so a row action would have opened with an
  empty box.
- '-' placeholders are truthy, so the modal's `email || username` fallback
  picked '-' over the real username for accounts without an email (rocket.cat).
  The EZ_USER line now carries an empty string; '-' stays in the readable line.

Mattermost's listing is rebuilt on `mmctl --json`, which carries roles and
delete_at. The text listing has neither, and there is no --system-admin filter
on user list, so every account was reported as a plain user. Two parsing notes
that cost time: mmctl prints status lines both before and after the JSON, so it
needs raw_decode rather than json.loads; and --per-page above 200 makes it emit
a warning line ahead of the payload.

The modal's delete button also stops asserting "Delete user" over whatever the
tool actually does — it takes its label and icon from the tool, because most of
these deactivate and Matrix cannot delete at all.

Verified by replaying the modal's own parser over real tool output: 2 rows for
Matrix, 4 for Mattermost, 3 for Rocket.Chat, with admin and deactivated states
resolving correctly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 23:23:12 +01:00
librelad
4b6b05db81 fix(rocketchat): correct the roles call, satisfy the password policy, add enable
Three things running the tools against a live instance exposed:

- roles.addUserToRole takes roleId + username and nothing else. Passing roleName
  fails schema validation with "must NOT have additional properties", and
  roleId + userId is refused for a missing username. Set admin was broken in
  both directions.

- Rocket.Chat enables a password policy by default demanding lower, upper, digit
  AND special at 14+ characters, while generateRandomPassword is alphanumeric.
  Reset failed with "does not meet the server's password policy". Notably
  users.create does NOT enforce the policy, which is why creating an account
  worked and resetting the same account's password did not — an inconsistency
  worth knowing about rather than guessing at. Generated passwords now carry one
  character from each class appended, leaving the generated entropy untouched.

- Deactivation had no counterpart, so "reversible from Admin → Users" was only
  true if you left the WebUI. Adds an Enable tool, matching Stoat's.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 21:39:42 +01:00
librelad
5835fa09d7 fix(auth): only generate an admin password where something creates the account
Answers "should we stop creating an admin/pass on start" with the split the
catalog actually has, rather than one way for everything.

Ten apps need it: adguard, authelia, bookstack, matrix, nextcloud, owncloud,
pihole, rocketchat, stalwart, speedtest and headscale either pass the generated
password into the container or hand it to an install hook that creates the
account. There the password IS the working credential — dropping it would lock
you out. Left alone.

Three do not create an account at all: gitea, invidious and mattermost seed no
user (the first one comes from their own signup flow or the Create Account tool),
so the password minted at install named nothing. The WebUI credentials card
showed a password that could not log in. They now match linkding — an empty,
unslotted ADMIN_PASSWORD the auth adapter fills when the operator makes the first
admin, and keeps in step on later resets. Unslotted because the slot number marks
a value the installer generates.

mattermost's adapter also had linkding's bug: it persists ADMIN_PASSWORD but the
config declared only ADMIN_EMAIL, so the write was a no-op.

WebUI: rocketchat's generated admin password had no field mapping, so the card
could not show it. Added, plus a generic ADMIN_USER entry — six apps record an
admin username the card had no way to display.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 21:31:05 +01:00