diff options
| author | F. Jason Park | 2022-11-23 21:31:19 -0800 |
|---|---|---|
| committer | Amin Bandali | 2022-11-29 00:01:13 -0500 |
| commit | 00de296d1b4f629fd828cdeff588bb4f742d9ffe (patch) | |
| tree | 7b791016be2034d15c749ab0206957a164cbc389 | |
| parent | 83b9496a193c41ed7e41b36838727e234c81a2d1 (diff) | |
| download | emacs-00de296d1b4f629fd828cdeff588bb4f742d9ffe.tar.gz emacs-00de296d1b4f629fd828cdeff588bb4f742d9ffe.zip | |
Simplify erc-sasl's auth-source API
* doc/misc/erc.texi: Revise descriptions in SASL chapter to reflect
simplified auth-source options.
* lisp/erc/erc-sasl.el (erc-sasl-password,
erc-sasl-auth-source-function): Revise doc strings.
(erc-sasl-auth-source-password-as-host): New function to serve as
more useful choice for option `erc-sasl-auth-source-function'.
(erc-sasl--read-password): Promote auth-source to pole position, above
an explicit string and `:password'.
* test/lisp/erc/erc-sasl-tests.el (erc-sasl--read-password--basic):
Massage tests to conform to simplified `erc-sasl-password'
API. (Bug#29108.)
| -rw-r--r-- | doc/misc/erc.texi | 40 | ||||
| -rw-r--r-- | lisp/erc/erc-sasl.el | 77 | ||||
| -rw-r--r-- | test/lisp/erc/erc-sasl-tests.el | 38 |
3 files changed, 98 insertions, 57 deletions
diff --git a/doc/misc/erc.texi b/doc/misc/erc.texi index 2835baa3d6b..a8d1dd78235 100644 --- a/doc/misc/erc.texi +++ b/doc/misc/erc.texi | |||
| @@ -1055,17 +1055,10 @@ borrowing that parameter for its own uses, thus allowing you to call | |||
| 1055 | @code{erc-tls} with @code{:password} set to your NickServ password. | 1055 | @code{erc-tls} with @code{:password} set to your NickServ password. |
| 1056 | 1056 | ||
| 1057 | You can also set this to a nonemtpy string, and ERC will send that | 1057 | You can also set this to a nonemtpy string, and ERC will send that |
| 1058 | when needed, no questions asked. If you instead give a non-@code{nil} | 1058 | when needed, no questions asked. Or, if you'd rather use auth-source, |
| 1059 | symbol (other than @code{:password}), like @samp{Libera.Chat}, ERC | 1059 | set @code{erc-sasl-auth-source-function} to a function, and ERC will |
| 1060 | will use it for the @code{:host} field in an auth-source query. | 1060 | perform an auth-source query instead. As last resort in all cases, |
| 1061 | Actually, the same goes for when this option is @code{nil} but an | 1061 | ERC will prompt you for input. |
| 1062 | explicit session ID is already on file (@pxref{Network Identifier}). | ||
| 1063 | For all such queries, ERC specifies the resolved value of | ||
| 1064 | @code{erc-sasl-user} for the @code{:user} (@code{:login}) param. Keep | ||
| 1065 | in mind that none of this matters unless | ||
| 1066 | @code{erc-sasl-auth-source-function} holds a function, and it's | ||
| 1067 | @code{nil} by default. As a last resort, ERC will prompt you for | ||
| 1068 | input. | ||
| 1069 | 1062 | ||
| 1070 | Lastly, if your mechanism is @code{ecdsa-nist256p-challenge}, this | 1063 | Lastly, if your mechanism is @code{ecdsa-nist256p-challenge}, this |
| 1071 | option should instead hold the file name of your key. | 1064 | option should instead hold the file name of your key. |
| @@ -1075,7 +1068,23 @@ option should instead hold the file name of your key. | |||
| 1075 | This is nearly identical to the other ERC @samp{auth-source} function | 1068 | This is nearly identical to the other ERC @samp{auth-source} function |
| 1076 | options (@pxref{ERC auth-source functions}) except that the default | 1069 | options (@pxref{ERC auth-source functions}) except that the default |
| 1077 | value here is @code{nil}, meaning you have to set it to something like | 1070 | value here is @code{nil}, meaning you have to set it to something like |
| 1078 | @code{erc-auth-source-search} for queries to be performed. | 1071 | @code{erc-auth-source-search} for queries to be performed. For |
| 1072 | convenience, this module provides the following as a possible value: | ||
| 1073 | |||
| 1074 | @defun erc-sasl-auth-source-password-as-host &rest plist | ||
| 1075 | Setting @code{erc-sasl-auth-source-function} to this function tells | ||
| 1076 | ERC to use @code{erc-sasl-password} for the @code{:host} field when | ||
| 1077 | querying auth-source, even if its value is the default | ||
| 1078 | @code{:password}, in which case ERC knows to ``resolve'' it to | ||
| 1079 | @code{erc-session-password} and use that as long as it's | ||
| 1080 | non-@code{nil}. Otherwise, ERC just defers to | ||
| 1081 | @code{erc-auth-source-search} to determine the @code{:host}, along | ||
| 1082 | with everything else. | ||
| 1083 | @end defun | ||
| 1084 | |||
| 1085 | As long as this option specifies a function, ERC will pass it the | ||
| 1086 | ``resolved'' value of @code{erc-sasl-user} for the auth-source | ||
| 1087 | @code{:user} param. | ||
| 1079 | @end defopt | 1088 | @end defopt |
| 1080 | 1089 | ||
| 1081 | @defopt erc-sasl-authzid | 1090 | @defopt erc-sasl-authzid |
| @@ -1143,10 +1152,11 @@ machine Example.Net login aph-bot password sesame | |||
| 1143 | (erc-tls :server "irc.libera.chat" :port 6697 | 1152 | (erc-tls :server "irc.libera.chat" :port 6697 |
| 1144 | :client-certificate t))) | 1153 | :client-certificate t))) |
| 1145 | ('example | 1154 | ('example |
| 1146 | (let ((erc-sasl-auth-source-function #'erc-auth-source-search) | 1155 | (let ((erc-sasl-auth-source-function |
| 1147 | (erc-sasl-password 'Example.Net)) | 1156 | #'erc-sasl-auth-source-password-as-host)) |
| 1148 | (erc-tls :server "irc.example.net" :port 6697 | 1157 | (erc-tls :server "irc.example.net" :port 6697 |
| 1149 | :user "alyssa"))))) | 1158 | :user "alyssa" |
| 1159 | :password "Example.Net"))))) | ||
| 1150 | @end lisp | 1160 | @end lisp |
| 1151 | 1161 | ||
| 1152 | You've started storing your credentials with auth-source and have | 1162 | You've started storing your credentials with auth-source and have |
diff --git a/lisp/erc/erc-sasl.el b/lisp/erc/erc-sasl.el index 5ee7169de5f..5b2c93988af 100644 --- a/lisp/erc/erc-sasl.el +++ b/lisp/erc/erc-sasl.el | |||
| @@ -77,15 +77,14 @@ version is used." | |||
| 77 | 77 | ||
| 78 | (defcustom erc-sasl-password :password | 78 | (defcustom erc-sasl-password :password |
| 79 | "Optional account password to send when authenticating. | 79 | "Optional account password to send when authenticating. |
| 80 | When the value is a string, ERC will use it unconditionally for | 80 | When `erc-sasl-auth-source-function' is a function, ERC will |
| 81 | most mechanisms. Likewise with `:password', except ERC will | 81 | attempt an auth-source query and prompt for input if it fails. |
| 82 | instead use the \"session password\" on file, which often | 82 | Otherwise, when the value is a nonempty string, ERC will use it |
| 83 | originates from the entry-point commands `erc' or `erc-tls'. | 83 | unconditionally for most mechanisms. Likewise with `:password', |
| 84 | Otherwise, when `erc-sasl-auth-source-function' is a function, | 84 | except ERC will instead use the \"session password\" on file, if |
| 85 | ERC will attempt an auth-source query, possibly using a non-nil | 85 | any, which often originates from the entry-point commands `erc' |
| 86 | symbol for the suggested `:host' parameter if set as this | 86 | or `erc-tls'. As with auth-source, ERC will prompt for input as |
| 87 | option's value or passed as an `:id' to `erc-tls'. Failing that, | 87 | a fallback. |
| 88 | ERC will prompt for input. | ||
| 89 | 88 | ||
| 90 | Note that, with `:password', ERC will forgo sending a traditional | 89 | Note that, with `:password', ERC will forgo sending a traditional |
| 91 | server password via the IRC \"PASS\" command. Also, when | 90 | server password via the IRC \"PASS\" command. Also, when |
| @@ -95,15 +94,18 @@ option should hold the file name of the key." | |||
| 95 | 94 | ||
| 96 | (defcustom erc-sasl-auth-source-function nil | 95 | (defcustom erc-sasl-auth-source-function nil |
| 97 | "Function to query auth-source for an SASL password. | 96 | "Function to query auth-source for an SASL password. |
| 98 | Called with keyword params known to `auth-source-search', which | 97 | If provided, this function should expect to be called with any |
| 99 | includes `erc-sasl-user' for the `:user' field and | 98 | number of keyword params known to `auth-source-search', even |
| 100 | `erc-sasl-password' for the `:host' field, when the latter option | 99 | though ERC itself only specifies `:user' paired with a |
| 101 | is a non-nil, non-keyword symbol. In return, ERC expects a | 100 | \"resolved\" `erc-sasl-user' value. When calling this function, |
| 102 | string to send as the SASL password, or nil, to move on to the | 101 | ERC binds all options defined in this library, such as |
| 103 | next approach, as described in the doc string for the option | 102 | `erc-sasl-password', to their values from entry-point invocation. |
| 104 | `erc-sasl-password'. See info node `(erc) Connecting' for | 103 | In return, ERC expects a string to send as the SASL password, or |
| 105 | details on ERC's auth-source integration." | 104 | nil, in which case, ERC will prompt the for input. See info |
| 106 | :type '(choice (function-item erc-auth-source-search) | 105 | node `(erc) Connecting' for details on ERC's auth-source |
| 106 | integration." | ||
| 107 | :type '(choice (function-item erc-sasl-auth-source-password-as-host) | ||
| 108 | (function-item erc-auth-source-search) | ||
| 107 | (const nil) | 109 | (const nil) |
| 108 | function)) | 110 | function)) |
| 109 | 111 | ||
| @@ -130,20 +132,35 @@ details on ERC's auth-source integration." | |||
| 130 | (:nick (erc-downcase (erc-current-nick))) | 132 | (:nick (erc-downcase (erc-current-nick))) |
| 131 | (v v))) | 133 | (v v))) |
| 132 | 134 | ||
| 135 | (defun erc-sasl-auth-source-password-as-host (&rest plist) | ||
| 136 | "Call `erc-auth-source-search' with `erc-sasl-password' as `:host'. | ||
| 137 | But only do so when it's a string or a non-nil symbol, unless | ||
| 138 | that symbol is `:password', in which case, use a non-nil | ||
| 139 | `erc-session-password' instead. Otherwise, just defer to | ||
| 140 | `erc-auth-source-search' to pick a suitable `:host'. Expect | ||
| 141 | PLIST to contain keyword params known to `auth-source-search'." | ||
| 142 | (when erc-sasl-password | ||
| 143 | (when-let ((host (if (eq :password erc-sasl-password) | ||
| 144 | (and (not (functionp erc-session-password)) | ||
| 145 | erc-session-password) | ||
| 146 | erc-sasl-password))) | ||
| 147 | (setq plist `(,@plist :host ,(format "%s" host))))) | ||
| 148 | (apply #'erc-auth-source-search plist)) | ||
| 149 | |||
| 133 | (defun erc-sasl--read-password (prompt) | 150 | (defun erc-sasl--read-password (prompt) |
| 134 | "Return configured option or server password. | 151 | "Return configured option or server password. |
| 135 | PROMPT is passed to `read-passwd' if necessary." | 152 | If necessary, pass PROMPT to `read-passwd'." |
| 136 | (if-let | 153 | (if-let ((found (pcase (alist-get 'password erc-sasl--options) |
| 137 | ((found (pcase (alist-get 'password erc-sasl--options) | 154 | ((guard (alist-get 'authfn erc-sasl--options)) |
| 138 | (:password erc-session-password) | 155 | (let-alist erc-sasl--options |
| 139 | ((and (pred stringp) v) (unless (string-empty-p v) v)) | 156 | (let ((erc-sasl-user .user) |
| 140 | ((and (let fn (alist-get 'authfn erc-sasl--options)) | 157 | (erc-sasl-password .password) |
| 141 | (guard fn) v | 158 | (erc-sasl-mechanism .mechanism) |
| 142 | (let host | 159 | (erc-sasl-authzid .authzid) |
| 143 | (or v (erc-networks--id-given erc-networks--id)))) | 160 | (erc-sasl-auth-source-function .authfn)) |
| 144 | (apply fn | 161 | (funcall .authfn :user (erc-sasl--get-user))))) |
| 145 | :user (erc-sasl--get-user) | 162 | (:password erc-session-password) |
| 146 | (and host (list :host (symbol-name host)))))))) | 163 | ((and (pred stringp) v) (unless (string-empty-p v) v))))) |
| 147 | (copy-sequence (erc--unfun found)) | 164 | (copy-sequence (erc--unfun found)) |
| 148 | (read-passwd prompt))) | 165 | (read-passwd prompt))) |
| 149 | 166 | ||
diff --git a/test/lisp/erc/erc-sasl-tests.el b/test/lisp/erc/erc-sasl-tests.el index 3e6828ff644..0e5ea60e5f0 100644 --- a/test/lisp/erc/erc-sasl-tests.el +++ b/test/lisp/erc/erc-sasl-tests.el | |||
| @@ -57,6 +57,8 @@ | |||
| 57 | (erc-sasl--read-password "pwd:")) | 57 | (erc-sasl--read-password "pwd:")) |
| 58 | "baz"))))) | 58 | "baz"))))) |
| 59 | 59 | ||
| 60 | ;; This mainly tests `erc-sasl-auth-source-password-as-host'. | ||
| 61 | |||
| 60 | (ert-deftest erc-sasl--read-password--auth-source () | 62 | (ert-deftest erc-sasl--read-password--auth-source () |
| 61 | (ert-with-temp-file netrc-file | 63 | (ert-with-temp-file netrc-file |
| 62 | :text (string-join | 64 | :text (string-join |
| @@ -70,33 +72,42 @@ | |||
| 70 | (erc-session-server "irc.gnu.org") | 72 | (erc-session-server "irc.gnu.org") |
| 71 | (erc-session-port 6697) | 73 | (erc-session-port 6697) |
| 72 | (erc-networks--id (erc-networks--id-create nil)) | 74 | (erc-networks--id (erc-networks--id-create nil)) |
| 73 | calls | ||
| 74 | (fn (lambda (&rest r) | ||
| 75 | (push r calls) | ||
| 76 | (apply #'erc-auth-source-search r))) | ||
| 77 | erc-server-announced-name ; too early | 75 | erc-server-announced-name ; too early |
| 78 | auth-source-do-cache) | 76 | auth-source-do-cache |
| 77 | ;; | ||
| 78 | (fn #'erc-sasl-auth-source-password-as-host) | ||
| 79 | calls) | ||
| 80 | |||
| 81 | (advice-add 'erc-auth-source-search :before | ||
| 82 | (lambda (&rest r) (push r calls)) | ||
| 83 | '((name . erc-sasl--read-password--auth-source))) | ||
| 79 | 84 | ||
| 80 | (ert-info ("Symbol as password specifies machine") | 85 | (ert-info ("Symbol as password specifies machine") |
| 81 | (let ((erc-sasl--options | 86 | (let ((erc-sasl--options |
| 82 | `((user . "bob") (password . FSF.chat) (authfn . ,fn))) | 87 | `((user . "bob") (password . FSF.chat) (authfn . ,fn)))) |
| 83 | (erc-networks--id (make-erc-networks--id))) | ||
| 84 | (should (string= (erc-sasl--read-password nil) "sesame")) | 88 | (should (string= (erc-sasl--read-password nil) "sesame")) |
| 85 | (should (equal (pop calls) '(:user "bob" :host "FSF.chat"))))) | 89 | (should (equal (pop calls) '(:user "bob" :host "FSF.chat"))))) |
| 86 | 90 | ||
| 87 | (ert-info ("ID for :host and `erc-session-username' for :user") ; *1 | 91 | (ert-info (":password as password resolved to machine") |
| 92 | (let ((erc-session-password "FSF.chat") | ||
| 93 | (erc-sasl--options | ||
| 94 | `((user . "bob") (password . :password) (authfn . ,fn)))) | ||
| 95 | (should (string= (erc-sasl--read-password nil) "sesame")) | ||
| 96 | (should (equal (pop calls) '(:user "bob" :host "FSF.chat"))))) | ||
| 97 | |||
| 98 | (ert-info (":user resolved to `erc-session-username'") ; *1 | ||
| 88 | (let ((erc-session-username "bob") | 99 | (let ((erc-session-username "bob") |
| 89 | (erc-sasl--options `((user . :user) (password) (authfn . ,fn))) | 100 | (erc-sasl--options `((user . :user) (password) (authfn . ,fn))) |
| 90 | (erc-networks--id (erc-networks--id-create 'GNU/chat))) | 101 | (erc-networks--id (erc-networks--id-create 'GNU/chat))) |
| 91 | (should (string= (erc-sasl--read-password nil) "spam")) | 102 | (should (string= (erc-sasl--read-password nil) "spam")) |
| 92 | (should (equal (pop calls) '(:user "bob" :host "GNU/chat"))))) | 103 | (should (equal (pop calls) '(:user "bob"))))) |
| 93 | 104 | ||
| 94 | (ert-info ("ID for :host and current nick for :user") ; *1 | 105 | (ert-info (":user resolved to current nick") ; *1 |
| 95 | (let ((erc-server-current-nick "bob") | 106 | (let ((erc-server-current-nick "bob") |
| 96 | (erc-sasl--options `((user . :nick) (password) (authfn . ,fn))) | 107 | (erc-sasl--options `((user . :nick) (password) (authfn . ,fn))) |
| 97 | (erc-networks--id (erc-networks--id-create 'GNU/chat))) | 108 | (erc-networks--id (erc-networks--id-create 'GNU/chat))) |
| 98 | (should (string= (erc-sasl--read-password nil) "spam")) | 109 | (should (string= (erc-sasl--read-password nil) "spam")) |
| 99 | (should (equal (pop calls) '(:user "bob" :host "GNU/chat"))))) | 110 | (should (equal (pop calls) '(:user "bob"))))) |
| 100 | 111 | ||
| 101 | (ert-info ("Symbol as password, entry lacks user field") | 112 | (ert-info ("Symbol as password, entry lacks user field") |
| 102 | (let ((erc-server-current-nick "fake") | 113 | (let ((erc-server-current-nick "fake") |
| @@ -104,7 +115,10 @@ | |||
| 104 | `((user . :nick) (password . MyHost) (authfn . ,fn))) | 115 | `((user . :nick) (password . MyHost) (authfn . ,fn))) |
| 105 | (erc-networks--id (erc-networks--id-create 'GNU/chat))) | 116 | (erc-networks--id (erc-networks--id-create 'GNU/chat))) |
| 106 | (should (string= (erc-sasl--read-password nil) "123")) | 117 | (should (string= (erc-sasl--read-password nil) "123")) |
| 107 | (should (equal (pop calls) '(:user "fake" :host "MyHost")))))))) | 118 | (should (equal (pop calls) '(:user "fake" :host "MyHost"))))) |
| 119 | |||
| 120 | (advice-remove 'erc-auth-source-search | ||
| 121 | 'erc-sasl--read-password--auth-source)))) | ||
| 108 | 122 | ||
| 109 | (ert-deftest erc-sasl-create-client--plain () | 123 | (ert-deftest erc-sasl-create-client--plain () |
| 110 | (let* ((erc-session-password "password123") | 124 | (let* ((erc-session-password "password123") |