From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: "J.P." Newsgroups: gmane.emacs.bugs Subject: bug#48598: Questions regarding auth-source integration (bug#48598) Date: Sat, 09 Apr 2022 14:22:15 -0700 Message-ID: <87bkxaeyuw.fsf__35309.7873668716$1649539401$gmane$org@neverwas.me> References: <875yzakzvi.fsf@neverwas.me> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="29762"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/29.0.50 (gnu/linux) Cc: Damien Cassou , Sam Steingold , emacs-erc@gnu.org To: 48598@debbugs.gnu.org Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Sat Apr 09 23:23:11 2022 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1ndIY2-0007TO-8L for geb-bug-gnu-emacs@m.gmane-mx.org; Sat, 09 Apr 2022 23:23:10 +0200 Original-Received: from localhost ([::1]:44140 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ndIY1-0008Hs-6k for geb-bug-gnu-emacs@m.gmane-mx.org; Sat, 09 Apr 2022 17:23:09 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:40740) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ndIXu-0008HS-6U for bug-gnu-emacs@gnu.org; Sat, 09 Apr 2022 17:23:02 -0400 Original-Received: from debbugs.gnu.org ([209.51.188.43]:44927) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1ndIXt-0001dK-Sy for bug-gnu-emacs@gnu.org; Sat, 09 Apr 2022 17:23:01 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1ndIXt-0004Iz-ON for bug-gnu-emacs@gnu.org; Sat, 09 Apr 2022 17:23:01 -0400 X-Loop: help-debbugs@gnu.org In-Reply-To: <875yzakzvi.fsf@neverwas.me> Resent-From: "J.P." Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sat, 09 Apr 2022 21:23:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 48598 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch Original-Received: via spool by 48598-submit@debbugs.gnu.org id=B48598.164953935916520 (code B ref 48598); Sat, 09 Apr 2022 21:23:01 +0000 Original-Received: (at 48598) by debbugs.gnu.org; 9 Apr 2022 21:22:39 +0000 Original-Received: from localhost ([127.0.0.1]:38824 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ndIXQ-0004IG-1i for submit@debbugs.gnu.org; Sat, 09 Apr 2022 17:22:38 -0400 Original-Received: from mail-108-mta186.mxroute.com ([136.175.108.186]:39285) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ndIXL-0004I1-6J for 48598@debbugs.gnu.org; Sat, 09 Apr 2022 17:22:30 -0400 Original-Received: from filter006.mxroute.com ([140.82.40.27] 140.82.40.27.vultrusercontent.com) (Authenticated sender: mN4UYu2MZsgR) by mail-108-mta186.mxroute.com (ZoneMTA) with ESMTPSA id 1801034dc10000fe85.002 for <48598@debbugs.gnu.org> (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES128-GCM-SHA256); Sat, 09 Apr 2022 21:22:21 +0000 X-Zone-Loop: d7fbbcc8252acfe5ab6ab8fa4bbdb8d63986f0b7abce X-Originating-IP: [140.82.40.27] DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=neverwas.me ; s=x; h=Content-Type:MIME-Version:Message-ID:Date:References:Subject:Cc:To: From:Sender:Reply-To:Content-Transfer-Encoding:Content-ID:Content-Description :Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: In-Reply-To:List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post: List-Owner:List-Archive; bh=H84EQ2ltLHUwDXxnjjrZCQvFsOZBaUQ6RIvU9RpCGrc=; b=Y 66iJWlV3nEfCFHs6CCqqb9P1nyl9ljSYE8RXVOcSdXwPntCHPGCnofGjIbNhQdjJldtuDiWIlmltR fI3Ebv8e6LLGXSjcgtPnIgjyfoOUAfXKo9fdoXj91DD6FIOUJ5Che6G0kGChmPMsq3kQ+QLGTVuB6 ZkbnANuRgje6gCarmFwT2xYK89QzjBEUs9Vfs3NadU4em/WSS/oEOgdEoEp8YIQAFkX3jUK0UuPQR PnaBrzo3ozq6Jjo/mxkmWKhBoASht22mAkpSFeCzRgf2q2osAQBQIp3/0AaiVNH0z5tRAvK2z436x mUtiZmxNk9c2Wwa9j0ki+zt0jGwYX9DUw==; X-AuthUser: masked@neverwas.me X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.io gmane.emacs.bugs:229639 Archived-At: --=-=-= Content-Type: text/plain Hi people, I Cc'd Damien because a few of these questions involve auth-source-pass, and also Sam because they recently touched related areas in ERC. But anyone in the know, please don't hold back. Your input is welcome. In this bug set, I'm trying to make ERC smarter about how it integrates with auth-source. The current implementation suffers directly from the central problem this bug aims to address, namely, ERC's inability to understand the concept of logical (IRC) connections and how they relate to physical (TCP) connections. Strategies stemming from this improved awareness require a prioritizing of auth-source entries, not only when choosing query parameters but when filtering returned results. In particular, various compatibility crutches were required to make auth-source-pass produce output conforming to the shape and substance delivered by the netrc back end. The following patch set contains various unsightly machinations meant to force a more agreeable outcome (agreeable to ERC, that is). What I'm seeking from you (all) is confirmation that these patches don't suffer from any glaring misconceptions regarding the API. Note that I'm certainly *not* requesting any upstream changes, perhaps in part because ERC is tethered to the past (currently 27.2), so we'd have to wait an eternity before reaping any reward. Anyway, when you have a sec, please glance briefly at the following areas, all of them located in the first patch attached below: 1. The new option `erc-auth-source-parameters-function'. 2. The ERT tests residing in test/lisp/erc/erc-services-tests.el. Those should convey a feel for the behavior ERC is expecting. 3. The various business-related functions that touch the API in lisp/erc/erc.el. 4. The ugly adapters touching internal auth-source functions in lisp/erc/erc-compat.el. The reaction I'm hoping for is something along the lines of "I see what you're doing, and no, auth-source doesn't provide that OOTB, so knock yourself out" or, conversely, "lo, you're going about that all wrong, use this instead." Thanks, J.P. P.S. For additional context, the full patch series can be found at https://jpneverwas.gitlab.io/erc-tools/48598/patches.tar.gz and additional integrations involving SASL can be found in #49860. --=-=-= Content-Type: text/x-patch; charset=utf-8 Content-Disposition: attachment; filename=0024-Standardize-auth-source-queries-in-ERC.patch Content-Transfer-Encoding: quoted-printable >From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Mon, 16 Aug 2021 04:38:18 -0700 Subject: [PATCH 24/34] Standardize auth-source queries in ERC * lisp/erc/erc.el (erc-password): deprecate variable only used by `erc-select-read-args'. Server passwords are primarily used as surrogates for other forms of authentication. Such use is common but nonstandard and often discouraged in favor of the de facto standard, SASL. Fans of invoking `erc(-tls)' interactively should be coerced into using auth-source instead. (erc-select-read-args): Before this change, `erc-select-read-args' offered to use the value of a non-nil `erc-password' as the :password argument for `erc' and `erc-tls', referring to it as the "default" password. And when `erc-prompt-for-password' was nil and `erc-password' wasn't, the latter was passed along unconditionally. This only further complicated an already confusing situation for new users, who in most cases shouldn't be worried about sending a PASS command at all. Until SASL arrives, they should provide server passwords manually or learn to use auth-source. (erc-auth-source-parameters-function): New user option to provide a function for determining the default params to use when calling `auth-source-search'. (erc--auth-source-determine-params): New helper for `erc--auth-source-search' with potential for wider role as default value of custom function. Favors :host and :port fields above others. Prioritizes session IDs over announced servers and dialed endpoints. (erc--auth-source-search): New function for consulting auth-source and sorting result as per default params provided by above functions. (erc-server-join-channel): Use helper for consulting auth-source facility. Also accept nil for first argument (instead of server). In this case, allow default params option above to determine best course of action. (erc-cmd-JOIN): use above-mentioned facilities when joining new channel. Omit server when calling `erc-server-join-channel'. Don't filter target buffers twice. Don't call `switch-to-buffer', which would create phantom buffers with names like target/server that were never used. IOW, only switch to existing target buffers. (erc-open, erc-determine-parameters, erc-compute-password): Move password figuring from former to latter, and from there to `erc-compute-password', which is a new function that figures out how to call `auth-source-search' based on the value of the new option `erc-connect-auth-source-host'. (erc-connect-auth-source-host): Add new option for customizing the :host param passed to `auth-source-search' while looking up the initial PASS arg. The default setting preserves existing behavior of matching against the dialed host name or IP address stored in `erc-session-server'. Other options allow skipping auth-source lookup altogether or favoring network ID, when non-nil. * lisp/erc/erc-services.el (erc-nickserv-get-password): pass network ID, i.e., effective session ID, when looking up password in `erc-nickserv-passwords' and when formatting prompt for user input. (erc-nickserv-passwords): add comment to custom option definition type tag. * test/lisp/erc/erc-services-tests.el: add new test file for above changes. For now, also store auth-source-related tests belonging in erc-tests.el here. * lisp/erc/erc-join.el (erc-autojoin--join): Don't pass session-like entity from `erc-autojoin-alist' match to `erc-server-join-channel'. Allow that function to decide for itself which host to look up if necessary. * lisp/erc/erc-compat.el (erc-compat--auth-source-pass--couch, erc-compat--auth-source-pass--find-match, erc-compat--auth-source-pass--build-result, erc-compat--auth-source-pass-search, erc-compat--auth-source-pass-backend-parse): Add some adapters to make auth-source-pass behave more like netrc in ways ERC relies on. --- lisp/erc/erc-compat.el | 86 +++++++ lisp/erc/erc-join.el | 2 +- lisp/erc/erc-services.el | 40 ++-- lisp/erc/erc.el | 200 ++++++++++++---- test/lisp/erc/erc-services-tests.el | 358 ++++++++++++++++++++++++++++ 5 files changed, 614 insertions(+), 72 deletions(-) create mode 100644 test/lisp/erc/erc-services-tests.el diff --git a/lisp/erc/erc-compat.el b/lisp/erc/erc-compat.el index 16cfb15a5a..a833a61456 100644 --- a/lisp/erc/erc-compat.el +++ b/lisp/erc/erc-compat.el @@ -150,6 +150,92 @@ erc-subseq (setq i (1+ i) start (1+ start))) res)))))) =20 +;;;; Auth Source + +;; We want a unified interface to auth-source, but that depends on +;; upstream providing a consistent experience. As of at least +;; +;; lisp/auth-source-pass.el: Support multiple hosts in search +;; b09ee1406205e8b6298411b9a18c1cd26e201689 Fri Jul 2 2021 +;; +;; auth-source-pass only returns singletons on success. But we want +;; all possible matches. This provides some hacks to do that, but it +;; depends on internal functions. We also need to pass lists of +;; candidates for host, user, and port selectors, which aren't yet +;; fully supported. +;; + +(require 'auth-source) + +(declare-function auth-source-pass--get-attr + "auth-source-pass" (key entry-data)) +(declare-function auth-source-pass--disambiguate + "auth-source-pass" (host &optional user port)) +(declare-function auth-source-pass--find-match-unambiguous + "auth-source-pass" (hostname user port)) +(declare-function auth-source-backend-parse-parameters + "auth-source-pass" (entry backend)) + +(defun erc-compat--auth-source-pass--couch (s) + (lambda () (auth-source-pass--get-attr 'secret s))) + +(defun erc-compat--auth-source-pass--find-match (hosts ports users) + "Return a plist of HOSTS, PORTS, USERS, and secret. +This is not a drop-in for `auth-source-pass--find-match', which +returns an alist." + (unless (listp hosts) (setq hosts (list hosts))) + (unless (listp users) (setq users (list users))) + (unless (listp ports) (setq ports (list ports))) + ;; Try combinations of Hosts x Users x Ports, filter out nonexistent + (cl-loop for host in hosts + for (h u p) =3D (auth-source-pass--disambiguate host) + append + (cl-loop for user in (or users (list u)) + append + (cl-loop for port in (or ports (list p)) + for s =3D (auth-source-pass--find-match-unamb= iguous + h user port) + when s collect + ;; Keep original host + `(:host + ,host + ,@(and user (list :user user)) + ,@(and port (list :port port)) + :secret + ,(erc-compat--auth-source-pass--couch s))))= )) + +(defun erc-compat--auth-source-pass--build-result (hosts ports users + &optional max) + "Multi-valued `auth-source-pass--build-result'." + (unless max (setq max 1)) + (let ((entries (erc-compat--auth-source-pass--find-match hosts ports use= rs)) + (count -1) + entry + out) + (while (and (setq entry (pop entries)) (< (cl-incf count) max)) + (push entry out)) + out)) + +(cl-defun erc-compat--auth-source-pass-search + (&rest spec &key backend type host user port max &allow-other-keys) + (cl-assert (or (null type) (eq type (oref backend type))) + t "Invalid password-store search: %s %s") + (cl-assert (and host (not (eq host t))) + t "Invalid password-store search: %s %s") + (erc-compat--auth-source-pass--build-result host port user max)) + +;; Temporary until we decide whether to load compat by default + +;;;###autoload +(defun erc-compat--auth-source-pass-backend-parse (entry) + (when (eq entry 'password-store) + (auth-source-backend-parse-parameters + entry (auth-source-backend + :source "." + :type 'password-store + :search-function #'erc-compat--auth-source-pass-search)))) + + (provide 'erc-compat) =20 ;;; erc-compat.el ends here diff --git a/lisp/erc/erc-join.el b/lisp/erc/erc-join.el index fcfb961bff..b812dfc512 100644 --- a/lisp/erc/erc-join.el +++ b/lisp/erc/erc-join.el @@ -141,7 +141,7 @@ erc-autojoin--join (let ((buf (erc-get-buffer chan erc-server-process))) (unless (and buf (with-current-buffer buf (erc--current-buffer-joined-p))) - (erc-server-join-channel match chan))))))) + (erc-server-join-channel nil chan))))))) =20 (defun erc-autojoin-after-ident (_network _nick) "Autojoin channels in `erc-autojoin-channels-alist'. diff --git a/lisp/erc/erc-services.el b/lisp/erc/erc-services.el index cc5d5701e4..f042a52250 100644 --- a/lisp/erc/erc-services.el +++ b/lisp/erc/erc-services.el @@ -202,7 +202,7 @@ erc-nickserv-passwords (const QuakeNet) (const Rizon) (const SlashNET) - (symbol :tag "Network name")) + (symbol :tag "Network name or session ID")) (repeat :tag "Nickname and password" (cons :tag "Identity" (string :tag "Nick") @@ -431,31 +431,19 @@ erc-nickserv-get-password lookups stops and this function returns it (or returns nil if it is empty). Otherwise, no corresponding password was found, and it returns nil." - (let (network server port) - ;; Fill in local vars, switching to the server buffer once only - (erc-with-server-buffer - (setq network erc-network - server erc-session-server - port erc-session-port)) - (let ((ret - (or - (when erc-nickserv-passwords - (cdr (assoc nick - (cl-second (assoc network - erc-nickserv-passwords))))) - (when erc-use-auth-source-for-nickserv-password - (auth-source-pick-first-password - :require '(:secret) - :host server - ;; Ensure a string for :port - :port (format "%s" port) - :user nick)) - (when erc-prompt-for-nickserv-password - (read-passwd - (format "NickServ password for %s on %s (RET to cancel): " - nick network)))))) - (when (and ret (not (string=3D ret ""))) - ret)))) + (when-let* + ((esid (erc-networks--id-symbol erc-networks--id)) + (ret (or (when erc-nickserv-passwords + (assoc-default nick + (cadr (assq esid erc-nickserv-passwords))= )) + (when erc-use-auth-source-for-nickserv-password + (erc--auth-source-search :user nick)) + (when erc-prompt-for-nickserv-password + (read-passwd + (format "NickServ password for %s on %s (RET to cancel)= : " + nick esid))))) + ((not (string-empty-p ret)))) + ret)) =20 (defvar erc-auto-discard-away) =20 diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index 230cfe456f..f25b2f2305 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -227,9 +227,14 @@ erc-rename-buffers "old behavior when t now permanent" "29.1") =20 (defvar erc-password nil - "Password to use when authenticating to an IRC server. -It is not strictly necessary to provide this, since ERC will -prompt you for it.") + "Password to use when authenticating to an IRC server interactively. + +This variable only exists for legacy reasons. It's not customizable and +is limited to a single server password. Users looking for similar +functionality should consider auth-source instead. See info +node `(auth) Top' and info node `(erc) Connecting'.") + +(make-obsolete-variable 'erc-password "use auth-source instead" "29.1") =20 (defcustom erc-user-mode "+i" ;; +i "Invisible". Hides user from global /who and /names. @@ -240,10 +245,32 @@ erc-user-mode =20 =20 (defcustom erc-prompt-for-password t - "Asks before using the default password, or whether to enter a new one." + "Ask for a server password when invoking `erc-tls' interactively." :group 'erc :type 'boolean) =20 +(defcustom erc-connect-auth-source-host 'server + "Host \"type\" for querying auth-source when first connecting. +This is for determining the \"server password\" argument of the IRC +\"PASS\" command sent to the server. The entry points `erc' and +`erc-tls' query auth-source for such a password when a :password +argument isn't provided. Because ERC also interfaces with auth-source +for other secrets, such as NickServ passwords and channel keys, +additional ways of selecting entries are sometimes necessary. See info +node `(auth) Top'. + +Note that there aren't any options for specifying a network, like +Libera.Chat, or a network-specific server, such as foo.libera.chat, +because such information isn't available until after initial +introductions have completed (\"registration\" in IRC speak)." + :package-version '(ERC . "5.4.1") ; FIXME increment upon publishing to E= LPA + :group 'erc + :type '(choice (const :tag "Don't query auth-source" nil) + (const :tag "Dialed host name or IP address" server) + (const :tag "Prompt for a machine/host value" prompt) + (const :tag "Session ID, if set, otherwise server" t) + (string :tag "Literal value to use for :host"))) + (defcustom erc-warn-about-blank-lines t "Warn the user if they attempt to send a blank line." :group 'erc @@ -2160,15 +2187,6 @@ erc-open (setq erc-logged-in nil) ;; The local copy of `erc-nick' - the list of nicks to choose (setq erc-default-nicks (if (consp erc-nick) erc-nick (list erc-nick))) - ;; password stuff - (setq erc-session-password - (or passwd - (auth-source-pick-first-password - :host server - :user nick - ;; secrets.el wouldn=E2=80=99t accept a number - :port (if (numberp port) (number-to-string port) port) - :require '(:secret)))) ;; client certificate (only useful if connecting over TLS) (setq erc-session-client-certificate client-certificate) (setq erc-networks--id (if connect @@ -2190,7 +2208,7 @@ erc-open (erc-display-prompt) (goto-char (point-max))) =20 - (erc-determine-parameters server port nick full-name user) + (erc-determine-parameters server port nick full-name user passwd) =20 ;; Saving log file on exit (run-hook-with-args 'erc-connect-pre-hook buffer) @@ -2288,11 +2306,9 @@ erc-select-read-args (setq server user-input) =20 (setq passwd (if erc-prompt-for-password - (if (and erc-password - (y-or-n-p "Use the default password? ")) - erc-password - (read-passwd "Password: ")) - erc-password)) + (read-passwd "Server password: ") + (with-suppressed-warnings ((obsolete erc-password)) + erc-password))) (when (and passwd (string=3D "" passwd)) (setq passwd nil)) =20 @@ -3305,18 +3321,104 @@ erc-cmd-HELP (defalias 'erc-cmd-H #'erc-cmd-HELP) (put 'erc-cmd-HELP 'process-not-needed t) =20 +(defcustom erc-auth-source-parameters-function + #'erc--auth-source-determine-params + "A function providing args to pass to `auth-source-search'. +This is called with no arguments and should return a plist of keyword +args accepted by `auth-source-search'. The ordering of the pairs +influences how results are filtered as does the ordering of the members +of any composite pair values, when applicable. If necessary, the former +takes priority over the latter. For example, if the function returns + + (:host (foo bar) :port (123 456) :require (:secret)) + +the secret from an auth-source entry of host foo and port 456 +will be chosen over another of host bar and port 123. However, +if the function returns + + (:port (123 456) :host (foo bar) :require (:secret)) + +the opposite will be true. In both cases, two entries with the same +host but different ports would see the one with port 123 being selected. +Much the same would happen for entries sharing only a port: the one with +host foo would win. + +Some auth-source back ends may not be compatible (netrc and pass are +currently supported)." + :package-version '(ERC . "5.4.1") ; FIXME increment upon publishing to E= LPA + :group 'erc + :type 'function) + +(defun erc--auth-source-determine-params () + "Return a plist of default args to pass to `auth-source-search'. +Favor a network ID over an announced server unless `erc--target' is a +local channel. Treat the dialed server address as a fallback for the +announced name in both cases." + (let* ((net (and-let* ((esid (erc-networks--id-symbol erc-networks--id)) + ((symbol-name esid))))) + (localp (and erc--target (erc--target-channel-local-p erc--target= ))) + (hosts (if localp + (list erc-server-announced-name erc-session-server net) + (list net erc-server-announced-name erc-session-server))) + (ports (list (cl-typecase erc-session-port + (integer (number-to-string erc-session-port)) + (string (and (string=3D erc-session-port "irc") + erc-session-port)) ; or nil + (t erc-session-port)) + "irc"))) + (list :host (delq nil hosts) + :port (delq nil ports) + :require '(:secret)))) + +(declare-function erc-compat--auth-source-pass-backend-parse + "erc-compat" (entry)) + +(defun erc--auth-source-search (&rest plist) + "Ask auth-source for a secret and return it if found. +Favor overrides in PLIST, if any. Otherwise, use whatever's present in +the list returned by `erc-auth-source-parameters-function'. Return a +string if found or nil otherwise." + (let* ((auth-source-backend-parser-functions + (if (memq 'password-store auth-sources) + (cons #'erc-compat--auth-source-pass-backend-parse + auth-source-backend-parser-functions) + auth-source-backend-parser-functions)) + (defaults (funcall erc-auth-source-parameters-function)) + priority + (test (lambda (a b) + (catch 'done + (dolist (key priority) + (let* ((d (plist-get defaults key)) + (default-value (if (listp d) d (list d))) + ;; featurep 'seq via auth-source > json > map + (p (seq-position default-value (plist-get a ke= y))) + (q (seq-position default-value (plist-get b ke= y)))) + (unless (eql p q) + (throw 'done (when p (or (not q) (< p q))))))))))) + (cl-loop for (key value) on defaults by #'cddr + when value unless (plist-get plist key) + do (setq plist (plist-put plist key value))) + (let ((keys (nreverse (map-keys defaults)))) + (dolist (key (map-keys plist)) + (cl-pushnew key keys)) + (setq priority (nreverse keys))) + (unless (plist-get plist :max) ; from `auth-source-netrc-parse' + (setq plist (plist-put plist :max 5000))) + (when-let* ((sorted (sort (apply #'auth-source-search plist) test)) + (secret (plist-get (car sorted) :secret))) + (if (functionp secret) (funcall secret) secret)))) + (defun erc-server-join-channel (server channel &optional secret) - (let ((password - (or secret - (auth-source-pick-first-password - :host server - :port "irc" - :user channel)))) - (erc-log (format "cmd: JOIN: %s" channel)) - (erc-server-send (concat "JOIN " channel - (if password - (concat " " password) - ""))))) + "Join CHANNEL, optionally with SECRET. +Without SECRET, consult auth source, using SERVER if non-nil." + (unless secret + (unless server + (when (and erc-server-announced-name (erc-valid-local-channel-p chan= nel)) + (setq server erc-server-announced-name))) + (let ((args `(,@(when server (list :host server)) :user channel))) + (setq secret (apply #'erc--auth-source-search args)))) + (erc-log (format "cmd: JOIN: %s" channel)) + (erc-server-send (concat "JOIN " channel (when secret (concat " " secret= ))))) =20 (defun erc-valid-local-channel-p (channel) "Non-nil when channel is server-local on a network that allows them." @@ -3338,19 +3440,12 @@ erc-cmd-JOIN (setq chnl (erc-ensure-channel-name channel))) (when chnl ;; Prevent double joining of same channel on same server. - (let* ((joined-channels - (mapcar (lambda (chanbuf) - (with-current-buffer chanbuf (erc-default-target))) - (erc-channel-list erc-server-process))) - (server (with-current-buffer (process-buffer erc-server-proce= ss) - (or erc-session-server erc-server-announced-name))) - (chnl-name (car (erc-member-ignore-case chnl joined-channels)= ))) - (if chnl-name - (switch-to-buffer (if (get-buffer chnl-name) - chnl-name - (concat chnl-name "/" server))) - (setq erc--server-last-reconnect-count 0) - (erc-server-join-channel server chnl key))))) + (if-let* ((existing (erc-get-buffer chnl erc-server-process)) + ((with-current-buffer existing + (erc-get-channel-user (erc-current-nick))))) + (switch-to-buffer existing) + (setq erc--server-last-reconnect-count 0) + (erc-server-join-channel nil chnl key)))) t) =20 (defalias 'erc-cmd-CHANNEL #'erc-cmd-JOIN) @@ -6305,7 +6400,7 @@ erc-login =20 ;; connection properties' heuristics =20 -(defun erc-determine-parameters (&optional server port nick name user) +(defun erc-determine-parameters (&optional server port nick name user pass= wd) "Determine the connection and authentication parameters. Sets the buffer local variables: =20 @@ -6314,12 +6409,14 @@ erc-determine-parameters - `erc-session-port' - `erc-session-user-full-name' - `erc-session-username' +- `erc-session-password' - `erc-server-current-nick'" (setq erc-session-connector erc-server-connect-function erc-session-server (erc-compute-server server) erc-session-port (or port erc-default-port) erc-session-user-full-name (erc-compute-full-name name) - erc-session-username (erc-compute-user user)) + erc-session-username (erc-compute-user user) + erc-session-password (erc-compute-server-password passwd nick)) (erc-set-current-nick (erc-compute-nick nick))) =20 (defun erc-compute-server (&optional server) @@ -6356,6 +6453,19 @@ erc-compute-nick (getenv "IRCNICK") (user-login-name))) =20 +(defun erc-compute-server-password (password nick) + "Determine initial PASSWORD value for IRC PASS command. +Use the value of `erc-connect-auth-source-host' to determine the +machine/host query param. Use NICK for the user/login query param." + (or password + (when erc-connect-auth-source-host + (let* ((host (pcase erc-connect-auth-source-host + ('server erc-session-server) + ((and (pred stringp) v) v) + ('prompt (read-string "Auth-source host: " + nil t (list nil))))) + (args `(,@(when host (list :host host)) :user ,nick))) + (apply #'erc--auth-source-search args))))) =20 (defun erc-compute-full-name (&optional full-name) "Return user's full name. diff --git a/test/lisp/erc/erc-services-tests.el b/test/lisp/erc/erc-servic= es-tests.el new file mode 100644 index 0000000000..f954d4a77e --- /dev/null +++ b/test/lisp/erc/erc-services-tests.el @@ -0,0 +1,358 @@ +;;; erc-services-tests.el --- Tests for erc-services. -*- lexical-binding= :t -*- + +;; Copyright (C) 2020-2021 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. +;; +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published +;; by the Free Software Foundation, either version 3 of the License, +;; or (at your option) any later version. +;; +;; GNU Emacs is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Commentary: + +;; For convenience, some tests involving core auth-source +;; functionality have been stashed here for the time being. + +;;; Code: + +(require 'ert-x) +(require 'erc-services) +(require 'erc-compat) + +;;;; Core auth-source + +;; Some of the following may be related to bug#23438. + +(defvar erc-join-tests--auth-source-entries + '("machine irc.gnu.org port irc user \"#chan\" password bar" + "machine my.gnu.org port irc user \"#chan\" password baz" + "machine GNU.chat port irc user \"#chan\" password foo")) + +(defun erc-services-tests--auth-source-shuffle (&rest extra) + (string-join `(,@(sort (append erc-join-tests--auth-source-entries extra) + (lambda (&rest _) (zerop (random 2)))) + "") + "\n")) + +(ert-deftest erc--auth-source-search--standard () + (ert-with-temp-file netrc-file + :prefix "erc--auth-source-search--standard" + :text (erc-services-tests--auth-source-shuffle) + (let ((auth-sources (list netrc-file)) + (auth-source-do-cache nil)) + + (ert-info ("Normal ordering") + + (ert-info ("Session wins") + (let ((erc-session-server "irc.gnu.org") + (erc-server-announced-name "my.gnu.org") + (erc-session-port 6697) + (erc-network 'fake) + (erc-server-current-nick "tester") + (erc-networks--id (erc-networks--id-create 'GNU.chat))) + (should (string=3D (erc--auth-source-search :user "#chan") + "foo")))) + + (ert-info ("Network wins") + (let* ((erc-session-server "irc.gnu.org") + (erc-server-announced-name "my.gnu.org") + (erc-session-port 6697) + (erc-network 'GNU.chat) + (erc-server-current-nick "tester") + (erc-networks--id (erc-networks--id-create nil))) + (should (string=3D (erc--auth-source-search :user "#chan") + "foo")))) + + (ert-info ("Announced wins") + (let ((erc-session-server "irc.gnu.org") + (erc-server-announced-name "my.gnu.org") + (erc-session-port 6697) + erc-network + (erc-networks--id (erc-networks--id-create nil))) + (should (string=3D (erc--auth-source-search :user "#chan") + "baz")))))))) + +(ert-deftest erc--auth-source-search--announced () + (ert-with-temp-file netrc-file + :prefix "erc--auth-source-search--announced" + :text (erc-services-tests--auth-source-shuffle) + (let* ((auth-sources (list netrc-file)) + (auth-source-do-cache nil) + (erc--isupport-params (make-hash-table)) + (erc-server-parameters '(("CHANTYPES" . "&#"))) + (erc--target (erc--target-from-string "&chan"))) + + (ert-info ("Announced prioritized") + + (ert-info ("Announced wins") + (let* ((erc-session-server "irc.gnu.org") + (erc-server-announced-name "my.gnu.org") + (erc-session-port 6697) + (erc-network 'GNU.chat) + (erc-server-current-nick "tester") + (erc-networks--id (erc-networks--id-create nil))) + (should (string=3D (erc--auth-source-search :user "#chan") + "baz")))) + + (ert-info ("Peer next") + (let* ((erc-server-announced-name "irc.gnu.org") + (erc-session-port 6697) + (erc-network 'GNU.chat) + (erc-server-current-nick "tester") + (erc-networks--id (erc-networks--id-create nil))) + (should (string=3D (erc--auth-source-search :user "#chan") + "bar")))) + + (ert-info ("Network used as fallback") + (let* ((erc-session-port 6697) + (erc-network 'GNU.chat) + (erc-server-current-nick "tester") + (erc-networks--id (erc-networks--id-create nil))) + (should (string=3D (erc--auth-source-search :user "#chan") + "foo")))))))) + +(ert-deftest erc--auth-source-search--overrides () + (ert-with-temp-file netrc-file + :prefix "erc--auth-source-search--overrides" + :text (erc-services-tests--auth-source-shuffle + "machine GNU.chat port 6697 user \"#chan\" password spam" + "machine my.gnu.org port irc user \"#fsf\" password 42" + "machine irc.gnu.org port 6667 password sesame" + "machine MyHost port irc password 456" + "machine MyHost port 6667 password 123") + + (let* ((auth-sources (list netrc-file)) + (auth-source-do-cache nil) + (erc-session-server "irc.gnu.org") + (erc-server-announced-name "my.gnu.org") + (erc-network 'GNU.chat) + (erc-server-current-nick "tester") + (erc-networks--id (erc-networks--id-create nil)) + (erc-session-port 6667)) + + (ert-info ("Specificity and overrides") + + (ert-info ("More specific port") + (let ((erc-session-port 6697)) + (should (string=3D (erc--auth-source-search :user "#chan") + "spam")))) + + (ert-info ("More specific user (network loses)") + (should (string=3D (erc--auth-source-search :user '("#fsf")) + "42"))) + + (ert-info ("Actual override") + (should (string=3D (erc--auth-source-search :port "6667") + "sesame"))) + + (ert-info ("Overrides don't interfere with post-processing") + (should (string=3D (erc--auth-source-search :host "MyHost") + "123"))))))) + +;; auth-source-pass backend + +(require 'auth-source-pass) + +;; `auth-source-pass--find-match-unambiguous' returns something like: +;; +;; (list :host "irc.gnu.org" +;; :port "6697" +;; :user "rms" +;; :secret +;; #[0 "\301\302\300\"\207" +;; [((secret . "freedom")) auth-source-pass--get-attr secret] = 3]) +;; +;; This function gives ^ (faked here to avoid gpg and file IO). See +;; `auth-source-pass--with-store' in ../auth-source-pass-tests.el +(defun erc-services-tests--asp-parse-entry (store entry) + (when-let ((found (cl-find entry store :key #'car :test #'string=3D))) + (list (assoc 'secret (cdr found))))) + +(defvar erc-join-tests--auth-source-pass-entries + '(("irc.gnu.org:irc/#chan" + ("port" . "irc") ("user" . "#chan") (secret . "bar")) + ("my.gnu.org:irc/#chan" + ("port" . "irc") ("user" . "#chan") (secret . "baz")) + ("GNU.chat:irc/#chan" + ("port" . "irc") ("user" . "#chan") (secret . "foo")))) + +(ert-deftest erc-services-tests--auth-source-pass--standard () + (let ((store erc-join-tests--auth-source-pass-entries) + (auth-sources '(password-store)) + (auth-source-do-cache nil)) + + (cl-letf (((symbol-function 'auth-source-pass-parse-entry) + (apply-partially #'erc-services-tests--asp-parse-entry stor= e)) + ((symbol-function 'auth-source-pass-entries) + (lambda () (mapcar #'car store)))) + + (ert-info ("Normal ordering") + + (ert-info ("Session wins") + (let ((erc-session-server "irc.gnu.org") + (erc-server-announced-name "my.gnu.org") + (erc-session-port 6697) + (erc-network 'fake) + (erc-server-current-nick "tester") + (erc-networks--id (erc-networks--id-create 'GNU.chat))) + (should (string=3D (erc--auth-source-search :user "#chan") + "foo")))) + + (ert-info ("Network wins") + (let* ((erc-session-server "irc.gnu.org") + (erc-server-announced-name "my.gnu.org") + (erc-session-port 6697) + (erc-network 'GNU.chat) + (erc-server-current-nick "tester") + (erc-networks--id (erc-networks--id-create nil))) + (should (string=3D (erc--auth-source-search :user "#chan") + "foo")))) + + (ert-info ("Announced wins") + (let ((erc-session-server "irc.gnu.org") + (erc-server-announced-name "my.gnu.org") + (erc-session-port 6697) + erc-network + (erc-networks--id (erc-networks--id-create nil))) + (should (string=3D (erc--auth-source-search :user "#chan") + "baz")))))))) + +(ert-deftest erc-services-tests--auth-source-pass--announced () + (let ((store erc-join-tests--auth-source-pass-entries) + (auth-sources '(password-store)) + (auth-source-do-cache nil)) + + (cl-letf (((symbol-function 'auth-source-pass-parse-entry) + (apply-partially #'erc-services-tests--asp-parse-entry stor= e)) + ((symbol-function 'auth-source-pass-entries) + (lambda () (mapcar #'car store)))) + + (let* ((erc--isupport-params (make-hash-table)) + (erc-server-parameters '(("CHANTYPES" . "&#"))) + (erc--target (erc--target-from-string "&chan"))) + + (ert-info ("Announced prioritized") + + (ert-info ("Announced wins") + (let* ((erc-session-server "irc.gnu.org") + (erc-server-announced-name "my.gnu.org") + (erc-session-port 6697) + (erc-network 'GNU.chat) + (erc-server-current-nick "tester") + (erc-networks--id (erc-networks--id-create nil))) + (should (string=3D (erc--auth-source-search :user "#chan") + "baz")))) + + (ert-info ("Peer next") + (let* ((erc-server-announced-name "irc.gnu.org") + (erc-session-port 6697) + (erc-network 'GNU.chat) + (erc-server-current-nick "tester") + (erc-networks--id (erc-networks--id-create nil))) + (should (string=3D (erc--auth-source-search :user "#chan") + "bar")))) + + (ert-info ("Network used as fallback") + (let* ((erc-session-port 6697) + (erc-network 'GNU.chat) + (erc-server-current-nick "tester") + (erc-networks--id (erc-networks--id-create nil))) + (should (string=3D (erc--auth-source-search :user "#chan") + "foo"))))))))) + +(ert-deftest erc-services-tests--auth-source-pass--overrides () + (let* ((store + `(,@erc-join-tests--auth-source-pass-entries + ("GNU.chat:6697/#chan" + ("port" . "6697") ("user" . "#chan") (secret . "spam")) + ("my.gnu.org:irc/#fsf" + ("port" . "irc") ("user" . "#fsf") (secret . "42")) + ("irc.gnu.org:6667" + ("port" . "6667") (secret . "sesame")) + ("MyHost:irc" + ("port" . "irc") (secret . "456")) + ("MyHost:6667" + ("port" . "6667") (secret . "123")))) + (auth-sources '(password-store)) + (auth-source-do-cache nil) + (erc-session-server "irc.gnu.org") + (erc-server-announced-name "my.gnu.org") + (erc-network 'GNU.chat) + (erc-server-current-nick "tester") + (erc-networks--id (erc-networks--id-create nil)) + (erc-session-port 6667)) + + (cl-letf (((symbol-function 'auth-source-pass-parse-entry) + (apply-partially #'erc-services-tests--asp-parse-entry stor= e)) + ((symbol-function 'auth-source-pass-entries) + (lambda () (mapcar #'car store)))) + + (ert-info ("More specific port") + (let ((erc-session-port 6697)) + (should (string=3D (erc--auth-source-search :user "#chan") "spam= ")))) + + (ert-info ("Network wins") + (should (string=3D (erc--auth-source-search :user '("#fsf")) "42")= )) + + (ert-info ("Actual override") + (should (string=3D (erc--auth-source-search :port "6667") "sesame"= ))) + + (ert-info ("Overrides don't interfere with post-processing") + (should (string=3D (erc--auth-source-search :host "MyHost") + "123")))))) + +;;;; The services module + +(ert-deftest erc-nickserv-get-password () + (should erc-prompt-for-nickserv-password) + (ert-with-temp-file netrc-file + :prefix "erc-nickserv-get-password" + :text (mapconcat 'identity + '("machine GNU/chat port 6697 user bob password spam" + "machine FSF.chat port 6697 user bob password sesam= e" + "machine MyHost port irc password 123") + "\n") + + (let* ((auth-sources (list netrc-file)) + (auth-source-do-cache nil) + (erc-nickserv-passwords '((FSF.chat (("alice" . "foo") + ("joe" . "bar"))))) + (erc-use-auth-source-for-nickserv-password t) + (erc-session-server "irc.gnu.org") + (erc-server-announced-name "my.gnu.org") + (erc-network 'FSF.chat) + (erc-server-current-nick "tester") + (erc-networks--id (erc-networks--id-create nil)) + (erc-session-port 6697)) + + (ert-info ("Lookup custom option") + (should (string=3D (erc-nickserv-get-password "alice") "foo"))) + + (ert-info ("Auth source") + (ert-info ("Network") + (should (string=3D (erc-nickserv-get-password "bob") "sesame"))) + + (ert-info ("Network ID") + (let ((erc-networks--id (erc-networks--id-create 'GNU/chat))) + (should (string=3D (erc-nickserv-get-password "bob") "spam")))= )) + + (ert-info ("Read input") + (should (string=3D + (ert-simulate-keys "baz\r" (erc-nickserv-get-password "mi= ke")) + "baz"))) + + (ert-info ("Failed") + (should-not (ert-simulate-keys "\r" + (erc-nickserv-get-password "fake"))))))) + + +;;; erc-services-tests.el ends here --=20 2.35.1 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0025-SQUASH-ME-Add-ERC-test-scenarios-involving-auth-sour.patch >From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Wed, 29 Sep 2021 01:30:16 -0700 Subject: [PATCH 25/34] SQUASH-ME: Add ERC test scenarios involving auth-source XXX this should be combined with the commit entitled "Make auth-source searches session-ID aware in ERC". It was split off for the sake of flexibility during code review. * test/lisp/erc/erc-scenarios.el: Add session-aware scenarios involving the auth-source queries. See bug#48598 for background. --- .../base/auth-source/foonet.eld | 23 +++ .../base/auth-source/nopass.eld | 22 +++ .../services/auth-source/libera.eld | 49 +++++++ test/lisp/erc/erc-scenarios.el | 132 ++++++++++++++++++ 4 files changed, 226 insertions(+) create mode 100644 test/lisp/erc/erc-scenarios-resources/base/auth-source/foonet.eld create mode 100644 test/lisp/erc/erc-scenarios-resources/base/auth-source/nopass.eld create mode 100644 test/lisp/erc/erc-scenarios-resources/services/auth-source/libera.eld diff --git a/test/lisp/erc/erc-scenarios-resources/base/auth-source/foonet.eld b/test/lisp/erc/erc-scenarios-resources/base/auth-source/foonet.eld new file mode 100644 index 0000000000..1fe772c7e2 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-resources/base/auth-source/foonet.eld @@ -0,0 +1,23 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Tue, 04 May 2021 05:06:18 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=FooNet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 1.2 "MODE tester +i") + (0 ":irc.foonet.org 221 tester +i") + (0 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) diff --git a/test/lisp/erc/erc-scenarios-resources/base/auth-source/nopass.eld b/test/lisp/erc/erc-scenarios-resources/base/auth-source/nopass.eld new file mode 100644 index 0000000000..3fdb4ecf7b --- /dev/null +++ b/test/lisp/erc/erc-scenarios-resources/base/auth-source/nopass.eld @@ -0,0 +1,22 @@ +;; -*- mode: lisp-data; -*- +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Tue, 04 May 2021 05:06:18 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=FooNet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 1.2 "MODE tester +i") + (0 ":irc.foonet.org 221 tester +i") + (0 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) diff --git a/test/lisp/erc/erc-scenarios-resources/services/auth-source/libera.eld b/test/lisp/erc/erc-scenarios-resources/services/auth-source/libera.eld new file mode 100644 index 0000000000..c8dbc9d425 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-resources/services/auth-source/libera.eld @@ -0,0 +1,49 @@ +;; -*- mode: lisp-data; -*- +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0.26 ":zirconium.libera.chat NOTICE * :*** Checking Ident") + (0.01 ":zirconium.libera.chat NOTICE * :*** Looking up your hostname...") + (0.01 ":zirconium.libera.chat NOTICE * :*** No Ident response") + (0.02 ":zirconium.libera.chat NOTICE * :*** Found your hostname: static-198-54-131-100.cust.tzulo.com") + (0.02 ":zirconium.libera.chat 001 tester :Welcome to the Libera.Chat Internet Relay Chat Network tester") + (0.01 ":zirconium.libera.chat 002 tester :Your host is zirconium.libera.chat[46.16.175.175/6697], running version solanum-1.0-dev") + (0.03 ":zirconium.libera.chat 003 tester :This server was created Wed Jun 9 2021 at 01:38:28 UTC") + (0.02 ":zirconium.libera.chat 004 tester zirconium.libera.chat solanum-1.0-dev DGQRSZaghilopsuwz CFILMPQSbcefgijklmnopqrstuvz bkloveqjfI") + (0.00 ":zirconium.libera.chat 005 tester ETRACE WHOX FNC MONITOR=100 SAFELIST ELIST=CTU CALLERID=g KNOCK CHANTYPES=# EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLMPQScgimnprstuz :are supported by this server") + (0.03 ":zirconium.libera.chat 005 tester CHANLIMIT=#:250 PREFIX=(ov)@+ MAXLIST=bqeI:100 MODES=4 NETWORK=Libera.Chat STATUSMSG=@+ CASEMAPPING=rfc1459 NICKLEN=16 MAXNICKLEN=16 CHANNELLEN=50 TOPICLEN=390 DEAF=D :are supported by this server") + (0.02 ":zirconium.libera.chat 005 tester TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: EXTBAN=$,ajrxz CLIENTVER=3.0 :are supported by this server") + (0.02 ":zirconium.libera.chat 251 tester :There are 68 users and 37640 invisible on 25 servers") + (0.00 ":zirconium.libera.chat 252 tester 36 :IRC Operators online") + (0.01 ":zirconium.libera.chat 253 tester 5 :unknown connection(s)") + (0.00 ":zirconium.libera.chat 254 tester 19341 :channels formed") + (0.01 ":zirconium.libera.chat 255 tester :I have 3321 clients and 1 servers") + (0.01 ":zirconium.libera.chat 265 tester 3321 4289 :Current local users 3321, max 4289") + (0.00 ":zirconium.libera.chat 266 tester 37708 38929 :Current global users 37708, max 38929") + (0.01 ":zirconium.libera.chat 250 tester :Highest connection count: 4290 (4289 clients) (38580 connections received)") + (0.21 ":zirconium.libera.chat 375 tester :- zirconium.libera.chat Message of the Day - ") + (0.00 ":zirconium.libera.chat 372 tester :- This server provided by Seeweb ") + (0.01 ":zirconium.libera.chat 372 tester :- Welcome to Libera Chat, the IRC network for") + (0.01 ":zirconium.libera.chat 372 tester :- free & open-source software and peer directed projects.") + (0.00 ":zirconium.libera.chat 372 tester :- ") + (0.00 ":zirconium.libera.chat 372 tester :- Use of Libera Chat is governed by our network policies.") + (0.00 ":zirconium.libera.chat 372 tester :- ") + (0.01 ":zirconium.libera.chat 372 tester :- Please visit us in #libera for questions and support.") + (0.01 ":zirconium.libera.chat 372 tester :- ") + (0.01 ":zirconium.libera.chat 372 tester :- Website and documentation: https://libera.chat") + (0.01 ":zirconium.libera.chat 372 tester :- Webchat: https://web.libera.chat") + (0.01 ":zirconium.libera.chat 372 tester :- Network policies: https://libera.chat/policies") + (0.01 ":zirconium.libera.chat 372 tester :- Email: support@libera.chat") + (0.00 ":zirconium.libera.chat 376 tester :End of /MOTD command.")) + +((mode-user 1.2 "MODE tester +i") + (0.02 ":tester MODE tester :+Zi") + (0.02 ":NickServ!NickServ@services.libera.chat NOTICE tester :This nickname is registered. Please choose a different nickname, or identify via \2/msg NickServ IDENTIFY tester \2")) + +((privmsg 2 "PRIVMSG NickServ :IDENTIFY changeme") + (0.96 ":NickServ!NickServ@services.libera.chat NOTICE tester :You are now identified for \2tester\2.") + (0.25 ":NickServ!NickServ@services.libera.chat NOTICE tester :Last login from: \2~tester@school.edu/tester\2 on Jun 18 01:15:56 2021 +0000.")) + +((quit 5 "QUIT :\2ERC\2") + (0.19 ":tester!~user@static-198-54-131-100.cust.tzulo.com QUIT :Client Quit")) + +((linger 1 LINGER)) diff --git a/test/lisp/erc/erc-scenarios.el b/test/lisp/erc/erc-scenarios.el index 37b58227e7..94a3a5bb37 100644 --- a/test/lisp/erc/erc-scenarios.el +++ b/test/lisp/erc/erc-scenarios.el @@ -1899,6 +1899,138 @@ erc-scenarios-base-association-nick-bumped-mandated-renick (should (not (get-buffer "foonet/dummy"))) (should (get-buffer "foonet"))))) +;; Auth source consulted for initial PASS arg. Option +;; `erc-connect-auth-source-host' obeyed. + +(defun erc-scenarios-common--auth-source (id dialog &rest rest) + (push "machine GNU.chat port %d user \"#chan\" password spam" rest) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/auth-source") + (dumb-server (erc-d-run "localhost" t dialog)) + (port (process-contact dumb-server :service)) + (ents `(,@(mapcar (lambda (fmt) (format fmt port)) rest) + "machine MyHost port irc password 123")) + (netrc-file (make-temp-file "auth-source-test" nil nil + (string-join ents "\n"))) + (auth-sources (list netrc-file)) + (auth-source-do-cache nil) + (erc-scenarios-common-extra-teardown (lambda () + (delete-file netrc-file)))) + + (ert-info ("Connect") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :full-name "tester" + :id id) + (should (string= (buffer-name) (if id + (symbol-name id) + (format "127.0.0.1:%d" port)))) + (erc-d-t-wait-for 1 (eq erc-network 'FooNet)))))) + +(ert-deftest erc-scenarios-base-auth-source--dialed () + (should (eq erc-connect-auth-source-host 'server)) + (erc-scenarios-common--auth-source + nil 'foonet + "machine GNU.chat port %d user tester password fake" + "machine 127.0.0.1 port %d user tester password changeme" + "machine 127.0.0.1 port %d user imposter password fake")) + +(ert-deftest erc-scenarios-base-auth-source--dialed-fallback () + (let ((erc-connect-auth-source-host t)) + (erc-scenarios-common--auth-source + nil 'foonet + "machine FooNet port %d user tester password fake" + "machine 127.0.0.1 port %d user tester password changeme" + "machine 127.0.0.1 port %d user imposter password fake"))) + +(ert-deftest erc-scenarios-base-auth-source--network-id () + (let ((erc-connect-auth-source-host t)) + (erc-scenarios-common--auth-source + 'MySession 'foonet + "machine MySession port %d user tester password changeme" + "machine 127.0.0.1 port %d user tester password fake" + "machine FooNet port %d user tester password fake"))) + +(ert-deftest erc-scenarios-base-auth-source--string--network-id () + (let ((erc-connect-auth-source-host "MyHost")) + (erc-scenarios-common--auth-source + 'MySession 'foonet + "machine 127.0.0.1 port %d user tester password fake" + "machine MyHost port %d user tester password changeme" + "machine MySession port %d user tester password fake"))) + +(ert-deftest erc-scenarios-base-auth-source--nopass () + (let (erc-connect-auth-source-host) ; nil + (erc-scenarios-common--auth-source nil 'nopass))) + +(ert-deftest erc-scenarios-base-auth-source--nopass--network-id () + (let (erc-connect-auth-source-host) ; nil + (erc-scenarios-common--auth-source 'MySession 'nopass))) + +;; Identify via auth source with no initial password + +(defun erc-scenarios-common--services-auth-source (&rest rest) + (defvar erc-use-auth-source-for-nickserv-password) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "services/auth-source") + (erc-server-flood-penalty 0.1) + (dumb-server (erc-d-run "localhost" t 'libera)) + (port (process-contact dumb-server :service)) + (ents `(,@(mapcar (lambda (fmt) (format fmt port)) rest) + "machine MyHost port irc password 123")) + (netrc-file (make-temp-file "auth-source-test" nil nil + (string-join ents "\n"))) + (auth-sources (list netrc-file)) + (auth-source-do-cache nil) + (erc-modules (cons 'services erc-modules)) + (erc-use-auth-source-for-nickserv-password t) ; do consult for NickServ + (expect (erc-d-t-make-expecter)) + (erc-scenarios-common-extra-teardown (lambda () + (delete-file netrc-file)))) + + (cl-letf (((symbol-function 'read-passwd) + (lambda (&rest _) (error "Unexpected read-passwd call")))) + (ert-info ("Connect without password") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :full-name "tester") + (should (string= (buffer-name) (format "127.0.0.1:%d" port))) + (erc-d-t-wait-for 3 (eq erc-network 'Libera.Chat)) + (funcall expect 3 "This nickname is registered.") + (funcall expect 3 "You are now identified") + (funcall expect 3 "Last login from") + (erc-cmd-QUIT "")))) + + (erc-services-mode -1) + + (should-not (memq 'services erc-modules)))) + +(ert-deftest erc-scenarios-services-auth-source--network () + (let (erc-connect-auth-source-host) ; don't consult auth-source for PASS + (erc-scenarios-common--services-auth-source + "machine 127.0.0.1 port %d user tester password spam" + "machine zirconium.libera.chat port %d user tester password fake" + "machine Libera.Chat port %d user tester password changeme"))) + +(ert-deftest erc-scenarios-services-auth-source--network-connect-lookup () + (should (eq erc-connect-auth-source-host 'server)) + (erc-scenarios-common--services-auth-source + "machine zirconium.libera.chat port %d user tester password fake" + "machine Libera.Chat port %d user tester password changeme")) + +(ert-deftest erc-scenarios-services-auth-source--announced () + (let (erc-connect-auth-source-host) ; don't consult auth-source for PASS + (erc-scenarios-common--services-auth-source + "machine 127.0.0.1 port %d user tester password spam" + "machine zirconium.libera.chat port %d user tester password changeme"))) + +(ert-deftest erc-scenarios-services-auth-source--dialed () + (let (erc-connect-auth-source-host) ; don't consult auth-source for PASS + (erc-scenarios-common--services-auth-source + "machine 127.0.0.1 port %d user tester password changeme"))) + (ert-deftest erc-scenarios-services-password () (erc-scenarios-common-with-cleanup -- 2.35.1 --=-=-=--