]> git.eshelyaron.com Git - emacs.git/commitdiff
auth-source-pass: Add documentation; fix tests and indentation.
authorDamien Cassou <damien@cassou.me>
Mon, 3 Apr 2017 19:36:03 +0000 (21:36 +0200)
committerTed Zlatanov <tzz@lifelogs.com>
Thu, 27 Apr 2017 21:37:58 +0000 (17:37 -0400)
* doc/misc/auth.texi: Document new integration with Pass. Use @itemize
  instead of @enumerate.
* lisp/auth-source-pass.el: Fix indentation.
  (auth-source-pass--remove-directory-name): Remove.
* test/lisp/auth-source-pass-tests.el: Adjust test macros.

doc/misc/auth.texi
lisp/auth-source-pass.el
test/lisp/auth-source-pass-tests.el

index 29e55eda52ca041688378d3deaa3694a826aa87e..94229e69f79cacdda1f49936aa46fd0cb8e6d099 100644 (file)
@@ -85,8 +85,9 @@ password (known as the secret).
 
 Similarly, the auth-source library supports multiple storage backend,
 currently either the classic ``netrc'' backend, examples of which you
-can see later in this document, or the Secret Service API@.  This is
-done with EIEIO-based backends and you can write your own if you want.
+can see later in this document, the Secret Service API, and pass, the
+standard unix password manager.  This is done with EIEIO-based
+backends and you can write your own if you want.
 
 @node Help for users
 @chapter Help for users
@@ -150,9 +151,9 @@ auth-source library is not loaded for some other reason.
 @defvar auth-sources
 
 The @code{auth-sources} variable tells the auth-source library where
-your netrc files or Secret Service API collection items live for a
-particular host and protocol.  While you can get fancy, the default
-and simplest configuration is:
+your netrc files, Secret Service API collection items, or your
+password store live for a particular host and protocol.  While you can
+get fancy, the default and simplest configuration is:
 
 @lisp
 ;;; old default: required :host and :port, not needed anymore
@@ -164,6 +165,9 @@ and simplest configuration is:
 ;;; use the Secrets API @var{Login} collection
 ;;; (@pxref{Secret Service API})
 (setq auth-sources '("secrets:Login"))
+;;; use pass (@file{~/.password-store})
+;;; (@pxref{Pass, the Unix password store})
+(setq auth-sources '(password-store))
 @end lisp
 
 By adding multiple entries to @code{auth-sources} with a particular
@@ -402,6 +406,34 @@ then fall back to @file{~/.authinfo.gpg}.
                      "~/.authinfo.gpg"))
 @end example
 
+@node Pass, the Unix password store
+@chapter Pass, the Unix password store
+
+@uref{http://www.passwordstore.org,,The standard unix password
+manager} (or just @code{pass}) stores your passwords in
+@code{gpg}-protected files following the Unix philosophy.
+
+Emacs integration of @code{pass} follows the first approach suggested
+by the pass project itself for data organization to find data. This
+means that the filename of the file containing the password for a user
+on a particular host must contain the host name.  The file itself must
+contain the password on the first line, as well as a @code{username}
+field containing the username on a subsequent line. A @code{port}
+field can be used to differentiate the authentication data for several
+services with the same username on the same host.
+
+Users of @code{pass} may also be interested in functionality provided
+by other Emacs packages dealing with pass:
+
+@itemize
+@item
+@uref{https://git.zx2c4.com/password-store/tree/contrib/emacs/password-store.el,,password-store}: library wrapping @code{pass};
+@item
+@uref{https://github.com/NicolasPetton/pass,,pass}: major mode to manipulate the store and edit entries;
+@item
+@uref{https://github.com/jabranham/helm-pass,,helm-pass}: helm interface for pass.
+@end itemize
+
 @node Help for developers
 @chapter Help for developers
 
@@ -517,14 +549,14 @@ or EasyPG Assistant
 
 To quick start, here are some questions:
 
-@enumerate
+@itemize
 @item
 Do you use GnuPG version 2 instead of GnuPG version 1?
 @item
 Do you use symmetric encryption rather than public key encryption?
 @item
 Do you want to use gpg-agent?
-@end enumerate
+@end itemize
 
 Here are configurations depending on your answers:
 
index e59cfa2d25f4281a4b76a8c67806f280fef8c8b5..a83c7de0cc51f50368eaa7c12c763f6d090b38a3 100644 (file)
@@ -39,8 +39,8 @@
 (require 'url-parse)
 
 (cl-defun auth-source-pass-search (&rest spec
-                         &key backend type host user port
-                         &allow-other-keys)
+                                         &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."
   (cl-assert (or (null type) (eq type (oref backend type)))
@@ -60,7 +60,7 @@ See `auth-source-search' for details on SPEC."
                      :user (or (auth-source-pass-get "user" entry) user)
                      :secret (lambda () (auth-source-pass-get 'secret entry)))))
         (auth-source-pass--do-debug "return %s as final result (plus hidden password)"
-                    (seq-subseq retval 0 -2)) ;; remove password
+                                    (seq-subseq retval 0 -2)) ;; remove password
         retval))))
 
 ;;;###autoload
@@ -159,11 +159,6 @@ CONTENTS is the contents of a password-store formatted file."
      (hostname hostname)
      (t host))))
 
-(defun auth-source-pass--remove-directory-name (name)
-  "Remove directories from NAME.
-E.g., if NAME is \"foo/bar\", return \"bar\"."
-  (replace-regexp-in-string ".*/" "" name))
-
 (defun auth-source-pass--do-debug (&rest msg)
   "Call `auth-source-do-debug` with MSG and a prefix."
   (apply #'auth-source-do-debug
@@ -216,7 +211,7 @@ Only return valid entries as of `auth-source-pass--entry-valid-p'."
                          (member entryname (split-string entry "/"))))
                     (and (= (length components-host-user) 2)
                          (string-equal user (cadr components-host-user))))
-                  (string-equal entryname (auth-source-pass--remove-directory-name entry)))
+                  (string-equal entryname (file-name-nondirectory entry)))
                  (auth-source-pass--entry-valid-p entry)))
               (auth-source-pass-entries)))
 
@@ -225,8 +220,8 @@ Only return valid entries as of `auth-source-pass--entry-valid-p'."
 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)
+                              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")
index 1a7c9a70365f2eb34e2481fb909d093b26d1b8c3..102611d2faef89cf11073b40a9ddbf00ac4833c5 100644 (file)
 This function is intended to be set to `auth-source-debug`."
   (add-to-list 'auth-source-pass--debug-log (apply #'format msg) t))
 
-(defmacro auth-source-pass--deftest (name arglist store &rest body)
-  "Define a new ert-test NAME with ARGLIST using STORE as password-store.
-BODY is a sequence of instructions that will be evaluated.
-
-This macro overrides `auth-source-pass-parse-entry' and `auth-source-pass-entries' to
-test code without touching the file system."
-  (declare (indent 3))
-  `(ert-deftest ,name ,arglist
-     (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)))
-       (let ((auth-source-debug #'auth-source-pass--debug)
-             (auth-source-pass--debug-log nil))
-         ,@body))))
-
-(auth-source-pass--deftest auth-source-pass-find-match-matching-at-entry-name ()
-           '(("foo"))
-  (should (equal (auth-source-pass--find-match "foo" nil)
-                 "foo")))
-
-(auth-source-pass--deftest auth-source-pass-find-match-matching-at-entry-name-part ()
-                   '(("foo"))
-  (should (equal (auth-source-pass--find-match "https://foo" nil)
-                 "foo")))
-
-(auth-source-pass--deftest auth-source-pass-find-match-matching-at-entry-name-ignoring-user ()
-                   '(("foo"))
-  (should (equal (auth-source-pass--find-match "https://SomeUser@foo" nil)
-                 "foo")))
-
-(auth-source-pass--deftest auth-source-pass-find-match-matching-at-entry-name-with-user ()
-                   '(("SomeUser@foo"))
-                   (should (equal (auth-source-pass--find-match "https://SomeUser@foo" nil)
-                                  "SomeUser@foo")))
-
-(auth-source-pass--deftest auth-source-pass-find-match-matching-at-entry-name-prefer-full ()
-                   '(("SomeUser@foo") ("foo"))
-                   (should (equal (auth-source-pass--find-match "https://SomeUser@foo" nil)
-                                  "SomeUser@foo")))
-
-;; same as previous one except the store is in another order
-(auth-source-pass--deftest auth-source-pass-find-match-matching-at-entry-name-prefer-full-reversed ()
-                   '(("foo") ("SomeUser@foo"))
-                   (should (equal (auth-source-pass--find-match "https://SomeUser@foo" nil)
-                                  "SomeUser@foo")))
-
-(auth-source-pass--deftest auth-source-pass-find-match-matching-at-entry-name-without-subdomain ()
-                   '(("bar.com"))
-                   (should (equal (auth-source-pass--find-match "foo.bar.com" nil)
-                                  "bar.com")))
-
-(auth-source-pass--deftest auth-source-pass-find-match-matching-at-entry-name-without-subdomain-with-user ()
-                   '(("someone@bar.com"))
-                   (should (equal (auth-source-pass--find-match "foo.bar.com" "someone")
-                                  "someone@bar.com")))
-
-(auth-source-pass--deftest auth-source-pass-find-match-matching-at-entry-name-without-subdomain-with-bad-user ()
-                   '(("someoneelse@bar.com"))
-                   (should (equal (auth-source-pass--find-match "foo.bar.com" "someone")
-                                  nil)))
-
-(auth-source-pass--deftest auth-source-pass-find-match-matching-at-entry-name-without-subdomain-prefer-full ()
-                   '(("bar.com") ("foo.bar.com"))
-                   (should (equal (auth-source-pass--find-match "foo.bar.com" nil)
-                                  "foo.bar.com")))
-
-(auth-source-pass--deftest auth-source-pass-dont-match-at-folder-name ()
-           '(("foo.bar.com/foo"))
-  (should (equal (auth-source-pass--find-match "foo.bar.com" nil)
-                 nil)))
-
-(auth-source-pass--deftest auth-source-pass-search-with-user-first ()
-           '(("foo") ("user@foo"))
-  (should (equal (auth-source-pass--find-match "foo" "user")
-                 "user@foo"))
-  (auth-source-pass--should-have-message-containing "Found 1 match"))
-
-(auth-source-pass--deftest auth-source-pass-give-priority-to-desired-user ()
-           '(("foo") ("subdir/foo" ("user" . "someone")))
-  (should (equal (auth-source-pass--find-match "foo" "someone")
-                 "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--deftest auth-source-pass-give-priority-to-desired-user-reversed ()
-           '(("foo" ("user" . "someone")) ("subdir/foo"))
-  (should (equal (auth-source-pass--find-match "foo" "someone")
-                 "foo"))
-  (auth-source-pass--should-have-message-containing "Found 2 matches")
-  (auth-source-pass--should-have-message-containing "matching user field"))
-
-(auth-source-pass--deftest auth-source-pass-return-first-when-several-matches ()
-           '(("foo") ("subdir/foo"))
-  (should (equal (auth-source-pass--find-match "foo" nil)
-                 "foo"))
-  (auth-source-pass--should-have-message-containing "Found 2 matches")
-  (auth-source-pass--should-have-message-containing "the first one"))
-
-(auth-source-pass--deftest auth-source-pass-make-divansantana-happy ()
-           '(("host.com"))
-  (should (equal (auth-source-pass--find-match "smtp.host.com" "myusername@host.co.za")
-                 "host.com")))
+(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)))
+     (let ((auth-source-debug #'auth-source-pass--debug)
+           (auth-source-pass--debug-log nil))
+       ,@body)))
+
+(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)
+                   "foo"))))
+
+(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)
+                   "foo"))))
+
+(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)
+                   "foo"))))
+
+(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)
+                   "SomeUser@foo"))))
+
+(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)
+                   "SomeUser@foo"))))
+
+(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)
+                   "SomeUser@foo"))))
+
+(ert-deftest auth-source-pass-find-match-matching-at-entry-name-without-subdomain ()
+  (auth-source-pass--with-store '(("bar.com"))
+    (should (equal (auth-source-pass--find-match "foo.bar.com" nil)
+                   "bar.com"))))
+
+(ert-deftest auth-source-pass-find-match-matching-at-entry-name-without-subdomain-with-user ()
+  (auth-source-pass--with-store '(("someone@bar.com"))
+    (should (equal (auth-source-pass--find-match "foo.bar.com" "someone")
+                   "someone@bar.com"))))
+
+(ert-deftest auth-source-pass-find-match-matching-at-entry-name-without-subdomain-with-bad-user ()
+  (auth-source-pass--with-store '(("someoneelse@bar.com"))
+    (should (equal (auth-source-pass--find-match "foo.bar.com" "someone")
+                   nil))))
+
+(ert-deftest auth-source-pass-find-match-matching-at-entry-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)
+                   "foo.bar.com"))))
+
+(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))))
+
+(ert-deftest auth-source-pass-search-with-user-first ()
+  (auth-source-pass--with-store '(("foo") ("user@foo"))
+    (should (equal (auth-source-pass--find-match "foo" "user")
+                   "user@foo"))
+    (auth-source-pass--should-have-message-containing "Found 1 match")))
+
+(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")
+                   "subdir/foo"))
+    (auth-source-pass--should-have-message-containing "Found 2 matches")
+    (auth-source-pass--should-have-message-containing "matching user field")))
+
+(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")
+                   "foo"))
+    (auth-source-pass--should-have-message-containing "Found 2 matches")
+    (auth-source-pass--should-have-message-containing "matching user field")))
+
+(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)
+                   "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 '(("host.com"))
+    (should (equal (auth-source-pass--find-match "smtp.host.com" "myusername@host.co.za")
+                   "host.com"))))
 
 (ert-deftest auth-source-pass-hostname ()
   (should (equal (auth-source-pass--hostname "https://foo.bar") "foo.bar"))
@@ -176,37 +170,32 @@ test code without touching the file system."
   (should (equal (auth-source-pass--hostname-with-user "http://foo.bar") "foo.bar"))
   (should (equal (auth-source-pass--hostname-with-user "https://SomeUser@foo.bar") "SomeUser@foo.bar")))
 
-(defmacro auth-source-pass--deftest-build-result (name arglist store &rest body)
-  "Define a new ert-test NAME with ARGLIST using STORE as password-store.
-BODY is a sequence of instructions that will be evaluated.
-
-This macro overrides `auth-source-pass-parse-entry',
-`auth-source-pass-entries', and `auth-source-pass--find-match' to
-ease testing."
-  (declare (indent 3))
-  `(auth-source-pass--deftest ,name ,arglist ,store
+(defmacro auth-source-pass--with-store-find-foo (store &rest body)
+  "Use STORE while executing BODY.  \"foo\" is the matched entry."
+  (declare (indent 1))
+  `(auth-source-pass--with-store ,store
      (cl-letf (((symbol-function 'auth-source-pass-find-match)
                 (lambda (_host _user)
                   "foo")))
        ,@body)))
 
-(auth-source-pass--deftest-build-result auth-source-pass-build-result-return-parameters ()
-                        '(("foo"))
-  (let ((result (auth-source-pass--build-result "foo" 512 "user")))
-    (should (equal (plist-get result :port) 512))
-    (should (equal (plist-get result :user) "user"))))
+(ert-deftest auth-source-pass-build-result-return-parameters ()
+  (auth-source-pass--with-store-find-foo '(("foo"))
+    (let ((result (auth-source-pass--build-result "foo" 512 "user")))
+      (should (equal (plist-get result :port) 512))
+      (should (equal (plist-get result :user) "user")))))
 
-(auth-source-pass--deftest-build-result auth-source-pass-build-result-return-entry-values ()
-                        '(("foo" ("port" . 512) ("user" . "anuser")))
-  (let ((result (auth-source-pass--build-result "foo" nil nil)))
-    (should (equal (plist-get result :port) 512))
-    (should (equal (plist-get result :user) "anuser"))))
+(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 :port) 512))
+      (should (equal (plist-get result :user) "anuser")))))
 
-(auth-source-pass--deftest-build-result auth-source-pass-build-result-entry-takes-precedence ()
-                        '(("foo" ("port" . 512) ("user" . "anuser")))
-  (let ((result (auth-source-pass--build-result "foo" 1024 "anotheruser")))
-    (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")))
+    (let ((result (auth-source-pass--build-result "foo" 1024 "anotheruser")))
+      (should (equal (plist-get result :port) 512))
+      (should (equal (plist-get result :user) "anuser")))))
 
 (ert-deftest auth-source-pass-only-return-entries-that-can-be-open ()
   (cl-letf (((symbol-function 'auth-source-pass-entries)
@@ -220,7 +209,7 @@ ease testing."
                    '("foo.site.com")))
     (should (equal (auth-source-pass--find-all-by-entry-name "bar.site.com" "someuser")
                    '()))
-    (should (equal (auth-pass--find-all-by-entry-name "baz.site.com" "scott")
+    (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 ()