| 
									
										
										
										
											2017-02-17 19:28:55 +01:00
										 |  |  | import ./make-test.nix ({ pkgs, ... }: let | 
					
						
							|  |  |  |   snakeOil = pkgs.runCommand "snakeoil-certs" { | 
					
						
							|  |  |  |     outputs = [ "out" "cacert" "cert" "key" "crl" ]; | 
					
						
							|  |  |  |     buildInputs = [ pkgs.gnutls.bin ]; | 
					
						
							|  |  |  |     caTemplate = pkgs.writeText "snakeoil-ca.template" ''
 | 
					
						
							|  |  |  |       cn = server | 
					
						
							|  |  |  |       expiration_days = -1 | 
					
						
							|  |  |  |       cert_signing_key | 
					
						
							|  |  |  |       ca | 
					
						
							|  |  |  |     '';
 | 
					
						
							|  |  |  |     certTemplate = pkgs.writeText "snakeoil-cert.template" ''
 | 
					
						
							|  |  |  |       cn = server | 
					
						
							|  |  |  |       expiration_days = -1 | 
					
						
							|  |  |  |       tls_www_server | 
					
						
							|  |  |  |       encryption_key | 
					
						
							|  |  |  |       signing_key | 
					
						
							|  |  |  |     '';
 | 
					
						
							|  |  |  |     crlTemplate = pkgs.writeText "snakeoil-crl.template" ''
 | 
					
						
							|  |  |  |       expiration_days = -1 | 
					
						
							|  |  |  |     '';
 | 
					
						
							|  |  |  |     userCertTemplace = pkgs.writeText "snakoil-user-cert.template" ''
 | 
					
						
							|  |  |  |       organization = snakeoil | 
					
						
							|  |  |  |       cn = server | 
					
						
							|  |  |  |       expiration_days = -1 | 
					
						
							|  |  |  |       tls_www_client | 
					
						
							|  |  |  |       encryption_key | 
					
						
							|  |  |  |       signing_key | 
					
						
							|  |  |  |     '';
 | 
					
						
							|  |  |  |   } ''
 | 
					
						
							|  |  |  |     certtool -p --bits 4096 --outfile ca.key | 
					
						
							|  |  |  |     certtool -s --template "$caTemplate" --load-privkey ca.key \ | 
					
						
							|  |  |  |                 --outfile "$cacert" | 
					
						
							|  |  |  |     certtool -p --bits 4096 --outfile "$key" | 
					
						
							|  |  |  |     certtool -c --template "$certTemplate" \ | 
					
						
							|  |  |  |                 --load-ca-privkey ca.key \ | 
					
						
							|  |  |  |                 --load-ca-certificate "$cacert" \ | 
					
						
							|  |  |  |                 --load-privkey "$key" \ | 
					
						
							|  |  |  |                 --outfile "$cert" | 
					
						
							|  |  |  |     certtool --generate-crl --template "$crlTemplate" \ | 
					
						
							|  |  |  |                             --load-ca-privkey ca.key \ | 
					
						
							|  |  |  |                             --load-ca-certificate "$cacert" \ | 
					
						
							|  |  |  |                             --outfile "$crl" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     mkdir "$out" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Stripping key information before the actual PEM-encoded values is solely | 
					
						
							|  |  |  |     # to make test output a bit less verbose when copying the client key to the | 
					
						
							|  |  |  |     # actual client. | 
					
						
							|  |  |  |     certtool -p --bits 4096 | sed -n \ | 
					
						
							|  |  |  |       -e '/^----* *BEGIN/,/^----* *END/p' > "$out/alice.key" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     certtool -c --template "$userCertTemplace" \ | 
					
						
							|  |  |  |                 --load-privkey "$out/alice.key" \ | 
					
						
							|  |  |  |                 --load-ca-privkey ca.key \ | 
					
						
							|  |  |  |                 --load-ca-certificate "$cacert" \ | 
					
						
							|  |  |  |                 --outfile "$out/alice.cert" | 
					
						
							|  |  |  |   '';
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | in { | 
					
						
							| 
									
										
										
										
											2016-04-05 17:52:55 +02:00
										 |  |  |   name = "taskserver"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-11 12:03:16 +02:00
										 |  |  |   nodes = rec { | 
					
						
							| 
									
										
										
										
											2016-04-05 17:52:55 +02:00
										 |  |  |     server = { | 
					
						
							|  |  |  |       services.taskserver.enable = true; | 
					
						
							| 
									
										
										
										
											2016-04-11 12:26:34 +02:00
										 |  |  |       services.taskserver.listenHost = "::"; | 
					
						
							| 
									
										
										
										
											2016-04-11 12:42:20 +02:00
										 |  |  |       services.taskserver.fqdn = "server"; | 
					
						
							| 
									
										
										
										
											2016-04-05 17:52:55 +02:00
										 |  |  |       services.taskserver.organisations = { | 
					
						
							|  |  |  |         testOrganisation.users = [ "alice" "foo" ]; | 
					
						
							|  |  |  |         anotherOrganisation.users = [ "bob" ]; | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-17 19:28:55 +01:00
										 |  |  |     # New generation of the server with manual config | 
					
						
							|  |  |  |     newServer = { lib, nodes, ... }: { | 
					
						
							|  |  |  |       imports = [ server ]; | 
					
						
							|  |  |  |       services.taskserver.pki.manual = { | 
					
						
							|  |  |  |         ca.cert = snakeOil.cacert; | 
					
						
							|  |  |  |         server.cert = snakeOil.cert; | 
					
						
							|  |  |  |         server.key = snakeOil.key; | 
					
						
							|  |  |  |         server.crl = snakeOil.crl; | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       # This is to avoid assigning a different network address to the new | 
					
						
							|  |  |  |       # generation. | 
					
						
							|  |  |  |       networking = lib.mapAttrs (lib.const lib.mkForce) { | 
					
						
							|  |  |  |         inherit (nodes.server.config.networking) | 
					
						
							|  |  |  |           hostName interfaces primaryIPAddress extraHosts; | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-05 17:52:55 +02:00
										 |  |  |     client1 = { pkgs, ... }: { | 
					
						
							| 
									
										
										
										
											2016-04-12 01:08:34 +02:00
										 |  |  |       environment.systemPackages = [ pkgs.taskwarrior pkgs.gnutls ]; | 
					
						
							| 
									
										
										
										
											2016-04-05 17:52:55 +02:00
										 |  |  |       users.users.alice.isNormalUser = true; | 
					
						
							|  |  |  |       users.users.bob.isNormalUser = true; | 
					
						
							|  |  |  |       users.users.foo.isNormalUser = true; | 
					
						
							| 
									
										
										
										
											2016-04-11 12:03:16 +02:00
										 |  |  |       users.users.bar.isNormalUser = true; | 
					
						
							| 
									
										
										
										
											2016-04-05 17:52:55 +02:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-11 12:03:16 +02:00
										 |  |  |     client2 = client1; | 
					
						
							| 
									
										
										
										
											2016-04-05 17:52:55 +02:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   testScript = { nodes, ... }: let | 
					
						
							|  |  |  |     cfg = nodes.server.config.services.taskserver; | 
					
						
							| 
									
										
										
										
											2016-04-11 12:26:34 +02:00
										 |  |  |     portStr = toString cfg.listenPort; | 
					
						
							| 
									
										
										
										
											2017-02-17 19:28:55 +01:00
										 |  |  |     newServerSystem = nodes.newServer.config.system.build.toplevel; | 
					
						
							|  |  |  |     switchToNewServer = "${newServerSystem}/bin/switch-to-configuration test"; | 
					
						
							| 
									
										
										
										
											2016-04-05 17:52:55 +02:00
										 |  |  |   in ''
 | 
					
						
							|  |  |  |     sub su ($$) { | 
					
						
							|  |  |  |       my ($user, $cmd) = @_; | 
					
						
							|  |  |  |       my $esc = $cmd =~ s/'/'\\${"'"}'/gr; | 
					
						
							|  |  |  |       return "su - $user -c '$esc'"; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-17 19:28:55 +01:00
										 |  |  |     sub setupClientsFor ($$;$) { | 
					
						
							|  |  |  |       my ($org, $user, $extraInit) = @_; | 
					
						
							| 
									
										
										
										
											2016-04-05 17:52:55 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       for my $client ($client1, $client2) { | 
					
						
							|  |  |  |         $client->nest("initialize client for user $user", sub { | 
					
						
							|  |  |  |           $client->succeed( | 
					
						
							| 
									
										
										
										
											2016-04-12 03:42:13 +02:00
										 |  |  |             (su $user, "rm -rf /home/$user/.task"), | 
					
						
							|  |  |  |             (su $user, "task rc.confirmation=no config confirmation no") | 
					
						
							| 
									
										
										
										
											2016-04-05 17:52:55 +02:00
										 |  |  |           ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           my $exportinfo = $server->succeed( | 
					
						
							| 
									
										
										
										
											2016-04-12 05:38:37 +02:00
										 |  |  |             "nixos-taskserver user export $org $user" | 
					
						
							| 
									
										
										
										
											2016-04-05 17:52:55 +02:00
										 |  |  |           ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           $exportinfo =~ s/'/'\\'''/g;
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-12 01:49:47 +02:00
										 |  |  |           $client->nest("importing taskwarrior configuration", sub { | 
					
						
							|  |  |  |             my $cmd = su $user, "eval '$exportinfo' >&2"; | 
					
						
							|  |  |  |             my ($status, $out) = $client->execute_($cmd); | 
					
						
							|  |  |  |             if ($status != 0) { | 
					
						
							|  |  |  |               $client->log("output: $out"); | 
					
						
							|  |  |  |               die "command `$cmd' did not succeed (exit code $status)\n"; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-17 19:28:55 +01:00
										 |  |  |           eval { &$extraInit($client, $org, $user) }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-05 17:52:55 +02:00
										 |  |  |           $client->succeed(su $user, | 
					
						
							|  |  |  |             "task config taskd.server server:${portStr} >&2" | 
					
						
							|  |  |  |           ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           $client->succeed(su $user, "task sync init >&2"); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-12 01:08:34 +02:00
										 |  |  |     sub restartServer { | 
					
						
							|  |  |  |       $server->succeed("systemctl restart taskserver.service"); | 
					
						
							|  |  |  |       $server->waitForOpenPort(${portStr}); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     sub readdImperativeUser { | 
					
						
							|  |  |  |       $server->nest("(re-)add imperative user bar", sub { | 
					
						
							| 
									
										
										
										
											2016-04-12 05:38:37 +02:00
										 |  |  |         $server->execute("nixos-taskserver org remove imperativeOrg"); | 
					
						
							| 
									
										
										
										
											2016-04-12 01:08:34 +02:00
										 |  |  |         $server->succeed( | 
					
						
							| 
									
										
										
										
											2016-04-12 05:38:37 +02:00
										 |  |  |           "nixos-taskserver org add imperativeOrg", | 
					
						
							|  |  |  |           "nixos-taskserver user add imperativeOrg bar" | 
					
						
							| 
									
										
										
										
											2016-04-12 01:08:34 +02:00
										 |  |  |         ); | 
					
						
							|  |  |  |         setupClientsFor "imperativeOrg", "bar"; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-11 12:03:16 +02:00
										 |  |  |     sub testSync ($) { | 
					
						
							|  |  |  |       my $user = $_[0]; | 
					
						
							|  |  |  |       subtest "sync for user $user", sub { | 
					
						
							|  |  |  |         $client1->succeed(su $user, "task add foo >&2"); | 
					
						
							|  |  |  |         $client1->succeed(su $user, "task sync >&2"); | 
					
						
							|  |  |  |         $client2->fail(su $user, "task list >&2"); | 
					
						
							|  |  |  |         $client2->succeed(su $user, "task sync >&2"); | 
					
						
							|  |  |  |         $client2->succeed(su $user, "task list >&2"); | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-12 01:08:34 +02:00
										 |  |  |     sub checkClientCert ($) { | 
					
						
							|  |  |  |       my $user = $_[0]; | 
					
						
							|  |  |  |       my $cmd = "gnutls-cli". | 
					
						
							|  |  |  |         " --x509cafile=/home/$user/.task/keys/ca.cert". | 
					
						
							|  |  |  |         " --x509keyfile=/home/$user/.task/keys/private.key". | 
					
						
							|  |  |  |         " --x509certfile=/home/$user/.task/keys/public.cert". | 
					
						
							|  |  |  |         " --port=${portStr} server < /dev/null"; | 
					
						
							|  |  |  |       return su $user, $cmd; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-17 19:28:55 +01:00
										 |  |  |     # Explicitly start the VMs so that we don't accidentally start newServer | 
					
						
							|  |  |  |     $server->start; | 
					
						
							|  |  |  |     $client1->start; | 
					
						
							|  |  |  |     $client2->start; | 
					
						
							| 
									
										
										
										
											2016-04-05 17:52:55 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     $server->waitForUnit("taskserver.service"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $server->succeed( | 
					
						
							| 
									
										
										
										
											2016-04-12 05:38:37 +02:00
										 |  |  |       "nixos-taskserver user list testOrganisation | grep -qxF alice", | 
					
						
							|  |  |  |       "nixos-taskserver user list testOrganisation | grep -qxF foo", | 
					
						
							|  |  |  |       "nixos-taskserver user list anotherOrganisation | grep -qxF bob" | 
					
						
							| 
									
										
										
										
											2016-04-05 17:52:55 +02:00
										 |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $server->waitForOpenPort(${portStr}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $client1->waitForUnit("multi-user.target"); | 
					
						
							|  |  |  |     $client2->waitForUnit("multi-user.target"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     setupClientsFor "testOrganisation", "alice"; | 
					
						
							|  |  |  |     setupClientsFor "testOrganisation", "foo"; | 
					
						
							|  |  |  |     setupClientsFor "anotherOrganisation", "bob"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-11 12:03:16 +02:00
										 |  |  |     testSync $_ for ("alice", "bob", "foo"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-12 05:38:37 +02:00
										 |  |  |     $server->fail("nixos-taskserver user add imperativeOrg bar"); | 
					
						
							| 
									
										
										
										
											2016-04-12 01:08:34 +02:00
										 |  |  |     readdImperativeUser; | 
					
						
							| 
									
										
										
										
											2016-04-11 12:03:16 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     testSync "bar"; | 
					
						
							| 
									
										
										
										
											2016-04-12 01:08:34 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     subtest "checking certificate revocation of user bar", sub { | 
					
						
							|  |  |  |       $client1->succeed(checkClientCert "bar"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-12 05:38:37 +02:00
										 |  |  |       $server->succeed("nixos-taskserver user remove imperativeOrg bar"); | 
					
						
							| 
									
										
										
										
											2016-04-12 01:08:34 +02:00
										 |  |  |       restartServer; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       $client1->fail(checkClientCert "bar"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       $client1->succeed(su "bar", "task add destroy everything >&2"); | 
					
						
							|  |  |  |       $client1->fail(su "bar", "task sync >&2"); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     readdImperativeUser; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     subtest "checking certificate revocation of org imperativeOrg", sub { | 
					
						
							|  |  |  |       $client1->succeed(checkClientCert "bar"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-12 05:38:37 +02:00
										 |  |  |       $server->succeed("nixos-taskserver org remove imperativeOrg"); | 
					
						
							| 
									
										
										
										
											2016-04-12 01:08:34 +02:00
										 |  |  |       restartServer; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       $client1->fail(checkClientCert "bar"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       $client1->succeed(su "bar", "task add destroy even more >&2"); | 
					
						
							|  |  |  |       $client1->fail(su "bar", "task sync >&2"); | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2016-04-12 03:42:13 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     readdImperativeUser; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     subtest "check whether declarative config overrides user bar", sub { | 
					
						
							|  |  |  |       restartServer; | 
					
						
							|  |  |  |       testSync "bar"; | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2017-02-17 19:28:55 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     subtest "check manual configuration", sub { | 
					
						
							| 
									
										
										
										
											2017-07-16 18:57:38 +02:00
										 |  |  |       # Remove the keys from automatic CA creation, to make sure the new | 
					
						
							|  |  |  |       # generation doesn't use keys from before. | 
					
						
							|  |  |  |       $server->succeed('rm -rf ${cfg.dataDir}/keys/* >&2');
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-17 19:28:55 +01:00
										 |  |  |       $server->succeed('${switchToNewServer} >&2'); | 
					
						
							|  |  |  |       $server->waitForUnit("taskserver.service"); | 
					
						
							|  |  |  |       $server->waitForOpenPort(${portStr}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       $server->succeed( | 
					
						
							|  |  |  |         "nixos-taskserver org add manualOrg", | 
					
						
							|  |  |  |         "nixos-taskserver user add manualOrg alice" | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       setupClientsFor "manualOrg", "alice", sub { | 
					
						
							|  |  |  |         my ($client, $org, $user) = @_; | 
					
						
							|  |  |  |         my $cfgpath = "/home/$user/.task"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $client->copyFileFromHost("${snakeOil.cacert}", "$cfgpath/ca.cert"); | 
					
						
							|  |  |  |         for my $file ('alice.key', 'alice.cert') { | 
					
						
							|  |  |  |           $client->copyFileFromHost("${snakeOil}/$file", "$cfgpath/$file"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for my $file ("$user.key", "$user.cert") { | 
					
						
							|  |  |  |           $client->copyFileFromHost( | 
					
						
							|  |  |  |             "${snakeOil}/$file", "$cfgpath/$file" | 
					
						
							|  |  |  |           ); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         $client->copyFileFromHost( | 
					
						
							|  |  |  |           "${snakeOil.cacert}", "$cfgpath/ca.cert" | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |         $client->succeed( | 
					
						
							|  |  |  |           (su "alice", "task config taskd.ca $cfgpath/ca.cert"), | 
					
						
							|  |  |  |           (su "alice", "task config taskd.key $cfgpath/$user.key"), | 
					
						
							|  |  |  |           (su $user, "task config taskd.certificate $cfgpath/$user.cert") | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       testSync "alice"; | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2016-04-05 17:52:55 +02:00
										 |  |  |   '';
 | 
					
						
							| 
									
										
										
										
											2017-02-17 19:28:55 +01:00
										 |  |  | }) |