aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorF. Jason Park2022-11-23 21:31:19 -0800
committerAmin Bandali2022-11-29 00:01:13 -0500
commit00de296d1b4f629fd828cdeff588bb4f742d9ffe (patch)
tree7b791016be2034d15c749ab0206957a164cbc389
parent83b9496a193c41ed7e41b36838727e234c81a2d1 (diff)
downloademacs-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.texi40
-rw-r--r--lisp/erc/erc-sasl.el77
-rw-r--r--test/lisp/erc/erc-sasl-tests.el38
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
1057You can also set this to a nonemtpy string, and ERC will send that 1057You can also set this to a nonemtpy string, and ERC will send that
1058when needed, no questions asked. If you instead give a non-@code{nil} 1058when needed, no questions asked. Or, if you'd rather use auth-source,
1059symbol (other than @code{:password}), like @samp{Libera.Chat}, ERC 1059set @code{erc-sasl-auth-source-function} to a function, and ERC will
1060will use it for the @code{:host} field in an auth-source query. 1060perform an auth-source query instead. As last resort in all cases,
1061Actually, the same goes for when this option is @code{nil} but an 1061ERC will prompt you for input.
1062explicit session ID is already on file (@pxref{Network Identifier}).
1063For all such queries, ERC specifies the resolved value of
1064@code{erc-sasl-user} for the @code{:user} (@code{:login}) param. Keep
1065in 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
1068input.
1069 1062
1070Lastly, if your mechanism is @code{ecdsa-nist256p-challenge}, this 1063Lastly, if your mechanism is @code{ecdsa-nist256p-challenge}, this
1071option should instead hold the file name of your key. 1064option should instead hold the file name of your key.
@@ -1075,7 +1068,23 @@ option should instead hold the file name of your key.
1075This is nearly identical to the other ERC @samp{auth-source} function 1068This is nearly identical to the other ERC @samp{auth-source} function
1076options (@pxref{ERC auth-source functions}) except that the default 1069options (@pxref{ERC auth-source functions}) except that the default
1077value here is @code{nil}, meaning you have to set it to something like 1070value 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
1072convenience, this module provides the following as a possible value:
1073
1074@defun erc-sasl-auth-source-password-as-host &rest plist
1075Setting @code{erc-sasl-auth-source-function} to this function tells
1076ERC to use @code{erc-sasl-password} for the @code{:host} field when
1077querying 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
1080non-@code{nil}. Otherwise, ERC just defers to
1081@code{erc-auth-source-search} to determine the @code{:host}, along
1082with everything else.
1083@end defun
1084
1085As 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
1152You've started storing your credentials with auth-source and have 1162You'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.
80When the value is a string, ERC will use it unconditionally for 80When `erc-sasl-auth-source-function' is a function, ERC will
81most mechanisms. Likewise with `:password', except ERC will 81attempt an auth-source query and prompt for input if it fails.
82instead use the \"session password\" on file, which often 82Otherwise, when the value is a nonempty string, ERC will use it
83originates from the entry-point commands `erc' or `erc-tls'. 83unconditionally for most mechanisms. Likewise with `:password',
84Otherwise, when `erc-sasl-auth-source-function' is a function, 84except ERC will instead use the \"session password\" on file, if
85ERC will attempt an auth-source query, possibly using a non-nil 85any, which often originates from the entry-point commands `erc'
86symbol for the suggested `:host' parameter if set as this 86or `erc-tls'. As with auth-source, ERC will prompt for input as
87option's value or passed as an `:id' to `erc-tls'. Failing that, 87a fallback.
88ERC will prompt for input.
89 88
90Note that, with `:password', ERC will forgo sending a traditional 89Note that, with `:password', ERC will forgo sending a traditional
91server password via the IRC \"PASS\" command. Also, when 90server 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.
98Called with keyword params known to `auth-source-search', which 97If provided, this function should expect to be called with any
99includes `erc-sasl-user' for the `:user' field and 98number of keyword params known to `auth-source-search', even
100`erc-sasl-password' for the `:host' field, when the latter option 99though ERC itself only specifies `:user' paired with a
101is a non-nil, non-keyword symbol. In return, ERC expects a 100\"resolved\" `erc-sasl-user' value. When calling this function,
102string to send as the SASL password, or nil, to move on to the 101ERC binds all options defined in this library, such as
103next 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 103In return, ERC expects a string to send as the SASL password, or
105details on ERC's auth-source integration." 104nil, in which case, ERC will prompt the for input. See info
106 :type '(choice (function-item erc-auth-source-search) 105node `(erc) Connecting' for details on ERC's auth-source
106integration."
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'.
137But only do so when it's a string or a non-nil symbol, unless
138that 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
141PLIST 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.
135PROMPT is passed to `read-passwd' if necessary." 152If 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")