emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] master 0a580c1 08/13: Minimize entry parsing in auth-sourc


From: Damien Cassou
Subject: [Emacs-diffs] master 0a580c1 08/13: Minimize entry parsing in auth-source-pass
Date: Mon, 24 Jun 2019 03:22:49 -0400 (EDT)

branch: master
commit 0a580c187da9840298921a507bfe3dae3d1a00a7
Author: Keith Amidon <address@hidden>
Commit: Damien Cassou <address@hidden>

    Minimize entry parsing in auth-source-pass
    
    Prior to this commit, while searching for the most applicable entry
    password-store entries were decrypted and parsed to ensure they were
    valid.  The entries were parsed in the order they were found on the
    filesystem and all applicable entries would be decrypted and parsed,
    which varied based on the contents of the password-store and the entry
    to be found.
    
    This is fine when the GPG key is cached and each entry can be
    decrypted without user interaction.  However, for security some people
    have their GPG on a hardware token like a Yubikey setup so that they
    have to touch a sensor on the toke for every cryptographic operation,
    in which case it becomes inconvenient as each attempt to find an entry
    requires a variable number of touches of the hardware token.
    
    The implementation already assumes that names which contain more of
    the information in the search key should be preferred so there is an
    ordering of preference of applicable entries.  If the decrypt and
    parsing is removed from the initial identification of applicable
    entries in the store then in most cases a single decrypt and parse of
    the most preferred entry will suffice, improving the experience for
    hardware token users that require interaction with the token.
    
    This commit implements that strategy.  It is in spirit a refactor of
    the existing code.
    
    * lisp/auth-source-pass.el (auth-source-pass--matching-entries): New
    function, generate an ordered list of regular expression matchers for
    all possible names that could be in the password-store for the entry
    to be found and then makes a pass over the password-store entry names
    accumulating the matching entries in a list after the regexp that
    matched.  This implementation ensures the password-store entry list
    still only has to be scanned once.
    (auth-source-pass--find-match-unambiguous): Use it to obtain candidate
    entries and then parse them one by one until an entry containing the
    desired information is located.  When complete, return the parsed data
    of the entry instead of the entry name so that the information can be
    used directly to construct the auth-source response.
    (auth-source-pass--build-result): Update accordingly.
    (auth-source-pass--find-match): Update docstring accordingly.
    (auth-source-pass--select-one-entry)
    (auth-source-pass--entry-valid-p)
    (auth-source-pass--find-all-by-entry-name)
    (auth-source-pass--find-one-by-entry-name): Remove.
    (auth-source-pass--select-from-entries)
    (auth-source-pass--accumulate-matches)
    (auth-source-pass--entry-reducer)
    (auth-source-pass--generate-entry-suffixes)
    (auth-source-pass--domains)
    (auth-source-pass--name-port-user-suffixes): New functions.
    
    * test/lisp/auth-source-pass-tests.el: One test case was added to the
    test suite to verify that only the minimal number of entries are
    parsed in common cases.  The
    auth-source-pass-only-return-entries-that-can-be-open test case had to
    be re-implemented because the function it was used eliminated as the
    functionality is provided elsewhere.  All the other fairly substantial
    changes to the test suite are the result of mechanical changes that
    were required to adapt to auth-source-pass--find-match returning the
    data from a parsed password-store entry instead of the entry name.
---
 lisp/auth-source-pass.el            | 209 ++++++++++++++++------------
 test/lisp/auth-source-pass-tests.el | 264 ++++++++++++++++++++++--------------
 2 files changed, 281 insertions(+), 192 deletions(-)

diff --git a/lisp/auth-source-pass.el b/lisp/auth-source-pass.el
index a0b0841..bcb215a 100644
--- a/lisp/auth-source-pass.el
+++ b/lisp/auth-source-pass.el
@@ -77,14 +77,13 @@ See `auth-source-search' for details on SPEC."
 
 (defun auth-source-pass--build-result (host port user)
   "Build auth-source-pass entry matching HOST, PORT and USER."
-  (let ((entry (auth-source-pass--find-match host user port)))
-    (when entry
-      (let* ((entry-data (auth-source-pass-parse-entry entry))
-             (retval (list
-                      :host host
-                      :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)))))
+  (let ((entry-data (auth-source-pass--find-match host user port)))
+    (when entry-data
+      (let ((retval (list
+                     :host host
+                     :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)))))
         (auth-source-pass--do-debug "return %s as final result (plus hidden 
password)"
                                     (seq-subseq retval 0 -2)) ;; remove 
password
         retval))))
@@ -183,33 +182,6 @@ CONTENTS is the contents of a password-store formatted 
file."
          (cons (concat "auth-source-pass: " (car msg))
                (cdr msg))))
 
-(defun auth-source-pass--select-one-entry (entries user)
-  "Select one entry from ENTRIES by searching for a field matching USER."
-  (let ((number (length entries))
-        (entry-with-user
-         (and user
-              (seq-find (lambda (entry)
-                          (string-equal (auth-source-pass-get "user" entry) 
user))
-                        entries))))
-    (auth-source-pass--do-debug "found %s matches: %s" number
-                                (mapconcat #'identity entries ", "))
-    (if entry-with-user
-        (progn
-          (auth-source-pass--do-debug "return %s as it contains matching user 
field"
-                                      entry-with-user)
-          entry-with-user)
-      (auth-source-pass--do-debug "return %s as it is the first one" (car 
entries))
-      (car entries))))
-
-(defun auth-source-pass--entry-valid-p (entry)
-  "Return t iff ENTRY can be opened.
-Also displays a warning if not.  This function is slow, don't call it too
-often."
-  (if (auth-source-pass-parse-entry entry)
-      t
-    (auth-source-pass--do-debug "entry '%s' is not valid" entry)
-    nil))
-
 ;; TODO: add tests for that when `assess-with-filesystem' is included
 ;; in Emacs
 (defun auth-source-pass-entries ()
@@ -219,37 +191,8 @@ often."
      (lambda (file) (file-name-sans-extension (file-relative-name file 
store-dir)))
      (directory-files-recursively store-dir "\\.gpg$"))))
 
-(defun auth-source-pass--find-all-by-entry-name (entryname user)
-  "Search the store for all entries either matching ENTRYNAME/USER or 
ENTRYNAME.
-Only return valid entries as of `auth-source-pass--entry-valid-p'."
-  (seq-filter (lambda (entry)
-                (and
-                 (or
-                  (let ((components-host-user
-                         (member entryname (split-string entry "/"))))
-                    (and (= (length components-host-user) 2)
-                         (string-equal user (cadr components-host-user))))
-                  (string-equal entryname (file-name-nondirectory entry)))
-                 (auth-source-pass--entry-valid-p entry)))
-              (auth-source-pass-entries)))
-
-(defun auth-source-pass--find-one-by-entry-name (entryname user)
-  "Search the store for an entry matching ENTRYNAME.
-If USER is non nil, give precedence to entries containing a user field
-matching USER."
-  (auth-source-pass--do-debug "searching for '%s' in entry names (user: %s)"
-                              entryname
-                              user)
-  (let ((matching-entries (auth-source-pass--find-all-by-entry-name entryname 
user)))
-    (pcase (length matching-entries)
-      (0 (auth-source-pass--do-debug "no match found")
-         nil)
-      (1 (auth-source-pass--do-debug "found 1 match: %s" (car 
matching-entries))
-         (car matching-entries))
-      (_ (auth-source-pass--select-one-entry matching-entries user)))))
-
 (defun auth-source-pass--find-match (host user port)
-  "Return a password-store entry name matching HOST, USER and PORT.
+  "Return password-store entry data matching HOST, USER and PORT.
 
 Disambiguate between user provided inside HOST (e.g., address@hidden) and
 inside USER by giving priority to USER.  Same for PORT."
@@ -263,33 +206,123 @@ inside USER by giving priority to USER.  Same for PORT."
      (or port (number-to-string (url-port url))))))
 
 (defun auth-source-pass--find-match-unambiguous (hostname user port)
-  "Return a password-store entry name matching HOSTNAME, USER and PORT.
+  "Return password-store entry data matching HOSTNAME, USER and PORT.
 If many matches are found, return the first one.  If no match is found,
 return nil.
 
 HOSTNAME should not contain any username or port number."
-  (or
-   (and user port (auth-source-pass--find-one-by-entry-name
-                   (format "%s@%s%s%s" user hostname 
auth-source-pass-port-separator port)
-                   user))
-   (and user port (auth-source-pass--find-one-by-entry-name
-                   (format "%s%s%s" hostname auth-source-pass-port-separator 
port)
-                   user))
-   (and user (auth-source-pass--find-one-by-entry-name
-              (format "%s@%s" user hostname)
-              user))
-   (and port (auth-source-pass--find-one-by-entry-name
-              (format "%s%s%s" hostname auth-source-pass-port-separator port)
-              nil))
-   (auth-source-pass--find-one-by-entry-name hostname user)
-   ;; if that didn't work, remove subdomain: foo.bar.com -> bar.com
-   (let ((components (split-string hostname "\\.")))
-     (when (= (length components) 3)
-       ;; start from scratch
-       (auth-source-pass--find-match-unambiguous
-        (mapconcat 'identity (cdr components) ".")
-        user
-        port)))))
+  (cl-reduce
+   (lambda (result entries)
+     (or result
+         (pcase (length entries)
+           (0 nil)
+           (1 (auth-source-pass-parse-entry (car entries)))
+           (_ (auth-source-pass--select-from-entries entries user)))))
+   (auth-source-pass--matching-entries hostname user port)
+   :initial-value nil))
+
+(defun auth-source-pass--select-from-entries (entries user)
+  "Return best matching password-store entry data from ENTRIES.
+
+If USER is non nil, give precedence to entries containing a user field
+matching USER."
+  (cl-reduce
+   (lambda (result entry)
+     (let ((entry-data (auth-source-pass-parse-entry entry)))
+       (cond ((equal (auth-source-pass--get-attr "user" result) user)
+              result)
+             ((equal (auth-source-pass--get-attr "user" entry-data) user)
+              entry-data)
+             (t
+              result))))
+   entries
+   :initial-value (auth-source-pass-parse-entry (car entries))))
+
+(defun auth-source-pass--matching-entries (hostname user port)
+  "Return all matching password-store entries for HOSTNAME, USER, & PORT.
+
+The result is a list of lists of password-store entries, where
+each sublist contains entries that actually exist in the
+password-store matching one of the entry name formats that
+auth-source-pass expects, most specific to least specific."
+  (let* ((entries-lists (mapcar
+                         #'cdr
+                         (auth-source-pass--accumulate-matches hostname user 
port)))
+         (entries (apply #'cl-concatenate (cons 'list entries-lists))))
+    (if entries
+        (auth-source-pass--do-debug (format "found: %S" entries))
+      (auth-source-pass--do-debug "no matches found"))
+    entries-lists))
+
+(defun auth-source-pass--accumulate-matches (hostname user port)
+  "Accumulate matching password-store entries into sublists.
+
+Entries matching supported formats that combine HOSTNAME, USER, &
+PORT are accumulated into sublists where the car of each sublist
+is a regular expression for matching paths in the password-store
+and the remainder is the list of matching entries."
+  (let ((suffix-match-lists
+         (mapcar (lambda (suffix) (list (format "\\(^\\|/\\)%s$" suffix)))
+                 (auth-source-pass--generate-entry-suffixes hostname user 
port))))
+    (cl-reduce #'auth-source-pass--entry-reducer
+               (auth-source-pass-entries)
+               :initial-value suffix-match-lists)))
+
+(defun auth-source-pass--entry-reducer (match-lists entry)
+  "Match MATCH-LISTS sublists against ENTRY.
+
+The result is a copy of match-lists with the entry added to the
+end of any sublists for which the regular expression at the head
+of the list matches the entry name."
+  (mapcar (lambda (match-list)
+            (if (string-match (car match-list) entry)
+                (append match-list (list entry))
+              match-list))
+          match-lists))
+
+(defun auth-source-pass--generate-entry-suffixes (hostname user port)
+  "Return a list of possible entry path suffixes in the password-store.
+
+Based on the supported pathname patterns for HOSTNAME, USER, &
+PORT, return a list of possible suffixes for matching entries in
+the password-store."
+  (let ((domains (auth-source-pass--domains (split-string hostname "\\."))))
+    (seq-mapcat (lambda (n)
+                  (auth-source-pass--name-port-user-suffixes n user port))
+                domains)))
+
+(defun auth-source-pass--domains (name-components)
+  "Return a list of possible domain names matching the hostname.
+
+This function takes a list of NAME-COMPONENTS, the strings
+separated by periods in the hostname, and returns a list of full
+domain names containing the trailing sequences of those
+components, from longest to shortest."
+  (cl-maplist (lambda (components) (mapconcat #'identity components "."))
+              name-components))
+
+(defun auth-source-pass--name-port-user-suffixes (name user port)
+  "Return a list of possible path suffixes for NAME, USER, & PORT.
+
+The resulting list is ordered from most specifc to least
+specific, with paths matching all of NAME, USER, & PORT first,
+then NAME & USER, then NAME & PORT, then just NAME."
+  (seq-mapcat
+   #'identity
+   (list
+    (when (and user port)
+      (list
+       (format "%s@%s%s%s" user name auth-source-pass-port-separator port)
+       (format "%s%s%s/%s" name auth-source-pass-port-separator port user)))
+    (when user
+      (list
+       (format "%s@%s" user name)
+       (format "%s/%s" name user)))
+    (when port
+      (list
+       (format "%s%s%s" name auth-source-pass-port-separator port)))
+    (list
+     (format "%s" name)))))
 
 (provide 'auth-source-pass)
 ;;; auth-source-pass.el ends here
diff --git a/test/lisp/auth-source-pass-tests.el 
b/test/lisp/auth-source-pass-tests.el
index 1539d96..2c28f79 100644
--- a/test/lisp/auth-source-pass-tests.el
+++ b/test/lisp/auth-source-pass-tests.el
@@ -63,14 +63,19 @@
 This function is intended to be set to `auth-source-debug`."
   (add-to-list 'auth-source-pass--debug-log (apply #'format msg) t))
 
+(defvar auth-source-pass--parse-log nil)
+
 (defmacro auth-source-pass--with-store (store &rest body)
   "Use STORE as password-store while executing BODY."
   (declare (indent 1))
-  `(cl-letf (((symbol-function 'auth-source-pass-parse-entry) (lambda (entry) 
(cdr (cl-find entry ,store :key #'car :test #'string=))) )
-             ((symbol-function 'auth-source-pass-entries) (lambda () (mapcar 
#'car ,store)))
-             ((symbol-function 'auth-source-pass--entry-valid-p) (lambda 
(_entry) t)))
+  `(cl-letf (((symbol-function 'auth-source-pass-parse-entry)
+              (lambda (entry)
+                (add-to-list 'auth-source-pass--parse-log entry)
+                (cdr (cl-find entry ,store :key #'car :test #'string=))))
+             ((symbol-function 'auth-source-pass-entries) (lambda () (mapcar 
#'car ,store))))
      (let ((auth-source-debug #'auth-source-pass--debug)
-           (auth-source-pass--debug-log nil))
+           (auth-source-pass--debug-log nil)
+           (auth-source-pass--parse-log nil))
        ,@body)))
 
 (ert-deftest auth-source-pass-any-host ()
@@ -88,125 +93,184 @@ This function is intended to be set to 
`auth-source-debug`."
                                   ("bar"))
     (should-not (auth-source-pass-search :host "baz"))))
 
+(ert-deftest auth-source-pass-find-match-minimal-parsing ()
+  (let ((store-contents
+         '(("baz" ("secret" . "baz password"))
+           ("baz:123" ("secret" . "baz:123 password"))
+           ("baz/foo" ("secret" . "baz/foo password"))
+           ("foo@baz" ("secret" . "foo@baz password"))
+           ("baz:123/foo" ("secret" . "baz:123/foo password"))
+           ("foo@baz:123" ("secret" . "foo@baz:123 password"))
+           ("bar.baz" ("secret" . "bar.baz password"))
+           ("bar.baz:123" ("secret" . "bar.baz:123 password"))
+           ("bar.baz/foo" ("secret" . "bar.baz/foo password"))
+           ("address@hidden" ("secret" . "address@hidden password"))
+           ("bar.baz:123/foo" ("secret" . "bar.baz:123/foo password"))
+           ("address@hidden:123" ("secret" . "address@hidden:123 password")))))
+    (auth-source-pass--with-store store-contents
+      (auth-source-pass--find-match "bar.baz" "foo" "123")
+      (should (equal auth-source-pass--parse-log '("address@hidden:123"))))
+    (auth-source-pass--with-store store-contents
+      (auth-source-pass--find-match "bar.baz" "foo" nil)
+      (should (equal auth-source-pass--parse-log '("address@hidden"))))
+    (auth-source-pass--with-store store-contents
+      (auth-source-pass--find-match "bar.baz" nil "123")
+      (should (equal auth-source-pass--parse-log '("bar.baz:123"))))
+    (auth-source-pass--with-store store-contents
+      (auth-source-pass--find-match "bar.baz" nil nil)
+      (should (equal auth-source-pass--parse-log '("bar.baz"))))
+    (auth-source-pass--with-store store-contents
+      (auth-source-pass--find-match "baz" nil nil)
+      (should (equal auth-source-pass--parse-log '("baz"))))))
 
 (ert-deftest auth-source-pass-find-match-matching-at-entry-name ()
-  (auth-source-pass--with-store '(("foo"))
-    (should (equal (auth-source-pass--find-match "foo" nil nil)
-                   "foo"))))
+  (auth-source-pass--with-store
+      '(("foo" ("secret" . "foo password")))
+    (let ((result (auth-source-pass--find-match "foo" nil nil)))
+      (should (equal (auth-source-pass--get-attr "secret" result)
+                     "foo password")))))
 
 (ert-deftest auth-source-pass-find-match-matching-at-entry-name-part ()
-  (auth-source-pass--with-store '(("foo"))
-    (should (equal (auth-source-pass--find-match "https://foo"; nil nil)
-                   "foo"))))
+  (auth-source-pass--with-store
+      '(("foo" ("secret" . "foo password")))
+    (let ((result (auth-source-pass--find-match "https://foo"; nil nil)))
+      (should (equal (auth-source-pass--get-attr "secret" result)
+                     "foo password")))))
 
 (ert-deftest auth-source-pass-find-match-matching-at-entry-name-ignoring-user 
()
-  (auth-source-pass--with-store '(("foo"))
-    (should (equal (auth-source-pass--find-match "https://SomeUser@foo"; nil 
nil)
-                   "foo"))))
+  (auth-source-pass--with-store
+      '(("foo" ("secret" . "foo password")))
+    (let ((result (auth-source-pass--find-match "https://SomeUser@foo"; nil 
nil)))
+      (should (equal (auth-source-pass--get-attr "secret" result)
+                     "foo password")))))
 
 (ert-deftest auth-source-pass-find-match-matching-at-entry-name-with-user ()
-  (auth-source-pass--with-store '(("SomeUser@foo"))
-    (should (equal (auth-source-pass--find-match "https://SomeUser@foo"; nil 
nil)
-                   "SomeUser@foo"))))
+  (auth-source-pass--with-store
+      '(("SomeUser@foo" ("secret" . "SomeUser@foo password")))
+    (let ((result (auth-source-pass--find-match "https://SomeUser@foo"; nil 
nil)))
+      (should (equal (auth-source-pass--get-attr "secret" result)
+                     "SomeUser@foo password")))))
 
 (ert-deftest auth-source-pass-find-match-matching-at-entry-name-prefer-full ()
-  (auth-source-pass--with-store '(("SomeUser@foo") ("foo"))
-    (should (equal (auth-source-pass--find-match "https://SomeUser@foo"; nil 
nil)
-                   "SomeUser@foo"))))
+  (auth-source-pass--with-store
+      '(("SomeUser@foo" ("secret" . "SomeUser@foo password"))
+        ("foo" ("secret" . "foo password")))
+    (let ((result (auth-source-pass--find-match "https://SomeUser@foo"; nil 
nil)))
+      (should (equal (auth-source-pass--get-attr "secret" result)
+                     "SomeUser@foo password")))))
 
 (ert-deftest 
auth-source-pass-find-match-matching-at-entry-name-prefer-full-reversed ()
-  (auth-source-pass--with-store '(("foo") ("SomeUser@foo"))
-    (should (equal (auth-source-pass--find-match "https://SomeUser@foo"; nil 
nil)
-                   "SomeUser@foo"))))
-
-(ert-deftest 
auth-source-pass-find-match-matching-at-entry-name-without-subdomain ()
+  (auth-source-pass--with-store
+      '(("foo" ("secret" . "foo password"))
+        ("SomeUser@foo" ("secret" . "SomeUser@foo password")))
+    (let ((result (auth-source-pass--find-match "https://SomeUser@foo"; nil 
nil)))
+      (should (equal (auth-source-pass--get-attr "secret" result)
+                     "SomeUser@foo password")))))
+
+(ert-deftest auth-source-pass-matching-entries-name-without-subdomain ()
   (auth-source-pass--with-store '(("bar.com"))
-    (should (equal (auth-source-pass--find-match "foo.bar.com" nil nil)
-                   "bar.com"))))
+    (should (equal (auth-source-pass--matching-entries "foo.bar.com" nil nil)
+                   '(nil ("bar.com") nil)))))
 
-(ert-deftest 
auth-source-pass-find-match-matching-at-entry-name-without-subdomain-with-user 
()
+(ert-deftest 
auth-source-pass-matching-entries-name-without-subdomain-with-user ()
   (auth-source-pass--with-store '(("address@hidden"))
-    (should (equal (auth-source-pass--find-match "foo.bar.com" "someone" nil)
-                   "address@hidden"))))
+    (should (equal (auth-source-pass--matching-entries "foo.bar.com" "someone" 
nil)
+                   '(nil nil nil ("address@hidden") nil nil nil nil nil)))))
 
-(ert-deftest 
auth-source-pass-find-match-matching-at-entry-name-without-subdomain-with-bad-user
 ()
+(ert-deftest 
auth-source-pass-matching-entries-name-without-subdomain-with-bad-user ()
   (auth-source-pass--with-store '(("address@hidden"))
-    (should (equal (auth-source-pass--find-match "foo.bar.com" "someone" nil)
-                   nil))))
+    (should (equal (auth-source-pass--matching-entries "foo.bar.com" "someone" 
nil)
+                   '(nil nil nil nil nil nil nil nil nil)))))
 
-(ert-deftest 
auth-source-pass-find-match-matching-at-entry-name-without-subdomain-prefer-full
 ()
+(ert-deftest 
auth-source-pass-matching-entries-name-without-subdomain-prefer-full ()
   (auth-source-pass--with-store '(("bar.com") ("foo.bar.com"))
-    (should (equal (auth-source-pass--find-match "foo.bar.com" nil nil)
-                   "foo.bar.com"))))
+    (should (equal (auth-source-pass--matching-entries "foo.bar.com" nil nil)
+                   '(("foo.bar.com") ("bar.com") nil)))))
 
 (ert-deftest auth-source-pass-dont-match-at-folder-name ()
   (auth-source-pass--with-store '(("foo.bar.com/foo"))
-    (should (equal (auth-source-pass--find-match "foo.bar.com" nil nil)
-                   nil))))
+    (should (equal (auth-source-pass--matching-entries "foo.bar.com" nil nil)
+                   '(nil nil nil)))))
 
-(ert-deftest auth-source-pass-find-match-matching-host-port-and-subdir-user ()
+(ert-deftest auth-source-pass-matching-entries-host-port-and-subdir-user ()
   (auth-source-pass--with-store '(("bar.com:443/someone"))
-    (should (equal (auth-source-pass--find-match "bar.com" "someone" "443")
-                   "bar.com:443/someone"))))
+    (should (equal (auth-source-pass--matching-entries "bar.com" "someone" 
"443")
+                   '(nil ("bar.com:443/someone") nil nil nil nil
+                         nil nil nil nil nil nil)))))
 
-(ert-deftest 
auth-source-pass-find-match-matching-host-port-and-subdir-user-with-custom-separator
 ()
+(ert-deftest 
auth-source-pass-matching-entries-host-port-and-subdir-user-with-custom-separator
 ()
   (let ((auth-source-pass-port-separator "#"))
     (auth-source-pass--with-store '(("bar.com#443/someone"))
-      (should (equal (auth-source-pass--find-match "bar.com" "someone" "443")
-                     "bar.com#443/someone")))))
-
-(ert-deftest auth-source-pass-find-match-matching-extracting-user-from-host ()
-  (auth-source-pass--with-store '(("foo.com/bar"))
-    (should (equal (auth-source-pass--find-match "https://address@hidden"; nil 
nil)
-                   "foo.com/bar"))))
-
-(ert-deftest auth-source-pass-search-with-user-first ()
+      (should (equal (auth-source-pass--matching-entries "bar.com" "someone" 
"443")
+                     '(nil ("bar.com#443/someone") nil nil nil nil
+                           nil nil nil nil nil nil))))))
+
+(ert-deftest auth-source-pass-matching-entries-extracting-user-from-host ()
+  (auth-source-pass--with-store
+      '(("foo.com/bar" ("secret" . "foo.com/bar password")))
+    (let ((result (auth-source-pass--find-match "https://address@hidden"; nil 
nil)))
+      (should (equal (auth-source-pass--get-attr "secret" result)
+                     "foo.com/bar password")))))
+
+(ert-deftest auth-source-pass-matching-entries-with-user-first ()
   (auth-source-pass--with-store '(("foo") ("user@foo"))
-    (should (equal (auth-source-pass--find-match "foo" "user" nil)
-                   "user@foo"))
-    (auth-source-pass--should-have-message-containing "Found 1 match")))
+    (should (equal (auth-source-pass--matching-entries "foo" "user" nil)
+                   '(("user@foo") nil ("foo"))))
+    (auth-source-pass--should-have-message-containing "found: (\"user@foo\" 
\"foo\"")))
 
 (ert-deftest auth-source-pass-give-priority-to-desired-user ()
-  (auth-source-pass--with-store '(("foo") ("subdir/foo" ("user" . "someone")))
-    (should (equal (auth-source-pass--find-match "foo" "someone" nil)
-                   "subdir/foo"))
-    (auth-source-pass--should-have-message-containing "Found 2 matches")
-    (auth-source-pass--should-have-message-containing "matching user field")))
+  (auth-source-pass--with-store
+      '(("foo" ("secret" . "foo password"))
+        ("subdir/foo" ("secret" . "subdir/foo password") ("user" . "someone")))
+    (let ((result (auth-source-pass--find-match "foo" "someone" nil)))
+      (should (equal (auth-source-pass--get-attr "secret" result)
+                     "subdir/foo password"))
+      (should (equal (auth-source-pass--get-attr "user" result)
+                     "someone")))
+    (auth-source-pass--should-have-message-containing "found: (\"foo\" 
\"subdir/foo\"")))
 
 (ert-deftest auth-source-pass-give-priority-to-desired-user-reversed ()
-  (auth-source-pass--with-store '(("foo" ("user" . "someone")) ("subdir/foo"))
-    (should (equal (auth-source-pass--find-match "foo" "someone" nil)
-                   "foo"))
-    (auth-source-pass--should-have-message-containing "Found 2 matches")
-    (auth-source-pass--should-have-message-containing "matching user field")))
+  (auth-source-pass--with-store
+      '(("foo" ("secret" . "foo password") ("user" . "someone"))
+        ("subdir/foo" ("secret" . "subdir/foo password")))
+    (let ((result (auth-source-pass--find-match "foo" "someone" nil)))
+      (should (equal (auth-source-pass--get-attr "secret" result)
+                     "foo password")))
+    (auth-source-pass--should-have-message-containing "found: (\"foo\" 
\"subdir/foo\"")))
 
 (ert-deftest auth-source-pass-return-first-when-several-matches ()
-  (auth-source-pass--with-store '(("foo") ("subdir/foo"))
-    (should (equal (auth-source-pass--find-match "foo" nil nil)
-                   "foo"))
-    (auth-source-pass--should-have-message-containing "Found 2 matches")
-    (auth-source-pass--should-have-message-containing "the first one")))
-
-(ert-deftest auth-source-pass-make-divansantana-happy ()
+  (auth-source-pass--with-store
+      '(("foo" ("secret" . "foo password"))
+        ("subdir/foo" ("secret" . "subdir/foo password")))
+    (let ((result (auth-source-pass--find-match "foo" nil nil)))
+      (should (equal (auth-source-pass--get-attr "secret" result)
+                     "foo password")))
+    (auth-source-pass--should-have-message-containing "found: (\"foo\" 
\"subdir/foo\"")))
+
+(ert-deftest auth-source-pass-matching-entries-make-divansantana-happy ()
   (auth-source-pass--with-store '(("host.com"))
-    (should (equal (auth-source-pass--find-match "smtp.host.com" 
"address@hidden" nil)
-                   "host.com"))))
+    (should (equal (auth-source-pass--matching-entries "smtp.host.com" 
"address@hidden" nil)
+                   '(nil nil nil nil nil ("host.com") nil nil nil)))))
 
 (ert-deftest auth-source-pass-find-host-without-port ()
-  (auth-source-pass--with-store '(("host.com"))
-    (should (equal (auth-source-pass--find-match "host.com:8888" "someuser" 
nil)
-                   "host.com"))))
+  (auth-source-pass--with-store
+      '(("host.com" ("secret" . "host.com password")))
+    (let ((result (auth-source-pass--find-match "host.com:8888" "someuser" 
nil)))
+      (should (equal (auth-source-pass--get-attr "secret" result)
+                     "host.com password")))))
 
-(ert-deftest auth-source-pass-find-host-with-port ()
+(ert-deftest auth-source-pass-matching-entries-host-with-port ()
   (auth-source-pass--with-store '(("host.com:443"))
-    (should (equal (auth-source-pass--find-match "host.com" "someuser" "443")
-                   "host.com:443"))))
+    (should (equal (auth-source-pass--matching-entries "host.com" "someuser" 
"443")
+                   '(nil nil nil nil ("host.com:443") nil
+                         nil nil nil nil nil nil)))))
 
-(ert-deftest auth-source-pass-find-host-with-custom-port-separator ()
+(ert-deftest auth-source-pass-matching-entries-with-custom-port-separator ()
   (let ((auth-source-pass-port-separator "#"))
     (auth-source-pass--with-store '(("host.com#443"))
-      (should (equal (auth-source-pass--find-match "host.com" "someuser" "443")
-                     "host.com#443")))))
+      (should (equal (auth-source-pass--matching-entries "host.com" "someuser" 
"443")
+                     '(nil nil nil nil ("host.com#443") nil
+                           nil nil nil nil nil nil))))))
 
 (defmacro auth-source-pass--with-store-find-foo (store &rest body)
   "Use STORE while executing BODY.  \"foo\" is the matched entry."
@@ -218,7 +282,8 @@ This function is intended to be set to `auth-source-debug`."
        ,@body)))
 
 (ert-deftest auth-source-pass-build-result-return-parameters ()
-  (auth-source-pass--with-store-find-foo '(("foo"))
+  (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 :port) 512))
       (should (equal (plist-get result :user) "user")))))
@@ -238,7 +303,9 @@ This function is intended to be set to `auth-source-debug`."
 (ert-deftest auth-source-pass-build-result-passes-full-host-to-find-match ()
   (let (passed-host)
     (cl-letf (((symbol-function 'auth-source-pass--find-match)
-               (lambda (host _user _port) (setq passed-host host))))
+               (lambda (host _user _port)
+                 (setq passed-host host)
+                 nil)))
       (auth-source-pass--build-result "https://address@hidden:123"; nil nil)
       (should (equal passed-host "https://address@hidden:123";))
       (auth-source-pass--build-result "https://address@hidden"; nil nil)
@@ -249,27 +316,16 @@ This function is intended to be set to 
`auth-source-debug`."
       (should (equal passed-host "address@hidden:443")))))
 
 (ert-deftest auth-source-pass-only-return-entries-that-can-be-open ()
-  (cl-letf (((symbol-function 'auth-source-pass-entries)
-             (lambda () '("foo.site.com" "bar.site.com" 
"mail/baz.site.com/scott")))
-            ((symbol-function 'auth-source-pass--entry-valid-p)
-             ;; only foo.site.com and "mail/baz.site.com/scott" are valid
-             (lambda (entry) (member entry '("foo.site.com" 
"mail/baz.site.com/scott")))))
-    (should (equal (auth-source-pass--find-all-by-entry-name "foo.site.com" 
"someuser")
-                   '("foo.site.com")))
-    (should (equal (auth-source-pass--find-all-by-entry-name "bar.site.com" 
"someuser")
-                   '()))
-    (should (equal (auth-source-pass--find-all-by-entry-name "baz.site.com" 
"scott")
-                   '("mail/baz.site.com/scott")))))
-
-(ert-deftest auth-source-pass-entry-is-not-valid-when-unreadable ()
-  (cl-letf (((symbol-function 'auth-source-pass--read-entry)
-             (lambda (entry)
-               ;; only foo is a valid entry
-               (if (string-equal entry "foo")
-                   "password"
-                 nil))))
-    (should (auth-source-pass--entry-valid-p "foo"))
-    (should-not (auth-source-pass--entry-valid-p "bar"))))
+  (auth-source-pass--with-store
+      '(("foo.site.com" ("secret" . "foo.site.com password"))
+        ("bar.site.com") ; An entry name with no data is invalid
+        ("mail/baz.site.com/scott" ("secret" . "mail/baz.site.com/scott 
password")))
+    (should (equal (auth-source-pass--find-match "foo.site.com" "someuser" nil)
+                   '(("secret" . "foo.site.com password"))))
+    (should (equal (auth-source-pass--find-match "bar.site.com" "someuser" nil)
+                   nil))
+    (should (equal (auth-source-pass--find-match "baz.site.com" "scott" nil)
+                   '(("secret" . "mail/baz.site.com/scott password"))))))
 
 (ert-deftest auth-source-pass-can-start-from-auth-source-search ()
   (auth-source-pass--with-store '(("gitlab.com" ("user" . "someone")))



reply via email to

[Prev in Thread] Current Thread [Next in Thread]