at 23.05-pre 6.8 kB view raw
1{ system ? builtins.currentSystem 2, config ? { } 3, pkgs ? import ../.. { inherit system config; } 4}: 5 6# These tests will: 7# * Set up a vaultwarden server 8# * Have Firefox use the web vault to create an account, log in, and save a password to the valut 9# * Have the bw cli log in and read that password from the vault 10# 11# Note that Firefox must be on the same machine as the server for WebCrypto APIs to be available (or HTTPS must be configured) 12# 13# The same tests should work without modification on the official bitwarden server, if we ever package that. 14 15with import ../lib/testing-python.nix { inherit system pkgs; }; 16with pkgs.lib; 17let 18 backends = [ "sqlite" "mysql" "postgresql" ]; 19 20 dbPassword = "please_dont_hack"; 21 22 userEmail = "meow@example.com"; 23 userPassword = "also_super_secret_ZJWpBKZi668QGt"; # Must be complex to avoid interstitial warning on the signup page 24 25 storedPassword = "seeeecret"; 26 27 makeVaultwardenTest = backend: makeTest { 28 name = "vaultwarden-${backend}"; 29 meta = { 30 maintainers = with pkgs.lib.maintainers; [ jjjollyjim ]; 31 }; 32 33 nodes = { 34 server = { pkgs, ... }: 35 let backendConfig = { 36 mysql = { 37 services.mysql = { 38 enable = true; 39 initialScript = pkgs.writeText "mysql-init.sql" '' 40 CREATE DATABASE bitwarden; 41 CREATE USER 'bitwardenuser'@'localhost' IDENTIFIED BY '${dbPassword}'; 42 GRANT ALL ON `bitwarden`.* TO 'bitwardenuser'@'localhost'; 43 FLUSH PRIVILEGES; 44 ''; 45 package = pkgs.mariadb; 46 }; 47 48 services.vaultwarden.config.databaseUrl = "mysql://bitwardenuser:${dbPassword}@localhost/bitwarden"; 49 50 systemd.services.vaultwarden.after = [ "mysql.service" ]; 51 }; 52 53 postgresql = { 54 services.postgresql = { 55 enable = true; 56 initialScript = pkgs.writeText "postgresql-init.sql" '' 57 CREATE DATABASE bitwarden; 58 CREATE USER bitwardenuser WITH PASSWORD '${dbPassword}'; 59 GRANT ALL PRIVILEGES ON DATABASE bitwarden TO bitwardenuser; 60 ''; 61 }; 62 63 services.vaultwarden.config.databaseUrl = "postgresql://bitwardenuser:${dbPassword}@localhost/bitwarden"; 64 65 systemd.services.vaultwarden.after = [ "postgresql.service" ]; 66 }; 67 68 sqlite = { }; 69 }; 70 in 71 mkMerge [ 72 backendConfig.${backend} 73 { 74 services.vaultwarden = { 75 enable = true; 76 dbBackend = backend; 77 config = { 78 rocketAddress = "0.0.0.0"; 79 rocketPort = 80; 80 }; 81 }; 82 83 networking.firewall.allowedTCPPorts = [ 80 ]; 84 85 environment.systemPackages = 86 let 87 testRunner = pkgs.writers.writePython3Bin "test-runner" 88 { 89 libraries = [ pkgs.python3Packages.selenium ]; 90 } '' 91 92 from selenium.webdriver.common.by import By 93 from selenium.webdriver import Firefox 94 from selenium.webdriver.firefox.options import Options 95 from selenium.webdriver.support.ui import WebDriverWait 96 from selenium.webdriver.support import expected_conditions as EC 97 98 options = Options() 99 options.add_argument('--headless') 100 driver = Firefox(options=options) 101 102 driver.implicitly_wait(20) 103 driver.get('http://localhost/#/register') 104 105 wait = WebDriverWait(driver, 10) 106 107 wait.until(EC.title_contains("Create Account")) 108 109 driver.find_element(By.CSS_SELECTOR, 'input#email').send_keys( 110 '${userEmail}' 111 ) 112 driver.find_element(By.CSS_SELECTOR, 'input#name').send_keys( 113 'A Cat' 114 ) 115 driver.find_element(By.CSS_SELECTOR, 'input#masterPassword').send_keys( 116 '${userPassword}' 117 ) 118 driver.find_element(By.CSS_SELECTOR, 'input#masterPasswordRetype').send_keys( 119 '${userPassword}' 120 ) 121 122 driver.find_element(By.XPATH, "//button[contains(., 'Submit')]").click() 123 124 wait.until_not(EC.title_contains("Create Account")) 125 126 driver.find_element(By.CSS_SELECTOR, 'input#masterPassword').send_keys( 127 '${userPassword}' 128 ) 129 driver.find_element(By.XPATH, "//button[contains(., 'Log In')]").click() 130 131 wait.until(EC.title_contains("Bitwarden Web Vault")) 132 133 driver.find_element(By.XPATH, "//button[contains(., 'Add Item')]").click() 134 135 driver.find_element(By.CSS_SELECTOR, 'input#name').send_keys( 136 'secrets' 137 ) 138 driver.find_element(By.CSS_SELECTOR, 'input#loginPassword').send_keys( 139 '${storedPassword}' 140 ) 141 142 driver.find_element(By.XPATH, "//button[contains(., 'Save')]").click() 143 ''; 144 in 145 [ pkgs.firefox-unwrapped pkgs.geckodriver testRunner ]; 146 147 } 148 ]; 149 150 client = { pkgs, ... }: 151 { 152 environment.systemPackages = [ pkgs.bitwarden-cli ]; 153 }; 154 }; 155 156 testScript = '' 157 start_all() 158 server.wait_for_unit("vaultwarden.service") 159 server.wait_for_open_port(80) 160 161 with subtest("configure the cli"): 162 client.succeed("bw --nointeraction config server http://server") 163 164 with subtest("can't login to nonexistant account"): 165 client.fail( 166 "bw --nointeraction --raw login ${userEmail} ${userPassword}" 167 ) 168 169 with subtest("use the web interface to sign up, log in, and save a password"): 170 server.succeed("PYTHONUNBUFFERED=1 test-runner | systemd-cat -t test-runner") 171 172 with subtest("log in with the cli"): 173 key = client.succeed( 174 "bw --nointeraction --raw login ${userEmail} ${userPassword}" 175 ).strip() 176 177 with subtest("sync with the cli"): 178 client.succeed(f"bw --nointeraction --raw --session {key} sync -f") 179 180 with subtest("get the password with the cli"): 181 password = client.succeed( 182 f"bw --nointeraction --raw --session {key} list items | ${pkgs.jq}/bin/jq -r .[].login.password" 183 ) 184 assert password.strip() == "${storedPassword}" 185 ''; 186 }; 187in 188builtins.listToAttrs ( 189 map 190 (backend: { name = backend; value = makeVaultwardenTest backend; }) 191 backends 192)