about summary refs log tree commit diff
path: root/nixos/tests/xmpp
diff options
context:
space:
mode:
authorFélix Baylac-Jacqué <felix@alternativebit.fr>2020-04-20 20:27:53 +0200
committerFélix Baylac-Jacqué <felix@alternativebit.fr>2020-04-30 20:39:54 +0200
commit8aea5288725688f7f71bf12c8ee1bb83147b22c6 (patch)
tree6803897e41ad3d7316e6e9897ea6fab2bb94edd2 /nixos/tests/xmpp
parent5e4abf76c7e74660369bcb5e2648746db03142c8 (diff)
nixos/prosody: make defaults comply with XEP-0423
Setting up a XMPP chat server is a pretty deep rabbit whole to jump in
when you're not familiar with this whole universe. Your experience
with this environment will greatly depends on whether or not your
server implements the right set of XEPs.

To tackle this problem, the XMPP community came with the idea of
creating a meta-XEP in charge of listing the desirable XEPs to comply
with. This meta-XMP is issued every year under an new XEP number. The
2020 one being XEP-0423[1].

This prosody nixos module refactoring makes complying with XEP-0423
easier. All the necessary extensions are enabled by default. For some
extensions (MUC and HTTP_UPLOAD), we need some input from the user and
cannot provide a sensible default nixpkgs-wide. For those, we guide
the user using a couple of assertions explaining the remaining manual
steps to perform.

We took advantage of this substential refactoring to refresh the
associated nixos test.

Changelog:
- Update the prosody package to provide the necessary community
  modules in order to comply with XEP-0423. This is a tradeoff, as
  depending on their configuration, the user might end up not using them
  and wasting some disk space. That being said, adding those will
  allow the XEP-0423 users, which I expect to be the majority of
  users, to leverage a bit more the binary cache.
- Add a muc submodule populated with the prosody muc defaults.
- Add a http_upload submodule in charge of setting up a basic http
  server handling the user uploads. This submodule is in is
  spinning up an HTTP(s) server in charge of receiving and serving the
  user's attachments.
- Advertise both the MUCs and the http_upload endpoints using mod disco.
- Use the slixmpp library in place of the now defunct sleekxmpp for
  the prosody NixOS test.
- Update the nixos test to setup and test the MUC and http upload
  features.
- Add a couple of assertions triggered if the setup is not xep-0423
  compliant.

[1] https://xmpp.org/extensions/xep-0423.html
Diffstat (limited to 'nixos/tests/xmpp')
-rw-r--r--nixos/tests/xmpp/prosody.nix85
-rw-r--r--nixos/tests/xmpp/xmpp-sendmessage.nix105
2 files changed, 125 insertions, 65 deletions
diff --git a/nixos/tests/xmpp/prosody.nix b/nixos/tests/xmpp/prosody.nix
index 9d1374bff6bd6..e7755e24bab41 100644
--- a/nixos/tests/xmpp/prosody.nix
+++ b/nixos/tests/xmpp/prosody.nix
@@ -1,27 +1,80 @@
-import ../make-test-python.nix {
-  name = "prosody";
+let
+  cert = pkgs: pkgs.runCommandNoCC "selfSignedCerts" { buildInputs = [ pkgs.openssl ]; } ''
+    openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -subj '/CN=example.com/CN=uploads.example.com/CN=conference.example.com'
+    mkdir -p $out
+    cp key.pem cert.pem $out
+  '';
+  createUsers = pkgs: pkgs.writeScriptBin "create-prosody-users" ''
+    #!${pkgs.bash}/bin/bash
+    set -e
+
+    # Creates and set password for the 2 xmpp test users.
+    #
+    # Doing that in a bash script instead of doing that in the test
+    # script allow us to easily provision the users when running that
+    # test interactively.
+
+    prosodyctl register cthon98 example.com nothunter2
+    prosodyctl register azurediamond example.com hunter2
+  '';
+  delUsers = pkgs: pkgs.writeScriptBin "delete-prosody-users" ''
+    #!${pkgs.bash}/bin/bash
+    set -e
+
+    # Deletes the test users.
+    #
+    # Doing that in a bash script instead of doing that in the test
+    # script allow us to easily provision the users when running that
+    # test interactively.
 
+    prosodyctl deluser cthon98@example.com
+    prosodyctl deluser azurediamond@example.com
+  '';
+in import ../make-test-python.nix {
+  name = "prosody";
   nodes = {
-    client = { nodes, pkgs, ... }: {
+    client = { nodes, pkgs, config, ... }: {
+      security.pki.certificateFiles = [ "${cert pkgs}/cert.pem" ];
+      console.keyMap = "fr-bepo";
+      networking.extraHosts = ''
+        ${nodes.server.config.networking.primaryIPAddress} example.com
+        ${nodes.server.config.networking.primaryIPAddress} conference.example.com
+        ${nodes.server.config.networking.primaryIPAddress} uploads.example.com
+      '';
       environment.systemPackages = [
         (pkgs.callPackage ./xmpp-sendmessage.nix { connectTo = nodes.server.config.networking.primaryIPAddress; })
       ];
     };
     server = { config, pkgs, ... }: {
+      security.pki.certificateFiles = [ "${cert pkgs}/cert.pem" ];
+      console.keyMap = "fr-bepo";
       networking.extraHosts = ''
         ${config.networking.primaryIPAddress} example.com
+        ${config.networking.primaryIPAddress} conference.example.com
+        ${config.networking.primaryIPAddress} uploads.example.com
       '';
       networking.firewall.enable = false;
+      environment.systemPackages = [
+        (createUsers pkgs)
+        (delUsers pkgs)
+      ];
       services.prosody = {
         enable = true;
-        # TODO: use a self-signed certificate
-        c2sRequireEncryption = false;
-        extraConfig = ''
-          storage = "sql"
-        '';
-        virtualHosts.test = {
+        ssl.cert = "${cert pkgs}/cert.pem";
+        ssl.key = "${cert pkgs}/key.pem";
+        virtualHosts.example = {
           domain = "example.com";
           enabled = true;
+          ssl.cert = "${cert pkgs}/cert.pem";
+          ssl.key = "${cert pkgs}/key.pem";
+        };
+        muc = [
+          {
+            domain = "conference.example.com";
+          }
+        ];
+        uploadHttp = {
+          domain = "uploads.example.com";
         };
       };
     };
@@ -31,16 +84,8 @@ import ../make-test-python.nix {
     server.wait_for_unit("prosody.service")
     server.succeed('prosodyctl status | grep "Prosody is running"')
 
-    # set password to 'nothunter2' (it's asked twice)
-    server.succeed("yes nothunter2 | prosodyctl adduser cthon98@example.com")
-    # set password to 'y'
-    server.succeed("yes | prosodyctl adduser azurediamond@example.com")
-    # correct password to "hunter2"
-    server.succeed("yes hunter2 | prosodyctl passwd azurediamond@example.com")
-
-    client.succeed("send-message")
-
-    server.succeed("prosodyctl deluser cthon98@example.com")
-    server.succeed("prosodyctl deluser azurediamond@example.com")
+    server.succeed("create-prosody-users")
+    client.succeed('send-message 2>&1 | grep "XMPP SCRIPT TEST SUCCESS"')
+    server.succeed("delete-prosody-users")
   '';
 }
diff --git a/nixos/tests/xmpp/xmpp-sendmessage.nix b/nixos/tests/xmpp/xmpp-sendmessage.nix
index 2a075a0181340..349b9c6f38e49 100644
--- a/nixos/tests/xmpp/xmpp-sendmessage.nix
+++ b/nixos/tests/xmpp/xmpp-sendmessage.nix
@@ -1,46 +1,61 @@
-{ writeScriptBin, python3, connectTo ? "localhost" }:
-writeScriptBin "send-message" ''
-  #!${(python3.withPackages (ps: [ ps.sleekxmpp ])).interpreter}
-  # Based on the sleekxmpp send_client example, look there for more details:
-  # https://github.com/fritzy/SleekXMPP/blob/develop/examples/send_client.py
-  import sleekxmpp
-
-  class SendMsgBot(sleekxmpp.ClientXMPP):
-      """
-      A basic SleekXMPP bot that will log in, send a message,
-      and then log out.
-      """
-      def __init__(self, jid, password, recipient, message):
-          sleekxmpp.ClientXMPP.__init__(self, jid, password)
-
-          self.recipient = recipient
-          self.msg = message
-
-          self.add_event_handler("session_start", self.start, threaded=True)
-
-      def start(self, event):
-          self.send_presence()
-          self.get_roster()
-
-          self.send_message(mto=self.recipient,
-                            mbody=self.msg,
-                            mtype='chat')
-
-          self.disconnect(wait=True)
-
-
-  if __name__ == '__main__':
-      xmpp = SendMsgBot("cthon98@example.com", "nothunter2", "azurediamond@example.com", "hey, if you type in your pw, it will show as stars")
-      xmpp.register_plugin('xep_0030') # Service Discovery
-      xmpp.register_plugin('xep_0199') # XMPP Ping
-
-      # TODO: verify certificate
-      # If you want to verify the SSL certificates offered by a server:
-      # xmpp.ca_certs = "path/to/ca/cert"
-
-      if xmpp.connect(('${connectTo}', 5222)):
-          xmpp.process(block=True)
-      else:
-          print("Unable to connect.")
-          sys.exit(1)
+{ writeScriptBin, writeText, python3, connectTo ? "localhost" }:
+let
+  dummyFile = writeText "dummy-file" ''
+    Dear dog,
+
+    Please find this *really* important attachment.
+
+    Yours truly,
+    John
+  '';
+in writeScriptBin "send-message" ''
+#!${(python3.withPackages (ps: [ ps.slixmpp ])).interpreter}
+import logging
+import sys
+from types import MethodType
+
+from slixmpp import ClientXMPP
+from slixmpp.exceptions import IqError, IqTimeout
+
+
+class CthonTest(ClientXMPP):
+
+    def __init__(self, jid, password):
+        ClientXMPP.__init__(self, jid, password)
+        self.add_event_handler("session_start", self.session_start)
+
+    async def session_start(self, event):
+        log = logging.getLogger(__name__)
+        self.send_presence()
+        self.get_roster()
+        # Sending a test message
+        self.send_message(mto="azurediamond@example.com", mbody="Hello, this is dog.", mtype="chat")
+        log.info('Message sent')
+
+        # Test http upload (XEP_0363)
+        def timeout_callback(arg):
+            log.error("ERROR: Cannot upload file. XEP_0363 seems broken")
+            sys.exit(1)
+        url = await self['xep_0363'].upload_file("${dummyFile}",timeout=10, timeout_callback=timeout_callback)
+        log.info('Upload success!')
+        # Test MUC
+        self.plugin['xep_0045'].join_muc('testMucRoom', 'cthon98', wait=True)
+        log.info('MUC join success!')
+        log.info('XMPP SCRIPT TEST SUCCESS')
+        self.disconnect(wait=True)
+
+
+if __name__ == '__main__':
+    logging.basicConfig(level=logging.DEBUG,
+                        format='%(levelname)-8s %(message)s')
+
+    ct = CthonTest('cthon98@example.com', 'nothunter2')
+    ct.register_plugin('xep_0071')
+    ct.register_plugin('xep_0128')
+    # HTTP Upload
+    ct.register_plugin('xep_0363')
+    # MUC
+    ct.register_plugin('xep_0045')
+    ct.connect(("server", 5222))
+    ct.process(forever=False)
 ''