summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Cassou <damien@cassou.me>2017-04-03 21:36:03 +0200
committerTed Zlatanov <tzz@lifelogs.com>2017-04-26 15:11:35 -0400
commit14ff8b1fb60459c4c5cb147e07e3902dc569f8e0 (patch)
treef2200f9516a295cc6ae28593eb2ca12d5efee1dd
parentc2d4ed8f2ee18d5e3fb56b31c2e1b784b1ea70e0 (diff)
downloademacs-feature/auth-source-pass.tar.gz
auth-source-pass: Add documentation; fix tests and indentation.feature/auth-source-pass
* 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.texi46
-rw-r--r--lisp/auth-source-pass.el17
-rw-r--r--test/lisp/auth-source-pass-tests.el243
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 ()