189 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			189 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
{ system ? builtins.currentSystem
 | 
						|
, config ? { }
 | 
						|
, pkgs ? import ../.. { inherit system config; }
 | 
						|
}:
 | 
						|
 | 
						|
# These tests will:
 | 
						|
#  * Set up a bitwarden-rs server
 | 
						|
#  * Have Firefox use the web vault to create an account, log in, and save a password to the valut
 | 
						|
#  * Have the bw cli log in and read that password from the vault
 | 
						|
#
 | 
						|
# Note that Firefox must be on the same machine as the server for WebCrypto APIs to be available (or HTTPS must be configured)
 | 
						|
#
 | 
						|
# The same tests should work without modification on the official bitwarden server, if we ever package that.
 | 
						|
 | 
						|
with import ../lib/testing-python.nix { inherit system pkgs; };
 | 
						|
with pkgs.lib;
 | 
						|
let
 | 
						|
  backends = [ "sqlite" "mysql" "postgresql" ];
 | 
						|
 | 
						|
  dbPassword = "please_dont_hack";
 | 
						|
 | 
						|
  userEmail = "meow@example.com";
 | 
						|
  userPassword = "also_super_secret_ZJWpBKZi668QGt"; # Must be complex to avoid interstitial warning on the signup page
 | 
						|
 | 
						|
  storedPassword = "seeeecret";
 | 
						|
 | 
						|
  makeBitwardenTest = backend: makeTest {
 | 
						|
    name = "bitwarden_rs-${backend}";
 | 
						|
    meta = {
 | 
						|
      maintainers = with pkgs.stdenv.lib.maintainers; [ jjjollyjim ];
 | 
						|
    };
 | 
						|
 | 
						|
    nodes = {
 | 
						|
      server = { pkgs, ... }:
 | 
						|
        let backendConfig = {
 | 
						|
          mysql = {
 | 
						|
            services.mysql = {
 | 
						|
              enable = true;
 | 
						|
              initialScript = pkgs.writeText "mysql-init.sql" ''
 | 
						|
                CREATE DATABASE bitwarden;
 | 
						|
                CREATE USER 'bitwardenuser'@'localhost' IDENTIFIED BY '${dbPassword}';
 | 
						|
                GRANT ALL ON `bitwarden`.* TO 'bitwardenuser'@'localhost';
 | 
						|
                FLUSH PRIVILEGES;
 | 
						|
              '';
 | 
						|
              package = pkgs.mysql;
 | 
						|
            };
 | 
						|
 | 
						|
            services.bitwarden_rs.config.databaseUrl = "mysql://bitwardenuser:${dbPassword}@localhost/bitwarden";
 | 
						|
 | 
						|
            systemd.services.bitwarden_rs.after = [ "mysql.service" ];
 | 
						|
          };
 | 
						|
 | 
						|
          postgresql = {
 | 
						|
            services.postgresql = {
 | 
						|
              enable = true;
 | 
						|
              initialScript = pkgs.writeText "postgresql-init.sql" ''
 | 
						|
                CREATE DATABASE bitwarden;
 | 
						|
                CREATE USER bitwardenuser WITH PASSWORD '${dbPassword}';
 | 
						|
                GRANT ALL PRIVILEGES ON DATABASE bitwarden TO bitwardenuser;
 | 
						|
              '';
 | 
						|
            };
 | 
						|
 | 
						|
            services.bitwarden_rs.config.databaseUrl = "postgresql://bitwardenuser:${dbPassword}@localhost/bitwarden";
 | 
						|
 | 
						|
            systemd.services.bitwarden_rs.after = [ "postgresql.service" ];
 | 
						|
          };
 | 
						|
 | 
						|
          sqlite = { };
 | 
						|
        };
 | 
						|
        in
 | 
						|
        mkMerge [
 | 
						|
          backendConfig.${backend}
 | 
						|
          {
 | 
						|
            services.bitwarden_rs = {
 | 
						|
              enable = true;
 | 
						|
              dbBackend = backend;
 | 
						|
              config.rocketPort = 80;
 | 
						|
            };
 | 
						|
 | 
						|
            networking.firewall.allowedTCPPorts = [ 80 ];
 | 
						|
 | 
						|
            environment.systemPackages =
 | 
						|
              let
 | 
						|
                testRunner = pkgs.writers.writePython3Bin "test-runner"
 | 
						|
                  {
 | 
						|
                    libraries = [ pkgs.python3Packages.selenium ];
 | 
						|
                  } ''
 | 
						|
                  from selenium.webdriver import Firefox
 | 
						|
                  from selenium.webdriver.firefox.options import Options
 | 
						|
                  from selenium.webdriver.support.ui import WebDriverWait
 | 
						|
                  from selenium.webdriver.support import expected_conditions as EC
 | 
						|
 | 
						|
                  options = Options()
 | 
						|
                  options.add_argument('--headless')
 | 
						|
                  driver = Firefox(options=options)
 | 
						|
 | 
						|
                  driver.implicitly_wait(20)
 | 
						|
                  driver.get('http://localhost/#/register')
 | 
						|
 | 
						|
                  wait = WebDriverWait(driver, 10)
 | 
						|
 | 
						|
                  wait.until(EC.title_contains("Create Account"))
 | 
						|
 | 
						|
                  driver.find_element_by_css_selector('input#email').send_keys(
 | 
						|
                    '${userEmail}'
 | 
						|
                  )
 | 
						|
                  driver.find_element_by_css_selector('input#name').send_keys(
 | 
						|
                    'A Cat'
 | 
						|
                  )
 | 
						|
                  driver.find_element_by_css_selector('input#masterPassword').send_keys(
 | 
						|
                    '${userPassword}'
 | 
						|
                  )
 | 
						|
                  driver.find_element_by_css_selector('input#masterPasswordRetype').send_keys(
 | 
						|
                    '${userPassword}'
 | 
						|
                  )
 | 
						|
 | 
						|
                  driver.find_element_by_xpath("//button[contains(., 'Submit')]").click()
 | 
						|
 | 
						|
                  wait.until_not(EC.title_contains("Create Account"))
 | 
						|
 | 
						|
                  driver.find_element_by_css_selector('input#masterPassword').send_keys(
 | 
						|
                    '${userPassword}'
 | 
						|
                  )
 | 
						|
                  driver.find_element_by_xpath("//button[contains(., 'Log In')]").click()
 | 
						|
 | 
						|
                  wait.until(EC.title_contains("My Vault"))
 | 
						|
 | 
						|
                  driver.find_element_by_xpath("//button[contains(., 'Add Item')]").click()
 | 
						|
 | 
						|
                  driver.find_element_by_css_selector('input#name').send_keys(
 | 
						|
                    'secrets'
 | 
						|
                  )
 | 
						|
                  driver.find_element_by_css_selector('input#loginPassword').send_keys(
 | 
						|
                    '${storedPassword}'
 | 
						|
                  )
 | 
						|
 | 
						|
                  driver.find_element_by_xpath("//button[contains(., 'Save')]").click()
 | 
						|
                '';
 | 
						|
              in
 | 
						|
              [ pkgs.firefox-unwrapped pkgs.geckodriver testRunner ];
 | 
						|
 | 
						|
            virtualisation.memorySize = 768;
 | 
						|
          }
 | 
						|
        ];
 | 
						|
 | 
						|
      client = { pkgs, ... }:
 | 
						|
        {
 | 
						|
          environment.systemPackages = [ pkgs.bitwarden-cli ];
 | 
						|
        };
 | 
						|
    };
 | 
						|
 | 
						|
    testScript = ''
 | 
						|
      start_all()
 | 
						|
      server.wait_for_unit("bitwarden_rs.service")
 | 
						|
      server.wait_for_open_port(80)
 | 
						|
 | 
						|
      with subtest("configure the cli"):
 | 
						|
          client.succeed("bw --nointeraction config server http://server")
 | 
						|
 | 
						|
      with subtest("can't login to nonexistant account"):
 | 
						|
          client.fail(
 | 
						|
              "bw --nointeraction --raw login ${userEmail} ${userPassword}"
 | 
						|
          )
 | 
						|
 | 
						|
      with subtest("use the web interface to sign up, log in, and save a password"):
 | 
						|
          server.succeed("PYTHONUNBUFFERED=1 test-runner | systemd-cat -t test-runner")
 | 
						|
 | 
						|
      with subtest("log in with the cli"):
 | 
						|
          key = client.succeed(
 | 
						|
              "bw --nointeraction --raw login ${userEmail} ${userPassword}"
 | 
						|
          ).strip()
 | 
						|
 | 
						|
      with subtest("sync with the cli"):
 | 
						|
          client.succeed(f"bw --nointeraction --raw --session {key} sync -f")
 | 
						|
 | 
						|
      with subtest("get the password with the cli"):
 | 
						|
          password = client.succeed(
 | 
						|
              f"bw --nointeraction --raw --session {key} list items | ${pkgs.jq}/bin/jq -r .[].login.password"
 | 
						|
          )
 | 
						|
          assert password.strip() == "${storedPassword}"
 | 
						|
    '';
 | 
						|
  };
 | 
						|
in
 | 
						|
builtins.listToAttrs (
 | 
						|
  map
 | 
						|
    (backend: { name = backend; value = makeBitwardenTest backend; })
 | 
						|
    backends
 | 
						|
)
 |