aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/modules/user
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-16 09:56:48 +0200
committerDanilo M. <danix@danix.xyz>2026-10-16 09:56:48 +0200
commitd5eaa4855a90057c268bf4d717f366ccf3197dba (patch)
tree34e0f794e7accd57e20f42fa6593dc247c590e19 /desktop/modules/user
parent274d1f02dfa0ac7a28f5318fe8c4eb6a28702bbe (diff)
downloadquickshell-d5eaa4855a90057c268bf4d717f366ccf3197dba.tar.gz
quickshell-d5eaa4855a90057c268bf4d717f366ccf3197dba.zip
fix(user): userctl survives leading dashes and reports failed reads
busctl and kwriteconfig6 parse a value starting with '-' as an option, so a location like '-x' failed; '--' before the value ends option parsing (checked against both binaries). get ran without pipefail, so a failing busctl gave empty fields and exit 0, which the page would show as a blank form. It now reads all fields first and, on failure, prints one error= line and exits 1. keys listed revoked, expired or invalid primaries, keys that cannot sign, and could label a key with a revoked uid. It now skips those. Unsetting the signing key uses --unset-all, since git exits 5 on a multi-valued key with --unset. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Diffstat (limited to 'desktop/modules/user')
-rwxr-xr-xdesktop/modules/user/test-userctl.sh24
-rwxr-xr-xdesktop/modules/user/userctl28
2 files changed, 37 insertions, 15 deletions
diff --git a/desktop/modules/user/test-userctl.sh b/desktop/modules/user/test-userctl.sh
index cc90550..02a6e0c 100755
--- a/desktop/modules/user/test-userctl.sh
+++ b/desktop/modules/user/test-userctl.sh
@@ -42,7 +42,7 @@ for n in busctl kwriteconfig6 git gpg; do ln -s stub "$tmp/bin/$n"; done
export PATH="$tmp/bin:$PATH" HOME="$tmp" LOG="$tmp/log"
obj="/org/freedesktop/Accounts/User$(id -u)"
-acc="busctl [--system] [call] [org.freedesktop.Accounts] [$obj] [org.freedesktop.Accounts.User]"
+acc="busctl [--system] [call] [--] [org.freedesktop.Accounts] [$obj] [org.freedesktop.Accounts.User]"
pass=0
fail=0
@@ -73,10 +73,14 @@ cat > "$tmp/gpg.txt" <<'EOF'
sec:u:255:22:AAAA1111:1700000000:::u:::scESC:::+:::23::0:
fpr:::::::::1111222233334444555566667777AAAA1111:
grp:::::::::0000:
+uid:r::::1700000000::HASH::Old Name <old@example.org>::::::::::0:
uid:u::::1700000000::HASH::Jane Doèr <jane@example.org>::::::::::0:
uid:u::::1700000000::HASH::Jane D. <jd@example.org>::::::::::0:
ssb:u:255:18:SUB1:1700000000::::::e:::+:::23:
fpr:::::::::9999888877776666555544443333SUB1:
+sec:r:255:22:DEAD0000:1700000000:::u:::scESC:::#:::23::0:
+fpr:::::::::DEAD0000DEAD0000DEAD0000DEAD00000000:
+uid:r::::1700000000::HASH::Revoked Key <rev@example.org>::::::::::0:
sec:u:255:22:BBBB2222:1700000000:::u:::scESC:::#:::23::0:
fpr:::::::::AAAABBBBCCCCDDDDEEEEFFFF0000BBBB2222:
uid:u::::1700000000::HASH::Work Key <work@example.org>::::::::::0:
@@ -90,7 +94,7 @@ reset
out="$("$ctl" set name "Jane Doèr")"
check "set name prints nothing" "" "$out"
check "set name writes" "$acc [SetRealName] [s] [Jane Doèr]
-kwriteconfig6 [--file] [emaildefaults] [--group] [PROFILE_Default] [--key] [FullName] [Jane Doèr]
+kwriteconfig6 [--file] [emaildefaults] [--group] [PROFILE_Default] [--key] [FullName] [--] [Jane Doèr]
git [config] [--global] [user.name] [Jane D.]" "$(cat "$LOG")"
gitname() { reset; "$ctl" set name "$1" >/dev/null; grep '^git' "$LOG"; }
@@ -103,7 +107,7 @@ check "extra spaces" "git [config] [--global] [user.name] [Jane D.]" "$(gitname
reset
"$ctl" set email jane@example.org >/dev/null
check "set email writes" "$acc [SetEmail] [s] [jane@example.org]
-kwriteconfig6 [--file] [emaildefaults] [--group] [PROFILE_Default] [--key] [EmailAddress] [jane@example.org]
+kwriteconfig6 [--file] [emaildefaults] [--group] [PROFILE_Default] [--key] [EmailAddress] [--] [jane@example.org]
git [config] [--global] [user.email] [jane@example.org]" "$(cat "$LOG")"
# AccountsService only.
@@ -121,11 +125,23 @@ reset
export GIT_VALUE=OLD
"$ctl" set signingkey "" >/dev/null
check "unset signingkey" "git [config] [--global] [--get] [user.signingkey]
-git [config] [--global] [--unset] [user.signingkey]" "$(cat "$LOG")"
+git [config] [--global] [--unset-all] [user.signingkey]" "$(cat "$LOG")"
reset
"$ctl" set signingkey "" >/dev/null
check "unset when none is set" "git [config] [--global] [--get] [user.signingkey]" "$(cat "$LOG")"
+# A leading dash is a value, not an option.
+reset
+"$ctl" set location -x >/dev/null
+check "dash value" "$acc [SetLocation] [s] [-x]" "$(cat "$LOG")"
+
+# A failing read: one error line, exit 1, no key=value lines.
+reset
+export FAIL=busctl
+out="$("$ctl" get)"; rc=$?
+check "get failure exit" "1" "$rc"
+check "get failure output" "error=boom" "$out"
+
# A failing write stops there, says why on stdout, and exits 1.
reset
export FAIL=busctl
diff --git a/desktop/modules/user/userctl b/desktop/modules/user/userctl
index 07d3a63..d9720d8 100755
--- a/desktop/modules/user/userctl
+++ b/desktop/modules/user/userctl
@@ -49,12 +49,16 @@ run() {
}
# --json, because plain get-property prints non-ASCII as octal escapes.
+# Fails with the first line of stderr as its output, so get can report it.
prop() {
- busctl --system --json=short get-property "$ACC" "$OBJ" "$IFACE" "$1" | jq -r .data
+ local out
+ out="$(busctl --system --json=short get-property "$ACC" "$OBJ" "$IFACE" "$1" 2>&1)" \
+ || { head -n1 <<< "${out:-failed}"; return 1; }
+ jq -r .data <<< "$out"
}
-acc() { run busctl --system call "$ACC" "$OBJ" "$IFACE" "$1" s "$2"; }
-kde() { run kwriteconfig6 --file emaildefaults --group PROFILE_Default --key "$1" "$2"; }
+acc() { run busctl --system call -- "$ACC" "$OBJ" "$IFACE" "$1" s "$2"; }
+kde() { run kwriteconfig6 --file emaildefaults --group PROFILE_Default --key "$1" -- "$2"; }
# The surname as an initial, for commits: "Jane Doèr" -> "Jane D.".
gitname() {
@@ -69,20 +73,22 @@ gitname() {
case "$1" in
get)
- printf 'name=%s\n' "$(prop RealName)"
- printf 'email=%s\n' "$(prop Email)"
- printf 'avatar=%s\n' "$(prop IconFile)"
- printf 'language=%s\n' "$(prop Language)"
- printf 'location=%s\n' "$(prop Location)"
+ out=
+ # Read everything first: one error line and exit 1, never blank fields.
+ for p in RealName:name Email:email IconFile:avatar Language:language Location:location; do
+ val="$(prop "${p%%:*}")" || { printf 'error=%s\n' "$val"; exit 1; }
+ out+="${p#*:}=$val"$'\n'
+ done
+ printf '%s' "$out"
printf 'signingkey=%s\n' "$(git config --global --get user.signingkey)"
;;
keys)
# The first fpr after a sec is the primary key's; the first uid
# after that names it. ssb fingerprints come later and are skipped.
gpg --list-secret-keys --with-colons 2>/dev/null | awk -F: '
- $1 == "sec" { want = 1; next }
+ $1 == "sec" { want = ($2 !~ /^[rei]$/ && $12 ~ /S/) ? 1 : 0; next }
$1 == "fpr" && want == 1 { fpr = $10; want = 2; next }
- $1 == "uid" && want == 2 { print fpr "\t" $10; want = 0 }'
+ $1 == "uid" && want == 2 && $2 !~ /^[re]$/ { print fpr "\t" $10; want = 0 }'
;;
set)
[[ $# -eq 3 ]] || usage
@@ -105,7 +111,7 @@ case "$1" in
if [[ -n "$v" ]]; then
run git config --global user.signingkey "$v"
elif git config --global --get user.signingkey >/dev/null; then
- run git config --global --unset user.signingkey
+ run git config --global --unset-all user.signingkey
fi
;;
*) usage ;;