Support auth-source-pass in ERC

* doc/misc/erc.texi: Mention that the auth-source-pass backend is
supported.
* lisp/erc/erc-compat.el (erc-compat--29-auth-source-pass-search,
erc-compat--29-auth-source-pass--build-result-many,
erc-compat--29-auth-source-pass--retrieve-parsed,
erc-compat--29-auth-source-pass-backend-parse,
erc-compat--auth-source-backend-parser-functions): Adapt some yet
unreleased functions from auth-source-pass that mimic the netrc
backend, and add forward declarations to support them.

* lisp/erc/erc.el (erc--auth-source-search): Use own auth-source-pass
erc-compat backend.
* test/lisp/erc/erc-services-tests.el
(erc-join-tests--auth-source-pass-entries): Remove useless items.
(erc--auth-source-search--pass-standard,
erc--auth-source-search--pass-announced,
erc--auth-source-search--pass-overrides): Remove `ert-skip' guard.
(Bug#58985.)
This commit is contained in:
F. Jason Park 2022-04-24 06:20:09 -07:00
parent 2cf9e699ef
commit c5d91358b5
4 changed files with 122 additions and 5 deletions

View file

@ -861,7 +861,8 @@ The default value for all three options is the function
@code{erc-auth-source-search}. It tries to merge relevant contextual
parameters with those provided or discovered from the logical connection
or the underlying transport. Some auth-source back ends may not be
compatible; netrc, plstore, json, and secrets are currently supported.
compatible; netrc, plstore, json, secrets, and pass are currently
supported.
@end defopt
@subheading Full name

View file

@ -32,6 +32,8 @@
;;; Code:
(require 'compat nil 'noerror)
(eval-when-compile (require 'cl-lib))
;;;###autoload(autoload 'erc-define-minor-mode "erc-compat")
(define-obsolete-function-alias 'erc-define-minor-mode
@ -157,6 +159,121 @@ If START or END is negative, it counts from the end."
res))))))
;;;; Auth Source
(declare-function auth-source-pass--get-attr
"auth-source-pass" (key entry-data))
(declare-function auth-source-pass--disambiguate
"auth-source-pass" (host &optional user port))
(declare-function auth-source-backend-parse-parameters
"auth-source-pass" (entry backend))
(declare-function auth-source-backend "auth-source" (&rest slots))
(declare-function auth-source-pass-entries "auth-source-pass" nil)
(declare-function auth-source-pass-parse-entry "auth-source-pass" (entry))
(defvar auth-sources)
(defvar auth-source-backend-parser-functions)
;; This hard codes `auth-source-pass-port-separator' to ":"
(defun erc-compat--29-auth-source-pass--retrieve-parsed (seen e port-number-p)
(when (string-match (rx (or bot "/")
(or (: (? (group-n 20 (+ (not (in " /@")))) "@")
(group-n 10 (+ (not (in " /:@"))))
(? ":" (group-n 30 (+ (not (in " /:"))))))
(: (group-n 11 (+ (not (in " /:@"))))
(? ":" (group-n 31 (+ (not (in " /:")))))
(? "/" (group-n 21 (+ (not (in " /:")))))))
eot)
e)
(puthash e `( :host ,(or (match-string 10 e) (match-string 11 e))
,@(if-let* ((tr (match-string 21 e)))
(list :user tr :suffix t)
(list :user (match-string 20 e)))
:port ,(and-let* ((p (or (match-string 30 e)
(match-string 31 e)))
(n (string-to-number p)))
(if (or (zerop n) (not port-number-p))
(format "%s" p)
n)))
seen)))
;; This looks bad, but it just inlines `auth-source-pass--find-match-many'.
(defun erc-compat--29-auth-source-pass--build-result-many
(hosts users ports require max)
"Return a plist of HOSTS, PORTS, USERS, and secret."
(unless (listp hosts) (setq hosts (list hosts)))
(unless (listp users) (setq users (list users)))
(unless (listp ports) (setq ports (list ports)))
(unless max (setq max 1))
(let ((seen (make-hash-table :test #'equal))
(entries (auth-source-pass-entries))
(check (lambda (m k v)
(let ((mv (plist-get m k)))
(if (memq k require)
(and v (equal mv v))
(or (not v) (not mv) (equal mv v))))))
out suffixed suffixedp)
(catch 'done
(dolist (host hosts)
(pcase-let ((`(,_ ,u ,p) (auth-source-pass--disambiguate host)))
(unless (or (not (equal "443" p)) (string-prefix-p "https://" host))
(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)
(erc-compat--29-auth-source-pass--retrieve-parsed
seen e (integerp port))))
((equal host (plist-get m :host)))
((funcall check m :port port))
((funcall check m :user user))
(parsed (auth-source-pass-parse-entry e))
(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)))))
(setq suffixed (nreverse suffixed))
(while suffixed
(push (pop suffixed) out)
(when (zerop (cl-decf max))
(throw 'done out))))))))
(reverse out)))
(cl-defun erc-compat--29-auth-source-pass-search
(&rest spec &key host user port require max &allow-other-keys)
;; From `auth-source-pass-search'
(cl-assert (and host (not (eq host t)))
t "Invalid password-store search: %s %s")
(erc-compat--29-auth-source-pass--build-result-many
host user port require max))
(defun erc-compat--29-auth-source-pass-backend-parse (entry)
(when (eq entry 'password-store)
(auth-source-backend-parse-parameters
entry (auth-source-backend
:source "."
:type 'password-store
:search-function #'erc-compat--29-auth-source-pass-search))))
(defun erc-compat--auth-source-backend-parser-functions ()
(if (memq 'password-store auth-sources)
(progn
(require 'auth-source-pass)
`(,@(unless (bound-and-true-p auth-source-pass-extra-query-keywords)
'(erc-compat--29-auth-source-pass-backend-parse))
,@auth-source-backend-parser-functions))
auth-source-backend-parser-functions))
;;;; Misc 29.1
(defmacro erc-compat--with-memoization (table &rest forms)

View file

@ -3225,7 +3225,9 @@ host but different ports would result in the one with port 123 getting
the nod. Much the same would happen for entries sharing only a port:
the one with host foo would win."
(when-let*
((priority (map-keys defaults))
((auth-source-backend-parser-functions
(erc-compat--auth-source-backend-parser-functions))
(priority (map-keys defaults))
(test (lambda (a b)
(catch 'done
(dolist (key priority)

View file

@ -474,7 +474,6 @@
("GNU.chat:irc/#chan" (secret . "foo"))))
(ert-deftest erc--auth-source-search--pass-standard ()
(ert-skip "Pass backend not yet supported")
(let ((store erc-join-tests--auth-source-pass-entries)
(auth-sources '(password-store))
(auth-source-do-cache nil))
@ -487,7 +486,6 @@
(erc-services-tests--auth-source-standard #'erc-auth-source-search))))
(ert-deftest erc--auth-source-search--pass-announced ()
(ert-skip "Pass backend not yet supported")
(let ((store erc-join-tests--auth-source-pass-entries)
(auth-sources '(password-store))
(auth-source-do-cache nil))
@ -500,7 +498,6 @@
(erc-services-tests--auth-source-announced #'erc-auth-source-search))))
(ert-deftest erc--auth-source-search--pass-overrides ()
(ert-skip "Pass backend not yet supported")
(let ((store
`(,@erc-join-tests--auth-source-pass-entries
("GNU.chat:6697/#chan" (secret . "spam"))