#!/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." }