mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-15 10:30:25 -08:00
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.
This commit is contained in:
parent
b206b95ce3
commit
2a23577795
3 changed files with 145 additions and 129 deletions
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
(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)))
|
||||
|
||||
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))))
|
||||
(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"))))
|
||||
|
||||
(auth-source-pass--deftest auth-source-pass-find-match-matching-at-entry-name ()
|
||||
'(("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"))))
|
||||
|
||||
(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")))
|
||||
(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"))))
|
||||
|
||||
(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")))
|
||||
(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"))))
|
||||
|
||||
(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")))
|
||||
(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"))))
|
||||
|
||||
(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")))
|
||||
(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"))))
|
||||
|
||||
;; 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")))
|
||||
(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"))))
|
||||
|
||||
(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")))
|
||||
(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"))))
|
||||
|
||||
(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")))
|
||||
(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))))
|
||||
|
||||
(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)))
|
||||
(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"))))
|
||||
|
||||
(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")))
|
||||
(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))))
|
||||
|
||||
(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)))
|
||||
(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")))
|
||||
|
||||
(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"))
|
||||
(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")))
|
||||
|
||||
(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"))
|
||||
(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")))
|
||||
|
||||
(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"))
|
||||
(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")))
|
||||
|
||||
(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")))
|
||||
(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 ()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue