#!/bin/bash
# LibrePortal host-SSH-access helper — the only root-privileged management of the
# admin's authorized_keys and sshd PasswordAuthentication the manager may trigger.
# Installed root:root 0755 to /usr/local/sbin by init.sh. Self-contained (sources
# no manager code) so the scoped sudoers can allow it instead of blanket
# `sudo tee`/`sudo sed`/`sudo cp` on /etc/ssh + the admin's ~/.ssh (root). The
# lockout guards live HERE, in the trust boundary, so a compromised manager can't
# bypass them by editing the calling script.

set -u

[[ $EUID -eq 0 ]] || { echo "libreportal-ssh-access: must run as root" >&2; exit 1; }

# Baked by init.sh at install (placeholder replaced); default if run unbaked.
MANAGER="__MANAGER__"
[[ "$MANAGER" == "__MANAGER__" || -z "$MANAGER" ]] && MANAGER="libreportal"

if [[ "$MANAGER" == "root" ]]; then SSH_HOME="/root"; else SSH_HOME="/home/$MANAGER"; fi
SSH_DIR="$SSH_HOME/.ssh"
AKF="$SSH_DIR/authorized_keys"
SSHD_CONFIG="/etc/ssh/sshd_config"

_ensure_dir() {
    mkdir -p "$SSH_DIR"
    touch "$AKF"
    chmod 700 "$SSH_DIR"
    chmod 600 "$AKF"
    chown -R "$MANAGER:$MANAGER" "$SSH_DIR"
}

_key_count() {
    [[ -f "$AKF" ]] || { echo 0; return; }
    grep -cvE '^[[:space:]]*($|#)' "$AKF" 2>/dev/null || echo 0
}

# 0 = password auth enabled (or unspecified default), 1 = disabled.
_pw_enabled() {
    local v
    v=$(sshd -T 2>/dev/null | awk '/^passwordauthentication/ {print $2}')
    [[ -z "$v" ]] && v=$(grep -iE '^[[:space:]]*PasswordAuthentication' "$SSHD_CONFIG" 2>/dev/null | tail -1 | awk '{print tolower($2)}')
    [[ "$v" == "no" ]] && return 1
    return 0
}

key_add() {
    local key_b64="$1"
    [[ -z "$key_b64" ]] && { echo "key-add requires <base64-public-key>" >&2; return 1; }
    local pub
    pub=$(printf '%s' "$key_b64" | base64 -d 2>/dev/null | tr -d '\r' | grep -vE '^[[:space:]]*$' | head -1)
    [[ -z "$pub" ]] && { echo "empty key after decode" >&2; return 1; }
    if ! printf '%s\n' "$pub" | ssh-keygen -l -f - >/dev/null 2>&1; then
        echo "not a valid SSH public key" >&2; return 1
    fi
    _ensure_dir
    local body; body=$(awk '{print $2}' <<< "$pub")
    if grep -qF "$body" "$AKF" 2>/dev/null; then
        echo "already-authorized"
    else
        printf '%s\n' "$pub" >> "$AKF"
        echo "added"
    fi
    chown "$MANAGER:$MANAGER" "$AKF"
    chmod 600 "$AKF"
}

key_remove() {
    local fp="$1"
    [[ -z "$fp" ]] && { echo "key-remove requires <fingerprint>" >&2; return 1; }
    [[ "$fp" =~ ^[A-Za-z0-9:+/=._-]+$ ]] || { echo "invalid fingerprint" >&2; return 1; }
    [[ -f "$AKF" ]] || { echo "no authorized_keys file" >&2; return 1; }
    # Lockout guard (in the trust boundary): never remove the last key while
    # password login is off.
    if ! _pw_enabled && [[ "$(_key_count)" -le 1 ]]; then
        echo "refuse-last-key" >&2; return 2
    fi
    local tmp removed=0 line lfp
    tmp=$(mktemp)
    while IFS= read -r line; do
        if [[ -z "$line" || "$line" =~ ^[[:space:]]*# ]]; then
            printf '%s\n' "$line" >> "$tmp"; continue
        fi
        lfp=$(printf '%s\n' "$line" | ssh-keygen -l -f - 2>/dev/null | awk '{print $2}')
        if [[ -n "$lfp" && "$lfp" == "$fp" ]]; then removed=1; continue; fi
        printf '%s\n' "$line" >> "$tmp"
    done < "$AKF"
    if [[ "$removed" -eq 1 ]]; then
        cp "$tmp" "$AKF"
        chown "$MANAGER:$MANAGER" "$AKF"
        chmod 600 "$AKF"
        echo "removed"
    else
        echo "no-match"
    fi
    rm -f "$tmp"
}

pw_set() {
    local want="$1"
    case "$want" in on|off) ;; *) echo "pw-set requires on|off" >&2; return 1 ;; esac
    # Lockout guard: don't disable password login with no keys.
    if [[ "$want" == "off" && "$(_key_count)" -lt 1 ]]; then
        echo "refuse-no-keys" >&2; return 2
    fi
    local value="yes"; [[ "$want" == "off" ]] && value="no"
    local backup="${SSHD_CONFIG}.libreportal.$(date +%s)"
    cp "$SSHD_CONFIG" "$backup"
    sed -i '/^[[:space:]]*#\?[[:space:]]*PasswordAuthentication\b/d' "$SSHD_CONFIG"
    printf 'PasswordAuthentication %s\n' "$value" >> "$SSHD_CONFIG"
    if ! sshd -t 2>/dev/null; then
        cp "$backup" "$SSHD_CONFIG"
        echo "sshd-test-failed" >&2; return 1
    fi
    systemctl reload ssh 2>/dev/null || systemctl reload sshd 2>/dev/null
    echo "set:$want backup:$backup"
}

action="${1:-}"; shift 2>/dev/null || true
case "$action" in
    ensure-dir) _ensure_dir ;;
    key-count)  _key_count ;;
    pw-status)  if _pw_enabled; then echo on; else echo off; fi ;;
    has-keys)   [[ -f "$AKF" ]] ;;
    read-keys)  [[ -f "$AKF" ]] && cat "$AKF" ;;
    authkeys-path) printf '%s\n' "$AKF" ;;
    key-add)    key_add "${1:-}" ;;
    key-remove) key_remove "${1:-}" ;;
    pw-set)     pw_set "${1:-}" ;;
    *) echo "usage: libreportal-ssh-access {ensure-dir|key-count|pw-status|has-keys|read-keys|authkeys-path|key-add <b64>|key-remove <fp>|pw-set <on|off>}" >&2; exit 2 ;;
esac
