Five Tools-tab actions: create account, list users, reset password, set admin, deactivate. Driven by mmctl --local, which talks to the server's unix socket rather than the REST API — no credentials to store, no token to expire, and it keeps working when the admin account is locked out or the site URL is wrong. mmctl is also the only route available: the v11 image is distroless with no shell at all, so every call has to be a direct exec of a binary. Two things found by running them: - `user promote` / `user demote` convert between GUEST and member accounts and have nothing to do with administrator rights. Granting system admin is `roles system-admin` / `roles member`. The first version used the wrong pair and failed with "Unable to convert the guest to regular user because is not a guest." - mmctl errors are multi-line: a summary line, then an indented bullet carrying the part that explains anything. Reporting the first line alone surfaced "1 error occurred:" and threw the reason away. Deactivate rather than delete, deliberately: Mattermost's delete is a permanent content purge, which is not something a single WebUI button should do. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
122 lines
4.8 KiB
Bash
122 lines
4.8 KiB
Bash
#!/bin/bash
|
|
|
|
# Mattermost user management, via mmctl in local mode.
|
|
#
|
|
# mmctl is the current CLI — not the long-deprecated `mattermost` binary that
|
|
# used to ship alongside it. It lives in the image at /usr/local/bin/mmctl and
|
|
# is the only reason these tools are possible at all: the v11 image is
|
|
# distroless, with no shell, so every call has to be a direct exec of a binary
|
|
# with no pipes, redirects or shell built-ins available.
|
|
#
|
|
# --local talks to the server's unix socket instead of the REST API, which means
|
|
# no credentials to store, no token to expire, and it keeps working even when
|
|
# the admin account is locked out or the site URL is wrong.
|
|
|
|
_mmctl() {
|
|
runFileOp docker exec -i mattermost-service mmctl --local "$@" 2>&1
|
|
}
|
|
|
|
# mmctl exits non-zero on failure and writes the reason to stderr, which _mmctl
|
|
# folds into stdout.
|
|
#
|
|
# Its errors are multi-line — a summary ("Error: 1 error occurred:") followed by
|
|
# an indented bullet carrying the part that actually explains anything. Reporting
|
|
# only the first line threw the useful half away, so prefer the bullet when
|
|
# there is one.
|
|
_mmctlFailed() {
|
|
local out="$1" what="$2"
|
|
[[ "$out" != *"Error:"* ]] && return 1
|
|
local detail
|
|
detail=$(printf '%s' "$out" | sed -n 's/^[[:space:]]*\*[[:space:]]*//p' | head -1)
|
|
[[ -z "$detail" ]] && detail=$(printf '%s' "$out" | grep -m1 'Error:' | sed 's/.*Error: *//')
|
|
isError "$what failed: $detail"
|
|
return 0
|
|
}
|
|
|
|
authAdapter_mattermost_createUser() {
|
|
local email="$1" password="$2" username="$3" isAdmin="$4"
|
|
[[ -z "$email" ]] && { isError "An email address is required."; return 1; }
|
|
[[ -z "$username" ]] && username="${email%@*}"
|
|
[[ -z "$password" ]] && password=$(generateRandomPassword)
|
|
|
|
# Mattermost usernames are lowercase and restricted to letters, numbers and
|
|
# . - _ — sanitise rather than let the server reject the whole call.
|
|
username=$(printf '%s' "$username" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9._-' '-' | sed 's/^-*//; s/-*$//')
|
|
[[ -z "$username" ]] && username="user"
|
|
|
|
local out
|
|
out=$(_mmctl user create --email "$email" --username "$username" --password "$password")
|
|
_mmctlFailed "$out" "Creating $email" && return 1
|
|
|
|
if [[ "$isAdmin" == "true" ]]; then
|
|
local promote
|
|
promote=$(_mmctl roles system-admin "$email")
|
|
_mmctlFailed "$promote" "Granting system admin to $email" && return 1
|
|
fi
|
|
|
|
isSuccessful "Mattermost user created — Email: $email — Username: $username — Password: $password"
|
|
}
|
|
|
|
authAdapter_mattermost_setPassword() {
|
|
local email="$1" password="$2"
|
|
[[ -z "$email" ]] && { isError "An email address is required."; return 1; }
|
|
[[ -z "$password" ]] && password=$(generateRandomPassword)
|
|
|
|
local out
|
|
out=$(_mmctl user change-password "$email" --password "$password")
|
|
_mmctlFailed "$out" "Resetting $email" && return 1
|
|
|
|
# Keep the config in step if this is the account the WebUI card advertises.
|
|
[[ "$email" == "${CFG_MATTERMOST_ADMIN_EMAIL:-}" ]] && authPersistCfg mattermost ADMIN_PASSWORD "$password"
|
|
|
|
isSuccessful "Mattermost password set for $email — New password: $password"
|
|
}
|
|
|
|
authAdapter_mattermost_listUsers() {
|
|
local out
|
|
out=$(_mmctl user list --per-page 500)
|
|
_mmctlFailed "$out" "Listing users" && return 1
|
|
|
|
# `user list` prints "id: username (email)" per line, plus a trailing count.
|
|
local line count=0
|
|
while IFS= read -r line; do
|
|
[[ "$line" =~ ^[a-z0-9]+:\ ]] || continue
|
|
local rest="${line#*: }"
|
|
printf ' %s\n' "$rest"
|
|
((count++))
|
|
done <<< "$out"
|
|
isSuccessful "$count Mattermost account(s)."
|
|
}
|
|
|
|
# Mattermost distinguishes deactivate (reversible, frees nothing) from delete
|
|
# (permanent, purges content). This is the reversible one: it is what the
|
|
# product itself recommends, and a real delete is not undoable from a WebUI
|
|
# button click.
|
|
authAdapter_mattermost_deleteUser() {
|
|
local email="$1"
|
|
[[ -z "$email" ]] && { isError "An email address is required."; return 1; }
|
|
|
|
local out
|
|
out=$(_mmctl user deactivate "$email")
|
|
_mmctlFailed "$out" "Deactivating $email" && return 1
|
|
isSuccessful "Mattermost user '$email' deactivated. Re-enable them from the System Console if needed."
|
|
}
|
|
|
|
authAdapter_mattermost_setAdmin() {
|
|
local email="$1" isAdmin="$2"
|
|
[[ -z "$email" ]] && { isError "An email address is required."; return 1; }
|
|
|
|
# `roles system-admin` / `roles member`, NOT `user promote` / `user demote`:
|
|
# those two convert between guest and member accounts and have no bearing on
|
|
# administrator rights at all.
|
|
local out target="false"
|
|
if [[ "$isAdmin" == "true" ]]; then
|
|
target="true"
|
|
out=$(_mmctl roles system-admin "$email")
|
|
else
|
|
out=$(_mmctl roles member "$email")
|
|
fi
|
|
_mmctlFailed "$out" "Changing admin status for $email" && return 1
|
|
isSuccessful "Mattermost user '$email' system admin → $target."
|
|
}
|