From 8ea53f6372c29b5c5efc26ea22e2719bb9ec4572 Mon Sep 17 00:00:00 2001 From: Christoph Date: Tue, 29 Sep 2026 01:24:25 +0200 Subject: [PATCH] Add Mattermost OIDC plugin installer --- install-update-mattermost-oidc-plugin.sh | 977 +++++++++++++++++++++++ 1 file changed, 977 insertions(+) create mode 100755 install-update-mattermost-oidc-plugin.sh diff --git a/install-update-mattermost-oidc-plugin.sh b/install-update-mattermost-oidc-plugin.sh new file mode 100755 index 0000000..7db3758 --- /dev/null +++ b/install-update-mattermost-oidc-plugin.sh @@ -0,0 +1,977 @@ +#!/usr/bin/env bash + +script_name="$(basename $(realpath $0))" +working_dir="$(dirname $(realpath $0))" + +LOCK_DIR="/tmp/$(basename $0).$$.LOCK" +log_file="${LOCK_DIR}/${script_name%%.*}.log" + +backup_date="$(date +%Y-%m-%d-%H%M%S)" + +MM_OIDC_REPOSITORY="https://github.com/server-camp/mattermost-oidc-plugin.git" +MM_OIDC_RELEASE_BASE="https://github.com/server-camp/mattermost-oidc-plugin/releases/download" +MM_OIDC_PLUGIN_ID="mattermost-oidc" +MM_SOURCE_BASE="/usr/local/src/mattermost" +MM_INSTALL_DIR="/opt/mattermost" +MM_BIN="${MM_INSTALL_DIR}/bin/mattermost" +MMCTL="${MM_INSTALL_DIR}/bin/mmctl" +MM_CONFIG_FILE="${MM_INSTALL_DIR}/config/config.json" +MM_LOCAL_SOCKET="/var/tmp/mattermost_local.socket" +MM_SERVICE="mattermost.service" +MM_USER="mattermost" +MM_GROUP="mattermost" + +INSTALLATION_MODE="" +MM_CURRENT_VERSION="" +MM_NEW_VERSION="" +MM_LATEST_VERSION="" +MM_MIN_SERVER_VERSION="" +MM_SERVER_VERSION="" +MM_BUNDLE="" +MM_BUNDLE_BACKUP="" + +MM_ROLLBACK_BUNDLE="" +MM_PREVIOUS_PLUGIN_ENABLED=false +MM_ROLLBACK_REQUIRED=false + + +# ---------- +# Base Function(s) +# ---------- + +clean_up() { + rm -rf "$LOCK_DIR" + blank_line + exit $1 +} + +echononl(){ + if $terminal ; then + echo X\\c > /tmp/shprompt$$ + if [ `wc -c /tmp/shprompt$$ | awk '{print $1}'` -eq 1 ]; then + echo -e -n " $*\\c" 1>&2 + else + echo -e -n " $*" 1>&2 + fi + rm /tmp/shprompt$$ + fi +} + +fatal(){ + echo "" + if $terminal ; then + echo -e " [ \033[31m\033[1mFatal\033[m ] $*" + else + echo -e " [ Fatal ] $*" + fi + echo "" + if $terminal ; then + echo -e " \033[1mScript terminated\033[m.." + else + echo -e " Script terminated.." + fi + echo "" + clean_up 1 +} + +error (){ + echo "" + if $terminal ; then + echo -e " [ \033[31m\033[1mError\033[m ] $*" + else + echo " [ Error ] $*" + fi + echo "" +} + +warn (){ + echo "" + if $terminal ; then + echo -e " [ \033[33m\033[1mWarning\033[m ] $*" + else + echo " [ Warning ] $*" + fi + echo "" +} + +info (){ + if $terminal ; then + echo "" + echo -e " [ \033[32m\033[1mInfo\033[m ] $*" + echo "" + fi +} + +echo_ok() { + if $terminal ; then + echo -e "\033[85G[ \033[32mok\033[m ]" + fi +} + +echo_failed(){ + if $terminal ; then + echo -e "\033[85G[ \033[1;31mfailed\033[m ]" + fi +} + +echo_skipped() { + if $terminal ; then + echo -e "\033[85G[ \033[33m\033[1mskipped\033[m ]" + fi +} + +blank_line() { + if $terminal ; then + echo "" + fi +} + +ask_yes_no() { + local question="$1" + local default_answer="${2:-no}" + local answer="" + local prompt="[yes/no]" + + if [[ "$default_answer" = "yes" ]]; then + prompt="[yes/no, default: yes]" + elif [[ "$default_answer" = "no" ]]; then + prompt="[yes/no, default: no]" + fi + + while true ; do + echononl "$question $prompt: " + read answer + answer="${answer,,}" + [[ -z "$answer" ]] && answer="$default_answer" + case "$answer" in + yes) return 0 ;; + no) return 1 ;; + *) warn "Wrong entry! Please enter 'yes' or 'no'." ;; + esac + done +} + +version_ge() { + [[ "$(printf '%s\n%s\n' "$2" "$1" | sort -V | head -1)" = "$2" ]] +} + +plugin_state_json() { + "$MMCTL" --local --json plugin list 2>> "$log_file" +} + +installed_plugin_version() { + plugin_state_json | python3 -c ' +import json, sys +plugin_id = sys.argv[1] +try: + data = json.load(sys.stdin) +except Exception: + raise SystemExit(1) +if isinstance(data, list) and len(data) == 1 and isinstance(data[0], dict): + data = data[0] + +def walk(obj): + if isinstance(obj, dict): + if obj.get("id") == plugin_id and obj.get("version"): + print(obj["version"]) + raise SystemExit(0) + for value in obj.values(): + walk(value) + elif isinstance(obj, list): + for value in obj: + walk(value) + +walk(data) +raise SystemExit(2) +' "$MM_OIDC_PLUGIN_ID" +} + +plugin_is_enabled() { + plugin_state_json | python3 -c ' +import json, sys +plugin_id = sys.argv[1] +try: + data = json.load(sys.stdin) +except Exception: + raise SystemExit(1) + +if isinstance(data, list) and len(data) == 1 and isinstance(data[0], dict): + data = data[0] + +if not isinstance(data, dict): + raise SystemExit(1) + +active = data.get("active") +inactive = data.get("inactive") + +if not isinstance(active, list) or not isinstance(inactive, list): + raise SystemExit(1) + +for plugin in active: + if isinstance(plugin, dict) and plugin.get("id") == plugin_id: + raise SystemExit(0) + +for plugin in inactive: + if isinstance(plugin, dict) and plugin.get("id") == plugin_id: + raise SystemExit(2) + +raise SystemExit(1) +' "$MM_OIDC_PLUGIN_ID" +} + + +restore_release_bundle() { + if [[ -n "$MM_BUNDLE_BACKUP" && -f "$MM_BUNDLE_BACKUP" ]]; then + rm -f "$MM_BUNDLE" + if mv "$MM_BUNDLE_BACKUP" "$MM_BUNDLE" > "$log_file" 2>&1 ; then + MM_BUNDLE_BACKUP="" + return 0 + fi + return 1 + fi + + rm -f "$MM_BUNDLE" + return 0 +} + + +cleanup_prepared_bundles() { + if [[ -n "$MM_ROLLBACK_BUNDLE" ]]; then + rm -f "$MM_ROLLBACK_BUNDLE" + fi + + restore_release_bundle +} + + +rollback_plugin() { + local rollback_failed=false + local restore_command_failed=false + local _rollback_version="" + local _rollback_rc + local _plugin_enabled_rc + + blank_line + warn "Mattermost OIDC Plugin installation failed. Trying to restore the previous plugin state." + + if [[ "$INSTALLATION_MODE" = "upgrade" ]]; then + echononl "Restore Mattermost OIDC Plugin ${MM_CURRENT_VERSION}.." + if [[ -f "$MM_ROLLBACK_BUNDLE" ]] && + "$MMCTL" --local plugin add --force "$MM_ROLLBACK_BUNDLE" > "$log_file" 2>&1 ; then + echo_ok + else + echo_failed + restore_command_failed=true + fi + + if $MM_PREVIOUS_PLUGIN_ENABLED ; then + echononl "Restore previous plugin state 'enabled'.." + if "$MMCTL" --local plugin enable "$MM_OIDC_PLUGIN_ID" > "$log_file" 2>&1 ; then + echo_ok + else + echo_failed + restore_command_failed=true + fi + else + echononl "Restore previous plugin state 'disabled'.." + if "$MMCTL" --local plugin disable "$MM_OIDC_PLUGIN_ID" > "$log_file" 2>&1 ; then + echo_ok + else + echo_failed + restore_command_failed=true + fi + fi + + echononl "Verify restored plugin version '${MM_CURRENT_VERSION}'.." + _rollback_version="$(installed_plugin_version 2>/dev/null)" + if [[ "$_rollback_version" = "$MM_CURRENT_VERSION" ]]; then + echo_ok + else + echo_failed + rollback_failed=true + fi + + echononl "Verify restored plugin state.." + plugin_is_enabled + _plugin_enabled_rc=$? + if $MM_PREVIOUS_PLUGIN_ENABLED ; then + if [[ $_plugin_enabled_rc -eq 0 ]]; then + echo_ok + else + echo_failed + rollback_failed=true + fi + else + if [[ $_plugin_enabled_rc -eq 2 ]]; then + echo_ok + else + echo_failed + rollback_failed=true + fi + fi + + elif [[ "$INSTALLATION_MODE" = "initial-installation" ]]; then + installed_plugin_version > /dev/null 2>&1 + _rollback_rc=$? + + echononl "Remove newly installed Mattermost OIDC Plugin.." + if [[ $_rollback_rc -eq 2 ]]; then + echo_ok + elif [[ $_rollback_rc -eq 0 ]]; then + if "$MMCTL" --local plugin delete "$MM_OIDC_PLUGIN_ID" > "$log_file" 2>&1 ; then + echo_ok + else + echo_failed + restore_command_failed=true + fi + else + echo_failed + restore_command_failed=true + fi + + echononl "Verify Mattermost OIDC Plugin was removed.." + installed_plugin_version > /dev/null 2>&1 + _rollback_rc=$? + if [[ $_rollback_rc -eq 2 ]]; then + echo_ok + else + echo_failed + rollback_failed=true + fi + else + rollback_failed=true + fi + + if $rollback_failed ; then + error "Automatic plugin rollback could not fully restore the previous state." + return 1 + fi + + if $restore_command_failed ; then + warn "One or more rollback commands reported an error, but the previous plugin state was successfully verified." + fi + + info "Previous Mattermost OIDC Plugin state was successfully restored." + return 0 +} + + +fatal_with_plugin_rollback() { + local failure_message="$1" + + if $MM_ROLLBACK_REQUIRED ; then + if rollback_plugin ; then + MM_ROLLBACK_REQUIRED=false + + if [[ -n "$MM_ROLLBACK_BUNDLE" && -f "$MM_ROLLBACK_BUNDLE" ]]; then + echononl "Remove temporary rollback bundle.." + if rm -f "$MM_ROLLBACK_BUNDLE" > "$log_file" 2>&1 ; then + echo_ok + else + echo_failed + warn "Plugin rollback was successful, but temporary rollback bundle '${MM_ROLLBACK_BUNDLE}' could not be removed." + fi + fi + + if [[ -n "$MM_BUNDLE_BACKUP" && -f "$MM_BUNDLE_BACKUP" ]]; then + echononl "Remove previous release bundle backup.." + if rm -f "$MM_BUNDLE_BACKUP" > "$log_file" 2>&1 ; then + MM_BUNDLE_BACKUP="" + echo_ok + else + echo_failed + warn "Plugin rollback was successful, but previous release bundle backup '${MM_BUNDLE_BACKUP}' could not be removed." + fi + fi + + fatal "${failure_message} The previous plugin state was successfully restored." + else + fatal "${failure_message} Automatic rollback could not fully restore the previous state. Manual intervention is required." + fi + fi + + fatal "$failure_message" +} + + +handle_signal() { + trap - SIGHUP SIGINT SIGTERM + + blank_line + warn "Installation interrupted by signal." + + if $MM_ROLLBACK_REQUIRED ; then + if rollback_plugin ; then + MM_ROLLBACK_REQUIRED=false + + cleanup_prepared_bundles || + warn "Plugin rollback was successful, but previous release bundle state could not be fully restored." + else + error "Automatic rollback could not fully restore the previous plugin state. Manual intervention is required." + fi + else + cleanup_prepared_bundles || + warn "Could not fully restore previous release bundle state." + fi + + clean_up 1 +} + + +# ---------- +# Jobhandling +# ---------- + +trap 'handle_signal' SIGHUP SIGINT SIGTERM + +if ! mkdir "$LOCK_DIR" 2>/dev/null ; then + echo "Cannot create lock directory '$LOCK_DIR'." + exit 1 +fi + +if [[ -t 1 ]] ; then + terminal=true +else + fatal "Script must run in a terminal." +fi + + +# ========== +# Begin Main Script +# ========== + +if $terminal ; then + echo "" + echo -e "\033[1m----------\033[m" + echo -e "\033[32m\033[1mRunning script \033[m\033[1m$script_name\033[32m .. \033[m" + echo -e "\033[1m----------\033[m" +fi + +blank_line + +# ---------- +# Some checks +# ---------- + +echononl "Check if script is running as root.." +if [[ "$(id -u)" -eq 0 ]]; then + echo_ok +else + echo_failed + fatal "This script must be run as root." +fi + +for _cmd in git curl tar awk grep sed sort python3 systemctl stat uname mv cp mkdir ; do + echononl "Check for command '${_cmd}'.." + if command -v "$_cmd" > /dev/null 2>&1 ; then + echo_ok + else + echo_failed + fatal "Required command '${_cmd}' was not found." + fi +done + +echononl "Check Mattermost binary.." +if [[ -x "$MM_BIN" ]]; then + echo_ok +else + echo_failed + fatal "Mattermost binary '${MM_BIN}' was not found." +fi + +echononl "Check mmctl binary.." +if [[ -x "$MMCTL" ]]; then + echo_ok +else + echo_failed + fatal "mmctl binary '${MMCTL}' was not found." +fi + +echononl "Check Mattermost service '${MM_SERVICE}'.." +if systemctl is-active --quiet "$MM_SERVICE" ; then + echo_ok +else + echo_failed + fatal "Mattermost service '${MM_SERVICE}' is not active." +fi + +echononl "Check Mattermost user '${MM_USER}'.." +if id "$MM_USER" > /dev/null 2>&1 ; then + echo_ok +else + echo_failed + fatal "Mattermost user '${MM_USER}' does not exist." +fi + +echononl "Check Mattermost group '${MM_GROUP}'.." +if getent group "$MM_GROUP" > /dev/null 2>&1 ; then + echo_ok +else + echo_failed + fatal "Mattermost group '${MM_GROUP}' does not exist." +fi + +echononl "Check Mattermost configuration '${MM_CONFIG_FILE}'.." +if [[ -r "$MM_CONFIG_FILE" ]]; then + echo_ok +else + echo_failed + fatal "Mattermost configuration '${MM_CONFIG_FILE}' is not readable." +fi + +echononl "Check Mattermost Local Mode configuration.." +_local_data="$(python3 - "$MM_CONFIG_FILE" <<'PY_EOF' +import json, sys +with open(sys.argv[1], encoding="utf-8") as f: + data = json.load(f) +s = data.get("ServiceSettings", {}) +print("true" if s.get("EnableLocalMode") is True else "false") +print(s.get("LocalModeSocketLocation") or "/var/tmp/mattermost_local.socket") +PY_EOF +)" || { + echo_failed + fatal "Could not parse '${MM_CONFIG_FILE}'." +} +_local_enabled="$(printf '%s\n' "$_local_data" | sed -n '1p')" +_local_socket="$(printf '%s\n' "$_local_data" | sed -n '2p')" +if [[ "$_local_enabled" = "true" ]]; then + MM_LOCAL_SOCKET="$_local_socket" + echo_ok +else + echo_failed + fatal "Mattermost Local Mode is disabled. Set ServiceSettings.EnableLocalMode to true and restart Mattermost first." +fi + +echononl "Check Mattermost Local Mode socket '${MM_LOCAL_SOCKET}'.." +if [[ -S "$MM_LOCAL_SOCKET" ]]; then + echo_ok +else + echo_failed + fatal "Mattermost Local Mode socket '${MM_LOCAL_SOCKET}' does not exist." +fi + +echononl "Test mmctl Local Mode connection.." +if "$MMCTL" --local plugin list > "$log_file" 2>&1 ; then + echo_ok +else + echo_failed + fatal "mmctl Local Mode connection failed. $(cat "$log_file")" +fi + +echononl "Check Mattermost plugin configuration.." +_plugin_settings="$(python3 - "$MM_CONFIG_FILE" <<'PY_EOF' +import json, sys +with open(sys.argv[1], encoding="utf-8") as f: + data = json.load(f) +s = data.get("PluginSettings", {}) +print("true" if s.get("Enable") is True else "false") +print("true" if s.get("EnableUploads") is True else "false") +PY_EOF +)" || { + echo_failed + fatal "Could not parse Mattermost plugin settings in '${MM_CONFIG_FILE}'." +} + +_plugins_enabled="$(printf '%s\n' "$_plugin_settings" | sed -n '1p')" +_plugin_uploads_enabled="$(printf '%s\n' "$_plugin_settings" | sed -n '2p')" + +if [[ "$_plugins_enabled" != "true" ]]; then + echo_failed + fatal "Mattermost plugins are disabled. Set PluginSettings.Enable to true and restart Mattermost first." +elif [[ "$_plugin_uploads_enabled" != "true" ]]; then + echo_failed + fatal "Mattermost plugin uploads are disabled. Set PluginSettings.EnableUploads to true and restart Mattermost first." +else + echo_ok +fi + +MM_SERVER_VERSION="$("$MM_BIN" version 2>/dev/null | awk -F': ' '$1 == "Version" {print $2; exit}')" +[[ -n "$MM_SERVER_VERSION" ]] || fatal "Could not determine installed Mattermost version." + + +# ---------- +# Detect installation mode +# ---------- + +echononl "Detect existing Mattermost OIDC plugin installation.." +MM_CURRENT_VERSION="$(installed_plugin_version 2>/dev/null)" +_plugin_version_rc=$? +if [[ $_plugin_version_rc -eq 0 && -n "$MM_CURRENT_VERSION" ]]; then + INSTALLATION_MODE="upgrade" + plugin_is_enabled + _plugin_enabled_rc=$? + if [[ $_plugin_enabled_rc -eq 0 ]]; then + MM_PREVIOUS_PLUGIN_ENABLED=true + elif [[ $_plugin_enabled_rc -eq 2 ]]; then + MM_PREVIOUS_PLUGIN_ENABLED=false + else + echo_failed + fatal "Could not determine whether Mattermost OIDC Plugin '${MM_OIDC_PLUGIN_ID}' is enabled or disabled." + fi + echo_ok +elif [[ $_plugin_version_rc -eq 2 ]]; then + INSTALLATION_MODE="initial-installation" + MM_CURRENT_VERSION="" + echo_ok +else + echo_failed + fatal "Could not determine current Mattermost OIDC plugin state." +fi + + +# ---------- +# Determine latest stable release +# ---------- + +echononl "Determine latest stable Mattermost OIDC release.." +MM_LATEST_VERSION="$(git ls-remote --tags --refs "$MM_OIDC_REPOSITORY" 2> "$log_file" \ + | awk '{print $2}' \ + | sed -nE 's#refs/tags/v([0-9]+\.[0-9]+\.[0-9]+)$#\1#p' \ + | sort -V \ + | tail -1)" +if [[ -n "$MM_LATEST_VERSION" ]]; then + echo_ok +else + echo_failed + fatal "Could not determine latest stable release from '${MM_OIDC_REPOSITORY}'. $(cat "$log_file")" +fi + +blank_line +if [[ "$INSTALLATION_MODE" = "initial-installation" ]]; then + echo " Installation mode....................: Initial installation" +else + echo " Installation mode....................: Upgrade" + echo " Currently installed version.........: ${MM_CURRENT_VERSION}" +fi +echo " Installed Mattermost version........: ${MM_SERVER_VERSION}" +echo " Latest stable OIDC version..........: ${MM_LATEST_VERSION}" +blank_line + +while true ; do + echononl "New Mattermost OIDC Plugin Version [${MM_LATEST_VERSION}]: " + read MM_NEW_VERSION + MM_NEW_VERSION="${MM_NEW_VERSION#v}" + [[ -z "$MM_NEW_VERSION" ]] && MM_NEW_VERSION="$MM_LATEST_VERSION" + + echononl "Check release tag 'v${MM_NEW_VERSION}'.." + if git ls-remote --exit-code --tags "$MM_OIDC_REPOSITORY" \ + "refs/tags/v${MM_NEW_VERSION}" "refs/tags/v${MM_NEW_VERSION}^{}" \ + > /dev/null 2> "$log_file" ; then + echo_ok + break + else + echo_failed + warn "Tag 'v${MM_NEW_VERSION}' does not exist in the upstream repository." + fi +done + +if [[ "$INSTALLATION_MODE" = "upgrade" && "$MM_NEW_VERSION" = "$MM_CURRENT_VERSION" ]]; then + blank_line + warn "Mattermost OIDC Plugin ${MM_CURRENT_VERSION} is already installed." + if ! ask_yes_no "Reinstall version ${MM_NEW_VERSION}" "no" ; then + info "Version ${MM_CURRENT_VERSION} is already installed. Nothing to do." + clean_up 0 + fi +fi + +MM_BUNDLE="${MM_SOURCE_BASE}/mattermost-oidc-${MM_NEW_VERSION}.tar.gz" + + +# ---------- +# Summary +# ---------- + +blank_line +echo -e "\033[37m\033[1mMattermost OIDC Plugin installation settings:\033[m" +blank_line +echo " Installation mode....................: ${INSTALLATION_MODE}" +[[ -n "$MM_CURRENT_VERSION" ]] && echo " Current plugin version...............: ${MM_CURRENT_VERSION}" +echo " New plugin version...................: ${MM_NEW_VERSION}" +echo " Latest stable version...............: ${MM_LATEST_VERSION}" +echo " Installed Mattermost version........: ${MM_SERVER_VERSION}" +echo " Repository...........................: ${MM_OIDC_REPOSITORY}" +echo " Release bundle.......................: ${MM_BUNDLE}" +echo " Plugin ID............................: ${MM_OIDC_PLUGIN_ID}" +echo " Local Mode socket....................: ${MM_LOCAL_SOCKET}" +blank_line + +if ! ask_yes_no "einverstanden" "no" ; then + fatal "Stopped by user" +fi + + +# ---------- +# Prepare source directory +# ---------- + +blank_line + +# ---------- +# Download release bundle +# ---------- + +blank_line +echo -e "\033[37m\033[1mPrepare Mattermost OIDC Plugin release bundle..\033[m" +blank_line + +echononl "Create source directory '${MM_SOURCE_BASE}'.." +if mkdir -p "$MM_SOURCE_BASE" > "$log_file" 2>&1 ; then + echo_ok +else + echo_failed + fatal "$(cat "$log_file")" +fi + +if [[ -e "$MM_BUNDLE" ]]; then + MM_BUNDLE_BACKUP="${MM_BUNDLE}.${backup_date}" + echononl "Backup existing release bundle.." + if mv "$MM_BUNDLE" "$MM_BUNDLE_BACKUP" > "$log_file" 2>&1 ; then + echo_ok + else + echo_failed + fatal "$(cat "$log_file")" + fi +else + echononl "Backup existing release bundle.." + echo_skipped +fi + +echononl "Download Mattermost OIDC Plugin ${MM_NEW_VERSION}.." +_release_url="${MM_OIDC_RELEASE_BASE}/v${MM_NEW_VERSION}/mattermost-oidc-${MM_NEW_VERSION}.tar.gz" +if curl -fL --retry 2 --connect-timeout 15 -o "$MM_BUNDLE" "$_release_url" > "$log_file" 2>&1 ; then + echo_ok +else + _download_error="$(cat "$log_file")" + echo_failed + restore_release_bundle || warn "Could not restore previous release bundle." + fatal "Could not download '${_release_url}'. ${_download_error}" +fi + +echononl "Check release bundle archive.." +if tar tzf "$MM_BUNDLE" > "$log_file" 2>&1 ; then + echo_ok +else + echo_failed + restore_release_bundle || warn "Could not restore previous release bundle." + fatal "Downloaded release bundle is not a valid gzip-compressed tar archive." +fi + +echononl "Read and verify plugin metadata.." +_plugin_json="$(tar xOf "$MM_BUNDLE" mattermost-oidc/plugin.json 2> "$log_file")" || { + echo_failed + restore_release_bundle || warn "Could not restore previous release bundle." + fatal "Could not read mattermost-oidc/plugin.json from release bundle." +} +_metadata="$(printf '%s' "$_plugin_json" | python3 -c ' +import json, sys +expected_id, expected_version = sys.argv[1:3] +try: + data = json.load(sys.stdin) +except Exception: + raise SystemExit(1) +if data.get("id") != expected_id: + raise SystemExit(2) +if data.get("version") != expected_version: + raise SystemExit(3) +print(data.get("min_server_version", "")) +' "$MM_OIDC_PLUGIN_ID" "$MM_NEW_VERSION")" +_metadata_rc=$? +if [[ $_metadata_rc -eq 0 ]]; then + MM_MIN_SERVER_VERSION="$_metadata" + echo_ok +else + echo_failed + restore_release_bundle || warn "Could not restore previous release bundle." + fatal "Plugin metadata does not match expected ID '${MM_OIDC_PLUGIN_ID}' and version '${MM_NEW_VERSION}'." +fi + +if [[ -n "$MM_MIN_SERVER_VERSION" ]]; then + echononl "Check Mattermost version (installed: ${MM_SERVER_VERSION}, required: ${MM_MIN_SERVER_VERSION}).." + if version_ge "$MM_SERVER_VERSION" "$MM_MIN_SERVER_VERSION" ; then + echo_ok + else + echo_failed + restore_release_bundle || warn "Could not restore previous release bundle." + fatal "Mattermost ${MM_NEW_VERSION} requires Mattermost ${MM_MIN_SERVER_VERSION} or newer. Installed version is ${MM_SERVER_VERSION}." + fi +fi + +_arch="$(uname -m)" +case "$_arch" in + x86_64|amd64) _bundle_arch="amd64" ;; + aarch64|arm64) _bundle_arch="arm64" ;; + *) + restore_release_bundle || warn "Could not restore previous release bundle." + fatal "Unsupported system architecture '${_arch}'." + ;; +esac + +echononl "Check Linux server binary for architecture '${_bundle_arch}'.." +if tar tzf "$MM_BUNDLE" | grep -qx "mattermost-oidc/server/dist/plugin-linux-${_bundle_arch}" ; then + echo_ok +else + echo_failed + restore_release_bundle || warn "Could not restore previous release bundle." + fatal "Release bundle does not contain plugin-linux-${_bundle_arch}." +fi + + +# ---------- +# Prepare rollback bundle +# ---------- + +if [[ "$INSTALLATION_MODE" = "upgrade" ]]; then + MM_ROLLBACK_BUNDLE="${MM_SOURCE_BASE}/mattermost-oidc-${MM_CURRENT_VERSION}.rollback-${backup_date}.tar.gz" + + blank_line + echo -e "\033[37m\033[1mPrepare Mattermost OIDC Plugin rollback bundle..\033[m" + blank_line + + echononl "Download rollback bundle for Mattermost OIDC Plugin ${MM_CURRENT_VERSION}.." + _rollback_url="${MM_OIDC_RELEASE_BASE}/v${MM_CURRENT_VERSION}/mattermost-oidc-${MM_CURRENT_VERSION}.tar.gz" + if curl -fL --retry 2 --connect-timeout 15 -o "$MM_ROLLBACK_BUNDLE" "$_rollback_url" > "$log_file" 2>&1 ; then + echo_ok + else + _rollback_download_error="$(cat "$log_file")" + echo_failed + cleanup_prepared_bundles || warn "Could not fully restore previous release bundle state." + fatal "Could not prepare rollback bundle '${_rollback_url}'. No changes were made to the installed plugin. ${_rollback_download_error}" + fi + + echononl "Check rollback bundle archive.." + if tar tzf "$MM_ROLLBACK_BUNDLE" > "$log_file" 2>&1 ; then + echo_ok + else + echo_failed + cleanup_prepared_bundles || warn "Could not fully restore previous release bundle state." + fatal "Rollback bundle for version '${MM_CURRENT_VERSION}' is not a valid gzip-compressed tar archive." + fi + + echononl "Verify rollback bundle metadata.." + _rollback_plugin_json="$(tar xOf "$MM_ROLLBACK_BUNDLE" mattermost-oidc/plugin.json 2> "$log_file")" || { + echo_failed + cleanup_prepared_bundles || warn "Could not fully restore previous release bundle state." + fatal "Could not read plugin metadata from rollback bundle." + } + + if printf '%s' "$_rollback_plugin_json" | python3 -c ' +import json, sys +expected_id, expected_version = sys.argv[1:3] +try: + data = json.load(sys.stdin) +except Exception: + raise SystemExit(1) +if data.get("id") != expected_id or data.get("version") != expected_version: + raise SystemExit(1) +' "$MM_OIDC_PLUGIN_ID" "$MM_CURRENT_VERSION" ; then + echo_ok + else + echo_failed + cleanup_prepared_bundles || warn "Could not fully restore previous release bundle state." + fatal "Rollback bundle metadata does not match plugin '${MM_OIDC_PLUGIN_ID}' version '${MM_CURRENT_VERSION}'." + fi +fi + + + +# ---------- +# Install/upgrade plugin +# ---------- + +blank_line +echo -e "\033[37m\033[1mInstall Mattermost OIDC Plugin..\033[m" +blank_line + +echononl "Install Mattermost OIDC Plugin ${MM_NEW_VERSION}.." +MM_ROLLBACK_REQUIRED=true +if "$MMCTL" --local plugin add --force "$MM_BUNDLE" > "$log_file" 2>&1 ; then + echo_ok +else + _install_error="$(cat "$log_file")" + echo_failed + fatal_with_plugin_rollback "Plugin installation failed. ${_install_error}" +fi + +echononl "Enable Mattermost OIDC Plugin '${MM_OIDC_PLUGIN_ID}'.." +if "$MMCTL" --local plugin enable "$MM_OIDC_PLUGIN_ID" > "$log_file" 2>&1 ; then + echo_ok +else + _enable_error="$(cat "$log_file")" + echo_failed + fatal_with_plugin_rollback "Plugin was installed but could not be enabled. ${_enable_error}" +fi + + +# ---------- +# Verify installation +# ---------- + +blank_line +echo -e "\033[37m\033[1mVerify Mattermost OIDC Plugin installation..\033[m" +blank_line + +sleep 1 + +echononl "Verify installed plugin version '${MM_NEW_VERSION}'.." +_verified_version="$(installed_plugin_version 2>/dev/null)" +if [[ "$_verified_version" = "$MM_NEW_VERSION" ]]; then + echo_ok +else + echo_failed + fatal_with_plugin_rollback "Installed plugin version is '${_verified_version:-unknown}', expected '${MM_NEW_VERSION}'." +fi + +echononl "Verify plugin is enabled.." +plugin_is_enabled +_plugin_enabled_rc=$? +if [[ $_plugin_enabled_rc -eq 0 ]]; then + echo_ok + MM_ROLLBACK_REQUIRED=false +elif [[ $_plugin_enabled_rc -eq 2 ]]; then + echo_failed + fatal_with_plugin_rollback "Plugin '${MM_OIDC_PLUGIN_ID}' is installed but not enabled." +else + echo_failed + fatal_with_plugin_rollback "Could not determine whether Mattermost OIDC Plugin '${MM_OIDC_PLUGIN_ID}' is enabled." +fi + + +if [[ -n "$MM_ROLLBACK_BUNDLE" && -f "$MM_ROLLBACK_BUNDLE" ]]; then + echononl "Remove temporary rollback bundle.." + if rm -f "$MM_ROLLBACK_BUNDLE" > "$log_file" 2>&1 ; then + echo_ok + else + echo_failed + warn "Mattermost OIDC Plugin was successfully installed, but temporary rollback bundle '${MM_ROLLBACK_BUNDLE}' could not be removed." + fi +fi + + +if [[ -n "$MM_BUNDLE_BACKUP" && -f "$MM_BUNDLE_BACKUP" ]]; then + echononl "Remove previous release bundle backup.." + if rm -f "$MM_BUNDLE_BACKUP" > "$log_file" 2>&1 ; then + MM_BUNDLE_BACKUP="" + echo_ok + else + echo_failed + warn "Mattermost OIDC Plugin was successfully installed, but previous release bundle backup '${MM_BUNDLE_BACKUP}' could not be removed." + fi +fi + + +# ---------- +# Final information +# ---------- + +blank_line +echo -e "\033[37m\033[1mMattermost OIDC Plugin successfully installed.\033[m" +blank_line +echo " Installed version.....................: ${MM_NEW_VERSION}" +echo " Plugin ID.............................: ${MM_OIDC_PLUGIN_ID}" +echo " Release bundle........................: ${MM_BUNDLE}" +echo " Mattermost version....................: ${MM_SERVER_VERSION}" +echo " Minimum Mattermost version............: ${MM_MIN_SERVER_VERSION:-not specified}" +echo " Status................................: enabled" +blank_line + +clean_up 0