aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Albinus2018-04-13 15:21:24 +0200
committerMichael Albinus2018-04-13 15:21:24 +0200
commit1f31c1348c4ddec31664e78f8cf4b9514d2a32c6 (patch)
tree1b9ab34b0ff5cb0b24ebe7f0355fd2d18aac517e
parent9822a6a5708227897432f47d3f676c646b7bd4b2 (diff)
downloademacs-1f31c1348c4ddec31664e78f8cf4b9514d2a32c6.tar.gz
emacs-1f31c1348c4ddec31664e78f8cf4b9514d2a32c6.zip
Fix Bug#30246
* lisp/auth-source.el (auth-source-secrets-search): Do not suppress creation. (auth-source-secrets-create): Implement it. (Bug#30246) * lisp/net/secrets.el (secrets-debug): Set default to nil. * test/lisp/auth-source-tests.el (secrets): Require it. (auth-source-test-secrets-create-secret): New test.
-rw-r--r--lisp/auth-source.el167
-rw-r--r--lisp/net/secrets.el2
-rw-r--r--test/lisp/auth-source-tests.el34
3 files changed, 192 insertions, 11 deletions
diff --git a/lisp/auth-source.el b/lisp/auth-source.el
index 355c11fbf3a..a2ed47a0d45 100644
--- a/lisp/auth-source.el
+++ b/lisp/auth-source.el
@@ -1514,9 +1514,6 @@ authentication tokens:
1514" 1514"
1515 1515
1516 ;; TODO 1516 ;; TODO
1517 (cl-assert (not create) nil
1518 "The Secrets API auth-source backend doesn't support creation yet")
1519 ;; TODO
1520 ;; (secrets-delete-item coll elt) 1517 ;; (secrets-delete-item coll elt)
1521 (cl-assert (not delete) nil 1518 (cl-assert (not delete) nil
1522 "The Secrets API auth-source backend doesn't support deletion yet") 1519 "The Secrets API auth-source backend doesn't support deletion yet")
@@ -1576,12 +1573,168 @@ authentication tokens:
1576 returned-keys)) 1573 returned-keys))
1577 plist)) 1574 plist))
1578 items))) 1575 items)))
1576 (cond
1577 ;; if we need to create an entry AND none were found to match
1578 ((and create
1579 (not items))
1580
1581 ;; create based on the spec and record the value
1582 (setq items (or
1583 ;; if the user did not want to create the entry
1584 ;; in the file, it will be returned
1585 (apply (slot-value backend 'create-function) spec)
1586 ;; if not, we do the search again without :create
1587 ;; to get the updated data.
1588
1589 ;; the result will be returned, even if the search fails
1590 (apply #'auth-source-secrets-search
1591 (plist-put spec :create nil))))))
1579 items)) 1592 items))
1580 1593
1581(defun auth-source-secrets-create (&rest spec) 1594(cl-defun auth-source-secrets-create (&rest spec
1582 ;; TODO 1595 &key backend host port create
1583 ;; (apply 'secrets-create-item (auth-get-source entry) name passwd spec) 1596 &allow-other-keys)
1584 (debug spec)) 1597 (let* ((base-required '(host user port secret label))
1598 ;; we know (because of an assertion in auth-source-search) that the
1599 ;; :create parameter is either t or a list (which includes nil)
1600 (create-extra (if (eq t create) nil create))
1601 (current-data (car (auth-source-search :max 1
1602 :host host
1603 :port port)))
1604 (required (append base-required create-extra))
1605 (collection (oref backend source))
1606 ;; `args' are the arguments for `secrets-create-item'.
1607 args
1608 ;; `valist' is an alist
1609 valist
1610 ;; `artificial' will be returned if no creation is needed
1611 artificial)
1612
1613 ;; only for base required elements (defined as function parameters):
1614 ;; fill in the valist with whatever data we may have from the search
1615 ;; we complete the first value if it's a list and use the value otherwise
1616 (dolist (br base-required)
1617 (let ((val (plist-get spec (auth-source--symbol-keyword br))))
1618 (when val
1619 (let ((br-choice (cond
1620 ;; all-accepting choice (predicate is t)
1621 ((eq t val) nil)
1622 ;; just the value otherwise
1623 (t val))))
1624 (when br-choice
1625 (auth-source--aput valist br br-choice))))))
1626
1627 ;; for extra required elements, see if the spec includes a value for them
1628 (dolist (er create-extra)
1629 (let ((k (auth-source--symbol-keyword er))
1630 (keys (cl-loop for i below (length spec) by 2
1631 collect (nth i spec))))
1632 (when (memq k keys)
1633 (auth-source--aput valist er (plist-get spec k)))))
1634
1635 ;; for each required element
1636 (dolist (r required)
1637 (let* ((data (auth-source--aget valist r))
1638 ;; take the first element if the data is a list
1639 (data (or (auth-source-netrc-element-or-first data)
1640 (plist-get current-data
1641 (auth-source--symbol-keyword r))))
1642 ;; this is the default to be offered
1643 (given-default (auth-source--aget
1644 auth-source-creation-defaults r))
1645 ;; the default supplementals are simple:
1646 ;; for the user, try `given-default' and then (user-login-name);
1647 ;; for the label, try `given-default' and then user@host;
1648 ;; otherwise take `given-default'
1649 (default (cond
1650 ((and (not given-default) (eq r 'user))
1651 (user-login-name))
1652 ((and (not given-default) (eq r 'label))
1653 (format "%s@%s"
1654 (or (auth-source-netrc-element-or-first
1655 (auth-source--aget valist 'user))
1656 (plist-get artificial :user))
1657 (or (auth-source-netrc-element-or-first
1658 (auth-source--aget valist 'host))
1659 (plist-get artificial :host))))
1660 (t given-default)))
1661 (printable-defaults (list
1662 (cons 'user
1663 (or
1664 (auth-source-netrc-element-or-first
1665 (auth-source--aget valist 'user))
1666 (plist-get artificial :user)
1667 "[any user]"))
1668 (cons 'host
1669 (or
1670 (auth-source-netrc-element-or-first
1671 (auth-source--aget valist 'host))
1672 (plist-get artificial :host)
1673 "[any host]"))
1674 (cons 'port
1675 (or
1676 (auth-source-netrc-element-or-first
1677 (auth-source--aget valist 'port))
1678 (plist-get artificial :port)
1679 "[any port]"))
1680 (cons 'label
1681 (or
1682 (auth-source-netrc-element-or-first
1683 (auth-source--aget valist 'label))
1684 (plist-get artificial :label)
1685 "[any label]"))))
1686 (prompt (or (auth-source--aget auth-source-creation-prompts r)
1687 (cl-case r
1688 (secret "%p password for %u@%h: ")
1689 (user "%p user name for %h: ")
1690 (host "%p host name for user %u: ")
1691 (port "%p port for %u@%h: ")
1692 (label "Enter label for %u@%h: "))
1693 (format "Enter %s (%%u@%%h:%%p): " r)))
1694 (prompt (auth-source-format-prompt
1695 prompt
1696 `((?u ,(auth-source--aget printable-defaults 'user))
1697 (?h ,(auth-source--aget printable-defaults 'host))
1698 (?p ,(auth-source--aget printable-defaults 'port))))))
1699
1700 ;; Store the data, prompting for the password if needed.
1701 (setq data (or data
1702 (if (eq r 'secret)
1703 (or (eval default) (read-passwd prompt))
1704 (if (stringp default)
1705 (read-string (if (string-match ": *\\'" prompt)
1706 (concat (substring prompt 0 (match-beginning 0))
1707 " (default " default "): ")
1708 (concat prompt "(default " default ") "))
1709 nil nil default)
1710 (eval default)))))
1711
1712 (when data
1713 (setq artificial (plist-put artificial
1714 (auth-source--symbol-keyword r)
1715 (if (eq r 'secret)
1716 (let ((data data))
1717 (lambda () data))
1718 data))))
1719
1720 ;; When r is not an empty string...
1721 (when (and (stringp data)
1722 (< 0 (length data))
1723 (not (member r '(secret label))))
1724 ;; append the key (the symbol name of r)
1725 ;; and the value in r
1726 (setq args (append args (list (auth-source--symbol-keyword r) data))))))
1727
1728 (plist-put
1729 artificial
1730 :save-function
1731 (let* ((collection collection)
1732 (item (plist-get artificial :label))
1733 (secret (plist-get artificial :secret))
1734 (secret (if (functionp secret) (funcall secret) secret)))
1735 (lambda () (apply 'secrets-create-item collection item secret args))))
1736
1737 (list artificial)))
1585 1738
1586;;; Backend specific parsing: Mac OS Keychain (using /usr/bin/security) backend 1739;;; Backend specific parsing: Mac OS Keychain (using /usr/bin/security) backend
1587 1740
diff --git a/lisp/net/secrets.el b/lisp/net/secrets.el
index e5ab5b31ab0..8070ccf96e2 100644
--- a/lisp/net/secrets.el
+++ b/lisp/net/secrets.el
@@ -158,7 +158,7 @@
158(defvar secrets-enabled nil 158(defvar secrets-enabled nil
159 "Whether there is a daemon offering the Secret Service API.") 159 "Whether there is a daemon offering the Secret Service API.")
160 160
161(defvar secrets-debug t 161(defvar secrets-debug nil
162 "Write debug messages") 162 "Write debug messages")
163 163
164(defconst secrets-service "org.freedesktop.secrets" 164(defconst secrets-service "org.freedesktop.secrets"
diff --git a/test/lisp/auth-source-tests.el b/test/lisp/auth-source-tests.el
index eb93f7488e4..2f5a9320b17 100644
--- a/test/lisp/auth-source-tests.el
+++ b/test/lisp/auth-source-tests.el
@@ -29,9 +29,7 @@
29(require 'ert) 29(require 'ert)
30(require 'cl-lib) 30(require 'cl-lib)
31(require 'auth-source) 31(require 'auth-source)
32 32(require 'secrets)
33(defvar secrets-enabled t
34 "Enable the secrets backend to test its features.")
35 33
36(defun auth-source-ensure-ignored-backend (source) 34(defun auth-source-ensure-ignored-backend (source)
37 (auth-source-validate-backend source '((:source . "") 35 (auth-source-validate-backend source '((:source . "")
@@ -289,5 +287,35 @@
289 (should (equal found-as-string (concat testname ": " needed))))) 287 (should (equal found-as-string (concat testname ": " needed)))))
290 (delete-file netrc-file))) 288 (delete-file netrc-file)))
291 289
290(ert-deftest auth-source-test-secrets-create-secret ()
291 (skip-unless secrets-enabled)
292 ;; The "session" collection is temporary for the lifetime of the
293 ;; Emacs process. Therefore, we don't care to delete it.
294 (let ((auth-sources '((:source (:secrets "session"))))
295 (host (md5 (concat (prin1-to-string process-environment)
296 (current-time-string))))
297 (passwd (md5 (concat (prin1-to-string process-environment)
298 (current-time-string) (current-time-string))))
299 auth-info auth-passwd)
300 ;; Redefine `read-*' in order to avoid interactive input.
301 (cl-letf (((symbol-function 'read-passwd) (lambda (_) passwd))
302 ((symbol-function 'read-string)
303 (lambda (_prompt _initial _history default) default)))
304 (setq auth-info
305 (car (auth-source-search
306 :max 1 :host host :require '(:user :secret) :create t))))
307 (should (functionp (plist-get auth-info :save-function)))
308 (funcall (plist-get auth-info :save-function))
309
310 ;; Check, that the item has been created indeed.
311 (auth-source-forget+ :host t)
312 (setq auth-info (car (auth-source-search :host host))
313 auth-passwd (plist-get auth-info :secret)
314 auth-passwd (if (functionp auth-passwd)
315 (funcall auth-passwd)
316 auth-passwd))
317 (should (string-equal (plist-get auth-info :user) (user-login-name)))
318 (should (string-equal auth-passwd passwd))))
319
292(provide 'auth-source-tests) 320(provide 'auth-source-tests)
293;;; auth-source-tests.el ends here 321;;; auth-source-tests.el ends here