unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#72441: 31.0.50; Auth-source-pass doesn't match password attributes or hosts without user when extra-query-keywords is true
@ 2024-08-03 11:12 Björn Bidar via Bug reports for GNU Emacs, the Swiss army knife of text editors
  2024-08-09 18:02 ` J.P.
  0 siblings, 1 reply; 6+ messages in thread
From: Björn Bidar via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-08-03 11:12 UTC (permalink / raw)
  To: 72441


I noticed that auth-source-pass doesn't match agains password file
attributes such as those containing :user and only matches password
files which contain a host and a user when
auth-source-pass-extra-query-keywords is true.

Steps to reproduce:
1. Setup pass with the following structure
   WorkingTest/example.com/foo
   FailingTest/example2.com
   FailingTest/example3.com with user: foo in the password file
2. (auth-source-pass-enable)
3. (setq auth-source-pass-extra-query-keywords t)
4. (auth-source-search :host "example" :user "foo") -> works
5. (auth-source-search :host "example2.com") -> fails
6. (auth-source-search :host "example3.com" :user "foo")

Auth-source-pass should be able to query the password file for
additional attributes if one of the previous attributes such as :host
match to it. Quering the file attributes is quite important in use cases where it doesn't make
sense to the user to have a host-folder/user-file structure in cases
where there's only one password for said host.
Same it should maybe also match against :host if no user was provided,
I don't know how other sources do this thou.

In GNU Emacs 31.0.50 (build 1, x86_64-suse-linux-gnu, GTK+ Version
3.24.43, cairo version 1.18.0)
Windowing system distributor 'The X.Org Foundation', version 11.0.12101012
System Description: openSUSE Tumbleweed

Configured using:
 'configure --disable-build-details --without-pop --with-mailutils
 --without-hesiod --with-gameuser=:games --with-kerberos
 --with-kerberos5 --with-file-notification=inotify --with-modules
 --enable-autodepend --enable-link-time-optimization --prefix=/usr
 --mandir=/usr/share/man --infodir=/usr/share/info --datadir=/usr/share
 --localstatedir=/var --sharedstatedir=/var/lib
 --libexecdir=/usr/libexec --with-file-notification=yes
 --libdir=/usr/lib64 --with-native-compilation=aot
 --enable-locallisppath=/usr/share/emacs/31.0.50/site-lisp:/usr/share/emacs/site-lisp
 --with-x --with-xim --with-sound --with-xpm --with-jpeg --with-tiff
 --with-gif --with-png --with-rsvg --with-dbus --with-xft --without-gpm
 --with-tree-sitter --with-x-toolkit=gtk --without-pgtk
 --with-toolkit-scroll-bars --x-includes=/usr/include
 --x-libraries=/usr/lib64 --with-libotf --with-m17n-flt --with-cairo
 --with-xwidgets --build=x86_64-suse-linux --with-dumping=pdumper
 build_alias=x86_64-suse-linux 'CC=sccache cc' 'CFLAGS=-O2 -Wall
 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 -fstack-protector-strong
 -funwind-tables -fasynchronous-unwind-tables -fstack-clash-protection
 -Werror=return-type -flto=auto -march=znver3 -mmmx -mpopcnt -msse
 -msse2 -msse3 -mssse3 -msse4.1 -msse4.2 -mavx -mavx2 -msse4a -mno-fma4
 -mno-xop -mfma -mno-avx512f -mbmi -mbmi2 -maes -mpclmul -mno-avx512vl
 -mno-avx512bw -mno-avx512dq -mno-avx512cd -mno-avx512er -mno-avx512pf
 -mno-avx512vbmi -mno-avx512ifma -mno-avx5124vnniw -mno-avx5124fmaps
 -mno-avx512vpopcntdq -mno-avx512vbmi2 -mno-gfni -mvpclmulqdq
 -mno-avx512vnni -mno-avx512bitalg -mno-avx512bf16
 -mno-avx512vp2intersect -mno-3dnow -madx -mabm -mno-cldemote
 -mclflushopt -mclwb -mclzero -mcx16 -mno-enqcmd -mf16c -mfsgsbase
 -mfxsr -mno-hle -msahf -mno-lwp -mlzcnt -mmovbe -mno-movdir64b
 -mno-movdiri -mmwaitx -mno-pconfig -mpku -mno-prefetchwt1 -mprfchw
 -mno-ptwrite -mrdpid -mrdrnd -mrdseed -mno-rtm -mno-serialize -mno-sgx
 -msha -mshstk -mno-tbm -mno-tsxldtrk -mvaes -mno-waitpkg -mwbnoinvd
 -mxsave -mxsavec -mxsaveopt -mxsaves -mno-amx-tile -mno-amx-int8
 -mno-amx-bf16 -mno-uintr -mno-hreset -mno-kl -mno-widekl -mno-avxvnni
 -mno-avx512fp16 -mno-avxifma -mno-avxvnniint8 -mno-avxneconvert
 -mno-cmpccxadd -mno-amx-fp16 -mno-prefetchi -mno-raoint
 -mno-amx-complex --param l1-cache-size=32 --param l1-cache-line-size=64
 --param l2-cache-size=512 -mtune=znver3 -fno-optimize-sibling-calls -g
 -D_GNU_SOURCE -DGDK_DISABLE_DEPRECATION_WARNINGS
 -DGLIB_DISABLE_DEPRECATION_WARNINGS -pipe -Wno-pointer-sign
 -Wno-unused-variable -Wno-unused-label -DPDMP_BASE='\''"emacs-gtk"'\'''
 LDFLAGS=-Wl,-O2 'CXX=sccache c++'
 PKG_CONFIG_PATH=:/usr/lib64/pkgconfig:/usr/share/pkgconfig'

Configured features:
ACL CAIRO DBUS FREETYPE GIF GLIB GMP GNUTLS GSETTINGS HARFBUZZ JPEG
LCMS2 LIBOTF LIBSELINUX LIBSYSTEMD LIBXML2 M17N_FLT MODULES NATIVE_COMP
NOTIFY INOTIFY PDUMPER PNG RSVG SECCOMP SOUND SQLITE3 THREADS TIFF
TOOLKIT_SCROLL_BARS TREE_SITTER WEBP X11 XDBE XIM XINPUT2 XPM XWIDGETS
GTK3 ZLIB

Important settings:
  value of $LC_MONETARY: fi_FI.UTF-8
  value of $LC_NUMERIC: POSIX
  value of $LANG: en_GB.UTF-8
  value of $XMODIFIERS: @im=local
  locale-coding-system: utf-8-unix

Major mode: Eshell

Minor modes in effect:
  eshell-prompt-mode: t
  eshell-pred-mode: t
  eshell-hist-mode: t
  eshell-cmpl-mode: t
  eshell-proc-mode: t
  eshell-arg-mode: t
  circe-lagmon-mode: t
  magit-wip-initial-backup-mode: t
  magit-wip-before-change-mode: t
  magit-wip-after-apply-mode: t
  magit-wip-after-save-mode: t
  magit-wip-mode: t
  magit-auto-revert-mode: t
  pdf-occur-global-minor-mode: t
  pdf-virtual-global-minor-mode: t
  global-atomic-chrome-edit-mode: t
  minions-mode: t
  back-button-mode: t
  recentf-mode: t
  global-auto-revert-mode: t
  savehist-mode: t
  emms-playing-time-display-mode: t
  emms-playing-time-mode: t
  gnus-desktop-notify-mode: t
  tracking-mode: t
  org-super-agenda-mode: t
  goto-address-mode: t
  global-git-commit-mode: t
  projectile-mode: t
  global-company-mode: t
  company-mode: t
  electric-pair-mode: t
  editorconfig-mode: t
  frames-only-mode: t
  windmove-mode: t
  marginalia-mode: t
  which-key-mode: t
  dirvish-override-dired-mode: t
  dired-async-mode: t
  helm-mode: t
  helm-minibuffer-history-mode: t
  global-so-long-mode: t
  global-emojify-mode: t
  emojify-mode: t
  change-cursor-mode: t
  doom-modeline-mode: t
  server-mode: t
  helm-autoresize-mode: t
  desktop-save-mode: t
  Info-persist-history-mode: t
  save-place-mode: t
  delete-selection-mode: t
  override-global-mode: t
  tooltip-mode: t
  global-eldoc-mode: t
  show-paren-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  menu-bar-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  blink-cursor-mode: t
  undelete-frame-mode: t
  minibuffer-regexp-mode: t
  line-number-mode: t
  transient-mark-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t

Load-path shadows:
/home/bidar/.local/etc/emacs/lib/org/lisp/ox-koma-letter hides /home/bidar/.local/etc/emacs/lib/lisp/ox-koma-letter
/home/bidar/.local/etc/emacs/lib/org-contrib/lisp/ox-groff hides /home/bidar/.local/etc/emacs/lib/lisp/ox-groff
/home/bidar/.local/etc/emacs/lib/vim-modeline/vim-modeline hides /home/bidar/.local/etc/emacs/lib/lisp/vim-modeline
/home/bidar/.local/etc/emacs/lib/lisp/htmlize hides /home/bidar/.local/etc/emacs/lib/htmlize/htmlize
/home/bidar/.local/etc/emacs/lib/lisp/cursor-chg hides /home/bidar/.local/etc/emacs/lib/cursor-chg/cursor-chg
/home/bidar/.local/etc/emacs/lib/cmake-mode/cmake-mode hides /usr/share/emacs/site-lisp/cmake-mode
/home/bidar/.local/etc/emacs/lib/async/dired-async hides /usr/share/emacs/site-lisp/dired-async
/home/bidar/.local/etc/emacs/lib/async/async-bytecomp hides /usr/share/emacs/site-lisp/async-bytecomp
/home/bidar/.local/etc/emacs/lib/async/async-autoloads hides /usr/share/emacs/site-lisp/async-autoloads
/home/bidar/.local/etc/emacs/lib/async/smtpmail-async hides /usr/share/emacs/site-lisp/smtpmail-async
/home/bidar/.local/etc/emacs/lib/async/async hides /usr/share/emacs/site-lisp/async
/home/bidar/.local/etc/emacs/lib/compat/compat hides /usr/share/emacs/site-lisp/compat
/home/bidar/.local/etc/emacs/lib/compat/compat-29 hides /usr/share/emacs/site-lisp/compat-29
/home/bidar/.local/etc/emacs/lib/compat/compat-28 hides /usr/share/emacs/site-lisp/compat-28
/home/bidar/.local/etc/emacs/lib/compat/compat-27 hides /usr/share/emacs/site-lisp/compat-27
/home/bidar/.local/etc/emacs/lib/compat/compat-26 hides /usr/share/emacs/site-lisp/compat-26
/home/bidar/.local/etc/emacs/lib/compat/compat-25 hides /usr/share/emacs/site-lisp/compat-25
/home/bidar/.local/etc/emacs/lib/which-key/which-key hides /usr/share/emacs/31.0.50/lisp/which-key
/home/bidar/.local/etc/emacs/lib/transient/lisp/transient hides /usr/share/emacs/31.0.50/lisp/transient
/home/bidar/.local/etc/emacs/lib/editorconfig/editorconfig hides /usr/share/emacs/31.0.50/lisp/editorconfig
/home/bidar/.local/etc/emacs/lib/use-package/bind-key hides /usr/share/emacs/31.0.50/lisp/bind-key
/home/bidar/.local/etc/emacs/lib/editorconfig/editorconfig-tools hides /usr/share/emacs/31.0.50/lisp/editorconfig-tools
/home/bidar/.local/etc/emacs/lib/editorconfig/editorconfig-fnmatch hides /usr/share/emacs/31.0.50/lisp/editorconfig-fnmatch
/home/bidar/.local/etc/emacs/lib/editorconfig/editorconfig-core hides /usr/share/emacs/31.0.50/lisp/editorconfig-core
/home/bidar/.local/etc/emacs/lib/editorconfig/editorconfig-core-handle hides /usr/share/emacs/31.0.50/lisp/editorconfig-core-handle
/home/bidar/.local/etc/emacs/lib/editorconfig/editorconfig-conf-mode hides /usr/share/emacs/31.0.50/lisp/editorconfig-conf-mode
/home/bidar/.local/etc/emacs/lib/modus-themes/theme-loaddefs hides /usr/share/emacs/31.0.50/lisp/theme-loaddefs
/home/bidar/.local/etc/emacs/lib/use-package/use-package hides /usr/share/emacs/31.0.50/lisp/use-package/use-package
/home/bidar/.local/etc/emacs/lib/use-package/use-package-lint hides /usr/share/emacs/31.0.50/lisp/use-package/use-package-lint
/home/bidar/.local/etc/emacs/lib/use-package/use-package-jump hides /usr/share/emacs/31.0.50/lisp/use-package/use-package-jump
/home/bidar/.local/etc/emacs/lib/use-package/use-package-ensure hides /usr/share/emacs/31.0.50/lisp/use-package/use-package-ensure
/home/bidar/.local/etc/emacs/lib/use-package/use-package-ensure-system-package hides /usr/share/emacs/31.0.50/lisp/use-package/use-package-ensure-system-package
/home/bidar/.local/etc/emacs/lib/use-package/use-package-diminish hides /usr/share/emacs/31.0.50/lisp/use-package/use-package-diminish
/home/bidar/.local/etc/emacs/lib/use-package/use-package-delight hides /usr/share/emacs/31.0.50/lisp/use-package/use-package-delight
/home/bidar/.local/etc/emacs/lib/use-package/use-package-core hides /usr/share/emacs/31.0.50/lisp/use-package/use-package-core
/home/bidar/.local/etc/emacs/lib/use-package/use-package-bind-key hides /usr/share/emacs/31.0.50/lisp/use-package/use-package-bind-key
/home/bidar/.local/etc/emacs/lib/org/lisp/org-list hides /usr/share/emacs/31.0.50/lisp/org/org-list
/home/bidar/.local/etc/emacs/lib/org/lisp/org-refile hides /usr/share/emacs/31.0.50/lisp/org/org-refile
/home/bidar/.local/etc/emacs/lib/org/lisp/org-loaddefs hides /usr/share/emacs/31.0.50/lisp/org/org-loaddefs
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-lilypond hides /usr/share/emacs/31.0.50/lisp/org/ob-lilypond
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-core hides /usr/share/emacs/31.0.50/lisp/org/ob-core
/home/bidar/.local/etc/emacs/lib/org/lisp/ox-beamer hides /usr/share/emacs/31.0.50/lisp/org/ox-beamer
/home/bidar/.local/etc/emacs/lib/org/lisp/org hides /usr/share/emacs/31.0.50/lisp/org/org
/home/bidar/.local/etc/emacs/lib/org/lisp/org-table hides /usr/share/emacs/31.0.50/lisp/org/org-table
/home/bidar/.local/etc/emacs/lib/org/lisp/org-src hides /usr/share/emacs/31.0.50/lisp/org/org-src
/home/bidar/.local/etc/emacs/lib/org/lisp/org-num hides /usr/share/emacs/31.0.50/lisp/org/org-num
/home/bidar/.local/etc/emacs/lib/org/lisp/org-indent hides /usr/share/emacs/31.0.50/lisp/org/org-indent
/home/bidar/.local/etc/emacs/lib/org/lisp/org-capture hides /usr/share/emacs/31.0.50/lisp/org/org-capture
/home/bidar/.local/etc/emacs/lib/org/lisp/org-persist hides /usr/share/emacs/31.0.50/lisp/org/org-persist
/home/bidar/.local/etc/emacs/lib/org/lisp/org-element hides /usr/share/emacs/31.0.50/lisp/org/org-element
/home/bidar/.local/etc/emacs/lib/org/lisp/org-element-ast hides /usr/share/emacs/31.0.50/lisp/org/org-element-ast
/home/bidar/.local/etc/emacs/lib/org/lisp/org-colview hides /usr/share/emacs/31.0.50/lisp/org/org-colview
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-shell hides /usr/share/emacs/31.0.50/lisp/org/ob-shell
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-python hides /usr/share/emacs/31.0.50/lisp/org/ob-python
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-R hides /usr/share/emacs/31.0.50/lisp/org/ob-R
/home/bidar/.local/etc/emacs/lib/org/lisp/org-version hides /usr/share/emacs/31.0.50/lisp/org/org-version
/home/bidar/.local/etc/emacs/lib/org/lisp/ox hides /usr/share/emacs/31.0.50/lisp/org/ox
/home/bidar/.local/etc/emacs/lib/org/lisp/ox-texinfo hides /usr/share/emacs/31.0.50/lisp/org/ox-texinfo
/home/bidar/.local/etc/emacs/lib/org/lisp/ox-publish hides /usr/share/emacs/31.0.50/lisp/org/ox-publish
/home/bidar/.local/etc/emacs/lib/org/lisp/ox-org hides /usr/share/emacs/31.0.50/lisp/org/ox-org
/home/bidar/.local/etc/emacs/lib/org/lisp/ox-odt hides /usr/share/emacs/31.0.50/lisp/org/ox-odt
/home/bidar/.local/etc/emacs/lib/org/lisp/ox-md hides /usr/share/emacs/31.0.50/lisp/org/ox-md
/home/bidar/.local/etc/emacs/lib/org/lisp/ox-man hides /usr/share/emacs/31.0.50/lisp/org/ox-man
/home/bidar/.local/etc/emacs/lib/org/lisp/ox-latex hides /usr/share/emacs/31.0.50/lisp/org/ox-latex
/home/bidar/.local/etc/emacs/lib/org/lisp/ox-koma-letter hides /usr/share/emacs/31.0.50/lisp/org/ox-koma-letter
/home/bidar/.local/etc/emacs/lib/org/lisp/ox-icalendar hides /usr/share/emacs/31.0.50/lisp/org/ox-icalendar
/home/bidar/.local/etc/emacs/lib/org/lisp/ox-html hides /usr/share/emacs/31.0.50/lisp/org/ox-html
/home/bidar/.local/etc/emacs/lib/org/lisp/ox-ascii hides /usr/share/emacs/31.0.50/lisp/org/ox-ascii
/home/bidar/.local/etc/emacs/lib/org/lisp/org-timer hides /usr/share/emacs/31.0.50/lisp/org/org-timer
/home/bidar/.local/etc/emacs/lib/org/lisp/org-tempo hides /usr/share/emacs/31.0.50/lisp/org/org-tempo
/home/bidar/.local/etc/emacs/lib/org/lisp/org-protocol hides /usr/share/emacs/31.0.50/lisp/org/org-protocol
/home/bidar/.local/etc/emacs/lib/org/lisp/org-plot hides /usr/share/emacs/31.0.50/lisp/org/org-plot
/home/bidar/.local/etc/emacs/lib/org/lisp/org-pcomplete hides /usr/share/emacs/31.0.50/lisp/org/org-pcomplete
/home/bidar/.local/etc/emacs/lib/org/lisp/org-mouse hides /usr/share/emacs/31.0.50/lisp/org/org-mouse
/home/bidar/.local/etc/emacs/lib/org/lisp/org-mobile hides /usr/share/emacs/31.0.50/lisp/org/org-mobile
/home/bidar/.local/etc/emacs/lib/org/lisp/org-macs hides /usr/share/emacs/31.0.50/lisp/org/org-macs
/home/bidar/.local/etc/emacs/lib/org/lisp/org-macro hides /usr/share/emacs/31.0.50/lisp/org/org-macro
/home/bidar/.local/etc/emacs/lib/org/lisp/org-lint hides /usr/share/emacs/31.0.50/lisp/org/org-lint
/home/bidar/.local/etc/emacs/lib/org/lisp/org-keys hides /usr/share/emacs/31.0.50/lisp/org/org-keys
/home/bidar/.local/etc/emacs/lib/org/lisp/org-inlinetask hides /usr/share/emacs/31.0.50/lisp/org/org-inlinetask
/home/bidar/.local/etc/emacs/lib/org/lisp/org-id hides /usr/share/emacs/31.0.50/lisp/org/org-id
/home/bidar/.local/etc/emacs/lib/org/lisp/org-habit hides /usr/share/emacs/31.0.50/lisp/org/org-habit
/home/bidar/.local/etc/emacs/lib/org/lisp/org-goto hides /usr/share/emacs/31.0.50/lisp/org/org-goto
/home/bidar/.local/etc/emacs/lib/org/lisp/org-footnote hides /usr/share/emacs/31.0.50/lisp/org/org-footnote
/home/bidar/.local/etc/emacs/lib/org/lisp/org-fold hides /usr/share/emacs/31.0.50/lisp/org/org-fold
/home/bidar/.local/etc/emacs/lib/org/lisp/org-fold-core hides /usr/share/emacs/31.0.50/lisp/org/org-fold-core
/home/bidar/.local/etc/emacs/lib/org/lisp/org-feed hides /usr/share/emacs/31.0.50/lisp/org/org-feed
/home/bidar/.local/etc/emacs/lib/org/lisp/org-faces hides /usr/share/emacs/31.0.50/lisp/org/org-faces
/home/bidar/.local/etc/emacs/lib/org/lisp/org-entities hides /usr/share/emacs/31.0.50/lisp/org/org-entities
/home/bidar/.local/etc/emacs/lib/org/lisp/org-duration hides /usr/share/emacs/31.0.50/lisp/org/org-duration
/home/bidar/.local/etc/emacs/lib/org/lisp/org-datetree hides /usr/share/emacs/31.0.50/lisp/org/org-datetree
/home/bidar/.local/etc/emacs/lib/org/lisp/org-cycle hides /usr/share/emacs/31.0.50/lisp/org/org-cycle
/home/bidar/.local/etc/emacs/lib/org/lisp/org-ctags hides /usr/share/emacs/31.0.50/lisp/org/org-ctags
/home/bidar/.local/etc/emacs/lib/org/lisp/org-crypt hides /usr/share/emacs/31.0.50/lisp/org/org-crypt
/home/bidar/.local/etc/emacs/lib/org/lisp/org-compat hides /usr/share/emacs/31.0.50/lisp/org/org-compat
/home/bidar/.local/etc/emacs/lib/org/lisp/org-clock hides /usr/share/emacs/31.0.50/lisp/org/org-clock
/home/bidar/.local/etc/emacs/lib/org/lisp/org-attach hides /usr/share/emacs/31.0.50/lisp/org/org-attach
/home/bidar/.local/etc/emacs/lib/org/lisp/org-attach-git hides /usr/share/emacs/31.0.50/lisp/org/org-attach-git
/home/bidar/.local/etc/emacs/lib/org/lisp/org-archive hides /usr/share/emacs/31.0.50/lisp/org/org-archive
/home/bidar/.local/etc/emacs/lib/org/lisp/org-agenda hides /usr/share/emacs/31.0.50/lisp/org/org-agenda
/home/bidar/.local/etc/emacs/lib/org/lisp/ol hides /usr/share/emacs/31.0.50/lisp/org/ol
/home/bidar/.local/etc/emacs/lib/org/lisp/ol-w3m hides /usr/share/emacs/31.0.50/lisp/org/ol-w3m
/home/bidar/.local/etc/emacs/lib/org/lisp/ol-rmail hides /usr/share/emacs/31.0.50/lisp/org/ol-rmail
/home/bidar/.local/etc/emacs/lib/org/lisp/ol-mhe hides /usr/share/emacs/31.0.50/lisp/org/ol-mhe
/home/bidar/.local/etc/emacs/lib/org/lisp/ol-man hides /usr/share/emacs/31.0.50/lisp/org/ol-man
/home/bidar/.local/etc/emacs/lib/org/lisp/ol-irc hides /usr/share/emacs/31.0.50/lisp/org/ol-irc
/home/bidar/.local/etc/emacs/lib/org/lisp/ol-info hides /usr/share/emacs/31.0.50/lisp/org/ol-info
/home/bidar/.local/etc/emacs/lib/org/lisp/ol-gnus hides /usr/share/emacs/31.0.50/lisp/org/ol-gnus
/home/bidar/.local/etc/emacs/lib/org/lisp/ol-eww hides /usr/share/emacs/31.0.50/lisp/org/ol-eww
/home/bidar/.local/etc/emacs/lib/org/lisp/ol-eshell hides /usr/share/emacs/31.0.50/lisp/org/ol-eshell
/home/bidar/.local/etc/emacs/lib/org/lisp/ol-doi hides /usr/share/emacs/31.0.50/lisp/org/ol-doi
/home/bidar/.local/etc/emacs/lib/org/lisp/ol-docview hides /usr/share/emacs/31.0.50/lisp/org/ol-docview
/home/bidar/.local/etc/emacs/lib/org/lisp/ol-bibtex hides /usr/share/emacs/31.0.50/lisp/org/ol-bibtex
/home/bidar/.local/etc/emacs/lib/org/lisp/ol-bbdb hides /usr/share/emacs/31.0.50/lisp/org/ol-bbdb
/home/bidar/.local/etc/emacs/lib/org/lisp/oc hides /usr/share/emacs/31.0.50/lisp/org/oc
/home/bidar/.local/etc/emacs/lib/org/lisp/oc-natbib hides /usr/share/emacs/31.0.50/lisp/org/oc-natbib
/home/bidar/.local/etc/emacs/lib/org/lisp/oc-csl hides /usr/share/emacs/31.0.50/lisp/org/oc-csl
/home/bidar/.local/etc/emacs/lib/org/lisp/oc-bibtex hides /usr/share/emacs/31.0.50/lisp/org/oc-bibtex
/home/bidar/.local/etc/emacs/lib/org/lisp/oc-biblatex hides /usr/share/emacs/31.0.50/lisp/org/oc-biblatex
/home/bidar/.local/etc/emacs/lib/org/lisp/oc-basic hides /usr/share/emacs/31.0.50/lisp/org/oc-basic
/home/bidar/.local/etc/emacs/lib/org/lisp/ob hides /usr/share/emacs/31.0.50/lisp/org/ob
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-tangle hides /usr/share/emacs/31.0.50/lisp/org/ob-tangle
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-table hides /usr/share/emacs/31.0.50/lisp/org/ob-table
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-sqlite hides /usr/share/emacs/31.0.50/lisp/org/ob-sqlite
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-sql hides /usr/share/emacs/31.0.50/lisp/org/ob-sql
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-sed hides /usr/share/emacs/31.0.50/lisp/org/ob-sed
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-screen hides /usr/share/emacs/31.0.50/lisp/org/ob-screen
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-scheme hides /usr/share/emacs/31.0.50/lisp/org/ob-scheme
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-sass hides /usr/share/emacs/31.0.50/lisp/org/ob-sass
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-ruby hides /usr/share/emacs/31.0.50/lisp/org/ob-ruby
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-ref hides /usr/share/emacs/31.0.50/lisp/org/ob-ref
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-processing hides /usr/share/emacs/31.0.50/lisp/org/ob-processing
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-plantuml hides /usr/share/emacs/31.0.50/lisp/org/ob-plantuml
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-perl hides /usr/share/emacs/31.0.50/lisp/org/ob-perl
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-org hides /usr/share/emacs/31.0.50/lisp/org/ob-org
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-octave hides /usr/share/emacs/31.0.50/lisp/org/ob-octave
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-ocaml hides /usr/share/emacs/31.0.50/lisp/org/ob-ocaml
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-maxima hides /usr/share/emacs/31.0.50/lisp/org/ob-maxima
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-matlab hides /usr/share/emacs/31.0.50/lisp/org/ob-matlab
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-makefile hides /usr/share/emacs/31.0.50/lisp/org/ob-makefile
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-lua hides /usr/share/emacs/31.0.50/lisp/org/ob-lua
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-lob hides /usr/share/emacs/31.0.50/lisp/org/ob-lob
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-lisp hides /usr/share/emacs/31.0.50/lisp/org/ob-lisp
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-latex hides /usr/share/emacs/31.0.50/lisp/org/ob-latex
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-julia hides /usr/share/emacs/31.0.50/lisp/org/ob-julia
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-js hides /usr/share/emacs/31.0.50/lisp/org/ob-js
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-java hides /usr/share/emacs/31.0.50/lisp/org/ob-java
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-haskell hides /usr/share/emacs/31.0.50/lisp/org/ob-haskell
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-groovy hides /usr/share/emacs/31.0.50/lisp/org/ob-groovy
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-gnuplot hides /usr/share/emacs/31.0.50/lisp/org/ob-gnuplot
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-fortran hides /usr/share/emacs/31.0.50/lisp/org/ob-fortran
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-forth hides /usr/share/emacs/31.0.50/lisp/org/ob-forth
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-exp hides /usr/share/emacs/31.0.50/lisp/org/ob-exp
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-eval hides /usr/share/emacs/31.0.50/lisp/org/ob-eval
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-eshell hides /usr/share/emacs/31.0.50/lisp/org/ob-eshell
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-emacs-lisp hides /usr/share/emacs/31.0.50/lisp/org/ob-emacs-lisp
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-dot hides /usr/share/emacs/31.0.50/lisp/org/ob-dot
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-ditaa hides /usr/share/emacs/31.0.50/lisp/org/ob-ditaa
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-css hides /usr/share/emacs/31.0.50/lisp/org/ob-css
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-comint hides /usr/share/emacs/31.0.50/lisp/org/ob-comint
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-clojure hides /usr/share/emacs/31.0.50/lisp/org/ob-clojure
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-calc hides /usr/share/emacs/31.0.50/lisp/org/ob-calc
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-awk hides /usr/share/emacs/31.0.50/lisp/org/ob-awk
/home/bidar/.local/etc/emacs/lib/org/lisp/ob-C hides /usr/share/emacs/31.0.50/lisp/org/ob-C
/home/bidar/.local/etc/emacs/lib/compat/compat hides /usr/share/emacs/31.0.50/lisp/emacs-lisp/compat

Features:
(shadow emacsbug em-unix em-term em-script em-prompt em-pred em-ls
em-hist em-glob em-extpipe em-cmpl em-dirs em-basic em-banner em-alias
esh-mode esh-var eshell esh-cmd esh-ext esh-opt esh-proc esh-io esh-arg
esh-module esh-module-loaddefs esh-util make-mode move-text goto-chg
whitespace gitignore-mode dired-open helm-ls-git help-macro tabify
gnus-search eieio-opt speedbar ezimage dframe finder-inf gnus-delay
gnus-draft gitattributes-mode vc-dir two-column helm-descbinds mailalias
bbdb-pgp message-attachment-reminder bbdb-message gnus-alias
autocrypt-message git-rebase magit-extras shortdoc cl-print
adaptive-wrap adaptive-wrap-autoloads gitconfig-mode help-fns
emacsql-sqlite-builtin sqlite org-indent crux smart-region
multiple-cursors mc-separate-operations rectangular-region-mode
mc-mark-pop mc-edit-lines mc-hide-unmatched-lines-mode mc-mark-more
html-mode-expansions sgml-mode mc-cycle-cursors multiple-cursors-core
rect expand-region yaml-mode-expansions subword-mode-expansions
cperl-mode-expansions text-mode-expansions cc-mode-expansions
the-org-mode-expansions python-el-fgallina-expansions
er-basic-expansions expand-region-core expand-region-custom char-fold
helm-org-rifle misearch multi-isearch orgit-forge orgit doctor
helm-command org-clock-helpers flow-fill mastodon-notifications
mastodon-media mastodon-profile mastodon-auth mastodon-client plstore
mastodon mastodon-search mastodon-toot persist mastodon-http request
circe-lagmon ietf-drums-date gnus-gravatar gnus-cite message-view-patch
forge-repos forge-tablist forge-topics forge-commands forge-semi
forge-bitbucket buck forge-gogs gogs forge-gitea gtea forge-gitlab glab
forge-github ghub-graphql treepy gsexp ghub forge-notify forge-revnote
forge-pullreq forge-issue forge-topic yaml eieio-custom forge-post
markdown-mode forge-repo forge forge-core forge-db magit-popup
magit-bookmark magit-submodule magit-blame magit-stash magit-reflog
magit-bisect magit-push magit-pull magit-fetch magit-clone magit-remote
magit-commit magit-sequence magit-notes magit-worktree magit-tag
magit-merge magit-branch magit-reset magit-files magit-refs magit-status
magit magit-repos magit-apply magit-wip magit-log magit-diff smerge-mode
diff magit-core magit-autorevert magit-margin magit-transient
magit-process magit-mode w3m-form w3m-symbol w3m w3m-hist bookmark-w3m
w3m-ems w3m-favicon w3m-image w3m-fb tab-line w3m-proc w3m-util
mm-archive mail-extr textsec uni-scripts idna-mapping ucs-normalize
uni-confusable textsec-check gnus-async gnus-bcklg bbdb-gnus-aux qp
gnus-ml gnus-demon autocrypt-gnus nndraft nnmh gnus-topic utf-7 epa-file
nnfolder bbdb-gnus bbdb-mua nnnil hydra lv winner tramp-archive
tramp-gvfs zeroconf helm-x-files helm-for-files helm-external
network-stream pulse image-file image-converter company-shell
rpm-spec-mode company-anaconda highlight-indent-guides cap-words
superword subword anaconda-mode pythonic tramp-cache time-stamp tramp-sh
dired-collapse dirvish-yank dirvish-subtree dirvish-collapse
dirvish-icons dirvish-vc dirvish-widgets pdf-history pdf-outline
pdf-links pdf-annot pdf-sync pdf-occur tablist tablist-filter
semantic/wisent/comp semantic/wisent semantic/wisent/wisent
semantic/util-modes semantic/util semantic semantic/tag semantic/lex
semantic/fw mode-local cedet pdf-isearch pdf-misc pdf-virtual
whole-line-or-region pdf-tools package pdf-view pdf-cache pdf-info
pdf-util pdf-macs generic-x skeleton generic atomic-chrome websocket
minions back-button smartrep helm-projectile recentf tree-widget
autorevert cus-start savehist org-edit-indirect edit-indirect org-bug
bug-search bug-comment-mode bug-backend-bz-rpc bug-list-mode bug-mode
bug-format bug-rpc bug-debug bug-search-common bug-common-functions
bug-persistent-data bug-custom yeetube yeetube-mpv socks elfeed-autotag
elfeed-score elfeed-score-maint elfeed-score-scoring elfeed-score-serde
elfeed-score-rule-stats elfeed-score-rules elfeed-score-log elfeed-tube
elfeed-tube-utils aio elfeed-protocol-owncloud elfeed-protocol
elfeed-protocol-common elfeed-show elfeed-search rainbow-delimiters
ligature symbol-overlay hideshow vc-hg vc-bzr vc-src vc-sccs vc-svn
vc-cvs vc-rcs log-view bug-reference elfeed-csv elfeed elfeed-curl
elfeed-log elfeed-db elfeed-lib xml-query emms-i18n emms-history
emms-score emms-stream-info emms-metaplaylist-mode emms-bookmarks
emms-cue emms-mode-line-icon emms-browser sort emms-volume
emms-volume-sndioctl emms-volume-mixerctl emms-volume-pulse
emms-volume-amixer emms-playlist-sort emms-last-played emms-player-mpd
emms-playing-time emms-lyrics emms-url emms-player-simple emms-streams
emms-show-all emms-tag-editor emms-tag-tracktag emms-info-mp3info
emms-mark emms-mode-line emms-cache emms-playlist-mode emms-info-native
emms-info-native-spc emms-info-native-mp3 emms-info-native-ogg
emms-info-native-opus emms-info-native-flac emms-info-native-vorbis
emms-info-libtag emms-info emms-later-do emms-source-playlist
emms-source-file locate emms emms-compat khardel gravatar dns smiley
autocrypt gnus-icalendar org-capture smtpmail-async smtpmail gnus-agent
gnus-srvr gnus-score score-mode nnvirtual gnus-msg nntp gnus-cache
gnus-desktop-notify bbdb-vcard bbdb-com bbdb-vcard-vcard21 bbdb
bbdb-site timezone mastodon-async mastodon-tl url-cache mpv tq
mastodon-iso lui-track company-emoji company-emoji-list helm-circe
circe-notifications circe-display-images circe-color-nicks circe
lui-irc-colors irc lcs lui-logging lui-format lui tracking shorten
flyspell circe-compat ical2org org-modern org-caldav icalendar url-dav
url-http url-auth url-gw nsm url-handlers ox-koma-letter ox-extra
org-pomodoro alert notifications org-timer org-expiry ol-man org-clock
org-protocol ob-sqlite ob-sql ob-shell ob-python python ob-plantuml
ob-org ob-octave ob-lua ob-js ob-gnuplot ox-odt rng-loc rng-uri
rng-parse rng-match rng-dt rng-util rng-pttrn nxml-parse nxml-ns
nxml-enc xmltok nxml-util ox-latex ox-icalendar org-super-agenda ts
org-habit org-duration cdlatex reftex reftex-loaddefs reftex-vars
texmathp org-appear ws-butler selected jinx oc-basic ol-eww eww
url-queue mm-url ol-rmail ol-mhe ol-irc ol-info ol-gnus nnselect
gnus-art mm-uu mml2015 mm-view mml-smime smime gnutls dig gnus-sum shr
pixel-fill kinsoku url-file svg dom gnus-group gnus-undo gnus-start
gnus-dbus dbus gnus-cloud nnimap nnmail mail-source utf7 nnoo gnus-spec
gnus-int gnus-range gnus-win gnus nnheader range ol-docview doc-view
ol-bibtex bibtex ol-bbdb ol-w3m ol-doi org-link-doi goto-addr view
mule-util cal-china cal-bahai cal-islam cal-hebrew holidays
holiday-loaddefs cal-iso cal-julian lunar solar cal-dst vim-modeline
appt org-agenda ox-html table ox-ascii ox-publish ox org-element
org-persist org-id org-refile org-element-ast avl-tree ob-dot ob-ditaa
ob-clojure ob-C outshine outshine-org-cmds outorg smartparens-org
smartparens-text smartparens loadhist org-archive-subtree-hierarchy
org-archive org ob ob-tangle ob-ref ob-lob ob-table ob-exp org-macro
org-src ob-comint org-pcomplete org-list org-footnote org-faces
org-entities ob-emacs-lisp ob-core ob-eval org-cycle org-table ol
org-fold org-fold-core org-keys oc org-loaddefs org-version org-compat
org-macs noutline outline salt-mode rst mmm-jinja2 yaml-mode
perl-completion woman man ffap dabbrev cperl-mode facemenu helm-elisp
helm-eval edebug debug backtrace cc-mode cc-fonts cc-guess cc-menus
cc-cmds cc-styles cc-align cc-engine cc-vars cc-defs ggtags ewoc
flycheck-color-mode-line flycheck jka-compr let-alist meson-mode
bitbake-modes bitbake autoinsert conf-bitbake-mode conf-mode bitbake-ff
find-file bitbake-mmm sh-script smie treesit executable mmm-mode
mmm-univ mmm-class mmm-region mmm-auto mmm-vars mmm-utils mmm-compat
bitbake-functions bitbake-compat xterm-color vc-osc vc magit-libgit
libgit libegit2 git-commit magit-git magit-base which-func imenu vc-git
diff-mode track-changes vc-dispatcher magit-section benchmark
cursor-sensor crm log-edit message sendmail yank-media puny rfc822 mml
mml-sec epa epg rfc6068 epg-config gnus-util mm-decode mm-bodies
mm-encode mail-parse rfc2231 rfc2047 rfc2045 mm-util ietf-drums
mail-prsvr mailabbrev mail-utils gmm-utils mailheader pcvs-util add-log
diary-lib diary-loaddefs cal-menu calendar cal-loaddefs midnight
vlf-setup multi-vterm company-yasnippet vterm face-remap color term
disp-table ehelp vterm-module term/xterm xterm projectile ibuf-ext
ibuffer ibuffer-loaddefs company-oddmuse company-keywords company-etags
etags fileloop generator xref project company-gtags company-dabbrev-code
company-dabbrev company-files company-clang company-capf company-cmake
company-semantic company-template company-bbdb company elec-pair
editorconfig editorconfig-core editorconfig-core-handle
editorconfig-fnmatch wgrep-ag frames-only-mode windmove i3-integration
i3 bindat marginalia which-key dirvish transient helm-dired-history
dired-ranger dired-avfs dired-rainbow dired-filter dired-hacks-utils
dired-x dired-async dired-du find-dired wdired dired-aux helm-icons
treemacs-icons treemacs-scope treemacs-themes treemacs-core-utils
treemacs-logging treemacs-customization pfuture inline hl-line
treemacs-faces helm-bookmark helm-net browse-url xml url url-proxy
url-privacy url-expand url-methods url-history url-cookie url-domsuf
url-util mailcap helm-adaptive helm-info bookmark helm-mode helm-misc
helm-files image-dired image-dired-tags image-dired-external
image-dired-util image-mode dired dired-loaddefs exif filenotify tramp
trampver tramp-integration files-x tramp-message tramp-compat xdg
parse-time iso8601 time-date tramp-loaddefs helm-buffers all-the-icons
all-the-icons-faces data-material data-weathericons data-octicons
data-fileicons data-faicons data-alltheicons helm-occur helm-tags
helm-locate helm-grep wgrep-helm wgrep grep compile text-property-search
helm-regexp format-spec helm-utils helm-help helm-types so-long emojify
apropos tar-mode arc-mode archive-mode ht cursor-chg doom-modeline
doom-modeline-segments doom-modeline-env doom-modeline-core shrink-path
f s dash nerd-icons nerd-icons-faces nerd-icons-data
nerd-icons-data-mdicon nerd-icons-data-flicon nerd-icons-data-codicon
nerd-icons-data-devicon nerd-icons-data-sucicon nerd-icons-data-wicon
nerd-icons-data-faicon nerd-icons-data-powerline nerd-icons-data-octicon
nerd-icons-data-pomicon nerd-icons-data-ipsicon modus-vivendi-theme
modus-themes helm-pass password-store with-editor shell pcomplete comint
ansi-osc ansi-color ring server helm helm-global-bindings helm-easymenu
edmacro kmacro helm-core async-bytecomp helm-source helm-multi-match
helm-lib async auth-source-pass url-parse url-vars auth-source
password-cache printing ps-print ps-print-loaddefs lpr desktop frameset
derived advice info+ thingatpt cl saveplace delsel no-littering
epkg-elpa json map epkg-utils epkg-list epkg-desc find-func epkg closql
emacsql-sqlite-common emacsql emacsql-compiler eieio-base eieio byte-opt
eieio-core cl-macs llama comp-run compat use-package use-package-ensure
use-package-delight use-package-diminish use-package-bind-key bind-key
easy-mmode use-package-core zop-to-char-autoloads yeetube-autoloads
yasnippet-autoloads yaml-mode-autoloads yaml-autoloads
xterm-color-autoloads ws-butler-autoloads with-editor-autoloads
whole-line-or-region-autoloads which-key-autoloads wgrep-autoloads
websocket-autoloads web-mode-autoloads w3m-autoloads vlf-autoloads
visual-regexp-autoloads vim-modeline-autoloads vc-osc-autoloads
uuidgen-autoloads use-package-autoloads ts-autoloads treepy-autoloads
treemacs-nerd-icons-autoloads treemacs-autoloads transient-autoloads
toml-mode-autoloads systemd-autoloads symbol-overlay-autoloads
swiper-helm-autoloads ssh-config-mode-autoloads spinner-autoloads
smartrep-autoloads smartparens-autoloads smart-region-autoloads
skewer-mode-autoloads simple-httpd-autoloads shrink-path-autoloads
selected-autoloads salt-mode-autoloads s-autoloads
rpm-spec-mode-autoloads rich-minority-autoloads request-autoloads
rainbow-delimiters-autoloads qt-pro-mode-autoloads qml-mode-autoloads
pythonic-autoloads projectile-autoloads posframe-autoloads
pos-tip-autoloads popup-autoloads plantuml-mode-autoloads
pkgbuild-mode-autoloads piper-autoloads pfuture-autoloads
perspective-autoloads persp-mode-autoloads persist-autoloads
password-store-autoloads pass-autoloads outshine-autoloads
outorg-autoloads orgit-forge-autoloads orgit-autoloads
org-vcard-autoloads org-tree-slide-autoloads org-super-agenda-autoloads
org-pomodoro-autoloads org-modern-autoloads org-edit-indirect-autoloads
org-contrib-autoloads org-contacts-autoloads org-clock-helpers-autoloads
org-caldav-autoloads org-appear-autoloads org-autoloads
no-littering-autoloads nginx-mode-autoloads nerd-icons-ibuffer-autoloads
nerd-icons-autoloads navi-mode-autoloads multiple-cursors-autoloads
multi-vterm-autoloads mpv-autoloads move-text-autoloads
modus-themes-autoloads mode-icons-autoloads mmm-mode-autoloads
mmm-jinja2-autoloads minions-autoloads message-x-autoloads
message-view-patch-autoloads message-attachment-reminder-autoloads
meson-mode-autoloads mastodon-autoloads markdown-mode-autoloads
marginalia-autoloads magit-popup-autoloads magit-autoloads
lua-mode-autoloads lsp-ui-autoloads lsp-treemacs-autoloads
lsp-mode-autoloads lsp-docker-autoloads logview-autoloads
llama-autoloads lisp-autoloads link-hint-autoloads ligature-autoloads
levenshtein-autoloads khardel-autoloads js2-mode-autoloads
journalctl-autoloads jira-markup-mode-autoloads ivy-autoloads
irony-autoloads ir-black-theme-autoloads info+-autoloads iedit-autoloads
ical2org-autoloads ibuffer-projectile-autoloads
i3wm-config-mode-autoloads i3-autoloads hydra-autoloads
htmlize-autoloads ht-autoloads highlight-indent-guides-autoloads
helm-projectile-autoloads helm-pass-autoloads helm-org-rifle-autoloads
helm-make-autoloads helm-ls-git-autoloads helm-icons-autoloads
helm-ext-autoloads helm-emms-autoloads helm-dired-history-autoloads
helm-descbinds-autoloads helm-circe-autoloads helm-autoloads
guess-language-autoloads grep-context-autoloads goto-chg-autoloads
gnus-recent-autoloads gnus-notes-autoloads gnus-desktop-notify-autoloads
gnus-alias-autoloads gitconfig-autoloads git-modes-autoloads
ghub-autoloads ggtags-autoloads frames-only-mode-autoloads
forge-autoloads flycheck-color-mode-line-autoloads flycheck-autoloads
fedi-autoloads f-autoloads extmap-autoloads expand-region-autoloads
evil-multiedit-autoloads evil-autoloads emojify-autoloads
emacsql-autoloads elixir-mode-autoloads elfeed-tube-autoloads
elfeed-summary-autoloads elfeed-score-autoloads
elfeed-protocol-autoloads elfeed-autotag-autoloads elfeed-autoloads
el-mock-autoloads eimp-autoloads editorconfig-autoloads
edit-indirect-autoloads dumb-jump-autoloads doom-modeline-autoloads
docbook-autoloads dirvish-autoloads dired-rsync-autoloads
dired-hacks-autoloads dired-du-autoloads devhelp-autoloads
deferred-autoloads default-text-scale-autoloads debbugs-autoloads
datetime-autoloads dash-autoloads dap-mode-autoloads
cursor-chg-autoloads crux-autoloads copy-as-format-autoloads
compat-autoloads company-shell-autoloads company-quickhelp-autoloads
company-nginx-autoloads company-lua-autoloads company-irony-autoloads
company-emoji-autoloads company-anaconda-autoloads company-autoloads
code-review-autoloads cmake-mode-autoloads cmake-font-lock-autoloads
closql-autoloads circe-notifications-autoloads circe-autoloads
cdlatex-autoloads ccls-autoloads buttercup-autoloads bui-autoloads
bug-mode-autoloads bitbake-modes-autoloads bbdb-vcard-autoloads
bbdb-loaddefs back-button-autoloads avy-autoloads autocrypt-autoloads
auto-compile-autoloads atomic-chrome-autoloads async-autoloads
anaconda-mode-autoloads all-the-icons-autoloads alert-autoloads
aio-autoloads ag-autoloads ace-window-autoloads ace-link-autoloads
a-autoloads 2048-game-autoloads borg loaddefs-gen generate-lisp-file
lisp-mnt radix-tree info cus-edit pp pcase cus-load wid-edit comp cl-seq
comp-cstr cl-extra help-mode comp-common warnings icons subr-x rx gv
cl-loaddefs cl-lib bytecomp byte-compile preview-latex auctex tex-site
ispell rmc iso-transl tooltip cconv eldoc paren electric uniquify
ediff-hook vc-hooks lisp-float-type elisp-mode mwheel term/x-win x-win
term/common-win x-dnd touch-screen tool-bar dnd fontset image regexp-opt
fringe tabulated-list replace newcomment text-mode lisp-mode prog-mode
register page tab-bar menu-bar rfn-eshadow isearch easymenu timer select
scroll-bar mouse jit-lock font-lock syntax font-core term/tty-colors
frame minibuffer nadvice seq simple cl-generic indonesian philippine
cham georgian utf-8-lang misc-lang vietnamese tibetan thai tai-viet lao
korean japanese eucjp-ms cp51932 hebrew greek romanian slovak czech
european ethiopic indian cyrillic chinese composite emoji-zwj charscript
charprop case-table epa-hook jka-cmpr-hook help abbrev obarray oclosure
cl-preloaded button loaddefs theme-loaddefs faces cus-face macroexp
files window text-properties overlay sha1 md5 base64 format env
code-pages mule custom widget keymap hashtable-print-readable backquote
threads xwidget-internal dbusbind inotify lcms2 dynamic-setting
system-font-setting font-render-setting cairo gtk x-toolkit xinput2 x
multi-tty move-toolbar make-network-process native-compile emacs)

Memory information:
((conses 16 8814971 1834230) (symbols 48 113156 6)
 (strings 32 2138810 377553) (string-bytes 1 196033441)
 (vectors 16 420969) (vector-slots 8 7713005 1814464)
 (floats 8 10276 45198) (intervals 56 524388 35453) (buffers 992 455))





^ permalink raw reply	[flat|nested] 6+ messages in thread

* bug#72441: 31.0.50; Auth-source-pass doesn't match password attributes or hosts without user when extra-query-keywords is true
  2024-08-03 11:12 bug#72441: 31.0.50; Auth-source-pass doesn't match password attributes or hosts without user when extra-query-keywords is true Björn Bidar via Bug reports for GNU Emacs, the Swiss army knife of text editors
@ 2024-08-09 18:02 ` J.P.
  2024-08-09 19:20   ` Björn Bidar via Bug reports for GNU Emacs, the Swiss army knife of text editors
       [not found]   ` <87ed6xy03r.fsf@>
  0 siblings, 2 replies; 6+ messages in thread
From: J.P. @ 2024-08-09 18:02 UTC (permalink / raw)
  To: 72441; +Cc: Björn Bidar

[-- Attachment #1: Type: text/plain, Size: 8474 bytes --]

Björn Bidar via "Bug reports for GNU Emacs, the Swiss army knife of text
editors" <bug-gnu-emacs@gnu.org> writes:

> I noticed that auth-source-pass doesn't match agains password file
> attributes such as those containing :user and only matches password
> files which contain a host and a user when
> auth-source-pass-extra-query-keywords is true.

I don't use pass myself, nor have I ever, but I suppose I did add the
`auth-source-pass-extra-query-keywords' option (though mainly in a bid
to make auth-source-pass behave more like other back ends so it's usable
with ERC). Anyway, I do actually recall being somewhat aware of the
existence of the file attributes you mention. It seems I even left a
comment about the current lack of support [1].

Looking into this a bit, it seems the password store's web page
considers everything after the initial (password) line to be an opaque
text blob:

  The password store does not impose any particular schema or type of
  organization of your data, as it is simply a flat text file, which can
  contain arbitrary data. Though the most common case is storing a
  single password per entry, some power users find they would like to
  store more than just their password inside the password store, and
  additionally store answers to secret questions, website URLs, and
  other sensitive information or metadata. Since the password store does
  not impose a scheme of it's own, you can choose your own organization.
  There are many possibilities.

However, I do realize that the auth-source-pass back end without the
extra-keywords option already dips into a file's contents looking for an
attributes list like the one shown on the web page. (Whether that's wise
is pretty much moot after all these years.) Anyway, for that reason, I
suppose we _should_ attempt to at least explore doing the same when the
extra-keywords option is enabled. For me, the most important thing
remains mimicking the behavior of the other built-in back ends, which at
times is admittedly unintuitive but nevertheless consistent and thus
predictable from a mechanical POV.

> Steps to reproduce:
> 1. Setup pass with the following structure
>    WorkingTest/example.com/foo
>    FailingTest/example2.com
>    FailingTest/example3.com with user: foo in the password file
> 2. (auth-source-pass-enable)
> 3. (setq auth-source-pass-extra-query-keywords t)
> 4. (auth-source-search :host "example" :user "foo") -> works
> 5. (auth-source-search :host "example2.com") -> fails
> 6. (auth-source-search :host "example3.com" :user "foo")
>
> Auth-source-pass should be able to query the password file for
> additional attributes if one of the previous attributes such as :host
> match to it. Quering the file attributes is quite important in use
> cases where it doesn't make sense to the user to have a
> host-folder/user-file structure in cases where there's only one
> password for said host.

Currently, if you have a file in the root of your ~/.password-store
named something like "top-level-host.com", and it's contents feature a
"user: foo" attribute, both

  (auth-source-search :host "top-level-host.com")

and

  (auth-source-search :host "top-level-host.com" :user "foo")

return

  ((:host "top-level-host.com" :secret ...)).

If you're saying you want to see (:user "foo") in the results as well, I
guess we can do that (see attached patch as well as [2], below).
However, this still won't work on any of your examples, which all have
intervening path components between the root directory and the .gpg
files. The reason for this restriction is explained below.

If we do end up going with something like the attached patch, we'll need
to profile it. I can create a bunch of fake trees of varying shapes and
sizes, but I'd rather someone with real data and a sizable store assess
how much slower it is to visit (and thus decrypt) potentially every file
in the tree, which is what any attr-reading implementation must do. On
my machine, it takes roughly 0.18 seconds to decrypt a single two-line
file via `auth-source-pass--read-entry'. (This seems prohibitively
expensive to do en masse, no?) FWIW, most of this time is spent in
`epg-wait-for-status', which blocks until the subprocess exits.

> Same it should maybe also match against :host
> if no user was provided, I don't know how other sources do this thou.

While the reference implementation indeed succeeds with a plain :host
input (see test `auth-source-pass-extra-query-keywords--netrc-host'), I
believe the actual problem you perceive has more to do with the content
of the file paths, specifically, leading directory components. Still,
I'm inclined to agree that this would be nice to have. However, I do
seem to recall this being discussed on at least one occasion, with the
conclusion being that it's too complicated, if not impossible, to
disambiguate between a trailing "hostname/user" and "folder/hostname".

Nevertheless, we could add an option to do it anyway based on one or
more heuristics-based strategy (resolving hosts for real is surely a no
go). For example, one such strategy could ignore a penultimate file-path
component that's not an FQDN, even if it's, say, LDH-only and resolvable
as a hostname, so long as the leaf component _is_ an FQDN. However, such
an option would have to be disabled by default to prevent existing
entries like "localhost/test.user" from being parsed as (:host
"test.user").

In any case, I'm happy to review patches, but I think someone who
actually uses this back end should implement the feature.


[1] https://git.savannah.gnu.org/cgit/emacs.git/tree/lisp/auth-source-pass.el?id=423c86cb#n300

[2] The following describes details of the attached patch's logic for
    the inner (dolist (e entries) ...) loop of the primary matching
    function `auth-source-pass--find-match-many'. Hopefully it's
    somewhat sound with regard to deferring decryption for as long as
    possible.

    1. Parse the file path of each entry first and cache its results in
       a plist, the "cached entry metadata," which is filed under the
       entry's file-path in the `seen' hash table. If it doesn't match
       the basic filename format, it must not be a passwordstore file,
       so reject the entry.
    2. Check the :host field before reading the file. Unless it matches,
       reject the entry.
    3. Engage in a series of probing conditional checks for a :port
       field to match against a provided "port" query parameter, all
       while attempting to defer decryption until absolutely necessary.
       (A path-encoded :port always takes precedence over a :port in the
       file.)
       - If a `port' query parameter is not given for matching against,
         continue to the next steps for the current entry.
       - Otherwise, if a :port parsed from the file path is present and
         it doesn't match, reject the entry, meaning go to the beginning
         of the current loop, considering the next entry.
       - If a path-derived :port is absent, ensure the cached entry
         metadata contains an additional :attrs field (an alist). If the
         metadata lacks an :attrs field, the file has not yet been
         decrypted. Decrypt it now using `auth-source-pass-parse-entry',
         then add its secret and its attrs alist to the cached metadata,
         under :attrs.
       - Look in the cached entry metadata's :attrs alist for a "port"
         attr. If a "port" attr is indeed present and doesn't match the
         port query parameter, reject the entry.
       - If no such "port" attr exists and is required (meaning :port
         appears in the `require' query parameter), reject the entry.
    4. Repeat step 3 for :user. The same precedence rules apply, meaning
       any non-null path-derived :user field is immediately accepted,
       and the file is not decrypted.
    5. If we haven't yet decrypted the file, do so now and populate the
       :attrs item in the cached entry metadata. If it's already been
       decrypted at some point, :attrs will present (though possibly
       empty). In any case, add the items we care about if non-null
       (:user, :port, and :secret) to the matched results for this entry.
       However, only do so if a secret was either not required or is
       present; otherwise, reject the entry.

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-POC-Match-attrs-with-auth-source-pass-extra-query-ke.patch --]
[-- Type: text/x-patch, Size: 13646 bytes --]

From a36ef10d81e2c5afb0cf2515887084b6331d38d1 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Wed, 7 Aug 2024 22:23:09 -0700
Subject: [PATCH] [POC] Match attrs with auth-source-pass-extra-query-keywords

* lisp/auth-source-pass.el (auth-source-pass--retrieve-parsed):
Remove unused function.
(auth-source-pass--cast-port): New function, a helper to match an
entry's port to the given query param's type.
(auth-source-pass--match-parts): Return non-nil when a key is required
but the value is null.  Not doing produced behavior that deviated from
the reference netrc implementation and was thus a bug.
(auth-source-pass-check-attrs-with-extra-query-keywords): New
variable, a flag to opt out of arguably expensive attribute lookups.
(auth-source-pass--find-matched-entry): New function to isolate
processing logic for a single entry.
(auth-source-pass--find-match-many): Move single-entry processing
logic to separate helper, mainly for readability.
* test/lisp/auth-source-pass-tests.el
(auth-source-pass-extra-query-keywords--akib/attr)
(auth-source-pass-extra-query-keywords--netrc-akib/require)
(auth-source-pass-extra-query-keywords--akib/attr/require)
(auth-source-pass-extra-query-keywords--netrc-baseline): New tests.
(auth-source-pass-extra-query-keywords--baseline): Reverse expected
outcome to match reference implementation.  That it didn't before was
a bug.  (Bug#72441)
---
 lisp/auth-source-pass.el            | 130 +++++++++++++++++++---------
 test/lisp/auth-source-pass-tests.el |  79 ++++++++++++++++-
 2 files changed, 165 insertions(+), 44 deletions(-)

diff --git a/lisp/auth-source-pass.el b/lisp/auth-source-pass.el
index 03fd1f35811..8982e07a6be 100644
--- a/lisp/auth-source-pass.el
+++ b/lisp/auth-source-pass.el
@@ -256,32 +256,88 @@ auth-source-pass--find-match
                 hosts
               (list hosts))))
 
-(defun auth-source-pass--retrieve-parsed (seen path port-number-p)
-  (when (string-match auth-source-pass--match-regexp path)
-    (puthash path
-             `( :host ,(or (match-string 10 path) (match-string 11 path))
-                ,@(if-let* ((tr (match-string 21 path)))
-                      (list :user tr :suffix t)
-                    (list :user (match-string 20 path)))
-                :port ,(and-let* ((p (or (match-string 30 path)
-                                         (match-string 31 path)))
-                                  (n (string-to-number p)))
-                         (if (or (zerop n) (not port-number-p))
-                             (format "%s" p)
-                           n)))
-             seen)))
-
-(defun auth-source-pass--match-parts (parts key value require)
-  (let ((mv (plist-get parts key)))
+(defun auth-source-pass--cast-port (val ref)
+  (cond ((integerp val) val)
+        ((and-let* (((integerp ref))
+                    (n (string-to-number val))
+                    ((not (zerop n))))
+           n))
+        (t (format "%s" val))))
+
+(defun auth-source-pass--match-parts (parts key reference require)
+  (let ((value (plist-get parts key)))
     (if (memq key require)
-        (and value (equal mv value))
-      (or (not value) (not mv) (equal mv value)))))
+        (or (null reference) (equal value reference))
+      (or (null reference) (null value) (equal value reference)))))
+
+(defvar auth-source-pass-check-attrs-with-extra-query-keywords t
+  "When non-nil, decrypt files to find attributes matching parameters.
+However, give precedence to fields encoded in file names.  Only applies
+when `auth-source-pass-extra-query-keywords' is non-nil.")
+
+;; This function tries to defer decryption as long as possible.  For
+;; that reason, an entry's file-path-derived :port or :user field
+;; always takes precedence over their counterparts from a decrypted
+;; file's attribute list.
+(defun auth-source-pass--find-matched-entry (host user port require seen entry)
+  "Match ENTRY against query params HOST USER PORT REQUIRE with cache SEEN."
+  (when (string-match auth-source-pass--match-regexp entry)
+    (let* ((cached (gethash entry seen))
+           (optp auth-source-pass-check-attrs-with-extra-query-keywords)
+           (suffixedp nil)
+           (h (or (and cached (plist-get cached :host))
+                  (match-string 10 entry)
+                  (match-string 11 entry)))
+           (attrs (and cached (plist-get :attrs cached)))
+           (getat (lambda (k)
+                    (save-match-data
+                      (unless attrs
+                        (setq attrs (auth-source-pass-parse-entry entry)))
+                      (auth-source-pass--get-attr k attrs))))
+           (u (cond (cached (plist-get cached :user))
+                    ((and-let* ((u (match-string 21 entry)))
+                       (setq suffixedp t)
+                       u))
+                    ((match-string 20 entry))
+                    ((and user optp) (funcall getat "user"))))
+           (p (cond (cached (plist-get cached :port))
+                    ((match-string 30 entry))
+                    ((match-string 31 entry))
+                    ((and port optp) (funcall getat "port"))))
+           ;;
+           s)
+      (when p
+        (setq p (auth-source-pass--cast-port p port)))
+      (unless cached
+        (setq cached `( :host ,h
+                        ,@(and u (list :user u))
+                        ,@(and p (list :port p))
+                        ,@(and suffixedp (list :suffix t))
+                        ,@(and attrs (list :attrs attrs))))
+        (puthash entry cached seen))
+      (when (and (equal host h)
+                 (auth-source-pass--match-parts cached :port port require)
+                 (auth-source-pass--match-parts cached :user user require)
+                 (setq s (or (funcall getat 'secret)
+                             (not (memq :secret require)))))
+        (unless (or user u)
+          (when (setq u (funcall getat "user"))
+            (setq cached (plist-put cached :user u))))
+        (unless (or port p)
+          (when (setq p (funcall getat "port"))
+            (setq p (auth-source-pass--cast-port p port)
+                  cached (plist-put cached :port p))))
+        (puthash entry (plist-put cached :attrs attrs) seen)
+        `( :host ,host
+           ,@(and u (list :user u))
+           ,@(and p (list :port p))
+           ,@(and s (not (eq s t)) (list :secret s)))))))
 
 (defun auth-source-pass--find-match-many (hosts users ports require max)
   "Return plists for valid combinations of HOSTS, USERS, PORTS."
   (let ((seen (make-hash-table :test #'equal))
         (entries (auth-source-pass-entries))
-        out suffixed suffixedp)
+        out suffixed)
     (catch 'done
       (dolist (host hosts out)
         (pcase-let ((`(,_ ,u ,p) (auth-source-pass--disambiguate host)))
@@ -289,28 +345,18 @@ auth-source-pass--find-match-many
             (setq p nil))
           (dolist (user (or users (list u)))
             (dolist (port (or ports (list p)))
-              (dolist (e entries)
-                (when-let*
-                    ((m (or (gethash e seen) (auth-source-pass--retrieve-parsed
-                                              seen e (integerp port))))
-                     ((equal host (plist-get m :host)))
-                     ((auth-source-pass--match-parts m :port port require))
-                     ((auth-source-pass--match-parts m :user user require))
-                     (parsed (auth-source-pass-parse-entry e))
-                     ;; For now, ignore body-content pairs, if any,
-                     ;; from `auth-source-pass--parse-data'.
-                     (secret (or (auth-source-pass--get-attr 'secret parsed)
-                                 (not (memq :secret require)))))
-                  (push
-                   `( :host ,host ; prefer user-provided :host over h
-                      ,@(and-let* ((u (plist-get m :user))) (list :user u))
-                      ,@(and-let* ((p (plist-get m :port))) (list :port p))
-                      ,@(and secret (not (eq secret t)) (list :secret secret)))
-                   (if (setq suffixedp (plist-get m :suffix)) suffixed out))
-                  (unless suffixedp
-                    (when (or (zerop (cl-decf max))
-                              (null (setq entries (delete e entries))))
-                      (throw 'done out)))))
+              (dolist (entry entries)
+                (let* ((result (auth-source-pass--find-matched-entry
+                                host user port require seen entry))
+                       ;;
+                       suffixedp)
+                  (when result
+                    (setq suffixedp (plist-get (gethash entry seen) :suffix))
+                    (push result (if suffixedp suffixed out))
+                    (unless suffixedp
+                      (when (or (zerop (cl-decf max))
+                                (null (setq entries (delete entry entries))))
+                        (throw 'done out))))))
               (setq suffixed (nreverse suffixed))
               (while suffixed
                 (push (pop suffixed) out)
diff --git a/test/lisp/auth-source-pass-tests.el b/test/lisp/auth-source-pass-tests.el
index 6455c3393d5..2ce5d12a6bc 100644
--- a/test/lisp/auth-source-pass-tests.el
+++ b/test/lisp/auth-source-pass-tests.el
@@ -601,6 +601,73 @@ auth-source-pass-extra-query-keywords--akib
       (should (equal results
                      '((:host "disroot.org" :user "akib" :secret "b")))))))
 
+(ert-deftest auth-source-pass-extra-query-keywords--akib/attr ()
+  (auth-source-pass--with-store '(("x.com" (secret . "a"))
+                                  ("disroot.org" (secret . "b")
+                                   ("user" . "akib") ("port" . "42"))
+                                  ("z.com" (secret . "c")))
+    (auth-source-pass-enable)
+    (let* ((auth-source-pass-extra-query-keywords t)
+           results)
+
+      ;; Non-matching query param.
+      (setq results (auth-source-search :host "disroot.org" :user "?" :max 2))
+      (should-not results)
+
+      ;; No query params matching attrs.
+      (setq results (auth-source-search :host "disroot.org" :max 2))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results
+                     '(( :host "disroot.org" :user "akib"
+                         :port "42" :secret "b"))))
+
+      ;; Matching user query param.
+      (setq results (auth-source-search :host "disroot.org" :user "akib"))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results
+                     '(( :host "disroot.org" :user "akib"
+                         :port "42" :secret "b"))))
+
+      ;; Matching port typed query param.
+      (setq results (auth-source-search :host "disroot.org" :port 42))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results
+                     '(( :host "disroot.org" :user "akib"
+                         :port 42 :secret "b")))))))
+
+(ert-deftest auth-source-pass-extra-query-keywords--netrc-akib/require ()
+  (ert-with-temp-file netrc-file
+    :text "\
+machine x.com password a
+machine disroot.org user akib password b
+machine z.com password c
+"
+    (let* ((auth-sources (list netrc-file))
+           (auth-source-do-cache nil)
+           (results (auth-source-search :host "disroot.org"
+                                        :require '(:user) :max 2)))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results
+                     '((:host "disroot.org" :user "akib" :secret "b")))))))
+
+(ert-deftest auth-source-pass-extra-query-keywords--akib/attr/require ()
+  (auth-source-pass--with-store '(("x.com" (secret . "a"))
+                                  ("disroot.org" (secret . "b")
+                                   ("user" . "akib"))
+                                  ("z.com" (secret . "c")))
+    (auth-source-pass-enable)
+    (let* ((auth-source-pass-extra-query-keywords t)
+           (results (auth-source-search :host "disroot.org"
+                                        :require '(:user) :max 2)))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results
+                     '((:host "disroot.org" :user "akib" :secret "b")))))))
+
 ;; Searches for :host are case-sensitive, and a returned host isn't
 ;; normalized.
 
@@ -632,11 +699,19 @@ auth-source-pass-extra-query-keywords--host
 ;; A retrieved store entry mustn't be nil regardless of whether its
 ;; path contains port or user components.
 
+(ert-deftest auth-source-pass-extra-query-keywords--netrc-baseline ()
+  (ert-with-temp-file netrc-file
+    :text "machine foo\n"
+    (let* ((auth-sources (list netrc-file))
+           (auth-source-do-cache nil)
+           (results (auth-source-search :host "foo")))
+      (should (equal results '((:host "foo")))))))
+
 (ert-deftest auth-source-pass-extra-query-keywords--baseline ()
   (let ((auth-source-pass-extra-query-keywords t))
-    (auth-source-pass--with-store '(("x.com"))
+    (auth-source-pass--with-store '(("foo"))
       (auth-source-pass-enable)
-      (should-not (auth-source-search :host "x.com")))))
+      (should (equal (auth-source-search :host "foo") '((:host "foo")))))))
 
 ;; Output port type (int or string) matches that of input parameter.
 
-- 
2.45.2


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* bug#72441: 31.0.50; Auth-source-pass doesn't match password attributes or hosts without user when extra-query-keywords is true
  2024-08-09 18:02 ` J.P.
@ 2024-08-09 19:20   ` Björn Bidar via Bug reports for GNU Emacs, the Swiss army knife of text editors
       [not found]   ` <87ed6xy03r.fsf@>
  1 sibling, 0 replies; 6+ messages in thread
From: Björn Bidar via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-08-09 19:20 UTC (permalink / raw)
  To: J.P.; +Cc: 72441

"J.P." <jp@neverwas.me> writes:

> Björn Bidar via "Bug reports for GNU Emacs, the Swiss army knife of text
> editors" <bug-gnu-emacs@gnu.org> writes:
>
>> I noticed that auth-source-pass doesn't match agains password file
>> attributes such as those containing :user and only matches password
>> files which contain a host and a user when
>> auth-source-pass-extra-query-keywords is true.
>
> I don't use pass myself, nor have I ever, but I suppose I did add the
> `auth-source-pass-extra-query-keywords' option (though mainly in a bid
> to make auth-source-pass behave more like other back ends so it's usable
> with ERC). Anyway, I do actually recall being somewhat aware of the
> existence of the file attributes you mention. It seems I even left a
> comment about the current lack of support [1].

I started using the option since my girlfriends password store is
organized for many passwords as hostname.tld/user@exampl.com, when she
created a password that is <Topic e.g. SSO>/example.com/user where
without the option the wrong password files were used.
Her mail address is user@example.com and the login server is also
example.com with her name as the username (we selfhost).

> Looking into this a bit, it seems the password store's web page
> considers everything after the initial (password) line to be an opaque
> text blob:
>
>   The password store does not impose any particular schema or type of
>   organization of your data, as it is simply a flat text file, which can
>   contain arbitrary data. Though the most common case is storing a
>   single password per entry, some power users find they would like to
>   store more than just their password inside the password store, and
>   additionally store answers to secret questions, website URLs, and
>   other sensitive information or metadata. Since the password store does
>   not impose a scheme of it's own, you can choose your own organization.
>   There are many possibilities.
>
> However, I do realize that the auth-source-pass back end without the
> extra-keywords option already dips into a file's contents looking for an
> attributes list like the one shown on the web page. (Whether that's wise
> is pretty much moot after all these years.) Anyway, for that reason, I
> suppose we _should_ attempt to at least explore doing the same when the
> extra-keywords option is enabled. For me, the most important thing
> remains mimicking the behavior of the other built-in back ends, which at
> times is admittedly unintuitive but nevertheless consistent and thus
> predictable from a mechanical POV.

I agree fully with the comment. Other's that use pass as source for
passwords also use file contents to match or retrieve variables from.
E.g. most browser plugins derive the parameter for the login or user
name from either of these names.

>> Steps to reproduce:
>> 1. Setup pass with the following structure
>>    WorkingTest/example.com/foo
>>    FailingTest/example2.com
>>    FailingTest/example3.com with user: foo in the password file
>> 2. (auth-source-pass-enable)
>> 3. (setq auth-source-pass-extra-query-keywords t)
>> 4. (auth-source-search :host "example" :user "foo") -> works
>> 5. (auth-source-search :host "example2.com") -> fails
>> 6. (auth-source-search :host "example3.com" :user "foo")
>>
>> Auth-source-pass should be able to query the password file for
>> additional attributes if one of the previous attributes such as :host
>> match to it. Quering the file attributes is quite important in use
>> cases where it doesn't make sense to the user to have a
>> host-folder/user-file structure in cases where there's only one
>> password for said host.
>
> Currently, if you have a file in the root of your ~/.password-store
> named something like "top-level-host.com", and it's contents feature a
> "user: foo" attribute, both
>
>   (auth-source-search :host "top-level-host.com")
>
> and
>
>   (auth-source-search :host "top-level-host.com" :user "foo")
>
> return
>
>   ((:host "top-level-host.com" :secret ...)).
>
> If you're saying you want to see (:user "foo") in the results as well, I
> guess we can do that (see attached patch as well as [2], below).
> However, this still won't work on any of your examples, which all have
> intervening path components between the root directory and the .gpg
> files. The reason for this restriction is explained below.
>
> If we do end up going with something like the attached patch, we'll need
> to profile it. I can create a bunch of fake trees of varying shapes and
> sizes, but I'd rather someone with real data and a sizable store assess
> how much slower it is to visit (and thus decrypt) potentially every file
> in the tree, which is what any attr-reading implementation must do. On
> my machine, it takes roughly 0.18 seconds to decrypt a single two-line
> file via `auth-source-pass--read-entry'. (This seems prohibitively
> expensive to do en masse, no?) FWIW, most of this time is spent in
> `epg-wait-for-status', which blocks until the subprocess exits.

That is why I was arguing that we should attempt to not try decrypt the
password file unless a previous attribute such as :host or :user matched
before.
If we could do the search in parallel or at least without blocking Emacs
that would be a different story of course.

>> Same it should maybe also match against :host
>> if no user was provided, I don't know how other sources do this thou.
>
> While the reference implementation indeed succeeds with a plain :host
> input (see test `auth-source-pass-extra-query-keywords--netrc-host'), I
> believe the actual problem you perceive has more to do with the content
> of the file paths, specifically, leading directory components. Still,
> I'm inclined to agree that this would be nice to have. However, I do
> seem to recall this being discussed on at least one occasion, with the
> conclusion being that it's too complicated, if not impossible, to
> disambiguate between a trailing "hostname/user" and "folder/hostname".
>
> Nevertheless, we could add an option to do it anyway based on one or
> more heuristics-based strategy (resolving hosts for real is surely a no
> go). For example, one such strategy could ignore a penultimate file-path
> component that's not an FQDN, even if it's, say, LDH-only and resolvable
> as a hostname, so long as the leaf component _is_ an FQDN. However, such
> an option would have to be disabled by default to prevent existing
> entries like "localhost/test.user" from being parsed as (:host
> "test.user").
>

What do you mean by resolving hosts for real? I think another option
would be for the user to specific the hierarchy of their password store
to auth-source-pass e.g. word/%host%/%user or word/(or %host word)/%user
where word is any word that isn't used for matching but just for the
user to organize the hierarchy.

> In any case, I'm happy to review patches, but I think someone who
> actually uses this back end should implement the feature.

I'm not a good lisp programmar but I could give it a go with some help
such as your patch as a start point.

>
> [1] https://git.savannah.gnu.org/cgit/emacs.git/tree/lisp/auth-source-pass.el?id=423c86cb#n300
>
> [2] The following describes details of the attached patch's logic for
>     the inner (dolist (e entries) ...) loop of the primary matching
>     function `auth-source-pass--find-match-many'. Hopefully it's
>     somewhat sound with regard to deferring decryption for as long as
>     possible.
>
>     1. Parse the file path of each entry first and cache its results in
>        a plist, the "cached entry metadata," which is filed under the
>        entry's file-path in the `seen' hash table. If it doesn't match
>        the basic filename format, it must not be a passwordstore file,
>        so reject the entry.
>     2. Check the :host field before reading the file. Unless it matches,
>        reject the entry.
>     3. Engage in a series of probing conditional checks for a :port
>        field to match against a provided "port" query parameter, all
>        while attempting to defer decryption until absolutely necessary.
>        (A path-encoded :port always takes precedence over a :port in the
>        file.)
>        - If a `port' query parameter is not given for matching against,
>          continue to the next steps for the current entry.
>        - Otherwise, if a :port parsed from the file path is present and
>          it doesn't match, reject the entry, meaning go to the beginning
>          of the current loop, considering the next entry.
>        - If a path-derived :port is absent, ensure the cached entry
>          metadata contains an additional :attrs field (an alist). If the
>          metadata lacks an :attrs field, the file has not yet been
>          decrypted. Decrypt it now using `auth-source-pass-parse-entry',
>          then add its secret and its attrs alist to the cached metadata,
>          under :attrs.
>        - Look in the cached entry metadata's :attrs alist for a "port"
>          attr. If a "port" attr is indeed present and doesn't match the
>          port query parameter, reject the entry.
>        - If no such "port" attr exists and is required (meaning :port
>          appears in the `require' query parameter), reject the entry.
>     4. Repeat step 3 for :user. The same precedence rules apply, meaning
>        any non-null path-derived :user field is immediately accepted,
>        and the file is not decrypted.
>     5. If we haven't yet decrypted the file, do so now and populate the
>        :attrs item in the cached entry metadata. If it's already been
>        decrypted at some point, :attrs will present (though possibly
>        empty). In any case, add the items we care about if non-null
>        (:user, :port, and :secret) to the matched results for this entry.
>        However, only do so if a secret was either not required or is
>        present; otherwise, reject the entry.





^ permalink raw reply	[flat|nested] 6+ messages in thread

* bug#72441: 31.0.50; Auth-source-pass doesn't match password attributes or hosts without user when extra-query-keywords is true
       [not found]   ` <87ed6xy03r.fsf@>
@ 2024-08-10 13:58     ` J.P.
  2024-08-12 19:33       ` J.P.
  0 siblings, 1 reply; 6+ messages in thread
From: J.P. @ 2024-08-10 13:58 UTC (permalink / raw)
  To: Björn Bidar; +Cc: 72441

[-- Attachment #1: Type: text/plain, Size: 6077 bytes --]

Björn Bidar <bjorn.bidar@thaodan.de> writes:

> "J.P." <jp@neverwas.me> writes:
>
>> However, I do realize that the auth-source-pass back end without the
>> extra-keywords option already dips into a file's contents looking for an
>> attributes list like the one shown on the web page. (Whether that's wise
>> is pretty much moot after all these years.) Anyway, for that reason, I
>> suppose we _should_ attempt to at least explore doing the same when the
>> extra-keywords option is enabled. For me, the most important thing
>> remains mimicking the behavior of the other built-in back ends, which at
>> times is admittedly unintuitive but nevertheless consistent and thus
>> predictable from a mechanical POV.
>
> I agree fully with the comment. Other's that use pass as source for
> passwords also use file contents to match or retrieve variables from.
> E.g. most browser plugins derive the parameter for the login or user
> name from either of these names.

Hm, actually, I was not initially thinking of including all the
attributes, only :user and :port. But it seems I'd forgotten that the
netrc reference implementation does indeed include arbitrary key/value
pairs in the results from a successful match:

  # ~/.authinfo
  machine example.org login me password 123 foo bar

  M-: (auth-source-search :host "example.org")
  => (... :foo "bar")

I've updated my previous patch to do this.

>> If we do end up going with something like the attached patch, we'll need
>> to profile it. I can create a bunch of fake trees of varying shapes and
>> sizes, but I'd rather someone with real data and a sizable store assess
>> how much slower it is to visit (and thus decrypt) potentially every file
>> in the tree, which is what any attr-reading implementation must do. On
>> my machine, it takes roughly 0.18 seconds to decrypt a single two-line
>> file via `auth-source-pass--read-entry'. (This seems prohibitively
>> expensive to do en masse, no?) FWIW, most of this time is spent in
>> `epg-wait-for-status', which blocks until the subprocess exits.
>
> That is why I was arguing that we should attempt to not try decrypt the
> password file unless a previous attribute such as :host or :user matched
> before.
> If we could do the search in parallel or at least without blocking Emacs
> that would be a different story of course.

With what I'm proposing, we would actually decrypt to inspect the parsed
attrs if :port or :user isn't found in the filename and a port or user
query parameter is given (or :port or :user appears in a :require
parameter). This behavior is currently gated by a new variable called
`auth-source-pass-check-attrs-with-extra-query-keywords', but it's t by
default. Perhaps it's better to have it be nil? If we do that, then, by
default, :port and :user attributes won't be considered, but they will
still be included in successful matches along with all other attributes.

>>> Same it should maybe also match against :host
>>> if no user was provided, I don't know how other sources do this thou.
>>
>> While the reference implementation indeed succeeds with a plain :host
>> input (see test `auth-source-pass-extra-query-keywords--netrc-host'), I
>> believe the actual problem you perceive has more to do with the content
>> of the file paths, specifically, leading directory components. Still,
>> I'm inclined to agree that this would be nice to have. However, I do
>> seem to recall this being discussed on at least one occasion, with the
>> conclusion being that it's too complicated, if not impossible, to
>> disambiguate between a trailing "hostname/user" and "folder/hostname".
>>
>> Nevertheless, we could add an option to do it anyway based on one or
>> more heuristics-based strategy (resolving hosts for real is surely a no
>> go). For example, one such strategy could ignore a penultimate file-path
>> component that's not an FQDN, even if it's, say, LDH-only and resolvable
>> as a hostname, so long as the leaf component _is_ an FQDN. However, such
>> an option would have to be disabled by default to prevent existing
>> entries like "localhost/test.user" from being parsed as (:host
>> "test.user").
>>
>
> What do you mean by resolving hosts for real?

I just meant it'd be unrealistic to query the system resolver via
`network-lookup-address-info' or similar whenever we need to
disambiguate.

> I think another option would be for the user to specific the hierarchy
> of their password store to auth-source-pass e.g. word/%host%/%user or
> word/(or %host word)/%user where word is any word that isn't used for
> matching but just for the user to organize the hierarchy.

That could work, although it seems rather complex with pattern
substitutions and expressions (?). Perhaps it could be precomputed
somehow into a regexp before every query.

Another idea would be to just have the option be an integer indicating
the number of leading path components to mask off before matching. Given
a tree like

  ~/.password-store/
  - foo/
    - example.com
  - irc.bar.org/
    - example.net/
      - me
  - baz/
    - myvps/
      - my.user.name

if the option (which is 0 by default) were set to 1, then possible
results might be

 (:host "example.com" :user "some-attr")
 (:host "example.net" :user "me")
 (:host "myvps"       :user "my.user.name")

Although this won't work if a user wants different mask depths for
different sub-directories.

>> In any case, I'm happy to review patches, but I think someone who
>> actually uses this back end should implement the feature.
>
> I'm not a good lisp programmar but I could give it a go with some help
> such as your patch as a start point.

I should be able to handle the attribute feature, unless you want to
improve upon it. It's mainly the disambiguation feature that I'd want an
actual pass user, like yourself, to implement or at least help design.
(Although feel free to offer patches of any nature, including based on
anything I've proposed.)


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0000-v1-v2.diff --]
[-- Type: text/x-patch, Size: 5153 bytes --]

From a0381a48cb4ff960ef2dd55dd511f5c18e535f6e Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Sat, 10 Aug 2024 07:15:36 -0700
Subject: [PATCH 0/1] *** NOT A PATCH ***

*** BLURB HERE ***

F. Jason Park (1):
  [POC] Match attrs with auth-source-pass-extra-query-keywords

 lisp/auth-source-pass.el            | 143 ++++++++++++++++++++--------
 test/lisp/auth-source-pass-tests.el |  96 ++++++++++++++++++-
 2 files changed, 195 insertions(+), 44 deletions(-)

Interdiff:
diff --git a/lisp/auth-source-pass.el b/lisp/auth-source-pass.el
index 8982e07a6be..0df7817f501 100644
--- a/lisp/auth-source-pass.el
+++ b/lisp/auth-source-pass.el
@@ -264,10 +264,10 @@ auth-source-pass--cast-port
            n))
         (t (format "%s" val))))
 
-(defun auth-source-pass--match-parts (parts key reference require)
-  (let ((value (plist-get parts key)))
+(defun auth-source-pass--match-parts (cache key reference require)
+  (let ((value (plist-get cache key)))
     (if (memq key require)
-        (or (null reference) (equal value reference))
+        (if reference (equal value reference) value)
       (or (null reference) (null value) (equal value reference)))))
 
 (defvar auth-source-pass-check-attrs-with-extra-query-keywords t
@@ -299,13 +299,15 @@ auth-source-pass--find-matched-entry
                        (setq suffixedp t)
                        u))
                     ((match-string 20 entry))
-                    ((and user optp) (funcall getat "user"))))
+                    ((and optp (or user (memq :user require)))
+                     (funcall getat "user"))))
            (p (cond (cached (plist-get cached :port))
                     ((match-string 30 entry))
                     ((match-string 31 entry))
-                    ((and port optp) (funcall getat "port"))))
+                    ((and optp (or port (memq :port require)))
+                     (funcall getat "port"))))
            ;;
-           s)
+           s extras)
       (when p
         (setq p (auth-source-pass--cast-port p port)))
       (unless cached
@@ -320,18 +322,29 @@ auth-source-pass--find-matched-entry
                  (auth-source-pass--match-parts cached :user user require)
                  (setq s (or (funcall getat 'secret)
                              (not (memq :secret require)))))
-        (unless (or user u)
-          (when (setq u (funcall getat "user"))
-            (setq cached (plist-put cached :user u))))
-        (unless (or port p)
-          (when (setq p (funcall getat "port"))
-            (setq p (auth-source-pass--cast-port p port)
-                  cached (plist-put cached :port p))))
+        (let (tmp)
+          (while-let ((v (pop attrs))
+                      (k (pop v)))
+            (pcase k
+              ((or "user" "username")
+               (unless (or user u)
+                 (setq u v
+                       cached (plist-put cached :user u))))
+              ("port"
+               (unless (or port p)
+                 (setq p (auth-source-pass--cast-port v port)
+                       cached (plist-put cached :port p))))
+              ((pred stringp)
+               (push (intern (concat ":" k)) extras)
+               (push v extras)
+               (push (cons k v) tmp))))
+          (setq attrs (nreverse tmp)))
         (puthash entry (plist-put cached :attrs attrs) seen)
         `( :host ,host
            ,@(and u (list :user u))
            ,@(and p (list :port p))
-           ,@(and s (not (eq s t)) (list :secret s)))))))
+           ,@(and s (not (eq s t)) (list :secret s))
+           ,@(nreverse extras))))))
 
 (defun auth-source-pass--find-match-many (hosts users ports require max)
   "Return plists for valid combinations of HOSTS, USERS, PORTS."
diff --git a/test/lisp/auth-source-pass-tests.el b/test/lisp/auth-source-pass-tests.el
index 2ce5d12a6bc..c6662cd8b42 100644
--- a/test/lisp/auth-source-pass-tests.el
+++ b/test/lisp/auth-source-pass-tests.el
@@ -668,6 +668,23 @@ auth-source-pass-extra-query-keywords--akib/attr/require
       (should (equal results
                      '((:host "disroot.org" :user "akib" :secret "b")))))))
 
+(ert-deftest auth-source-pass-extra-query-keywords--akib/attr/extras ()
+  (auth-source-pass--with-store '(("x.com" (secret . "a"))
+                                  ("disroot.org" (secret . "b")
+                                   ("user" . "akib")
+                                   ("port" . "42")
+                                   ("foo" . "1")
+                                   ("bar" . "2"))
+                                  ("z.com" (secret . "c")))
+    (auth-source-pass-enable)
+    (let* ((auth-source-pass-extra-query-keywords t)
+           (results (auth-source-search :host "disroot.org" :max 2)))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results
+                     '((:host "disroot.org" :user "akib" :port "42"
+                              :secret "b" :foo "1" :bar "2")))))))
+
 ;; Searches for :host are case-sensitive, and a returned host isn't
 ;; normalized.
 
-- 
2.46.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0001-POC-Match-attrs-with-auth-source-pass-extra-query-ke.patch --]
[-- Type: text/x-patch, Size: 15077 bytes --]

From a0381a48cb4ff960ef2dd55dd511f5c18e535f6e Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Wed, 7 Aug 2024 22:23:09 -0700
Subject: [PATCH 1/1] [POC] Match attrs with
 auth-source-pass-extra-query-keywords

* lisp/auth-source-pass.el (auth-source-pass--retrieve-parsed):
Remove unused function.
(auth-source-pass--cast-port): New function, a helper to match an
entry's port to the given query param's type.
(auth-source-pass--match-parts): Return non-nil when a key is required
but the value is null.  Not doing produced behavior that deviated from
the reference netrc implementation and was thus a bug.
(auth-source-pass-check-attrs-with-extra-query-keywords): New
variable, a flag to opt out of arguably expensive attribute lookups.
(auth-source-pass--find-matched-entry): New function to isolate
processing logic for a single entry.
(auth-source-pass--find-match-many): Move single-entry processing
logic to separate helper, mainly for readability.
* test/lisp/auth-source-pass-tests.el
(auth-source-pass-extra-query-keywords--akib/attr)
(auth-source-pass-extra-query-keywords--netrc-akib/require)
(auth-source-pass-extra-query-keywords--akib/attr/require)
(auth-source-pass-extra-query-keywords--akib/attr/extras)
(auth-source-pass-extra-query-keywords--netrc-baseline): New tests.
(auth-source-pass-extra-query-keywords--baseline): Reverse expected
outcome to match reference implementation.  That it didn't before was
a bug.  (Bug#72441)
---
 lisp/auth-source-pass.el            | 143 ++++++++++++++++++++--------
 test/lisp/auth-source-pass-tests.el |  96 ++++++++++++++++++-
 2 files changed, 195 insertions(+), 44 deletions(-)

diff --git a/lisp/auth-source-pass.el b/lisp/auth-source-pass.el
index 03fd1f35811..0df7817f501 100644
--- a/lisp/auth-source-pass.el
+++ b/lisp/auth-source-pass.el
@@ -256,32 +256,101 @@ auth-source-pass--find-match
                 hosts
               (list hosts))))
 
-(defun auth-source-pass--retrieve-parsed (seen path port-number-p)
-  (when (string-match auth-source-pass--match-regexp path)
-    (puthash path
-             `( :host ,(or (match-string 10 path) (match-string 11 path))
-                ,@(if-let* ((tr (match-string 21 path)))
-                      (list :user tr :suffix t)
-                    (list :user (match-string 20 path)))
-                :port ,(and-let* ((p (or (match-string 30 path)
-                                         (match-string 31 path)))
-                                  (n (string-to-number p)))
-                         (if (or (zerop n) (not port-number-p))
-                             (format "%s" p)
-                           n)))
-             seen)))
-
-(defun auth-source-pass--match-parts (parts key value require)
-  (let ((mv (plist-get parts key)))
+(defun auth-source-pass--cast-port (val ref)
+  (cond ((integerp val) val)
+        ((and-let* (((integerp ref))
+                    (n (string-to-number val))
+                    ((not (zerop n))))
+           n))
+        (t (format "%s" val))))
+
+(defun auth-source-pass--match-parts (cache key reference require)
+  (let ((value (plist-get cache key)))
     (if (memq key require)
-        (and value (equal mv value))
-      (or (not value) (not mv) (equal mv value)))))
+        (if reference (equal value reference) value)
+      (or (null reference) (null value) (equal value reference)))))
+
+(defvar auth-source-pass-check-attrs-with-extra-query-keywords t
+  "When non-nil, decrypt files to find attributes matching parameters.
+However, give precedence to fields encoded in file names.  Only applies
+when `auth-source-pass-extra-query-keywords' is non-nil.")
+
+;; This function tries to defer decryption as long as possible.  For
+;; that reason, an entry's file-path-derived :port or :user field
+;; always takes precedence over their counterparts from a decrypted
+;; file's attribute list.
+(defun auth-source-pass--find-matched-entry (host user port require seen entry)
+  "Match ENTRY against query params HOST USER PORT REQUIRE with cache SEEN."
+  (when (string-match auth-source-pass--match-regexp entry)
+    (let* ((cached (gethash entry seen))
+           (optp auth-source-pass-check-attrs-with-extra-query-keywords)
+           (suffixedp nil)
+           (h (or (and cached (plist-get cached :host))
+                  (match-string 10 entry)
+                  (match-string 11 entry)))
+           (attrs (and cached (plist-get :attrs cached)))
+           (getat (lambda (k)
+                    (save-match-data
+                      (unless attrs
+                        (setq attrs (auth-source-pass-parse-entry entry)))
+                      (auth-source-pass--get-attr k attrs))))
+           (u (cond (cached (plist-get cached :user))
+                    ((and-let* ((u (match-string 21 entry)))
+                       (setq suffixedp t)
+                       u))
+                    ((match-string 20 entry))
+                    ((and optp (or user (memq :user require)))
+                     (funcall getat "user"))))
+           (p (cond (cached (plist-get cached :port))
+                    ((match-string 30 entry))
+                    ((match-string 31 entry))
+                    ((and optp (or port (memq :port require)))
+                     (funcall getat "port"))))
+           ;;
+           s extras)
+      (when p
+        (setq p (auth-source-pass--cast-port p port)))
+      (unless cached
+        (setq cached `( :host ,h
+                        ,@(and u (list :user u))
+                        ,@(and p (list :port p))
+                        ,@(and suffixedp (list :suffix t))
+                        ,@(and attrs (list :attrs attrs))))
+        (puthash entry cached seen))
+      (when (and (equal host h)
+                 (auth-source-pass--match-parts cached :port port require)
+                 (auth-source-pass--match-parts cached :user user require)
+                 (setq s (or (funcall getat 'secret)
+                             (not (memq :secret require)))))
+        (let (tmp)
+          (while-let ((v (pop attrs))
+                      (k (pop v)))
+            (pcase k
+              ((or "user" "username")
+               (unless (or user u)
+                 (setq u v
+                       cached (plist-put cached :user u))))
+              ("port"
+               (unless (or port p)
+                 (setq p (auth-source-pass--cast-port v port)
+                       cached (plist-put cached :port p))))
+              ((pred stringp)
+               (push (intern (concat ":" k)) extras)
+               (push v extras)
+               (push (cons k v) tmp))))
+          (setq attrs (nreverse tmp)))
+        (puthash entry (plist-put cached :attrs attrs) seen)
+        `( :host ,host
+           ,@(and u (list :user u))
+           ,@(and p (list :port p))
+           ,@(and s (not (eq s t)) (list :secret s))
+           ,@(nreverse extras))))))
 
 (defun auth-source-pass--find-match-many (hosts users ports require max)
   "Return plists for valid combinations of HOSTS, USERS, PORTS."
   (let ((seen (make-hash-table :test #'equal))
         (entries (auth-source-pass-entries))
-        out suffixed suffixedp)
+        out suffixed)
     (catch 'done
       (dolist (host hosts out)
         (pcase-let ((`(,_ ,u ,p) (auth-source-pass--disambiguate host)))
@@ -289,28 +358,18 @@ auth-source-pass--find-match-many
             (setq p nil))
           (dolist (user (or users (list u)))
             (dolist (port (or ports (list p)))
-              (dolist (e entries)
-                (when-let*
-                    ((m (or (gethash e seen) (auth-source-pass--retrieve-parsed
-                                              seen e (integerp port))))
-                     ((equal host (plist-get m :host)))
-                     ((auth-source-pass--match-parts m :port port require))
-                     ((auth-source-pass--match-parts m :user user require))
-                     (parsed (auth-source-pass-parse-entry e))
-                     ;; For now, ignore body-content pairs, if any,
-                     ;; from `auth-source-pass--parse-data'.
-                     (secret (or (auth-source-pass--get-attr 'secret parsed)
-                                 (not (memq :secret require)))))
-                  (push
-                   `( :host ,host ; prefer user-provided :host over h
-                      ,@(and-let* ((u (plist-get m :user))) (list :user u))
-                      ,@(and-let* ((p (plist-get m :port))) (list :port p))
-                      ,@(and secret (not (eq secret t)) (list :secret secret)))
-                   (if (setq suffixedp (plist-get m :suffix)) suffixed out))
-                  (unless suffixedp
-                    (when (or (zerop (cl-decf max))
-                              (null (setq entries (delete e entries))))
-                      (throw 'done out)))))
+              (dolist (entry entries)
+                (let* ((result (auth-source-pass--find-matched-entry
+                                host user port require seen entry))
+                       ;;
+                       suffixedp)
+                  (when result
+                    (setq suffixedp (plist-get (gethash entry seen) :suffix))
+                    (push result (if suffixedp suffixed out))
+                    (unless suffixedp
+                      (when (or (zerop (cl-decf max))
+                                (null (setq entries (delete entry entries))))
+                        (throw 'done out))))))
               (setq suffixed (nreverse suffixed))
               (while suffixed
                 (push (pop suffixed) out)
diff --git a/test/lisp/auth-source-pass-tests.el b/test/lisp/auth-source-pass-tests.el
index 6455c3393d5..c6662cd8b42 100644
--- a/test/lisp/auth-source-pass-tests.el
+++ b/test/lisp/auth-source-pass-tests.el
@@ -601,6 +601,90 @@ auth-source-pass-extra-query-keywords--akib
       (should (equal results
                      '((:host "disroot.org" :user "akib" :secret "b")))))))
 
+(ert-deftest auth-source-pass-extra-query-keywords--akib/attr ()
+  (auth-source-pass--with-store '(("x.com" (secret . "a"))
+                                  ("disroot.org" (secret . "b")
+                                   ("user" . "akib") ("port" . "42"))
+                                  ("z.com" (secret . "c")))
+    (auth-source-pass-enable)
+    (let* ((auth-source-pass-extra-query-keywords t)
+           results)
+
+      ;; Non-matching query param.
+      (setq results (auth-source-search :host "disroot.org" :user "?" :max 2))
+      (should-not results)
+
+      ;; No query params matching attrs.
+      (setq results (auth-source-search :host "disroot.org" :max 2))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results
+                     '(( :host "disroot.org" :user "akib"
+                         :port "42" :secret "b"))))
+
+      ;; Matching user query param.
+      (setq results (auth-source-search :host "disroot.org" :user "akib"))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results
+                     '(( :host "disroot.org" :user "akib"
+                         :port "42" :secret "b"))))
+
+      ;; Matching port typed query param.
+      (setq results (auth-source-search :host "disroot.org" :port 42))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results
+                     '(( :host "disroot.org" :user "akib"
+                         :port 42 :secret "b")))))))
+
+(ert-deftest auth-source-pass-extra-query-keywords--netrc-akib/require ()
+  (ert-with-temp-file netrc-file
+    :text "\
+machine x.com password a
+machine disroot.org user akib password b
+machine z.com password c
+"
+    (let* ((auth-sources (list netrc-file))
+           (auth-source-do-cache nil)
+           (results (auth-source-search :host "disroot.org"
+                                        :require '(:user) :max 2)))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results
+                     '((:host "disroot.org" :user "akib" :secret "b")))))))
+
+(ert-deftest auth-source-pass-extra-query-keywords--akib/attr/require ()
+  (auth-source-pass--with-store '(("x.com" (secret . "a"))
+                                  ("disroot.org" (secret . "b")
+                                   ("user" . "akib"))
+                                  ("z.com" (secret . "c")))
+    (auth-source-pass-enable)
+    (let* ((auth-source-pass-extra-query-keywords t)
+           (results (auth-source-search :host "disroot.org"
+                                        :require '(:user) :max 2)))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results
+                     '((:host "disroot.org" :user "akib" :secret "b")))))))
+
+(ert-deftest auth-source-pass-extra-query-keywords--akib/attr/extras ()
+  (auth-source-pass--with-store '(("x.com" (secret . "a"))
+                                  ("disroot.org" (secret . "b")
+                                   ("user" . "akib")
+                                   ("port" . "42")
+                                   ("foo" . "1")
+                                   ("bar" . "2"))
+                                  ("z.com" (secret . "c")))
+    (auth-source-pass-enable)
+    (let* ((auth-source-pass-extra-query-keywords t)
+           (results (auth-source-search :host "disroot.org" :max 2)))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results
+                     '((:host "disroot.org" :user "akib" :port "42"
+                              :secret "b" :foo "1" :bar "2")))))))
+
 ;; Searches for :host are case-sensitive, and a returned host isn't
 ;; normalized.
 
@@ -632,11 +716,19 @@ auth-source-pass-extra-query-keywords--host
 ;; A retrieved store entry mustn't be nil regardless of whether its
 ;; path contains port or user components.
 
+(ert-deftest auth-source-pass-extra-query-keywords--netrc-baseline ()
+  (ert-with-temp-file netrc-file
+    :text "machine foo\n"
+    (let* ((auth-sources (list netrc-file))
+           (auth-source-do-cache nil)
+           (results (auth-source-search :host "foo")))
+      (should (equal results '((:host "foo")))))))
+
 (ert-deftest auth-source-pass-extra-query-keywords--baseline ()
   (let ((auth-source-pass-extra-query-keywords t))
-    (auth-source-pass--with-store '(("x.com"))
+    (auth-source-pass--with-store '(("foo"))
       (auth-source-pass-enable)
-      (should-not (auth-source-search :host "x.com")))))
+      (should (equal (auth-source-search :host "foo") '((:host "foo")))))))
 
 ;; Output port type (int or string) matches that of input parameter.
 
-- 
2.46.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* bug#72441: 31.0.50; Auth-source-pass doesn't match password attributes or hosts without user when extra-query-keywords is true
  2024-08-10 13:58     ` J.P.
@ 2024-08-12 19:33       ` J.P.
  2024-09-06 23:16         ` J.P.
  0 siblings, 1 reply; 6+ messages in thread
From: J.P. @ 2024-08-12 19:33 UTC (permalink / raw)
  To: 72441; +Cc: Björn Bidar

[-- Attachment #1: Type: text/plain, Size: 481 bytes --]

While exploring ways to tackle this feature, I stumbled on a couple
minor bugs related to `auth-source-pass-extra-query-keywords'.

Because there's no telling when we'll end up with something installable
for this feature, I've gone ahead and isolated the fixes into a separate
patch (0001 in the attached). It's probably safe enough for Emacs 30,
but since the option was introduced back in 29, I'll just install it on
master (unless I hear otherwise in the coming days). Thanks.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0000-v2-v3.diff --]
[-- Type: text/x-patch, Size: 7149 bytes --]

From d9bd10debf6c3930669aedb896026f9f19b54466 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Mon, 12 Aug 2024 07:00:23 -0700
Subject: [PATCH 0/2] *** NOT A PATCH ***

*** BLURB HERE ***

F. Jason Park (2):
  Fix deviations in auth-source-pass behavior WRT netrc
  [POC] Match attrs with auth-source-pass-extra-query-keywords

 lisp/auth-source-pass.el            | 146 ++++++++++++++++++++--------
 test/lisp/auth-source-pass-tests.el | 138 +++++++++++++++++++++++++-
 2 files changed, 237 insertions(+), 47 deletions(-)

Interdiff:
diff --git a/lisp/auth-source-pass.el b/lisp/auth-source-pass.el
index 0df7817f501..a52dafc5ab2 100644
--- a/lisp/auth-source-pass.el
+++ b/lisp/auth-source-pass.el
@@ -266,9 +266,10 @@ auth-source-pass--cast-port
 
 (defun auth-source-pass--match-parts (cache key reference require)
   (let ((value (plist-get cache key)))
-    (if (memq key require)
-        (if reference (equal value reference) value)
-      (or (null reference) (null value) (equal value reference)))))
+    (cond ((memq key require)
+           (if reference (equal value reference) value))
+          ((and value reference) (equal value reference))
+          (t))))
 
 (defvar auth-source-pass-check-attrs-with-extra-query-keywords t
   "When non-nil, decrypt files to find attributes matching parameters.
diff --git a/test/lisp/auth-source-pass-tests.el b/test/lisp/auth-source-pass-tests.el
index c6662cd8b42..695635299f9 100644
--- a/test/lisp/auth-source-pass-tests.el
+++ b/test/lisp/auth-source-pass-tests.el
@@ -548,6 +548,44 @@ auth-source-pass-extra-query-keywords--wild-port-hit
                      '((:host "x.com" :secret "a")
                        (:host "x.com" :port 42 :secret "b")))))))
 
+;; The query requires a user and doesn't specify a user to match against.
+;; The only entry matching the host lacks a user, so the search fails.
+
+(ert-deftest auth-source-pass-extra-query-keywords--req-noparam-miss-netrc ()
+  (ert-with-temp-file netrc-file
+    :text "machine foo password a\n"
+    (let ((auth-sources (list netrc-file))
+          (auth-source-do-cache nil))
+      (should-not (auth-source-search :host "foo" :require '(:user) :max 2)))))
+
+(ert-deftest auth-source-pass-extra-query-keywords--req-noparam-miss ()
+  (let ((auth-source-pass-extra-query-keywords t))
+    (auth-source-pass--with-store '(("foo" (secret . "a")))
+      (auth-source-pass-enable)
+      (should-not (auth-source-search :host "foo" :require '(:user) :max 2)))))
+
+;; The query requires a user but does not provide a reference value to
+;; match against.  An entry matching the host that specifies a user is
+;; selected because any user will do.
+(ert-deftest auth-source-pass-extra-query-keywords--req-param-netrc ()
+  (ert-with-temp-file netrc-file
+    :text "machine foo login bob password a\n"
+    (let* ((auth-sources (list netrc-file))
+           (auth-source-do-cache nil)
+           (results (auth-source-search :host "foo" :require '(:user))))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results '((:host "foo" :user "bob" :secret "a")))))))
+
+(ert-deftest auth-source-pass-extra-query-keywords--req-param ()
+  (let ((auth-source-pass-extra-query-keywords t))
+    (auth-source-pass--with-store '(("foo/bob" (secret . "a")))
+      (auth-source-pass-enable)
+      (let ((results (auth-source-search :host "foo" :require '(:user))))
+        (dolist (result results)
+          (setf (plist-get result :secret) (auth-info-password result)))
+        (should (equal results '((:host "foo" :user "bob" :secret "a"))))))))
+
 ;; No entry has the requested port, but :port is required, so search fails.
 
 (ert-deftest auth-source-pass-extra-query-keywords--wild-port-req-miss-netrc ()
@@ -601,7 +639,7 @@ auth-source-pass-extra-query-keywords--akib
       (should (equal results
                      '((:host "disroot.org" :user "akib" :secret "b")))))))
 
-(ert-deftest auth-source-pass-extra-query-keywords--akib/attr ()
+(ert-deftest auth-source-pass-extra-query-keywords--akib-attr ()
   (auth-source-pass--with-store '(("x.com" (secret . "a"))
                                   ("disroot.org" (secret . "b")
                                    ("user" . "akib") ("port" . "42"))
@@ -638,23 +676,7 @@ auth-source-pass-extra-query-keywords--akib/attr
                      '(( :host "disroot.org" :user "akib"
                          :port 42 :secret "b")))))))
 
-(ert-deftest auth-source-pass-extra-query-keywords--netrc-akib/require ()
-  (ert-with-temp-file netrc-file
-    :text "\
-machine x.com password a
-machine disroot.org user akib password b
-machine z.com password c
-"
-    (let* ((auth-sources (list netrc-file))
-           (auth-source-do-cache nil)
-           (results (auth-source-search :host "disroot.org"
-                                        :require '(:user) :max 2)))
-      (dolist (result results)
-        (setf (plist-get result :secret) (auth-info-password result)))
-      (should (equal results
-                     '((:host "disroot.org" :user "akib" :secret "b")))))))
-
-(ert-deftest auth-source-pass-extra-query-keywords--akib/attr/require ()
+(ert-deftest auth-source-pass-extra-query-keywords--akib-attr-req ()
   (auth-source-pass--with-store '(("x.com" (secret . "a"))
                                   ("disroot.org" (secret . "b")
                                    ("user" . "akib"))
@@ -668,7 +690,23 @@ auth-source-pass-extra-query-keywords--akib/attr/require
       (should (equal results
                      '((:host "disroot.org" :user "akib" :secret "b")))))))
 
-(ert-deftest auth-source-pass-extra-query-keywords--akib/attr/extras ()
+(ert-deftest auth-source-pass-extra-query-keywords--akib-attr-extras-netrc ()
+  (ert-with-temp-file netrc-file
+    :text "\
+machine x.com password a
+machine disroot.org user akib port 42 password b foo 1 bar 2
+machine z.com password c
+"
+    (let* ((auth-sources (list netrc-file))
+           (auth-source-do-cache nil)
+           (results (auth-source-search :host "disroot.org" :max 2)))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results
+                     '((:host "disroot.org" :user "akib" :port "42"
+                              :secret "b" :foo "1" :bar "2")))))))
+
+(ert-deftest auth-source-pass-extra-query-keywords--akib-attr-extras ()
   (auth-source-pass--with-store '(("x.com" (secret . "a"))
                                   ("disroot.org" (secret . "b")
                                    ("user" . "akib")
@@ -713,8 +751,8 @@ auth-source-pass-extra-query-keywords--host
                      '((:host "Libera.Chat" :secret "b")))))))
 
 
-;; A retrieved store entry mustn't be nil regardless of whether its
-;; path contains port or user components.
+;; An effectively empty entry in the store returns nothing but the
+;; :host field matching the given host parameter.
 
 (ert-deftest auth-source-pass-extra-query-keywords--netrc-baseline ()
   (ert-with-temp-file netrc-file
-- 
2.46.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0001-Fix-deviations-in-auth-source-pass-behavior-WRT-netr.patch --]
[-- Type: text/x-patch, Size: 7898 bytes --]

From 1aa0f941d79b77de4a87a8043f13607c0719f5d0 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Sun, 11 Aug 2024 21:55:32 -0700
Subject: [PATCH 1/2] Fix deviations in auth-source-pass behavior WRT netrc

The option `auth-source-pass-extra-query-keywords' aims to make this
back end hew as close to the other built-in ones as possible, except
WRT features not yet implemented, such as arbitrary "attribute"
retrieval and new entry creation.  This change only concerns behavior
exhibited when the option is enabled.

* lisp/auth-source-pass.el (auth-source-pass--match-parts): Account
for the case in which a query lacks a reference parameter for a
`:port' or `:user' but still requires one or both via the `:require'
keyword.  Previously, such a query would fail even when an entry met
this requirement by simply specifying a field with any non-null value
corresponding to the required parameter.
(auth-source-pass--find-match-many): Account for the baseline case
where a matching entry lacks a secret and the user doesn't require
one.  Although this function doesn't currently return so-called
"attributes" from the contents of a matching decrypted file, were it
to eventually, this case would no longer be academic.
* test/lisp/auth-source-pass-tests.el
(auth-source-pass-extra-query-keywords--req-noparam-miss-netrc)
(auth-source-pass-extra-query-keywords--req-noparam-miss)
(auth-source-pass-extra-query-keywords--req-param-netrc)
(auth-source-pass-extra-query-keywords--req-param): New tests.
(auth-source-pass-extra-query-keywords--netrc-baseline): New test
asserting behavior of netrc backend when passed a lone `:host' as a
query parameter.
(auth-source-pass-extra-query-keywords--baseline): Reverse expected
outcome to match that of the netrc reference
implementation.  (bug#72441)
---
 lisp/auth-source-pass.el            | 19 +++++-----
 test/lisp/auth-source-pass-tests.el | 54 ++++++++++++++++++++++++++---
 2 files changed, 60 insertions(+), 13 deletions(-)

diff --git a/lisp/auth-source-pass.el b/lisp/auth-source-pass.el
index 03fd1f35811..dd93d414d5e 100644
--- a/lisp/auth-source-pass.el
+++ b/lisp/auth-source-pass.el
@@ -271,11 +271,12 @@ auth-source-pass--retrieve-parsed
                            n)))
              seen)))
 
-(defun auth-source-pass--match-parts (parts key value require)
-  (let ((mv (plist-get parts key)))
-    (if (memq key require)
-        (and value (equal mv value))
-      (or (not value) (not mv) (equal mv value)))))
+(defun auth-source-pass--match-parts (cache key reference require)
+  (let ((value (plist-get cache key)))
+    (cond ((memq key require)
+           (if reference (equal value reference) value))
+          ((and value reference) (equal value reference))
+          (t))))
 
 (defun auth-source-pass--find-match-many (hosts users ports require max)
   "Return plists for valid combinations of HOSTS, USERS, PORTS."
@@ -290,17 +291,17 @@ auth-source-pass--find-match-many
           (dolist (user (or users (list u)))
             (dolist (port (or ports (list p)))
               (dolist (e entries)
-                (when-let*
+                (when-let
                     ((m (or (gethash e seen) (auth-source-pass--retrieve-parsed
                                               seen e (integerp port))))
                      ((equal host (plist-get m :host)))
                      ((auth-source-pass--match-parts m :port port require))
                      ((auth-source-pass--match-parts m :user user require))
-                     (parsed (auth-source-pass-parse-entry e))
                      ;; For now, ignore body-content pairs, if any,
                      ;; from `auth-source-pass--parse-data'.
-                     (secret (or (auth-source-pass--get-attr 'secret parsed)
-                                 (not (memq :secret require)))))
+                     (secret (let ((parsed (auth-source-pass-parse-entry e)))
+                               (or (auth-source-pass--get-attr 'secret parsed)
+                                   (not (memq :secret require))))))
                   (push
                    `( :host ,host ; prefer user-provided :host over h
                       ,@(and-let* ((u (plist-get m :user))) (list :user u))
diff --git a/test/lisp/auth-source-pass-tests.el b/test/lisp/auth-source-pass-tests.el
index 6455c3393d5..c54936c3f92 100644
--- a/test/lisp/auth-source-pass-tests.el
+++ b/test/lisp/auth-source-pass-tests.el
@@ -548,6 +548,44 @@ auth-source-pass-extra-query-keywords--wild-port-hit
                      '((:host "x.com" :secret "a")
                        (:host "x.com" :port 42 :secret "b")))))))
 
+;; The query requires a user and doesn't specify a user to match against.
+;; The only entry matching the host lacks a user, so the search fails.
+
+(ert-deftest auth-source-pass-extra-query-keywords--req-noparam-miss-netrc ()
+  (ert-with-temp-file netrc-file
+    :text "machine foo password a\n"
+    (let ((auth-sources (list netrc-file))
+          (auth-source-do-cache nil))
+      (should-not (auth-source-search :host "foo" :require '(:user) :max 2)))))
+
+(ert-deftest auth-source-pass-extra-query-keywords--req-noparam-miss ()
+  (let ((auth-source-pass-extra-query-keywords t))
+    (auth-source-pass--with-store '(("foo" (secret . "a")))
+      (auth-source-pass-enable)
+      (should-not (auth-source-search :host "foo" :require '(:user) :max 2)))))
+
+;; The query requires a user but does not provide a reference value to
+;; match against.  An entry matching the host that specifies a user is
+;; selected because any user will do.
+(ert-deftest auth-source-pass-extra-query-keywords--req-param-netrc ()
+  (ert-with-temp-file netrc-file
+    :text "machine foo login bob password a\n"
+    (let* ((auth-sources (list netrc-file))
+           (auth-source-do-cache nil)
+           (results (auth-source-search :host "foo" :require '(:user))))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results '((:host "foo" :user "bob" :secret "a")))))))
+
+(ert-deftest auth-source-pass-extra-query-keywords--req-param ()
+  (let ((auth-source-pass-extra-query-keywords t))
+    (auth-source-pass--with-store '(("foo/bob" (secret . "a")))
+      (auth-source-pass-enable)
+      (let ((results (auth-source-search :host "foo" :require '(:user))))
+        (dolist (result results)
+          (setf (plist-get result :secret) (auth-info-password result)))
+        (should (equal results '((:host "foo" :user "bob" :secret "a"))))))))
+
 ;; No entry has the requested port, but :port is required, so search fails.
 
 (ert-deftest auth-source-pass-extra-query-keywords--wild-port-req-miss-netrc ()
@@ -629,14 +667,22 @@ auth-source-pass-extra-query-keywords--host
                      '((:host "Libera.Chat" :secret "b")))))))
 
 
-;; A retrieved store entry mustn't be nil regardless of whether its
-;; path contains port or user components.
+;; An effectively empty entry in the store returns nothing but the
+;; :host field matching the given host parameter.
+
+(ert-deftest auth-source-pass-extra-query-keywords--netrc-baseline ()
+  (ert-with-temp-file netrc-file
+    :text "machine foo\n"
+    (let* ((auth-sources (list netrc-file))
+           (auth-source-do-cache nil)
+           (results (auth-source-search :host "foo")))
+      (should (equal results '((:host "foo")))))))
 
 (ert-deftest auth-source-pass-extra-query-keywords--baseline ()
   (let ((auth-source-pass-extra-query-keywords t))
-    (auth-source-pass--with-store '(("x.com"))
+    (auth-source-pass--with-store '(("foo"))
       (auth-source-pass-enable)
-      (should-not (auth-source-search :host "x.com")))))
+      (should (equal (auth-source-search :host "foo") '((:host "foo")))))))
 
 ;; Output port type (int or string) matches that of input parameter.
 
-- 
2.46.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: 0002-POC-Match-attrs-with-auth-source-pass-extra-query-ke.patch --]
[-- Type: text/x-patch, Size: 13936 bytes --]

From d9bd10debf6c3930669aedb896026f9f19b54466 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <jp@neverwas.me>
Date: Wed, 7 Aug 2024 22:23:09 -0700
Subject: [PATCH 2/2] [POC] Match attrs with
 auth-source-pass-extra-query-keywords

* lisp/auth-source-pass.el (auth-source-pass--retrieve-parsed):
Remove unused function.
(auth-source-pass--cast-port): New function, a helper to match an
entry's port to the given query param's type.
(auth-source-pass--match-parts): Return non-nil when a key is required
but the value is null.  Not doing so produced behavior that deviated
from the reference netrc implementation and was thus a bug.
(auth-source-pass-check-attrs-with-extra-query-keywords): New
variable, a flag to opt out of arguably expensive attribute lookups.
(auth-source-pass--find-matched-entry): New function to isolate
processing logic for a single entry.
(auth-source-pass--find-match-many): Move single-entry processing
logic to separate helper, mainly for readability.
* test/lisp/auth-source-pass-tests.el
(auth-source-pass-extra-query-keywords--akib-attr)
(auth-source-pass-extra-query-keywords--akib-attr-req)
(auth-source-pass-extra-query-keywords--akib-attr-netrc)
(auth-source-pass-extra-query-keywords--akib-attr-extras)
(auth-source-pass-extra-query-keywords--netrc-baseline): New tests.
(auth-source-pass-extra-query-keywords--baseline): Reverse expected
outcome to match reference implementation.  That it didn't before was
a bug.  (Bug#72441)
---
 lisp/auth-source-pass.el            | 133 ++++++++++++++++++++--------
 test/lisp/auth-source-pass-tests.el |  84 ++++++++++++++++++
 2 files changed, 180 insertions(+), 37 deletions(-)

diff --git a/lisp/auth-source-pass.el b/lisp/auth-source-pass.el
index dd93d414d5e..a52dafc5ab2 100644
--- a/lisp/auth-source-pass.el
+++ b/lisp/auth-source-pass.el
@@ -256,20 +256,13 @@ auth-source-pass--find-match
                 hosts
               (list hosts))))
 
-(defun auth-source-pass--retrieve-parsed (seen path port-number-p)
-  (when (string-match auth-source-pass--match-regexp path)
-    (puthash path
-             `( :host ,(or (match-string 10 path) (match-string 11 path))
-                ,@(if-let* ((tr (match-string 21 path)))
-                      (list :user tr :suffix t)
-                    (list :user (match-string 20 path)))
-                :port ,(and-let* ((p (or (match-string 30 path)
-                                         (match-string 31 path)))
-                                  (n (string-to-number p)))
-                         (if (or (zerop n) (not port-number-p))
-                             (format "%s" p)
-                           n)))
-             seen)))
+(defun auth-source-pass--cast-port (val ref)
+  (cond ((integerp val) val)
+        ((and-let* (((integerp ref))
+                    (n (string-to-number val))
+                    ((not (zerop n))))
+           n))
+        (t (format "%s" val))))
 
 (defun auth-source-pass--match-parts (cache key reference require)
   (let ((value (plist-get cache key)))
@@ -278,11 +271,87 @@ auth-source-pass--match-parts
           ((and value reference) (equal value reference))
           (t))))
 
+(defvar auth-source-pass-check-attrs-with-extra-query-keywords t
+  "When non-nil, decrypt files to find attributes matching parameters.
+However, give precedence to fields encoded in file names.  Only applies
+when `auth-source-pass-extra-query-keywords' is non-nil.")
+
+;; This function tries to defer decryption as long as possible.  For
+;; that reason, an entry's file-path-derived :port or :user field
+;; always takes precedence over their counterparts from a decrypted
+;; file's attribute list.
+(defun auth-source-pass--find-matched-entry (host user port require seen entry)
+  "Match ENTRY against query params HOST USER PORT REQUIRE with cache SEEN."
+  (when (string-match auth-source-pass--match-regexp entry)
+    (let* ((cached (gethash entry seen))
+           (optp auth-source-pass-check-attrs-with-extra-query-keywords)
+           (suffixedp nil)
+           (h (or (and cached (plist-get cached :host))
+                  (match-string 10 entry)
+                  (match-string 11 entry)))
+           (attrs (and cached (plist-get :attrs cached)))
+           (getat (lambda (k)
+                    (save-match-data
+                      (unless attrs
+                        (setq attrs (auth-source-pass-parse-entry entry)))
+                      (auth-source-pass--get-attr k attrs))))
+           (u (cond (cached (plist-get cached :user))
+                    ((and-let* ((u (match-string 21 entry)))
+                       (setq suffixedp t)
+                       u))
+                    ((match-string 20 entry))
+                    ((and optp (or user (memq :user require)))
+                     (funcall getat "user"))))
+           (p (cond (cached (plist-get cached :port))
+                    ((match-string 30 entry))
+                    ((match-string 31 entry))
+                    ((and optp (or port (memq :port require)))
+                     (funcall getat "port"))))
+           ;;
+           s extras)
+      (when p
+        (setq p (auth-source-pass--cast-port p port)))
+      (unless cached
+        (setq cached `( :host ,h
+                        ,@(and u (list :user u))
+                        ,@(and p (list :port p))
+                        ,@(and suffixedp (list :suffix t))
+                        ,@(and attrs (list :attrs attrs))))
+        (puthash entry cached seen))
+      (when (and (equal host h)
+                 (auth-source-pass--match-parts cached :port port require)
+                 (auth-source-pass--match-parts cached :user user require)
+                 (setq s (or (funcall getat 'secret)
+                             (not (memq :secret require)))))
+        (let (tmp)
+          (while-let ((v (pop attrs))
+                      (k (pop v)))
+            (pcase k
+              ((or "user" "username")
+               (unless (or user u)
+                 (setq u v
+                       cached (plist-put cached :user u))))
+              ("port"
+               (unless (or port p)
+                 (setq p (auth-source-pass--cast-port v port)
+                       cached (plist-put cached :port p))))
+              ((pred stringp)
+               (push (intern (concat ":" k)) extras)
+               (push v extras)
+               (push (cons k v) tmp))))
+          (setq attrs (nreverse tmp)))
+        (puthash entry (plist-put cached :attrs attrs) seen)
+        `( :host ,host
+           ,@(and u (list :user u))
+           ,@(and p (list :port p))
+           ,@(and s (not (eq s t)) (list :secret s))
+           ,@(nreverse extras))))))
+
 (defun auth-source-pass--find-match-many (hosts users ports require max)
   "Return plists for valid combinations of HOSTS, USERS, PORTS."
   (let ((seen (make-hash-table :test #'equal))
         (entries (auth-source-pass-entries))
-        out suffixed suffixedp)
+        out suffixed)
     (catch 'done
       (dolist (host hosts out)
         (pcase-let ((`(,_ ,u ,p) (auth-source-pass--disambiguate host)))
@@ -290,28 +359,18 @@ auth-source-pass--find-match-many
             (setq p nil))
           (dolist (user (or users (list u)))
             (dolist (port (or ports (list p)))
-              (dolist (e entries)
-                (when-let
-                    ((m (or (gethash e seen) (auth-source-pass--retrieve-parsed
-                                              seen e (integerp port))))
-                     ((equal host (plist-get m :host)))
-                     ((auth-source-pass--match-parts m :port port require))
-                     ((auth-source-pass--match-parts m :user user require))
-                     ;; For now, ignore body-content pairs, if any,
-                     ;; from `auth-source-pass--parse-data'.
-                     (secret (let ((parsed (auth-source-pass-parse-entry e)))
-                               (or (auth-source-pass--get-attr 'secret parsed)
-                                   (not (memq :secret require))))))
-                  (push
-                   `( :host ,host ; prefer user-provided :host over h
-                      ,@(and-let* ((u (plist-get m :user))) (list :user u))
-                      ,@(and-let* ((p (plist-get m :port))) (list :port p))
-                      ,@(and secret (not (eq secret t)) (list :secret secret)))
-                   (if (setq suffixedp (plist-get m :suffix)) suffixed out))
-                  (unless suffixedp
-                    (when (or (zerop (cl-decf max))
-                              (null (setq entries (delete e entries))))
-                      (throw 'done out)))))
+              (dolist (entry entries)
+                (let* ((result (auth-source-pass--find-matched-entry
+                                host user port require seen entry))
+                       ;;
+                       suffixedp)
+                  (when result
+                    (setq suffixedp (plist-get (gethash entry seen) :suffix))
+                    (push result (if suffixedp suffixed out))
+                    (unless suffixedp
+                      (when (or (zerop (cl-decf max))
+                                (null (setq entries (delete entry entries))))
+                        (throw 'done out))))))
               (setq suffixed (nreverse suffixed))
               (while suffixed
                 (push (pop suffixed) out)
diff --git a/test/lisp/auth-source-pass-tests.el b/test/lisp/auth-source-pass-tests.el
index c54936c3f92..695635299f9 100644
--- a/test/lisp/auth-source-pass-tests.el
+++ b/test/lisp/auth-source-pass-tests.el
@@ -639,6 +639,90 @@ auth-source-pass-extra-query-keywords--akib
       (should (equal results
                      '((:host "disroot.org" :user "akib" :secret "b")))))))
 
+(ert-deftest auth-source-pass-extra-query-keywords--akib-attr ()
+  (auth-source-pass--with-store '(("x.com" (secret . "a"))
+                                  ("disroot.org" (secret . "b")
+                                   ("user" . "akib") ("port" . "42"))
+                                  ("z.com" (secret . "c")))
+    (auth-source-pass-enable)
+    (let* ((auth-source-pass-extra-query-keywords t)
+           results)
+
+      ;; Non-matching query param.
+      (setq results (auth-source-search :host "disroot.org" :user "?" :max 2))
+      (should-not results)
+
+      ;; No query params matching attrs.
+      (setq results (auth-source-search :host "disroot.org" :max 2))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results
+                     '(( :host "disroot.org" :user "akib"
+                         :port "42" :secret "b"))))
+
+      ;; Matching user query param.
+      (setq results (auth-source-search :host "disroot.org" :user "akib"))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results
+                     '(( :host "disroot.org" :user "akib"
+                         :port "42" :secret "b"))))
+
+      ;; Matching port typed query param.
+      (setq results (auth-source-search :host "disroot.org" :port 42))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results
+                     '(( :host "disroot.org" :user "akib"
+                         :port 42 :secret "b")))))))
+
+(ert-deftest auth-source-pass-extra-query-keywords--akib-attr-req ()
+  (auth-source-pass--with-store '(("x.com" (secret . "a"))
+                                  ("disroot.org" (secret . "b")
+                                   ("user" . "akib"))
+                                  ("z.com" (secret . "c")))
+    (auth-source-pass-enable)
+    (let* ((auth-source-pass-extra-query-keywords t)
+           (results (auth-source-search :host "disroot.org"
+                                        :require '(:user) :max 2)))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results
+                     '((:host "disroot.org" :user "akib" :secret "b")))))))
+
+(ert-deftest auth-source-pass-extra-query-keywords--akib-attr-extras-netrc ()
+  (ert-with-temp-file netrc-file
+    :text "\
+machine x.com password a
+machine disroot.org user akib port 42 password b foo 1 bar 2
+machine z.com password c
+"
+    (let* ((auth-sources (list netrc-file))
+           (auth-source-do-cache nil)
+           (results (auth-source-search :host "disroot.org" :max 2)))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results
+                     '((:host "disroot.org" :user "akib" :port "42"
+                              :secret "b" :foo "1" :bar "2")))))))
+
+(ert-deftest auth-source-pass-extra-query-keywords--akib-attr-extras ()
+  (auth-source-pass--with-store '(("x.com" (secret . "a"))
+                                  ("disroot.org" (secret . "b")
+                                   ("user" . "akib")
+                                   ("port" . "42")
+                                   ("foo" . "1")
+                                   ("bar" . "2"))
+                                  ("z.com" (secret . "c")))
+    (auth-source-pass-enable)
+    (let* ((auth-source-pass-extra-query-keywords t)
+           (results (auth-source-search :host "disroot.org" :max 2)))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results
+                     '((:host "disroot.org" :user "akib" :port "42"
+                              :secret "b" :foo "1" :bar "2")))))))
+
 ;; Searches for :host are case-sensitive, and a returned host isn't
 ;; normalized.
 
-- 
2.46.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* bug#72441: 31.0.50; Auth-source-pass doesn't match password attributes or hosts without user when extra-query-keywords is true
  2024-08-12 19:33       ` J.P.
@ 2024-09-06 23:16         ` J.P.
  0 siblings, 0 replies; 6+ messages in thread
From: J.P. @ 2024-09-06 23:16 UTC (permalink / raw)
  To: 72441; +Cc: Björn Bidar

"J.P." <jp@neverwas.me> writes:

> While exploring ways to tackle this feature, I stumbled on a couple
> minor bugs related to `auth-source-pass-extra-query-keywords'.
>
> Because there's no telling when we'll end up with something installable
> for this feature, I've gone ahead and isolated the fixes into a separate
> patch (0001 in the attached). It's probably safe enough for Emacs 30,
> but since the option was introduced back in 29, I'll just install it on
> master (unless I hear otherwise in the coming days). Thanks.

Installed on master as

  https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=80228d1f

As previously mentioned, the above commit only addresses an ancillary
issue, so the bug should remain open.





^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2024-09-06 23:16 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-08-03 11:12 bug#72441: 31.0.50; Auth-source-pass doesn't match password attributes or hosts without user when extra-query-keywords is true Björn Bidar via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-08-09 18:02 ` J.P.
2024-08-09 19:20   ` Björn Bidar via Bug reports for GNU Emacs, the Swiss army knife of text editors
     [not found]   ` <87ed6xy03r.fsf@>
2024-08-10 13:58     ` J.P.
2024-08-12 19:33       ` J.P.
2024-09-06 23:16         ` J.P.

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).