2021-03-14 14:44:34 +00:00
|
|
|
# This tests Discourse by:
|
|
|
|
# 1. logging in as the admin user
|
|
|
|
# 2. sending a private message to the admin user through the API
|
|
|
|
# 3. replying to that message via email.
|
|
|
|
|
|
|
|
import ./make-test-python.nix (
|
2021-08-12 10:34:14 +00:00
|
|
|
{ pkgs, lib, package ? pkgs.discourse, ... }:
|
2021-03-14 14:44:34 +00:00
|
|
|
let
|
|
|
|
certs = import ./common/acme/server/snakeoil-certs.nix;
|
|
|
|
clientDomain = "client.fake.domain";
|
|
|
|
discourseDomain = certs.domain;
|
|
|
|
adminPassword = "eYAX85qmMJ5GZIHLaXGDAoszD7HSZp5d";
|
|
|
|
secretKeyBase = "381f4ac6d8f5e49d804dae72aa9c046431d2f34c656a705c41cd52fed9b4f6f76f51549f0b55db3b8b0dded7a00d6a381ebe9a4367d2d44f5e743af6628b4d42";
|
|
|
|
admin = {
|
|
|
|
email = "alice@${clientDomain}";
|
|
|
|
username = "alice";
|
|
|
|
fullName = "Alice Admin";
|
|
|
|
passwordFile = "${pkgs.writeText "admin-pass" adminPassword}";
|
|
|
|
};
|
|
|
|
in
|
|
|
|
{
|
|
|
|
name = "discourse";
|
|
|
|
meta = with pkgs.lib.maintainers; {
|
|
|
|
maintainers = [ talyz ];
|
|
|
|
};
|
|
|
|
|
|
|
|
nodes.discourse =
|
|
|
|
{ nodes, ... }:
|
|
|
|
{
|
|
|
|
virtualisation.memorySize = 2048;
|
2021-10-06 08:27:33 +00:00
|
|
|
virtualisation.cores = 4;
|
|
|
|
virtualisation.useNixStoreImage = true;
|
2022-07-16 16:46:31 +00:00
|
|
|
virtualisation.writableStore = false;
|
2021-03-14 14:44:34 +00:00
|
|
|
|
|
|
|
imports = [ common/user-account.nix ];
|
|
|
|
|
|
|
|
security.pki.certificateFiles = [
|
|
|
|
certs.ca.cert
|
|
|
|
];
|
|
|
|
|
|
|
|
networking.extraHosts = ''
|
|
|
|
127.0.0.1 ${discourseDomain}
|
2023-02-09 14:31:41 +00:00
|
|
|
${nodes.client.networking.primaryIPAddress} ${clientDomain}
|
2021-03-14 14:44:34 +00:00
|
|
|
'';
|
|
|
|
|
|
|
|
services.postfix = {
|
|
|
|
enableSubmission = true;
|
|
|
|
enableSubmissions = true;
|
|
|
|
submissionsOptions = {
|
|
|
|
smtpd_sasl_auth_enable = "yes";
|
|
|
|
smtpd_client_restrictions = "permit";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
environment.systemPackages = [ pkgs.jq ];
|
|
|
|
|
2021-05-27 09:41:35 +00:00
|
|
|
services.postgresql.package = pkgs.postgresql_13;
|
|
|
|
|
2021-03-14 14:44:34 +00:00
|
|
|
services.discourse = {
|
|
|
|
enable = true;
|
2021-08-12 10:34:14 +00:00
|
|
|
inherit admin package;
|
2021-03-14 14:44:34 +00:00
|
|
|
hostname = discourseDomain;
|
|
|
|
sslCertificate = "${certs.${discourseDomain}.cert}";
|
|
|
|
sslCertificateKey = "${certs.${discourseDomain}.key}";
|
|
|
|
secretKeyBaseFile = "${pkgs.writeText "secret-key-base" secretKeyBase}";
|
|
|
|
enableACME = false;
|
|
|
|
mail.outgoing.serverAddress = clientDomain;
|
|
|
|
mail.incoming.enable = true;
|
|
|
|
siteSettings = {
|
|
|
|
posting = {
|
|
|
|
min_post_length = 5;
|
|
|
|
min_first_post_length = 5;
|
|
|
|
min_personal_message_post_length = 5;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
unicornTimeout = 900;
|
|
|
|
};
|
|
|
|
|
|
|
|
networking.firewall.allowedTCPPorts = [ 25 465 ];
|
|
|
|
};
|
|
|
|
|
|
|
|
nodes.client =
|
|
|
|
{ nodes, ... }:
|
|
|
|
{
|
|
|
|
imports = [ common/user-account.nix ];
|
|
|
|
|
|
|
|
security.pki.certificateFiles = [
|
|
|
|
certs.ca.cert
|
|
|
|
];
|
|
|
|
|
|
|
|
networking.extraHosts = ''
|
|
|
|
127.0.0.1 ${clientDomain}
|
2023-02-09 14:31:41 +00:00
|
|
|
${nodes.discourse.networking.primaryIPAddress} ${discourseDomain}
|
2021-03-14 14:44:34 +00:00
|
|
|
'';
|
|
|
|
|
|
|
|
services.dovecot2 = {
|
|
|
|
enable = true;
|
|
|
|
protocols = [ "imap" ];
|
|
|
|
modules = [ pkgs.dovecot_pigeonhole ];
|
|
|
|
};
|
|
|
|
|
|
|
|
services.postfix = {
|
|
|
|
enable = true;
|
|
|
|
origin = clientDomain;
|
|
|
|
relayDomains = [ clientDomain ];
|
|
|
|
config = {
|
|
|
|
compatibility_level = "2";
|
|
|
|
smtpd_banner = "ESMTP server";
|
|
|
|
myhostname = clientDomain;
|
|
|
|
mydestination = clientDomain;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
environment.systemPackages =
|
|
|
|
let
|
|
|
|
replyToEmail = pkgs.writeScriptBin "reply-to-email" ''
|
|
|
|
#!${pkgs.python3.interpreter}
|
|
|
|
import imaplib
|
|
|
|
import smtplib
|
|
|
|
import ssl
|
|
|
|
import email.header
|
|
|
|
from email import message_from_bytes
|
|
|
|
from email.message import EmailMessage
|
|
|
|
|
|
|
|
with imaplib.IMAP4('localhost') as imap:
|
|
|
|
imap.login('alice', 'foobar')
|
|
|
|
imap.select()
|
|
|
|
status, data = imap.search(None, 'ALL')
|
|
|
|
assert status == 'OK'
|
|
|
|
|
|
|
|
nums = data[0].split()
|
|
|
|
assert len(nums) == 1
|
|
|
|
|
|
|
|
status, msg_data = imap.fetch(nums[0], '(RFC822)')
|
|
|
|
assert status == 'OK'
|
|
|
|
|
|
|
|
msg = email.message_from_bytes(msg_data[0][1])
|
|
|
|
subject = str(email.header.make_header(email.header.decode_header(msg['Subject'])))
|
|
|
|
reply_to = email.header.decode_header(msg['Reply-To'])[0][0]
|
|
|
|
message_id = email.header.decode_header(msg['Message-ID'])[0][0]
|
|
|
|
date = email.header.decode_header(msg['Date'])[0][0]
|
|
|
|
|
|
|
|
ctx = ssl.create_default_context()
|
|
|
|
with smtplib.SMTP_SSL(host='${discourseDomain}', context=ctx) as smtp:
|
|
|
|
reply = EmailMessage()
|
|
|
|
reply['Subject'] = 'Re: ' + subject
|
|
|
|
reply['To'] = reply_to
|
|
|
|
reply['From'] = 'alice@${clientDomain}'
|
|
|
|
reply['In-Reply-To'] = message_id
|
|
|
|
reply['References'] = message_id
|
|
|
|
reply['Date'] = date
|
|
|
|
reply.set_content("Test reply.")
|
|
|
|
|
|
|
|
smtp.send_message(reply)
|
|
|
|
smtp.quit()
|
|
|
|
'';
|
|
|
|
in
|
|
|
|
[ replyToEmail ];
|
|
|
|
|
|
|
|
networking.firewall.allowedTCPPorts = [ 25 ];
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
testScript = { nodes }:
|
|
|
|
let
|
|
|
|
request = builtins.toJSON {
|
|
|
|
title = "Private message";
|
|
|
|
raw = "This is a test message.";
|
2023-09-10 11:28:54 +00:00
|
|
|
target_recipients = admin.username;
|
2021-03-14 14:44:34 +00:00
|
|
|
archetype = "private_message";
|
|
|
|
};
|
|
|
|
in ''
|
|
|
|
discourse.start()
|
|
|
|
client.start()
|
|
|
|
|
|
|
|
discourse.wait_for_unit("discourse.service")
|
|
|
|
discourse.wait_for_file("/run/discourse/sockets/unicorn.sock")
|
|
|
|
discourse.wait_until_succeeds("curl -sS -f https://${discourseDomain}")
|
|
|
|
discourse.succeed(
|
|
|
|
"curl -sS -f https://${discourseDomain}/session/csrf -c cookie -b cookie -H 'Accept: application/json' | jq -r '\"X-CSRF-Token: \" + .csrf' > csrf_token",
|
2023-02-09 14:31:41 +00:00
|
|
|
"curl -sS -f https://${discourseDomain}/session -c cookie -b cookie -H @csrf_token -H 'Accept: application/json' -d 'login=${nodes.discourse.services.discourse.admin.username}' -d \"password=${adminPassword}\" | jq -e '.user.username == \"${nodes.discourse.services.discourse.admin.username}\"'",
|
|
|
|
"curl -sS -f https://${discourseDomain}/login -v -H 'Accept: application/json' -c cookie -b cookie 2>&1 | grep ${nodes.discourse.services.discourse.admin.username}",
|
2021-03-14 14:44:34 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
client.wait_for_unit("postfix.service")
|
|
|
|
client.wait_for_unit("dovecot2.service")
|
|
|
|
|
|
|
|
discourse.succeed(
|
|
|
|
"sudo -u discourse discourse-rake api_key:create_master[master] >api_key",
|
|
|
|
'curl -sS -f https://${discourseDomain}/posts -X POST -H "Content-Type: application/json" -H "Api-Key: $(<api_key)" -H "Api-Username: system" -d \'${request}\' ',
|
|
|
|
)
|
|
|
|
|
|
|
|
client.wait_until_succeeds("reply-to-email")
|
|
|
|
|
|
|
|
discourse.wait_until_succeeds(
|
|
|
|
'curl -sS -f https://${discourseDomain}/topics/private-messages/system -H "Accept: application/json" -H "Api-Key: $(<api_key)" -H "Api-Username: system" | jq -e \'if .topic_list.topics[0].id != null then .topic_list.topics[0].id else null end\' >topic_id'
|
|
|
|
)
|
|
|
|
discourse.succeed(
|
|
|
|
'curl -sS -f https://${discourseDomain}/t/$(<topic_id) -H "Accept: application/json" -H "Api-Key: $(<api_key)" -H "Api-Username: system" | jq -e \'if .post_stream.posts[1].cooked == "<p>Test reply.</p>" then true else null end\' '
|
|
|
|
)
|
|
|
|
'';
|
|
|
|
})
|