diff options
author | Damien Cassou <damien@cassou.me> | 2017-04-03 21:36:03 +0200 |
---|---|---|
committer | Ted Zlatanov <tzz@lifelogs.com> | 2017-04-27 17:37:58 -0400 |
commit | 2a23577795f8b644a215fae68e990c07f0146d33 (patch) | |
tree | 097eb62e2e62798597754e976cc0d4f08f7def61 | |
parent | b206b95ce3c598a5f3c62cfe83030da14952c81d (diff) | |
download | emacs-2a23577795f8b644a215fae68e990c07f0146d33.tar.gz |
auth-source-pass: Add documentation; fix tests and indentation.
* 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.
-rw-r--r-- | doc/misc/auth.texi | 46 | ||||
-rw-r--r-- | lisp/auth-source-pass.el | 17 | ||||
-rw-r--r-- | test/lisp/auth-source-pass-tests.el | 243 |
3 files changed, 161 insertions, 145 deletions
diff --git a/doc/misc/auth.texi b/doc/misc/auth.texi index 29e55eda52c..94229e69f79 100644 --- a/doc/misc/auth.texi +++ b/doc/misc/auth.texi @@ -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: diff --git a/lisp/auth-source-pass.el b/lisp/auth-source-pass.el index e59cfa2d25f..a83c7de0cc5 100644 --- a/lisp/auth-source-pass.el +++ b/lisp/auth-source-pass.el @@ -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") diff --git a/test/lisp/auth-source-pass-tests.el b/test/lisp/auth-source-pass-tests.el index 1a7c9a70365..102611d2fae 100644 --- a/test/lisp/auth-source-pass-tests.el +++ b/test/lisp/auth-source-pass-tests.el @@ -63,108 +63,102 @@ 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 () |