diff options
| author | F. Jason Park | 2022-11-01 22:46:24 -0700 |
|---|---|---|
| committer | F. Jason Park | 2022-11-16 21:34:36 -0800 |
| commit | 2cf9e699ef0fc43a4eadaf00a1ed2f876765c64d (patch) | |
| tree | c01e9bdcd25372207f94650315d1596ae445454d /lisp/auth-source-pass.el | |
| parent | 0147e1ed831151dddac65727886d5a70bbab9f02 (diff) | |
| download | emacs-2cf9e699ef0fc43a4eadaf00a1ed2f876765c64d.tar.gz emacs-2cf9e699ef0fc43a4eadaf00a1ed2f876765c64d.zip | |
Make auth-source-pass behave more like other backends
* lisp/auth-source-pass.el (auth-source-pass-extra-query-keywords): Add
new option to bring search behavior more in line with other backends.
(auth-source-pass-search): Add new keyword params `max' and `require'
and consider new option `auth-source-pass-extra-query-keywords' for
dispatch.
(auth-source-pass--match-regexp, auth-source-pass--retrieve-parsed,
auth-source-pass--match-parts): Add supporting variable and helpers.
(auth-source-pass--build-result-many,
auth-source-pass--find-match-many): Add "-many" variants for existing
workhorse functions.
* test/lisp/auth-source-pass-tests.el: Require `ert-x'.
(auth-source-pass-can-start-from-auth-source-search): Ensure
`auth-source-pass-extra-query-keywords' is enabled around test body.
(auth-source-pass-extra-query-keywords--wild-port-miss-netrc,
auth-source-pass-extra-query-keywords--wild-port-miss,
auth-source-pass-extra-query-keywords--wild-port-hit-netrc,
auth-source-pass-extra-query-keywords--wild-port-hit,
auth-source-pass-extra-query-keywords--wild-port-req-miss-netrc,
auth-source-pass-extra-query-keywords--wild-port-req-miss,
auth-source-pass-extra-query-keywords--netrc-akib,
auth-source-pass-extra-query-keywords--akib,
auth-source-pass-extra-query-keywords--netrc-host,
auth-source-pass-extra-query-keywords--host,
auth-source-pass-extra-query-keywords--baseline,
auth-source-pass-extra-query-keywords--port-type,
auth-source-pass-extra-query-keywords--hosts-first,
auth-source-pass-extra-query-keywords--ambiguous-user-host,
auth-source-pass-extra-query-keywords--suffixed-user,
auth-source-pass-extra-query-keywords--user-priorities): Add
juxtaposed netrc and extra-query-keywords pairs to demo optional
extra-compliant behavior.
* doc/misc/auth.texi: Add option
`auth-source-pass-extra-query-keywords' to auth-source-pass section.
* etc/NEWS: Mention `auth-source-pass-extra-query-keywords' in Emacs
29.1 package changes section. (Bug#58985.)
Special thanks to Akib Azmain Turja <akib@disroot.org> for helping
improve this patch.
Diffstat (limited to 'lisp/auth-source-pass.el')
| -rw-r--r-- | lisp/auth-source-pass.el | 112 |
1 files changed, 111 insertions, 1 deletions
diff --git a/lisp/auth-source-pass.el b/lisp/auth-source-pass.el index 0955e2ed07e..dc274843e10 100644 --- a/lisp/auth-source-pass.el +++ b/lisp/auth-source-pass.el | |||
| @@ -55,13 +55,27 @@ | |||
| 55 | :type 'string | 55 | :type 'string |
| 56 | :version "27.1") | 56 | :version "27.1") |
| 57 | 57 | ||
| 58 | (defcustom auth-source-pass-extra-query-keywords t | ||
| 59 | "Whether to consider additional keywords when performing a query. | ||
| 60 | Specifically, when the value is t, recognize the `:max' and | ||
| 61 | `:require' keywords and accept lists of query parameters for | ||
| 62 | certain keywords, such as `:host' and `:user'. Also, wrap all | ||
| 63 | returned secrets in a function and forgo any further results | ||
| 64 | filtering unless given an applicable `:require' argument. When | ||
| 65 | this option is nil, do none of that, and enact the narrowing | ||
| 66 | behavior described toward the bottom of the Info node `(auth) The | ||
| 67 | Unix password store'." | ||
| 68 | :type 'boolean | ||
| 69 | :version "29.1") | ||
| 70 | |||
| 58 | (cl-defun auth-source-pass-search (&rest spec | 71 | (cl-defun auth-source-pass-search (&rest spec |
| 59 | &key backend type host user port | 72 | &key backend type host user port |
| 73 | require max | ||
| 60 | &allow-other-keys) | 74 | &allow-other-keys) |
| 61 | "Given some search query, return matching credentials. | 75 | "Given some search query, return matching credentials. |
| 62 | 76 | ||
| 63 | See `auth-source-search' for details on the parameters SPEC, BACKEND, TYPE, | 77 | See `auth-source-search' for details on the parameters SPEC, BACKEND, TYPE, |
| 64 | HOST, USER and PORT." | 78 | HOST, USER, PORT, REQUIRE, and MAX." |
| 65 | (cl-assert (or (null type) (eq type (oref backend type))) | 79 | (cl-assert (or (null type) (eq type (oref backend type))) |
| 66 | t "Invalid password-store search: %s %s") | 80 | t "Invalid password-store search: %s %s") |
| 67 | (cond ((eq host t) | 81 | (cond ((eq host t) |
| @@ -70,6 +84,8 @@ HOST, USER and PORT." | |||
| 70 | ((null host) | 84 | ((null host) |
| 71 | ;; Do not build a result, as none will match when HOST is nil | 85 | ;; Do not build a result, as none will match when HOST is nil |
| 72 | nil) | 86 | nil) |
| 87 | (auth-source-pass-extra-query-keywords | ||
| 88 | (auth-source-pass--build-result-many host port user require max)) | ||
| 73 | (t | 89 | (t |
| 74 | (when-let ((result (auth-source-pass--build-result host port user))) | 90 | (when-let ((result (auth-source-pass--build-result host port user))) |
| 75 | (list result))))) | 91 | (list result))))) |
| @@ -89,6 +105,39 @@ HOSTS can be a string or a list of strings." | |||
| 89 | (seq-subseq retval 0 -2)) ;; remove password | 105 | (seq-subseq retval 0 -2)) ;; remove password |
| 90 | retval)))) | 106 | retval)))) |
| 91 | 107 | ||
| 108 | (defvar auth-source-pass--match-regexp nil) | ||
| 109 | |||
| 110 | (defun auth-source-pass--match-regexp (s) | ||
| 111 | (rx-to-string ; autoloaded | ||
| 112 | `(: (or bot "/") | ||
| 113 | (or (: (? (group-n 20 (+ (not (in ?\ ?/ ?@ ,s)))) "@") | ||
| 114 | (group-n 10 (+ (not (in ?\ ?/ ?@ ,s)))) | ||
| 115 | (? ,s (group-n 30 (+ (not (in ?\ ?/ ,s)))))) | ||
| 116 | (: (group-n 11 (+ (not (in ?\ ?/ ?@ ,s)))) | ||
| 117 | (? ,s (group-n 31 (+ (not (in ?\ ?/ ,s))))) | ||
| 118 | (? "/" (group-n 21 (+ (not (in ?\ ?/ ,s))))))) | ||
| 119 | eot) | ||
| 120 | 'no-group)) | ||
| 121 | |||
| 122 | (defun auth-source-pass--build-result-many (hosts ports users require max) | ||
| 123 | "Return multiple `auth-source-pass--build-result' values." | ||
| 124 | (unless (listp hosts) (setq hosts (list hosts))) | ||
| 125 | (unless (listp users) (setq users (list users))) | ||
| 126 | (unless (listp ports) (setq ports (list ports))) | ||
| 127 | (let* ((auth-source-pass--match-regexp (auth-source-pass--match-regexp | ||
| 128 | auth-source-pass-port-separator)) | ||
| 129 | (rv (auth-source-pass--find-match-many hosts users ports | ||
| 130 | require (or max 1)))) | ||
| 131 | (when auth-source-debug | ||
| 132 | (auth-source-pass--do-debug "final result: %S" rv)) | ||
| 133 | (let (out) | ||
| 134 | (dolist (e rv out) | ||
| 135 | (when-let* ((s (plist-get e :secret)) ; not captured by closure in 29.1 | ||
| 136 | (v (auth-source--obfuscate s))) | ||
| 137 | (setf (plist-get e :secret) | ||
| 138 | (lambda () (auth-source--deobfuscate v)))) | ||
| 139 | (push e out))))) | ||
| 140 | |||
| 92 | ;;;###autoload | 141 | ;;;###autoload |
| 93 | (defun auth-source-pass-enable () | 142 | (defun auth-source-pass-enable () |
| 94 | "Enable auth-source-password-store." | 143 | "Enable auth-source-password-store." |
| @@ -206,6 +255,67 @@ HOSTS can be a string or a list of strings." | |||
| 206 | hosts | 255 | hosts |
| 207 | (list hosts)))) | 256 | (list hosts)))) |
| 208 | 257 | ||
| 258 | (defun auth-source-pass--retrieve-parsed (seen path port-number-p) | ||
| 259 | (when (string-match auth-source-pass--match-regexp path) | ||
| 260 | (puthash path | ||
| 261 | `( :host ,(or (match-string 10 path) (match-string 11 path)) | ||
| 262 | ,@(if-let* ((tr (match-string 21 path))) | ||
| 263 | (list :user tr :suffix t) | ||
| 264 | (list :user (match-string 20 path))) | ||
| 265 | :port ,(and-let* ((p (or (match-string 30 path) | ||
| 266 | (match-string 31 path))) | ||
| 267 | (n (string-to-number p))) | ||
| 268 | (if (or (zerop n) (not port-number-p)) | ||
| 269 | (format "%s" p) | ||
| 270 | n))) | ||
| 271 | seen))) | ||
| 272 | |||
| 273 | (defun auth-source-pass--match-parts (parts key value require) | ||
| 274 | (let ((mv (plist-get parts key))) | ||
| 275 | (if (memq key require) | ||
| 276 | (and value (equal mv value)) | ||
| 277 | (or (not value) (not mv) (equal mv value))))) | ||
| 278 | |||
| 279 | (defun auth-source-pass--find-match-many (hosts users ports require max) | ||
| 280 | "Return plists for valid combinations of HOSTS, USERS, PORTS." | ||
| 281 | (let ((seen (make-hash-table :test #'equal)) | ||
| 282 | (entries (auth-source-pass-entries)) | ||
| 283 | out suffixed suffixedp) | ||
| 284 | (catch 'done | ||
| 285 | (dolist (host hosts out) | ||
| 286 | (pcase-let ((`(,_ ,u ,p) (auth-source-pass--disambiguate host))) | ||
| 287 | (unless (or (not (equal "443" p)) (string-prefix-p "https://" host)) | ||
| 288 | (setq p nil)) | ||
| 289 | (dolist (user (or users (list u))) | ||
| 290 | (dolist (port (or ports (list p))) | ||
| 291 | (dolist (e entries) | ||
| 292 | (when-let* | ||
| 293 | ((m (or (gethash e seen) (auth-source-pass--retrieve-parsed | ||
| 294 | seen e (integerp port)))) | ||
| 295 | ((equal host (plist-get m :host))) | ||
| 296 | ((auth-source-pass--match-parts m :port port require)) | ||
| 297 | ((auth-source-pass--match-parts m :user user require)) | ||
| 298 | (parsed (auth-source-pass-parse-entry e)) | ||
| 299 | ;; For now, ignore body-content pairs, if any, | ||
| 300 | ;; from `auth-source-pass--parse-data'. | ||
| 301 | (secret (or (auth-source-pass--get-attr 'secret parsed) | ||
| 302 | (not (memq :secret require))))) | ||
| 303 | (push | ||
| 304 | `( :host ,host ; prefer user-provided :host over h | ||
| 305 | ,@(and-let* ((u (plist-get m :user))) (list :user u)) | ||
| 306 | ,@(and-let* ((p (plist-get m :port))) (list :port p)) | ||
| 307 | ,@(and secret (not (eq secret t)) (list :secret secret))) | ||
| 308 | (if (setq suffixedp (plist-get m :suffix)) suffixed out)) | ||
| 309 | (unless suffixedp | ||
| 310 | (when (or (zerop (cl-decf max)) | ||
| 311 | (null (setq entries (delete e entries)))) | ||
| 312 | (throw 'done out))))) | ||
| 313 | (setq suffixed (nreverse suffixed)) | ||
| 314 | (while suffixed | ||
| 315 | (push (pop suffixed) out) | ||
| 316 | (when (zerop (cl-decf max)) | ||
| 317 | (throw 'done out)))))))))) | ||
| 318 | |||
| 209 | (defun auth-source-pass--disambiguate (host &optional user port) | 319 | (defun auth-source-pass--disambiguate (host &optional user port) |
| 210 | "Return (HOST USER PORT) after disambiguation. | 320 | "Return (HOST USER PORT) after disambiguation. |
| 211 | Disambiguate between having user provided inside HOST (e.g., | 321 | Disambiguate between having user provided inside HOST (e.g., |