From b09ee1406205e8b6298411b9a18c1cd26e201689 Mon Sep 17 00:00:00 2001 From: Iku Iwasa Date: Sun, 27 Jun 2021 17:36:00 +0200 Subject: [PATCH] lisp/auth-source-pass.el: Support multiple hosts in search spec * lisp/auth-source-pass.el (auth-source-pass-search): Accept a list of strings for argument HOST. (auth-source-pass--build-result): Rename argument HOST to HOSTS. Also return value "host" from entry if it exists. (auth-source-pass--find-match): Rename argument HOST to HOSTS. Iterate over each host in HOSTS. * test/lisp/auth-source-pass-tests.el: Add corresponding tests --- lisp/auth-source-pass.el | 40 ++++++++++++++++++----------- test/lisp/auth-source-pass-tests.el | 18 ++++++++++++- 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/lisp/auth-source-pass.el b/lisp/auth-source-pass.el index a7b959c47fd..6e33970486d 100644 --- a/lisp/auth-source-pass.el +++ b/lisp/auth-source-pass.el @@ -61,13 +61,12 @@ &key backend type host user port &allow-other-keys) "Given a property list SPEC, return search matches from the :backend. -See `auth-source-search' for details on SPEC." +See `auth-source-search' for details on SPEC. + +HOST can be a string or a list of strings, but USER and PORT are expected +to be a string only." (cl-assert (or (null type) (eq type (oref backend type))) t "Invalid password-store search: %s %s") - (when (consp host) - (warn "auth-source-pass ignores all but first host in spec.") - ;; Take the first non-nil item of the list of hosts - (setq host (seq-find #'identity host))) (cond ((eq host t) (warn "auth-source-pass does not handle host wildcards.") nil) @@ -78,12 +77,14 @@ See `auth-source-search' for details on SPEC." (when-let ((result (auth-source-pass--build-result host port user))) (list result))))) -(defun auth-source-pass--build-result (host port user) - "Build auth-source-pass entry matching HOST, PORT and USER." - (let ((entry-data (auth-source-pass--find-match host user port))) +(defun auth-source-pass--build-result (hosts port user) + "Build auth-source-pass entry matching HOSTS, PORT and USER. + +HOSTS can be a string or a list of strings." + (let ((entry-data (auth-source-pass--find-match hosts user port))) (when entry-data (let ((retval (list - :host host + :host (auth-source-pass--get-attr "host" entry-data) :port (or (auth-source-pass--get-attr "port" entry-data) port) :user (or (auth-source-pass--get-attr "user" entry-data) user) :secret (lambda () (auth-source-pass--get-attr 'secret entry-data))))) @@ -194,12 +195,21 @@ CONTENTS is the contents of a password-store formatted file." (lambda (file) (file-name-sans-extension (file-relative-name file store-dir))) (directory-files-recursively store-dir "\\.gpg\\'")))) -(defun auth-source-pass--find-match (host user port) - "Return password-store entry data matching HOST, USER and PORT. - -Disambiguate between user provided inside HOST (e.g., user@server.com) and -inside USER by giving priority to USER. Same for PORT." - (apply #'auth-source-pass--find-match-unambiguous (auth-source-pass--disambiguate host user port))) +(defun auth-source-pass--find-match (hosts user port) + "Return password-store entry data matching HOSTS, USER and PORT. + +Disambiguate between user provided inside HOSTS (e.g., user@server.com) and +inside USER by giving priority to USER. Same for PORT. +HOSTS can be a string or a list of strings." + (seq-some (lambda (host) + (let ((entry (apply #'auth-source-pass--find-match-unambiguous + (auth-source-pass--disambiguate host user port)))) + (if (or (null entry) (assoc "host" entry)) + entry + (cons (cons "host" host) entry)))) + (if (listp hosts) + hosts + (list hosts)))) (defun auth-source-pass--disambiguate (host &optional user port) "Return (HOST USER PORT) after disambiguation. diff --git a/test/lisp/auth-source-pass-tests.el b/test/lisp/auth-source-pass-tests.el index bfbef53db97..a2f84f20e8e 100644 --- a/test/lisp/auth-source-pass-tests.el +++ b/test/lisp/auth-source-pass-tests.el @@ -424,21 +424,37 @@ HOSTNAME, USER and PORT are passed unchanged to (auth-source-pass--with-store-find-foo '(("foo" ("secret" . "foo password"))) (let ((result (auth-source-pass--build-result "foo" 512 "user"))) + (should (equal (plist-get result :host) "foo")) (should (equal (plist-get result :port) 512)) (should (equal (plist-get result :user) "user"))))) (ert-deftest auth-source-pass-build-result-return-entry-values () (auth-source-pass--with-store-find-foo '(("foo" ("port" . 512) ("user" . "anuser"))) (let ((result (auth-source-pass--build-result "foo" nil nil))) + (should (equal (plist-get result :host) "foo")) (should (equal (plist-get result :port) 512)) (should (equal (plist-get result :user) "anuser"))))) (ert-deftest auth-source-pass-build-result-entry-takes-precedence () - (auth-source-pass--with-store-find-foo '(("foo" ("port" . 512) ("user" . "anuser"))) + (auth-source-pass--with-store-find-foo '(("foo" ("host" . "bar") ("port" . 512) ("user" . "anuser"))) (let ((result (auth-source-pass--build-result "foo" 1024 "anotheruser"))) + (should (equal (plist-get result :host) "bar")) (should (equal (plist-get result :port) 512)) (should (equal (plist-get result :user) "anuser"))))) +(ert-deftest auth-source-pass-build-result-with-multiple-hosts () + (auth-source-pass--with-store-find-foo + '(("foo" ("secret" . "foo password"))) + (let ((result (auth-source-pass--build-result '("bar" "foo") 512 "user"))) + (should (equal (plist-get result :host) "foo")) + (should (equal (plist-get result :port) 512)) + (should (equal (plist-get result :user) "user"))))) + +(ert-deftest auth-source-pass-build-result-with-multiple-hosts-no-match () + (auth-source-pass--with-store-find-foo + '(("foo" ("secret" . "foo password"))) + (should-not (auth-source-pass--build-result '("bar" "baz") 512 "user")))) + (ert-deftest auth-source-pass-can-start-from-auth-source-search () (auth-source-pass--with-store '(("gitlab.com" ("user" . "someone"))) (auth-source-pass-enable) -- 2.39.2