diff --git a/test/acceptance.d/cups-test.sh b/test/acceptance.d/cups-test.sh new file mode 100644 index 00000000..b2c82fe7 --- /dev/null +++ b/test/acceptance.d/cups-test.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +set -euo pipefail + +source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" + +for package in cups cups-filters system-config-printer cups-pk-helper; do + pacman -Q "$package" >/dev/null 2>&1 || fail "printing packages are installed" "$package is missing" +done +pass "printing packages are installed" + +! pacman -Q cups-pdf >/dev/null 2>&1 || fail "the root CUPS-PDF backend is absent" +pass "the root CUPS-PDF backend is absent" + +! pacman -Q cups-browsed >/dev/null 2>&1 || fail "automatic printer discovery is absent" +! systemctl is-enabled --quiet cups-browsed.service 2>/dev/null || + fail "automatic printer discovery is not enabled" +! systemctl is-active --quiet cups-browsed.service 2>/dev/null || + fail "automatic printer discovery is not running" +! pgrep -x cups-browsed >/dev/null 2>&1 || fail "no cups-browsed process exists" +pass "automatic printer discovery is not installed or running" + +for path in \ + /etc/cups/cups-browsed.conf \ + /etc/cups/cups-browsed.conf.pacsave \ + /etc/cups/cups-browsed.conf.pacnew \ + /usr/bin/cups-browsed \ + /usr/lib/cups/backend/implicitclass \ + /usr/lib/systemd/system/cups-browsed.service \ + /etc/systemd/system/multi-user.target.wants/cups-browsed.service; do + [[ ! -e $path && ! -L $path ]] || + fail "automatic printer discovery leaves no package files" "$path still exists" +done +pass "automatic printer discovery leaves no package files" + +systemctl is-enabled --quiet cups.service || fail "CUPS is enabled" +systemctl is-active --quiet cups.service || fail "CUPS is running" +timeout 10 lpstat -r >/dev/null 2>&1 || fail "the CUPS scheduler answers" +pass "CUPS is enabled, running, and answering" + +policy_metadata=$(stat -c '%U:%G %a' /etc/cups/cups-files.conf) +[[ $policy_metadata == "root:cups 640" ]] || + fail "the CUPS authorization policy is protected" "$policy_metadata" +pass "the CUPS authorization policy is protected" + +if lpinfo_output=$(LC_ALL=C timeout 10 lpinfo -v &1); then + fail "the desktop user cannot administer CUPS without authentication" +elif [[ $lpinfo_output != *"Forbidden"* ]]; then + fail "CUPS explicitly denies unauthenticated desktop administration" "$lpinfo_output" +fi +pass "CUPS denies unauthenticated desktop administration" diff --git a/test/acceptance.d/system-test.sh b/test/acceptance.d/system-test.sh index ab3affa7..610fcb4b 100644 --- a/test/acceptance.d/system-test.sh +++ b/test/acceptance.d/system-test.sh @@ -47,7 +47,7 @@ verify_services() { local unit for unit in \ - avahi-daemon.service cups.service docker.socket \ + avahi-daemon.service docker.socket \ NetworkManager.service power-profiles-daemon.service sddm.service \ systemd-resolved.service ufw.service; do systemctl is-enabled --quiet "$unit" || fail "core system services are enabled" "$unit is not enabled" @@ -64,46 +64,6 @@ verify_services() { pass "user audio services are running" } -verify_printing_security() { - local lpinfo_output path - - ! pacman -Q cups-pdf >/dev/null 2>&1 || fail "CUPS-PDF is absent" - pass "the root CUPS-PDF backend is not installed" - - # Automatic discovery is temporarily out of the default install: a daemon that - # turns anything advertising itself on the network into a print queue is more - # exposure than the convenience is worth while it is reworked. CUPS itself - # stays, so what a stock machine proves here is that printing runs and that - # the desktop user still cannot administer it without authenticating. - ! pacman -Q cups-browsed >/dev/null 2>&1 || fail "automatic printer discovery is not installed" - ! systemctl is-enabled --quiet cups-browsed.service 2>/dev/null || - fail "no discovery service is enabled" - ! systemctl is-active --quiet cups-browsed.service 2>/dev/null || - fail "no discovery service is running" - - for path in \ - /etc/cups/cups-browsed.conf \ - /etc/cups/cups-browsed.conf.pacsave \ - /usr/bin/cups-browsed \ - /usr/lib/cups/backend/implicitclass \ - /usr/lib/systemd/system/cups-browsed.service \ - /etc/systemd/system/multi-user.target.wants/cups-browsed.service; do - [[ ! -e $path && ! -L $path ]] || - fail "automatic printer discovery leaves no installed package files" "$path still exists" - done - pass "automatic printer discovery is absent from a stock install" - - systemctl is-active --quiet cups.service || fail "CUPS is running" - - if lpinfo_output=$(LC_ALL=C timeout 10 lpinfo -v &1); then - fail "the desktop user cannot administer CUPS without authentication" - elif [[ $lpinfo_output != *"Forbidden"* ]]; then - fail "CUPS explicitly denies unauthenticated desktop administration" "$lpinfo_output" - fi - - pass "CUPS runs with passwordless desktop administration still denied" -} - verify_runtime_tools() { # Docker access is intentionally NOT granted to the desktop user: the docker # group is root-equivalent, so a rogue process running as the user could @@ -147,7 +107,7 @@ verify_user_setup() { pass "Omarchy user state and shell configuration exist" } -for check in verify_core_packages verify_defaults verify_services verify_printing_security verify_runtime_tools verify_user_setup; do +for check in verify_core_packages verify_defaults verify_services verify_runtime_tools verify_user_setup; do if ! ("$check"); then status=1 fi diff --git a/test/shell.d/cups-browsed-removal-migration-test.sh b/test/shell.d/cups-browsed-removal-migration-test.sh deleted file mode 100644 index a311e6e7..00000000 --- a/test/shell.d/cups-browsed-removal-migration-test.sh +++ /dev/null @@ -1,358 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -source "$(cd -- "$(dirname -- "$0")" && pwd)/base-test.sh" - -migration="$ROOT/migrations/1788009111.sh" - -test_tmp=$(mktemp -d) -trap 'rm -rf "$test_tmp"' EXIT - -mock_bin="$test_tmp/bin" -mkdir -p "$mock_bin" "$test_tmp/var/lib/omarchy/migrations" - -# Every privileged step is stubbed: a real run here would take printer -# discovery off the developer's own machine. -cat >"$mock_bin/omarchy-pkg-present" <<'SH' -#!/bin/bash -[[ " $BROWSED_INSTALLED " == *" $1 "* ]] -SH - -cat >"$mock_bin/omarchy-pkg-drop" <<'SH' -#!/bin/bash -printf 'omarchy-pkg-drop\t%s\n' "$*" >>"$BROWSED_LOG" -[[ -z $BROWSED_DROP_FAILS ]] -SH - -cat >"$mock_bin/pacman" <<'SH' -#!/bin/bash -printf 'pacman\t%s\n' "$*" >>"$BROWSED_LOG" -if [[ $* == *--print* ]]; then - [[ -z $BROWSED_REMOVAL_BLOCKED ]] -fi -SH - -cat >"$mock_bin/systemctl" <<'SH' -#!/bin/bash -printf 'systemctl\t%s\n' "$*" >>"$BROWSED_LOG" -unit=$3 -case $1 in - is-enabled) [[ " $BROWSED_ENABLED " == *" $unit "* ]] ;; - is-active) [[ " $BROWSED_ACTIVE " == *" $unit "* ]] ;; - *) : ;; -esac -SH - -cat >"$mock_bin/sudo" <<'SH' -#!/bin/bash -printf 'sudo\t%s\n' "$*" >>"$BROWSED_LOG" -exec "$@" -SH - -cat >"$mock_bin/install" <<'SH' -#!/bin/bash -printf 'install\t%s\n' "$*" >>"$BROWSED_LOG" -exec /usr/bin/install "$@" -SH - -# lpstat reports only package-generated queues on the implicitclass backend. -# Manual ipp:// and usb:// printers must survive. -cat >"$mock_bin/lpstat" <<'SH' -#!/bin/bash -printf 'lpstat\t%s\n' "$*" >>"$BROWSED_LOG" -case $1 in - -v) - if [[ -n $BROWSED_LPSTAT_FAILS ]]; then - echo "lpstat: Scheduler is not responding." >&2 - exit 1 - elif [[ -z $BROWSED_QUEUES ]]; then - echo "lpstat: No destinations added." >&2 - exit 1 - elif [[ $LC_ALL == "C" ]]; then - printf '%s\n' "$BROWSED_QUEUES" - else - printf '%s\n' "$BROWSED_QUEUES" | sed 's|^device for |Gerät für |' - fi - ;; - -p) - [[ " $BROWSED_GONE_QUEUES " != *" $2 "* ]] - ;; - -o) - if [[ -n $BROWSED_LPSTAT_O_FAILS ]]; then - echo "lpstat: Scheduler is not responding." >&2 - exit 1 - fi - if [[ " $BROWSED_BUSY_QUEUES " == *" $2 "* ]]; then - printf '%s-7 alice 1024\n' "$2" - fi - ;; -esac -SH - -cat >"$mock_bin/lpadmin" <<'SH' -#!/bin/bash -printf 'lpadmin\t%s\n' "$*" >>"$BROWSED_LOG" -if [[ -n $BROWSED_LPADMIN_FAILS ]]; then - echo "lpadmin: Printer does not exist." >&2 - exit 1 -fi -SH - -cat >"$mock_bin/cupsreject" <<'SH' -#!/bin/bash -printf 'cupsreject\t%s\n' "$*" >>"$BROWSED_LOG" -if [[ -n $BROWSED_REJECT_FAILS ]]; then - echo "cupsreject: Unable to reject jobs." >&2 - exit 1 -fi -SH - -chmod +x "$mock_bin"/* - -log="$test_tmp/actions.log" -output="$test_tmp/migration.out" -errors="$test_tmp/migration.err" -status=0 - -start_case() { - installed="cups-browsed" - enabled="cups-browsed.service" - active="cups-browsed.service cups.service" - blocked="" - drop_fails="" - queues="" - busy="" - lpstat_fails="" - lpstat_o_fails="" - lpadmin_fails="" - reject_fails="" - gone_queues="" - use_marker="$test_tmp/var/lib/omarchy/migrations/$1" - rm -f "$use_marker" -} - -run_migration() { - : >"$log" - : >"$output" - : >"$errors" - - if env -u LC_ALL -u LANGUAGE \ - BROWSED_LOG="$log" \ - BROWSED_INSTALLED="$installed" \ - BROWSED_ENABLED="$enabled" \ - BROWSED_ACTIVE="$active" \ - BROWSED_REMOVAL_BLOCKED="$blocked" \ - BROWSED_DROP_FAILS="$drop_fails" \ - BROWSED_QUEUES="$queues" \ - BROWSED_BUSY_QUEUES="$busy" \ - BROWSED_LPSTAT_FAILS="$lpstat_fails" \ - BROWSED_LPSTAT_O_FAILS="$lpstat_o_fails" \ - BROWSED_LPADMIN_FAILS="$lpadmin_fails" \ - BROWSED_REJECT_FAILS="$reject_fails" \ - BROWSED_GONE_QUEUES="$gone_queues" \ - PATH="$mock_bin:$PATH" \ - OMARCHY_PATH="$ROOT" \ - OMARCHY_CUPS_BROWSED_REMOVAL_MARKER="$use_marker" \ - bash -euo pipefail "$migration" >"$output" 2>"$errors"; then - status=0 - else - status=$? - fi -} - -assert_description_only() { - [[ $(<"$output") == "Temporarily remove automatic printer discovery" ]] || - fail "the migration prints only its description" "$(cat "$output")" -} - -assert_quiet_success() { - (( status == 0 )) || fail "the migration succeeds" "$(cat "$errors")" - assert_description_only - [[ ! -s $errors ]] || fail "a successful migration is quiet" "$(cat "$errors")" -} - -# ------------------------------------------------ a machine that has printers - -start_case printers -queues=$'device for Office: implicitclass://Office/\ndevice for Front_Desk: implicitclass://Front_Desk/\ndevice for all: implicitclass://all/\ndevice for Desk: ipp://192.168.1.9/ipp/print\ndevice for Attic: usb://HP/LaserJet%20P1102' -run_migration -assert_quiet_success - -grep -qxF $'pacman\t-Rs --print cups-browsed' "$log" || - fail "the migration preflights the package removal" "$(cat "$log")" -grep -qxF $'sudo\tsystemctl disable --now cups-browsed.service' "$log" || - fail "the migration disables and stops discovery" "$(cat "$log")" -for queue in Office Front_Desk all; do - grep -qxF "sudo lpadmin -x $queue" "$log" || - fail "the migration removes generated queue $queue" "$(cat "$log")" -done -grep -q 'lpadmin -x Desk' "$log" && - fail "the migration leaves a manually-added IPP printer alone" "$(cat "$log")" -grep -q 'lpadmin -x Attic' "$log" && - fail "the migration leaves a manually-added USB printer alone" "$(cat "$log")" -grep -qxF $'omarchy-pkg-drop\tcups-browsed' "$log" || - fail "the migration uses the standard package removal helper" "$(cat "$log")" -[[ -f $use_marker ]] || fail "the migration records machine-wide completion" - -disable_line=$(grep -n 'disable --now' "$log" | head -1 | cut -d: -f1) -cleanup_line=$(grep -n 'lpadmin -x' "$log" | tail -1 | cut -d: -f1) -removal_line=$(grep -n 'omarchy-pkg-drop' "$log" | head -1 | cut -d: -f1) -(( disable_line < cleanup_line && cleanup_line < removal_line )) || - fail "the service and queues are handled before package removal" "$(cat "$log")" -pass "generated queues are removed, manual printers survive, and normal output is quiet" - -# ------------------------------------ a second user, after a deliberate reinstall - -run_migration -assert_quiet_success -[[ ! -s $log ]] || fail "a completed machine-wide removal is left alone" "$(cat "$log")" -pass "a second user does not undo a deliberate reinstall" - -# ----------------------------------------- a machine that never had discovery - -start_case no-package -installed="" -run_migration -assert_quiet_success -[[ ! -s $log ]] || fail "a machine without discovery is left alone" "$(cat "$log")" -[[ ! -e $use_marker ]] || fail "a machine without discovery gets no marker" -pass "a machine without cups-browsed does no privileged work" - -# --------------------------------------------- a machine that has no printers - -start_case no-printers -run_migration -assert_quiet_success -grep -qxF $'omarchy-pkg-drop\tcups-browsed' "$log" || - fail "no printers does not prevent package removal" "$(cat "$log")" -grep -qE 'cupsreject|lpadmin' "$log" && - fail "no printers requires no queue administration" "$(cat "$log")" -[[ -f $use_marker ]] || fail "the no-printer migration records completion" -pass "CUPS reporting no destinations is a successful empty migration" - -# ------------------------------------------------- a blocked package removal - -start_case blocked -blocked="1" -run_migration -(( status != 0 )) || fail "a blocked package removal remains pending" -assert_description_only -grep -q 'disable --now' "$log" && - fail "a blocked removal does not stop discovery" "$(cat "$log")" -grep -q 'omarchy-pkg-drop' "$log" && - fail "a blocked removal does not invoke package removal" "$(cat "$log")" -[[ ! -e $use_marker ]] || fail "a blocked removal gets no marker" -pass "a package dependency prevents any partial migration" - -# --------------------------------------------------- a queue that has jobs - -start_case busy -queues=$'device for Office: implicitclass://Office/\ndevice for Spare: implicitclass://Spare/' -busy="Office" -run_migration -assert_quiet_success -grep -q 'lpadmin -x Office' "$log" && - fail "a queue with jobs is not deleted" "$(cat "$log")" -grep -qxF $'sudo\tlpadmin -x Spare' "$log" || - fail "an idle generated queue is deleted" "$(cat "$log")" -grep -qxF $'omarchy-pkg-drop\tcups-browsed' "$log" || - fail "a busy queue does not retain the discovery package" "$(cat "$log")" - -reject_line=$(grep -n 'cupsreject.*Spare' "$log" | head -1 | cut -d: -f1) -probe_line=$(grep -n $'^lpstat\t-o Spare' "$log" | head -1 | cut -d: -f1) -delete_line=$(grep -n 'lpadmin -x Spare' "$log" | head -1 | cut -d: -f1) -(( reject_line < probe_line && probe_line < delete_line )) || - fail "a queue is closed before it is checked and deleted" "$(cat "$log")" -pass "busy queues survive while idle discovery queues are removed" - -# ------------------------------------------------- a job query that fails - -start_case job-query-fails -queues=$'device for Office: implicitclass://Office/' -lpstat_o_fails="1" -run_migration -(( status != 0 )) || fail "a failed job query keeps the migration pending" -assert_description_only -grep -q 'lpadmin -x' "$log" && - fail "a queue with unknown job state is not deleted" "$(cat "$log")" -grep -q 'omarchy-pkg-drop' "$log" && - fail "a failed job query retains the package" "$(cat "$log")" -[[ ! -e $use_marker ]] || fail "a failed job query gets no marker" -pass "a failed job query is retryable instead of falsely completing" - -# ------------------------------------------------------ CUPS out of reach - -start_case no-cups -queues=$'device for Office: implicitclass://Office/' -lpstat_fails="1" -run_migration -(( status != 0 )) || fail "an unavailable CUPS server keeps the migration pending" -assert_description_only -grep -qxF "lpstat: Scheduler is not responding." "$errors" || - fail "the underlying CUPS failure is preserved" "$(cat "$errors")" -grep -q 'omarchy-pkg-drop' "$log" && - fail "an unavailable CUPS server retains the package" "$(cat "$log")" -[[ ! -e $use_marker ]] || fail "an unavailable CUPS server gets no marker" -pass "an actual CUPS failure remains pending without custom telemetry" - -# ------------------------------------------------ a queue that will not go - -start_case stuck-queue -queues=$'device for Office: implicitclass://Office/' -lpadmin_fails="1" -run_migration -(( status != 0 )) || fail "a failed queue deletion keeps the migration pending" -grep -q 'omarchy-pkg-drop' "$log" && - fail "a persistent generated queue retains the backend" "$(cat "$log")" -[[ ! -e $use_marker ]] || fail "a failed queue deletion gets no marker" -pass "a persistent generated queue prevents partial completion" - -# --------------------------------------------- a queue removed concurrently - -start_case vanished-queue -queues=$'device for Office: implicitclass://Office/' -lpadmin_fails="1" -gone_queues="Office" -run_migration -assert_quiet_success -grep -qxF $'omarchy-pkg-drop\tcups-browsed' "$log" || - fail "a concurrently removed queue does not block package removal" "$(cat "$log")" -pass "a queue removed concurrently counts as removed" - -# ------------------------------------------- a queue that cannot be closed - -start_case open-queue -queues=$'device for Office: implicitclass://Office/' -reject_fails="1" -run_migration -(( status != 0 )) || fail "a queue that cannot be closed keeps the migration pending" -grep -q 'lpadmin -x' "$log" && - fail "a queue still accepting jobs is not deleted" "$(cat "$log")" -grep -q 'omarchy-pkg-drop' "$log" && - fail "a queue still accepting jobs retains the backend" "$(cat "$log")" -[[ ! -e $use_marker ]] || fail "a queue still accepting jobs gets no marker" -pass "a queue that cannot be closed is retried later" - -# -------------------------------------------------- a masked but running daemon - -start_case masked -enabled="" -active="cups-browsed.service" -run_migration -assert_quiet_success -grep -qxF $'sudo\tsystemctl stop cups-browsed.service' "$log" || - fail "a masked running daemon is stopped" "$(cat "$log")" -grep -q 'disable --now' "$log" && - fail "a masked unit is not disabled" "$(cat "$log")" -pass "a masked running daemon is stopped without noisy output" - -# ------------------------------------------------ a package removal that fails - -start_case drop-fails -drop_fails="1" -run_migration -(( status != 0 )) || fail "a failed package removal keeps the migration pending" -assert_description_only -[[ ! -e $use_marker ]] || fail "a failed package removal gets no marker" -pass "a package removal failure cannot be recorded as complete"