#!/bin/bash

# Mode-aware privileged operations.
#
# Ownership model (single source of truth — see reconcileDockerOwnership):
#   The MANAGER user ($sudo_user_name, e.g. libreportal) runs the CLI + host
#   scripts and is in the docker group, so it owns and operates the LibrePortal
#   control plane in BOTH modes. root:root is never the intended owner — it only
#   ever appeared as an artifact of un-de-sudo'd `sudo` commands.
#     rooted   — the manager owns everything under /docker (it talks to the root
#                docker socket via the docker group); ops run AS the manager.
#     rootless — the manager owns the control plane; the docker install user owns
#                /docker/containers/** (the rootless daemon requires it).
#   Only genuine system administration (apt/systemctl/ufw/sysctl/useradd, /etc)
#   needs real root — that goes through runSystem.

# Run a command AS the manager user (plain if we're already it — the runtime
# case — otherwise sudo -u to it, e.g. at install time when we're root). This is
# how we keep files manager-owned instead of accidentally root-owned.
runAsManager() {
    local mgr="${sudo_user_name:-libreportal}"
    if [[ "$(id -un)" == "$mgr" ]]; then
        "$@"
    else
        sudo -u "$mgr" "$@"
    fi
}

# /docker data-plane command (mkdir/chown/rm/cp/mv/sed/sqlite3/docker/etc.) on
# app/container files.
#   rooted   -> as the manager user (owns /docker, in the docker group)
#   rootless -> as the docker install user (owns /docker/containers/**, and has
#               DOCKER_HOST set so `docker ...` hits the rootless socket)
# For stdin-fed writes (`… | sudo tee file`) use runFileWrite below.
runFileOp() {
    if [[ "$CFG_DOCKER_INSTALL_TYPE" == "rootless" ]]; then
        dockerCommandRunInstallUser --argv "$@"
    else
        runAsManager "$@"
    fi
}

# Write stdin to a /docker data-plane path (replaces `… | sudo tee path`).
# Pass -a/--append as the first arg to append instead of truncate.
# Usage: some_command | runFileWrite [-a] /path/to/file
runFileWrite() {
    local append_flag=()
    if [[ "$1" == "-a" || "$1" == "--append" ]]; then
        append_flag=(-a)
        shift
    fi
    local dest="$1"
    if [[ "$CFG_DOCKER_INSTALL_TYPE" == "rootless" ]]; then
        dockerCommandRunInstallUser "tee ${append_flag[*]} '$dest' >/dev/null"
    else
        runAsManager tee "${append_flag[@]}" "$dest" >/dev/null
    fi
}

# Op on a MANAGER-owned path — the LibrePortal clone/templates AND the /docker
# control plane (apps DB, configs/, logs/, scripts). Owned by the manager in
# BOTH modes, so it always runs as the manager.
runInstallOp() {
    runAsManager "$@"
}

# Write stdin to a MANAGER-owned path (apps DB sidecars, configs/, logs/ — e.g.
# the /docker/logs log-append idiom). Manager-owned in both modes.
# Pass -a/--append as the first arg to append.
runInstallWrite() {
    local append_flag=()
    if [[ "$1" == "-a" || "$1" == "--append" ]]; then
        append_flag=(-a)
        shift
    fi
    local dest="$1"
    runAsManager tee "${append_flag[@]}" "$dest" >/dev/null
}

# Backup-engine command (borg/restic/kopia) run AS the dedicated backup user
# ($docker_install_user), with the environment preserved (-E) so the repo
# password and BORG_/RESTIC_/KOPIA_ env vars reach the tool. Never root — the
# scoped sudoers lets the manager drop to this user. Single funnel so the
# backup subsystem's privilege drop has one audit point.
runBackupOp() {
    sudo -E -u "$docker_install_user" "$@"
}

# Run one of the ROOT-OWNED LibrePortal helpers installed (root:root 0755) under
# /usr/local/lib/libreportal/ by init.sh. These are how the manager-run runtime
# (Model A) performs the genuine-root operations it can't drop — establishing the
# /docker ownership model, editing /etc/resolv.conf, managing host SSH access —
# WITHOUT the scoped sudoers granting blanket `sudo chown/chmod/tee/sed/cp` (which
# would be root-equivalent: chown /etc/sudoers, tee a new sudoers drop-in, …).
# Each helper validates its own fixed-path operations, so the sudoers can allow it
# wholesale. At install time (already root) the installed helper may be absent, so
# run the bundled copy directly — no sudo, no escalation, since we are root.
_runRootHelper() {
    local name="$1"; shift
    local helper="/usr/local/lib/libreportal/$name"
    if [[ -x "$helper" ]]; then
        sudo "$helper" "$@"
    elif [[ $EUID -eq 0 ]]; then
        bash "${script_dir:-/libreportal-system/install}/scripts/system/$name" "$@"
    else
        sudo "$helper" "$@"
    fi
}

# Ownership reconcile: action ∈ {reconcile [mode]|traversal|containers-top|
#   app-perms|webui|taskdir|app-data-nobody <app>}
runOwnership()  { _runRootHelper libreportal-ownership "$@"; }

# /etc/resolv.conf edits: {clear|add <ip>}
runResolv()     { _runRootHelper libreportal-dns "$@"; }

# Host SSH access (authorized_keys + sshd PasswordAuthentication):
#   {ensure-dir|key-count|pw-status|has-keys|read-keys|authkeys-path|
#    key-add <b64>|key-remove <fp>|pw-set <on|off>}
runSshAccess()  { _runRootHelper libreportal-ssh-access "$@"; }

# Docker-socket read perms for the type switcher: {rootless|rooted} {on|off}
# (exit 3 = socket absent).
runSocket()     { _runRootHelper libreportal-socket "$@"; }

# Install/refresh the systemd task-processor unit (root generates the unit from
# config; no caller-supplied content): {install|enable|restart|start|status}
runSvc()        { _runRootHelper libreportal-svc "$@"; }

# Backup-engine binary install (restic/kopia) to /usr/local/bin: install <engine>
runBinInstall() { _runRootHelper libreportal-bininstall "$@"; }

# App config-file rewrites owned by in-container uids / root /etc:
#   {adguard-auth <user> <bcrypt>|crowdsec-priority|
#    owncloud-config <public> <host> <ip> <public_ip>}
runAppCfg()     { _runRootHelper libreportal-appcfg "$@"; }

# Genuine system-administration command (ufw/systemctl/apt/sysctl/useradd, /etc
# edits). Needs real root in both modes; funnelled through one place so it can
# later be confined to a scoped sudoers allowlist.
runSystem() {
    sudo "$@"
}
