Merge branch 'master' into staging-next
This commit is contained in:
@@ -726,6 +726,20 @@ environment.systemPackages = [
|
||||
The <package>yadm</package> dotfile manager has been updated from 2.x to 3.x, which has new (XDG) default locations for some data/state files. Most yadm commands will fail and print a legacy path warning (which describes how to upgrade/migrate your repository). If you have scripts, daemons, scheduled jobs, shell profiles, etc. that invoke yadm, expect them to fail or misbehave until you perform this migration and prepare accordingly.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Instead of determining <option>services.radicale.package</option>
|
||||
automatically based on <option>system.stateVersion</option>, the latest
|
||||
version is always used because old versions are not officially supported.
|
||||
</para>
|
||||
<para>
|
||||
Furthermore, Radicale's systemd unit was hardened which might break some
|
||||
deployments. In particular, a non-default
|
||||
<literal>filesystem_folder</literal> has to be added to
|
||||
<option>systemd.services.radicale.serviceConfig.ReadWritePaths</option> if
|
||||
the deprecated <option>services.radicale.config</option> is used.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
x86_64-linux = "/nix/store/iwfs2bfcy7lqwhri94p2i6jc87ih55zk-nix-2.3.10";
|
||||
i686-linux = "/nix/store/a3ccfvy9i5n418d5v0bir330kbcz3vj8-nix-2.3.10";
|
||||
aarch64-linux = "/nix/store/bh5g6cv7bv35iz853d3xv2sphn51ybmb-nix-2.3.10";
|
||||
x86_64-darwin = "/nix/store/8c98r6zlwn2d40qm7jnnrr2rdlqviszr-nix-2.3.10";
|
||||
x86_64-linux = "/nix/store/d1ppfhjhdwcsb4npfzyifv5z8i00fzsk-nix-2.3.11";
|
||||
i686-linux = "/nix/store/c6ikndcrzwpfn2sb5b9xb1f17p9b8iga-nix-2.3.11";
|
||||
aarch64-linux = "/nix/store/fb0lfrn0m8s197d264jzd64vhz9c8zbx-nix-2.3.11";
|
||||
x86_64-darwin = "/nix/store/qvb86ffv08q3r66qbd6nqifz425lyyhf-nix-2.3.11";
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ let
|
||||
"fritzbox"
|
||||
"json"
|
||||
"jitsi"
|
||||
"kea"
|
||||
"keylight"
|
||||
"knot"
|
||||
"lnd"
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
{ config
|
||||
, lib
|
||||
, pkgs
|
||||
, options
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.prometheus.exporters.kea;
|
||||
in {
|
||||
port = 9547;
|
||||
extraOpts = {
|
||||
controlSocketPaths = mkOption {
|
||||
type = types.listOf types.str;
|
||||
example = literalExample ''
|
||||
[
|
||||
"/run/kea/kea-dhcp4.socket"
|
||||
"/run/kea/kea-dhcp6.socket"
|
||||
]
|
||||
'';
|
||||
description = ''
|
||||
Paths to kea control sockets
|
||||
'';
|
||||
};
|
||||
};
|
||||
serviceOpts = {
|
||||
serviceConfig = {
|
||||
ExecStart = ''
|
||||
${pkgs.prometheus-kea-exporter}/bin/kea-exporter \
|
||||
--address ${cfg.listenAddress} \
|
||||
--port ${toString cfg.port} \
|
||||
${concatStringsSep " \\n" cfg.controlSocketPaths}
|
||||
'';
|
||||
SupplementaryGroups = [ "kea" ];
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -3,56 +3,103 @@
|
||||
with lib;
|
||||
|
||||
let
|
||||
|
||||
cfg = config.services.radicale;
|
||||
|
||||
confFile = pkgs.writeText "radicale.conf" cfg.config;
|
||||
|
||||
defaultPackage = if versionAtLeast config.system.stateVersion "20.09" then {
|
||||
pkg = pkgs.radicale3;
|
||||
text = "pkgs.radicale3";
|
||||
} else if versionAtLeast config.system.stateVersion "17.09" then {
|
||||
pkg = pkgs.radicale2;
|
||||
text = "pkgs.radicale2";
|
||||
} else {
|
||||
pkg = pkgs.radicale1;
|
||||
text = "pkgs.radicale1";
|
||||
format = pkgs.formats.ini {
|
||||
listToValue = concatMapStringsSep ", " (generators.mkValueStringDefault { });
|
||||
};
|
||||
in
|
||||
|
||||
{
|
||||
pkg = if isNull cfg.package then
|
||||
pkgs.radicale
|
||||
else
|
||||
cfg.package;
|
||||
|
||||
options = {
|
||||
services.radicale.enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Enable Radicale CalDAV and CardDAV server.
|
||||
'';
|
||||
confFile = if cfg.settings == { } then
|
||||
pkgs.writeText "radicale.conf" cfg.config
|
||||
else
|
||||
format.generate "radicale.conf" cfg.settings;
|
||||
|
||||
rightsFile = format.generate "radicale.rights" cfg.rights;
|
||||
|
||||
bindLocalhost = cfg.settings != { } && !hasAttrByPath [ "server" "hosts" ] cfg.settings;
|
||||
|
||||
in {
|
||||
options.services.radicale = {
|
||||
enable = mkEnableOption "Radicale CalDAV and CardDAV server";
|
||||
|
||||
package = mkOption {
|
||||
description = "Radicale package to use.";
|
||||
# Default cannot be pkgs.radicale because non-null values suppress
|
||||
# warnings about incompatible configuration and storage formats.
|
||||
type = with types; nullOr package // { inherit (package) description; };
|
||||
default = null;
|
||||
defaultText = "pkgs.radicale";
|
||||
};
|
||||
|
||||
services.radicale.package = mkOption {
|
||||
type = types.package;
|
||||
default = defaultPackage.pkg;
|
||||
defaultText = defaultPackage.text;
|
||||
description = ''
|
||||
Radicale package to use. This defaults to version 1.x if
|
||||
<literal>system.stateVersion < 17.09</literal>, version 2.x if
|
||||
<literal>17.09 ≤ system.stateVersion < 20.09</literal>, and
|
||||
version 3.x otherwise.
|
||||
'';
|
||||
};
|
||||
|
||||
services.radicale.config = mkOption {
|
||||
config = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
description = ''
|
||||
Radicale configuration, this will set the service
|
||||
configuration file.
|
||||
This option is mutually exclusive with <option>settings</option>.
|
||||
This option is deprecated. Use <option>settings</option> instead.
|
||||
'';
|
||||
};
|
||||
|
||||
services.radicale.extraArgs = mkOption {
|
||||
settings = mkOption {
|
||||
type = format.type;
|
||||
default = { };
|
||||
description = ''
|
||||
Configuration for Radicale. See
|
||||
<link xlink:href="https://radicale.org/3.0.html#documentation/configuration" />.
|
||||
This option is mutually exclusive with <option>config</option>.
|
||||
'';
|
||||
example = literalExample ''
|
||||
server = {
|
||||
hosts = [ "0.0.0.0:5232" "[::]:5232" ];
|
||||
};
|
||||
auth = {
|
||||
type = "htpasswd";
|
||||
htpasswd_filename = "/etc/radicale/users";
|
||||
htpasswd_encryption = "bcrypt";
|
||||
};
|
||||
storage = {
|
||||
filesystem_folder = "/var/lib/radicale/collections";
|
||||
};
|
||||
'';
|
||||
};
|
||||
|
||||
rights = mkOption {
|
||||
type = format.type;
|
||||
description = ''
|
||||
Configuration for Radicale's rights file. See
|
||||
<link xlink:href="https://radicale.org/3.0.html#documentation/authentication-and-rights" />.
|
||||
This option only works in conjunction with <option>settings</option>.
|
||||
Setting this will also set <option>settings.rights.type</option> and
|
||||
<option>settings.rights.file</option> to approriate values.
|
||||
'';
|
||||
default = { };
|
||||
example = literalExample ''
|
||||
root = {
|
||||
user = ".+";
|
||||
collection = "";
|
||||
permissions = "R";
|
||||
};
|
||||
principal = {
|
||||
user = ".+";
|
||||
collection = "{user}";
|
||||
permissions = "RW";
|
||||
};
|
||||
calendars = {
|
||||
user = ".+";
|
||||
collection = "{user}/[^/]+";
|
||||
permissions = "rw";
|
||||
};
|
||||
'';
|
||||
};
|
||||
|
||||
extraArgs = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
description = "Extra arguments passed to the Radicale daemon.";
|
||||
@@ -60,33 +107,94 @@ in
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.settings == { } || cfg.config == "";
|
||||
message = ''
|
||||
The options services.radicale.config and services.radicale.settings
|
||||
are mutually exclusive.
|
||||
'';
|
||||
}
|
||||
];
|
||||
|
||||
users.users.radicale =
|
||||
{ uid = config.ids.uids.radicale;
|
||||
description = "radicale user";
|
||||
home = "/var/lib/radicale";
|
||||
createHome = true;
|
||||
};
|
||||
warnings = optional (isNull cfg.package && versionOlder config.system.stateVersion "17.09") ''
|
||||
The configuration and storage formats of your existing Radicale
|
||||
installation might be incompatible with the newest version.
|
||||
For upgrade instructions see
|
||||
https://radicale.org/2.1.html#documentation/migration-from-1xx-to-2xx.
|
||||
Set services.radicale.package to suppress this warning.
|
||||
'' ++ optional (isNull cfg.package && versionOlder config.system.stateVersion "20.09") ''
|
||||
The configuration format of your existing Radicale installation might be
|
||||
incompatible with the newest version. For upgrade instructions see
|
||||
https://github.com/Kozea/Radicale/blob/3.0.6/NEWS.md#upgrade-checklist.
|
||||
Set services.radicale.package to suppress this warning.
|
||||
'' ++ optional (cfg.config != "") ''
|
||||
The option services.radicale.config is deprecated.
|
||||
Use services.radicale.settings instead.
|
||||
'';
|
||||
|
||||
users.groups.radicale =
|
||||
{ gid = config.ids.gids.radicale; };
|
||||
services.radicale.settings.rights = mkIf (cfg.rights != { }) {
|
||||
type = "from_file";
|
||||
file = toString rightsFile;
|
||||
};
|
||||
|
||||
environment.systemPackages = [ pkg ];
|
||||
|
||||
users.users.radicale.uid = config.ids.uids.radicale;
|
||||
|
||||
users.groups.radicale.gid = config.ids.gids.radicale;
|
||||
|
||||
systemd.services.radicale = {
|
||||
description = "A Simple Calendar and Contact Server";
|
||||
after = [ "network.target" ];
|
||||
requires = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
ExecStart = concatStringsSep " " ([
|
||||
"${cfg.package}/bin/radicale" "-C" confFile
|
||||
"${pkg}/bin/radicale" "-C" confFile
|
||||
] ++ (
|
||||
map escapeShellArg cfg.extraArgs
|
||||
));
|
||||
User = "radicale";
|
||||
Group = "radicale";
|
||||
StateDirectory = "radicale/collections";
|
||||
StateDirectoryMode = "0750";
|
||||
# Hardening
|
||||
CapabilityBoundingSet = [ "" ];
|
||||
DeviceAllow = [ "/dev/stdin" ];
|
||||
DevicePolicy = "strict";
|
||||
IPAddressAllow = mkIf bindLocalhost "localhost";
|
||||
IPAddressDeny = mkIf bindLocalhost "any";
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
NoNewPrivileges = true;
|
||||
PrivateDevices = true;
|
||||
PrivateTmp = true;
|
||||
PrivateUsers = true;
|
||||
ProcSubset = "pid";
|
||||
ProtectClock = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHome = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectProc = "invisible";
|
||||
ProtectSystem = "strict";
|
||||
ReadWritePaths = lib.optional
|
||||
(hasAttrByPath [ "storage" "filesystem_folder" ] cfg.settings)
|
||||
cfg.settings.storage.filesystem_folder;
|
||||
RemoveIPC = true;
|
||||
RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ];
|
||||
UMask = "0027";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ aneeshusa infinisil ];
|
||||
meta.maintainers = with lib.maintainers; [ aneeshusa infinisil dotlambda ];
|
||||
}
|
||||
|
||||
@@ -91,6 +91,7 @@ in
|
||||
custom-ca = handleTest ./custom-ca.nix {};
|
||||
croc = handleTest ./croc.nix {};
|
||||
deluge = handleTest ./deluge.nix {};
|
||||
dendrite = handleTest ./dendrite.nix {};
|
||||
dhparams = handleTest ./dhparams.nix {};
|
||||
discourse = handleTest ./discourse.nix {};
|
||||
dnscrypt-proxy2 = handleTestOn ["x86_64-linux"] ./dnscrypt-proxy2.nix {};
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import ./make-test-python.nix (
|
||||
{ pkgs, ... }:
|
||||
let
|
||||
homeserverUrl = "http://homeserver:8008";
|
||||
|
||||
private_key = pkgs.runCommand "matrix_key.pem" {
|
||||
buildInputs = [ pkgs.dendrite ];
|
||||
} "generate-keys --private-key $out";
|
||||
in
|
||||
{
|
||||
name = "dendrite";
|
||||
meta = with pkgs.lib; {
|
||||
maintainers = teams.matrix.members;
|
||||
};
|
||||
|
||||
nodes = {
|
||||
homeserver = { pkgs, ... }: {
|
||||
services.dendrite = {
|
||||
enable = true;
|
||||
settings = {
|
||||
global.server_name = "test-dendrite-server.com";
|
||||
global.private_key = private_key;
|
||||
client_api.registration_disabled = false;
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 8008 ];
|
||||
};
|
||||
|
||||
client = { pkgs, ... }: {
|
||||
environment.systemPackages = [
|
||||
(
|
||||
pkgs.writers.writePython3Bin "do_test"
|
||||
{ libraries = [ pkgs.python3Packages.matrix-nio ]; } ''
|
||||
import asyncio
|
||||
|
||||
from nio import AsyncClient
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
# Connect to dendrite
|
||||
client = AsyncClient("http://homeserver:8008", "alice")
|
||||
|
||||
# Register as user alice
|
||||
response = await client.register("alice", "my-secret-password")
|
||||
|
||||
# Log in as user alice
|
||||
response = await client.login("my-secret-password")
|
||||
|
||||
# Create a new room
|
||||
response = await client.room_create(federate=False)
|
||||
room_id = response.room_id
|
||||
|
||||
# Join the room
|
||||
response = await client.join(room_id)
|
||||
|
||||
# Send a message to the room
|
||||
response = await client.room_send(
|
||||
room_id=room_id,
|
||||
message_type="m.room.message",
|
||||
content={
|
||||
"msgtype": "m.text",
|
||||
"body": "Hello world!"
|
||||
}
|
||||
)
|
||||
|
||||
# Sync responses
|
||||
response = await client.sync(timeout=30000)
|
||||
|
||||
# Check the message was received by dendrite
|
||||
last_message = response.rooms.join[room_id].timeline.events[-1].body
|
||||
assert last_message == "Hello world!"
|
||||
|
||||
# Leave the room
|
||||
response = await client.room_leave(room_id)
|
||||
|
||||
# Close the client
|
||||
await client.close()
|
||||
|
||||
asyncio.get_event_loop().run_until_complete(main())
|
||||
''
|
||||
)
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
|
||||
with subtest("start the homeserver"):
|
||||
homeserver.wait_for_unit("dendrite.service")
|
||||
homeserver.wait_for_open_port(8008)
|
||||
|
||||
with subtest("ensure messages can be exchanged"):
|
||||
client.succeed("do_test")
|
||||
'';
|
||||
|
||||
}
|
||||
)
|
||||
@@ -326,6 +326,57 @@ let
|
||||
'';
|
||||
};
|
||||
|
||||
kea = {
|
||||
exporterConfig = {
|
||||
enable = true;
|
||||
controlSocketPaths = [
|
||||
"/run/kea/kea-dhcp6.sock"
|
||||
];
|
||||
};
|
||||
metricProvider = {
|
||||
users.users.kea = {
|
||||
isSystemUser = true;
|
||||
};
|
||||
users.groups.kea = {};
|
||||
|
||||
systemd.services.prometheus-kea-exporter.after = [ "kea-dhcp6.service" ];
|
||||
|
||||
systemd.services.kea-dhcp6 = let
|
||||
configFile = pkgs.writeText "kea-dhcp6.conf" (builtins.toJSON {
|
||||
Dhcp6 = {
|
||||
"control-socket" = {
|
||||
"socket-type" = "unix";
|
||||
"socket-name" = "/run/kea/kea-dhcp6.sock";
|
||||
};
|
||||
};
|
||||
});
|
||||
in
|
||||
{
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
serviceConfig = {
|
||||
DynamicUser = false;
|
||||
User = "kea";
|
||||
Group = "kea";
|
||||
ExecStart = "${pkgs.kea}/bin/kea-dhcp6 -c ${configFile}";
|
||||
StateDirectory = "kea";
|
||||
RuntimeDirectory = "kea";
|
||||
UMask = "0007";
|
||||
};
|
||||
};
|
||||
};
|
||||
exporterTest = ''
|
||||
wait_for_unit("kea-dhcp6.service")
|
||||
wait_for_file("/run/kea/kea-dhcp6.sock")
|
||||
wait_for_unit("prometheus-kea-exporter.service")
|
||||
wait_for_open_port(9547)
|
||||
succeed(
|
||||
"curl --fail localhost:9547/metrics | grep 'packets_received_total'"
|
||||
)
|
||||
'';
|
||||
};
|
||||
|
||||
knot = {
|
||||
exporterConfig = {
|
||||
enable = true;
|
||||
|
||||
+77
-122
@@ -1,140 +1,95 @@
|
||||
import ./make-test-python.nix ({ lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
user = "someuser";
|
||||
password = "some_password";
|
||||
port = builtins.toString 5232;
|
||||
port = "5232";
|
||||
filesystem_folder = "/data/radicale";
|
||||
|
||||
common = { pkgs, ... }: {
|
||||
cli = "${pkgs.calendar-cli}/bin/calendar-cli --caldav-user ${user} --caldav-pass ${password}";
|
||||
in {
|
||||
name = "radicale3";
|
||||
meta.maintainers = with lib.maintainers; [ dotlambda ];
|
||||
|
||||
machine = { pkgs, ... }: {
|
||||
services.radicale = {
|
||||
enable = true;
|
||||
config = ''
|
||||
[auth]
|
||||
type = htpasswd
|
||||
htpasswd_filename = /etc/radicale/htpasswd
|
||||
htpasswd_encryption = bcrypt
|
||||
|
||||
[storage]
|
||||
filesystem_folder = /tmp/collections
|
||||
'';
|
||||
settings = {
|
||||
auth = {
|
||||
type = "htpasswd";
|
||||
htpasswd_filename = "/etc/radicale/users";
|
||||
htpasswd_encryption = "bcrypt";
|
||||
};
|
||||
storage = {
|
||||
inherit filesystem_folder;
|
||||
hook = "git add -A && (git diff --cached --quiet || git commit -m 'Changes by '%(user)s)";
|
||||
};
|
||||
logging.level = "info";
|
||||
};
|
||||
rights = {
|
||||
principal = {
|
||||
user = ".+";
|
||||
collection = "{user}";
|
||||
permissions = "RW";
|
||||
};
|
||||
calendars = {
|
||||
user = ".+";
|
||||
collection = "{user}/[^/]+";
|
||||
permissions = "rw";
|
||||
};
|
||||
};
|
||||
};
|
||||
systemd.services.radicale.path = [ pkgs.git ];
|
||||
environment.systemPackages = [ pkgs.git ];
|
||||
systemd.tmpfiles.rules = [ "d ${filesystem_folder} 0750 radicale radicale -" ];
|
||||
# WARNING: DON'T DO THIS IN PRODUCTION!
|
||||
# This puts unhashed secrets directly into the Nix store for ease of testing.
|
||||
environment.etc."radicale/htpasswd".source = pkgs.runCommand "htpasswd" {} ''
|
||||
environment.etc."radicale/users".source = pkgs.runCommand "htpasswd" {} ''
|
||||
${pkgs.apacheHttpd}/bin/htpasswd -bcB "$out" ${user} ${password}
|
||||
'';
|
||||
};
|
||||
testScript = ''
|
||||
machine.wait_for_unit("radicale.service")
|
||||
machine.wait_for_open_port(${port})
|
||||
|
||||
in
|
||||
machine.succeed("sudo -u radicale git -C ${filesystem_folder} init")
|
||||
machine.succeed(
|
||||
"sudo -u radicale git -C ${filesystem_folder} config --local user.email radicale@example.com"
|
||||
)
|
||||
machine.succeed(
|
||||
"sudo -u radicale git -C ${filesystem_folder} config --local user.name radicale"
|
||||
)
|
||||
|
||||
import ./make-test-python.nix ({ lib, ... }@args: {
|
||||
name = "radicale";
|
||||
meta.maintainers = with lib.maintainers; [ aneeshusa infinisil ];
|
||||
with subtest("Test calendar and event creation"):
|
||||
machine.succeed(
|
||||
"${cli} --caldav-url http://localhost:${port}/${user} calendar create cal"
|
||||
)
|
||||
machine.succeed("test -d ${filesystem_folder}/collection-root/${user}/cal")
|
||||
machine.succeed('test -z "$(ls ${filesystem_folder}/collection-root/${user}/cal)"')
|
||||
machine.succeed(
|
||||
"${cli} --caldav-url http://localhost:${port}/${user}/cal calendar add 2021-04-23 testevent"
|
||||
)
|
||||
machine.succeed('test -n "$(ls ${filesystem_folder}/collection-root/${user}/cal)"')
|
||||
(status, stdout) = machine.execute(
|
||||
"sudo -u radicale git -C ${filesystem_folder} log --format=oneline | wc -l"
|
||||
)
|
||||
assert status == 0, "git log failed"
|
||||
assert stdout == "3\n", "there should be exactly 3 commits"
|
||||
|
||||
nodes = rec {
|
||||
radicale = radicale1; # Make the test script read more nicely
|
||||
radicale1 = lib.recursiveUpdate (common args) {
|
||||
nixpkgs.overlays = [
|
||||
(self: super: {
|
||||
radicale1 = super.radicale1.overrideAttrs (oldAttrs: {
|
||||
propagatedBuildInputs = with self.pythonPackages;
|
||||
(oldAttrs.propagatedBuildInputs or []) ++ [ passlib ];
|
||||
});
|
||||
})
|
||||
];
|
||||
system.stateVersion = "17.03";
|
||||
};
|
||||
radicale1_export = lib.recursiveUpdate radicale1 {
|
||||
services.radicale.extraArgs = [
|
||||
"--export-storage" "/tmp/collections-new"
|
||||
];
|
||||
system.stateVersion = "17.03";
|
||||
};
|
||||
radicale2_verify = lib.recursiveUpdate radicale2 {
|
||||
services.radicale.extraArgs = [ "--debug" "--verify-storage" ];
|
||||
system.stateVersion = "17.09";
|
||||
};
|
||||
radicale2 = lib.recursiveUpdate (common args) {
|
||||
system.stateVersion = "17.09";
|
||||
};
|
||||
radicale3 = lib.recursiveUpdate (common args) {
|
||||
system.stateVersion = "20.09";
|
||||
};
|
||||
};
|
||||
with subtest("Test rights file"):
|
||||
machine.fail(
|
||||
"${cli} --caldav-url http://localhost:${port}/${user} calendar create sub/cal"
|
||||
)
|
||||
machine.fail(
|
||||
"${cli} --caldav-url http://localhost:${port}/otheruser calendar create cal"
|
||||
)
|
||||
|
||||
# This tests whether the web interface is accessible to an authenticated user
|
||||
testScript = { nodes }: let
|
||||
switchToConfig = nodeName: let
|
||||
newSystem = nodes.${nodeName}.config.system.build.toplevel;
|
||||
in "${newSystem}/bin/switch-to-configuration test";
|
||||
in ''
|
||||
with subtest("Check Radicale 1 functionality"):
|
||||
radicale.succeed(
|
||||
"${switchToConfig "radicale1"} >&2"
|
||||
)
|
||||
radicale.wait_for_unit("radicale.service")
|
||||
radicale.wait_for_open_port(${port})
|
||||
radicale.succeed(
|
||||
"curl --fail http://${user}:${password}@localhost:${port}/someuser/calendar.ics/"
|
||||
)
|
||||
with subtest("Test web interface"):
|
||||
machine.succeed("curl --fail http://${user}:${password}@localhost:${port}/.web/")
|
||||
|
||||
with subtest("Export data in Radicale 2 format"):
|
||||
radicale.succeed("systemctl stop radicale")
|
||||
radicale.succeed("ls -al /tmp/collections")
|
||||
radicale.fail("ls -al /tmp/collections-new")
|
||||
|
||||
with subtest("Radicale exits immediately after exporting storage"):
|
||||
radicale.succeed(
|
||||
"${switchToConfig "radicale1_export"} >&2"
|
||||
)
|
||||
radicale.wait_until_fails("systemctl status radicale")
|
||||
radicale.succeed("ls -al /tmp/collections")
|
||||
radicale.succeed("ls -al /tmp/collections-new")
|
||||
|
||||
with subtest("Verify data in Radicale 2 format"):
|
||||
radicale.succeed("rm -r /tmp/collections/${user}")
|
||||
radicale.succeed("mv /tmp/collections-new/collection-root /tmp/collections")
|
||||
radicale.succeed(
|
||||
"${switchToConfig "radicale2_verify"} >&2"
|
||||
)
|
||||
radicale.wait_until_fails("systemctl status radicale")
|
||||
|
||||
(retcode, logs) = radicale.execute("journalctl -u radicale -n 10")
|
||||
assert (
|
||||
retcode == 0 and "Verifying storage" in logs
|
||||
), "Radicale 2 didn't verify storage"
|
||||
assert (
|
||||
"failed" not in logs and "exception" not in logs
|
||||
), "storage verification failed"
|
||||
|
||||
with subtest("Check Radicale 2 functionality"):
|
||||
radicale.succeed(
|
||||
"${switchToConfig "radicale2"} >&2"
|
||||
)
|
||||
radicale.wait_for_unit("radicale.service")
|
||||
radicale.wait_for_open_port(${port})
|
||||
|
||||
(retcode, output) = radicale.execute(
|
||||
"curl --fail http://${user}:${password}@localhost:${port}/someuser/calendar.ics/"
|
||||
)
|
||||
assert (
|
||||
retcode == 0 and "VCALENDAR" in output
|
||||
), "Could not read calendar from Radicale 2"
|
||||
|
||||
radicale.succeed("curl --fail http://${user}:${password}@localhost:${port}/.web/")
|
||||
|
||||
with subtest("Check Radicale 3 functionality"):
|
||||
radicale.succeed(
|
||||
"${switchToConfig "radicale3"} >&2"
|
||||
)
|
||||
radicale.wait_for_unit("radicale.service")
|
||||
radicale.wait_for_open_port(${port})
|
||||
|
||||
(retcode, output) = radicale.execute(
|
||||
"curl --fail http://${user}:${password}@localhost:${port}/someuser/calendar.ics/"
|
||||
)
|
||||
assert (
|
||||
retcode == 0 and "VCALENDAR" in output
|
||||
), "Could not read calendar from Radicale 3"
|
||||
|
||||
radicale.succeed("curl --fail http://${user}:${password}@localhost:${port}/.web/")
|
||||
'';
|
||||
with subtest("Test security"):
|
||||
output = machine.succeed("systemd-analyze security radicale.service")
|
||||
machine.log(output)
|
||||
assert output[-9:-1] == "SAFE :-}"
|
||||
'';
|
||||
})
|
||||
|
||||
@@ -38,6 +38,9 @@ import ./make-test-python.nix ({ pkgs, lib, ...} :
|
||||
|
||||
programs.sway.enable = true;
|
||||
|
||||
# To test pinentry via gpg-agent:
|
||||
programs.gnupg.agent.enable = true;
|
||||
|
||||
virtualisation.memorySize = 1024;
|
||||
# Need to switch to a different VGA card / GPU driver than the default one (std) so that Sway can launch:
|
||||
virtualisation.qemu.options = [ "-vga virtio" ];
|
||||
@@ -80,6 +83,17 @@ import ./make-test-python.nix ({ pkgs, lib, ...} :
|
||||
machine.send_key("alt-shift-q")
|
||||
machine.wait_until_fails("pgrep alacritty")
|
||||
|
||||
# Test gpg-agent starting pinentry-gnome3 via D-Bus (tests if
|
||||
# $WAYLAND_DISPLAY is correctly imported into the D-Bus user env):
|
||||
machine.succeed(
|
||||
"su - alice -c 'swaymsg -- exec gpg --no-tty --yes --quick-generate-key test'"
|
||||
)
|
||||
machine.wait_until_succeeds("pgrep --exact gpg")
|
||||
machine.wait_for_text("Passphrase")
|
||||
machine.screenshot("gpg_pinentry")
|
||||
machine.send_key("alt-shift-q")
|
||||
machine.wait_until_fails("pgrep --exact gpg")
|
||||
|
||||
# Test swaynag:
|
||||
machine.send_key("alt-shift-e")
|
||||
machine.wait_for_text("You pressed the exit shortcut.")
|
||||
|
||||
Reference in New Issue
Block a user