at 21.11-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 bitwarden-rs 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 makeBitwardenTest = backend: makeTest { 28 name = "bitwarden_rs-${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.mysql; 46 }; 47 48 services.bitwarden_rs.config.databaseUrl = "mysql://bitwardenuser:${dbPassword}@localhost/bitwarden"; 49 50 systemd.services.bitwarden_rs.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.bitwarden_rs.config.databaseUrl = "postgresql://bitwardenuser:${dbPassword}@localhost/bitwarden"; 64 65 systemd.services.bitwarden_rs.after = [ "postgresql.service" ]; 66 }; 67 68 sqlite = { }; 69 }; 70 in 71 mkMerge [ 72 backendConfig.${backend} 73 { 74 services.bitwarden_rs = { 75 enable = true; 76 dbBackend = backend; 77 config.rocketPort = 80; 78 }; 79 80 networking.firewall.allowedTCPPorts = [ 80 ]; 81 82 environment.systemPackages = 83 let 84 testRunner = pkgs.writers.writePython3Bin "test-runner" 85 { 86 libraries = [ pkgs.python3Packages.selenium ]; 87 } '' 88 from selenium.webdriver import Firefox 89 from selenium.webdriver.firefox.options import Options 90 from selenium.webdriver.support.ui import WebDriverWait 91 from selenium.webdriver.support import expected_conditions as EC 92 93 options = Options() 94 options.add_argument('--headless') 95 driver = Firefox(options=options) 96 97 driver.implicitly_wait(20) 98 driver.get('http://localhost/#/register') 99 100 wait = WebDriverWait(driver, 10) 101 102 wait.until(EC.title_contains("Create Account")) 103 104 driver.find_element_by_css_selector('input#email').send_keys( 105 '${userEmail}' 106 ) 107 driver.find_element_by_css_selector('input#name').send_keys( 108 'A Cat' 109 ) 110 driver.find_element_by_css_selector('input#masterPassword').send_keys( 111 '${userPassword}' 112 ) 113 driver.find_element_by_css_selector('input#masterPasswordRetype').send_keys( 114 '${userPassword}' 115 ) 116 driver.find_element_by_css_selector('input#acceptPolicies').click() 117 118 driver.find_element_by_xpath("//button[contains(., 'Submit')]").click() 119 120 wait.until_not(EC.title_contains("Create Account")) 121 122 driver.find_element_by_css_selector('input#masterPassword').send_keys( 123 '${userPassword}' 124 ) 125 driver.find_element_by_xpath("//button[contains(., 'Log In')]").click() 126 127 wait.until(EC.title_contains("My Vault")) 128 129 driver.find_element_by_xpath("//button[contains(., 'Add Item')]").click() 130 131 driver.find_element_by_css_selector('input#name').send_keys( 132 'secrets' 133 ) 134 driver.find_element_by_css_selector('input#loginPassword').send_keys( 135 '${storedPassword}' 136 ) 137 138 driver.find_element_by_xpath("//button[contains(., 'Save')]").click() 139 ''; 140 in 141 [ pkgs.firefox-unwrapped pkgs.geckodriver testRunner ]; 142 143 virtualisation.memorySize = 768; 144 } 145 ]; 146 147 client = { pkgs, ... }: 148 { 149 environment.systemPackages = [ pkgs.bitwarden-cli ]; 150 }; 151 }; 152 153 testScript = '' 154 start_all() 155 server.wait_for_unit("bitwarden_rs.service") 156 server.wait_for_open_port(80) 157 158 with subtest("configure the cli"): 159 client.succeed("bw --nointeraction config server http://server") 160 161 with subtest("can't login to nonexistant account"): 162 client.fail( 163 "bw --nointeraction --raw login ${userEmail} ${userPassword}" 164 ) 165 166 with subtest("use the web interface to sign up, log in, and save a password"): 167 server.succeed("PYTHONUNBUFFERED=1 test-runner | systemd-cat -t test-runner") 168 169 with subtest("log in with the cli"): 170 key = client.succeed( 171 "bw --nointeraction --raw login ${userEmail} ${userPassword}" 172 ).strip() 173 174 with subtest("sync with the cli"): 175 client.succeed(f"bw --nointeraction --raw --session {key} sync -f") 176 177 with subtest("get the password with the cli"): 178 password = client.succeed( 179 f"bw --nointeraction --raw --session {key} list items | ${pkgs.jq}/bin/jq -r .[].login.password" 180 ) 181 assert password.strip() == "${storedPassword}" 182 ''; 183 }; 184in 185builtins.listToAttrs ( 186 map 187 (backend: { name = backend; value = makeBitwardenTest backend; }) 188 backends 189)