Files
omarchy/test/shell.d/passwordless-grant-lifecycle-test.sh
T
Afonso OliveiraandClaude Fable 5.1 3eb3142313 Recognize legacy sudo grants for any account name
The legacy command wrote the caller's unvalidated name into both the
sudoers filename and the rule. Cleanup applied the current lower-case
account pattern to that suffix, so an exact legacy grant for an account
such as Alice was classified as administrator policy, left active, and
the machine-wide migration marker was written anyway.

Match a legacy grant by its exact filename and rule relationship instead
of the account policy, and cover it in the lifecycle suite through both
the unit cleanup and the real migration runner.

The account pattern itself stays lower-case: sudoers reads an upper-case
word such as ALICE as a User_Alias reference, and ALL as every user, so
such names must never reach the generated rule. Pin that with a test.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 23:01:13 +01:00

145 lines
6.4 KiB
Bash

#!/bin/bash
set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
source "$SHELL_TEST_DIR/fixtures/passwordless-sudo-test.sh"
(
source "$library"
printf 'deleteduser ALL=(ALL) NOPASSWD: ALL\n' >"$(rule_file 1000)"
printf 'buildbot$ ALL=(ALL) NOPASSWD: ALL\n' >"$test_tmp/etc/sudoers.d/99-omarchy-nopasswd-buildbot$"
# The legacy command never validated the account name, so a manual or NSS
# account outside the current policy still has its exact old grant removed.
printf 'Alice ALL=(ALL) NOPASSWD: ALL\n' >"$test_tmp/etc/sudoers.d/99-omarchy-nopasswd-Alice"
printf 'admin ALL=(ALL) NOPASSWD: /usr/bin/true\n' >"$test_tmp/etc/sudoers.d/99-omarchy-nopasswd-custom"
printf 'Alice ALL=(ALL) NOPASSWD: ALL\n' >"$test_tmp/etc/sudoers.d/99-omarchy-nopasswd-handwritten"
TEST_DELETE_FAIL=1 assert_status 1 cleanup_all_locked
[[ -e $(rule_file 1000) ]]
cleanup_all_locked
[[ ! -e $(rule_file 1000) && ! -e $test_tmp/etc/sudoers.d/99-omarchy-nopasswd-Alice ]]
[[ -e $test_tmp/etc/sudoers.d/99-omarchy-nopasswd-custom && -e $test_tmp/etc/sudoers.d/99-omarchy-nopasswd-handwritten ]]
! compgen -G "$test_tmp/etc/sudoers.d/99-omarchy-nopasswd-buildbot*"
rm "$test_tmp/etc/sudoers.d/99-omarchy-nopasswd-custom" "$test_tmp/etc/sudoers.d/99-omarchy-nopasswd-handwritten"
)
pass "legacy cleanup removes generated orphan rules for any account and preserves custom policy"
# Run the actual migration queue for separate temporary homes. Sudo only calls
# the mapped helper and can be refused without requesting host authorization.
mkdir -p "$test_tmp/source/migrations"
sed "s|/usr/bin/omarchy-sudo-passwordless|$test_tmp/omarchy-sudo-passwordless|g" \
"$ROOT/migrations/1788163635.sh" >"$test_tmp/source/migrations/1788163635.sh"
printf 'echo "later migration ran"\n' >"$test_tmp/source/migrations/1788163636.sh"
run_migrations() {
TEST_MIGRATION=1 OMARCHY_PATH="$test_tmp/source" OMARCHY_MIGRATION_STATE="$test_tmp/$1" \
PATH="$test_tmp/bin:$PATH" /usr/bin/bash "$ROOT/bin/omarchy-migrate" >"$test_tmp/migrations.log" 2>&1
}
marker="$test_tmp/var/lib/omarchy/migrations/1788163635"
(
source "$library"
printf 'audituser ALL=(ALL) NOPASSWD: ALL\n' >"$(rule_file 1000)"
printf 'Alice ALL=(ALL) NOPASSWD: ALL\n' >"$test_tmp/etc/sudoers.d/99-omarchy-nopasswd-Alice"
TEST_DELETE_FAIL=1 assert_status 1 run_migrations first
[[ ! -e $marker && ! -e $test_tmp/first/1788163636.sh ]]
run_migrations first
[[ -f $marker && -f $test_tmp/first/1788163636.sh ]]
[[ ! -e $test_tmp/etc/sudoers.d/99-omarchy-nopasswd-Alice ]]
enable_locked 1000 15
cp "$(rule_file 1000)" "$test_tmp/renewed"
: >"$test_tmp/commands"
TEST_NO_SUDO=1 run_migrations second
[[ -f $test_tmp/second/1788163636.sh ]]
! grep -q '^sudo ' "$test_tmp/commands"
cmp "$(rule_file 1000)" "$test_tmp/renewed"
)
pass "migration completion is machine-wide, retryable, and needs no sudo for later users"
(
source "$library"
TEST_BAD_PATH="$marker" assert_status 1 migration_complete
rm "$marker"
ln -s "$test_tmp/renewed" "$marker"
assert_status 1 migration_complete
assert_status 1 migrate_locked
[[ -L $marker ]]
rm "$marker"
)
pass "migration checks marker ownership and rejects symlinks"
# Keep real package scripts in the contract: source and packaging share the
# same lock and blocker, including the legacy scriptlet fallback.
pkgs_path=${OMARCHY_PKGS_PATH:-$ROOT/../omarchy-pkgs}
[[ ! -d $pkgs_path/pkgbuilds ]] || pkgs_path=$pkgs_path/pkgbuilds
for name in omarchy-settings omarchy-settings-dev; do
script="$pkgs_path/$name/$name.install"
[[ -f $script ]] || fail "set OMARCHY_PKGS_PATH to the companion package checkout"
sed -e "s|/etc/|$test_tmp/etc/|g" -e "s|/run|$test_tmp/run|g" \
-e "s|/usr/bin/stat|$test_tmp/bin/stat|g" -e "s|/usr/bin/rm|$test_tmp/bin/rm|g" \
"$script" >"$test_tmp/$name.install"
reset_grant
(
source "$library"
source "$test_tmp/$name.install"
_etc_overrides_apply() { :; }
enable_locked 1000 15
TEST_DELETE_FAIL=1 assert_status 1 pre_remove
[[ -e $REMOVAL_BLOCKER && -e $(rule_file 1000) ]]
assert_status 1 enable_locked 1000 15
pre_remove && post_remove
[[ ! -e $(rule_file 1000) ]]
post_install
[[ ! -e $REMOVAL_BLOCKER ]]
enable_locked 1000 15
pre_upgrade && post_upgrade
[[ ! -e $(rule_file 1000) && ! -e $REMOVAL_BLOCKER ]]
)
done
pass "both settings packages revoke grants, block publication, and recover on installation"
reset_grant
# Hold the source lock, then start package removal. A native flock on the
# mapped file must serialize both implementations.
cat >"$test_tmp/worker" <<'WORKER'
#!/bin/bash
set -euo pipefail
source "$TEST_LIBRARY"
critical() {
touch "$TEST_GRANT_ROOT/entered"
for (( attempt=0; attempt<500; attempt++ )); do
[[ ! -e $TEST_GRANT_ROOT/release ]] || break
sleep 0.01
done
[[ -e $TEST_GRANT_ROOT/release ]] || return 1
enable_locked 1000 15
}
with_root_lock critical
WORKER
TEST_LIBRARY="$library" /usr/bin/bash "$test_tmp/worker" >"$test_tmp/publisher.log" 2>&1 &
publisher=$!
children+=("$publisher")
for (( attempt=0; attempt<200; attempt++ )); do
[[ ! -e $test_tmp/entered ]] || break
sleep 0.01
done
[[ -f $test_tmp/entered ]] || fail "publisher failed to acquire the lock"
/usr/bin/bash -euo pipefail -c 'source "$1"; pre_remove; post_remove' bash "$test_tmp/omarchy-settings.install" >"$test_tmp/removal.log" 2>&1 &
removal=$!
children+=("$removal")
touch "$test_tmp/release"
wait "$publisher" || fail "publisher failed" "$(cat "$test_tmp/publisher.log")"
wait "$removal" || fail "removal failed" "$(cat "$test_tmp/removal.log")"
children=()
[[ ! -e $test_tmp/etc/sudoers.d/99-omarchy-nopasswd-1000 ]]
[[ -f $test_tmp/run/omarchy-sudo-passwordless-package-removing ]]
pass "native lock serializes grant publication with package removal"
# systemd-tmpfiles operates on an explicit disposable root, never the host.
reset_grant
: >"$test_tmp/etc/sudoers.d/99-omarchy-nopasswd-1000"
: >"$test_tmp/etc/sudoers.d/unrelated"
rule='r! /etc/sudoers.d/99-omarchy-nopasswd-*'
/usr/bin/systemd-tmpfiles --root="$test_tmp" --remove --inline "$rule"
[[ -f $test_tmp/etc/sudoers.d/99-omarchy-nopasswd-1000 ]] || fail "routine tmpfiles shortened a live grant"
/usr/bin/systemd-tmpfiles --root="$test_tmp" --remove --boot --inline "$rule"
[[ ! -e $test_tmp/etc/sudoers.d/99-omarchy-nopasswd-1000 && -f $test_tmp/etc/sudoers.d/unrelated ]] || fail "boot cleanup boundary"
pass "native boot cleanup removes grants while routine tmpfiles preserves them"