diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2023-09-01 00:00:00 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2023-09-01 00:00:00 +0000 |
| commit | 96660a90e4cd296a2922d7a547de4cd9d0b1928b (patch) | |
| tree | e5216e22ee1a3e1653d8d1ecd856f4f03615d6a1 /tests | |
| parent | 6423baebd92e45c9be85157c443dff42e65d8d14 (diff) | |
feat(login) password login using encrypted nsec
Enables the user to only handle the nsec upon first use of the tool
by encrypting it with a password and storing it on disk in an
application cache.
The approach to encryption draws heavily from that used by the gossip
nostr client.
- unencrypted nsec is zeroed from memory
- a salt is used to defend against rainbow tables
- computationally expensive key stretching defends against
brute-force attacks of passwords with low entropy.
There is UX trade-off between decryption speed and key-stretching
computation. This UX challenge is exacerbated in a cli tool as
decryption must take place more regularly. Thought was put into the
selected n_log and a heavily reduced value is provided for long
passwords where security benefits are smaller.
A more granular reducing in computation was also considered by
rejected to avoided to revealing just how weak a password is as most
weak passwords are reused.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/login.rs | 334 |
1 files changed, 292 insertions, 42 deletions
diff --git a/tests/login.rs b/tests/login.rs index a7e1889..a75608d 100644 --- a/tests/login.rs +++ b/tests/login.rs | |||
| @@ -3,6 +3,9 @@ use serial_test::serial; | |||
| 3 | use test_utils::*; | 3 | use test_utils::*; |
| 4 | 4 | ||
| 5 | static EXPECTED_NSEC_PROMPT: &str = "login with nsec (or hex private key)"; | 5 | static EXPECTED_NSEC_PROMPT: &str = "login with nsec (or hex private key)"; |
| 6 | static EXPECTED_SET_PASSWORD_PROMPT: &str = "encrypt with password"; | ||
| 7 | static EXPECTED_SET_PASSWORD_CONFIRM_PROMPT: &str = "confirm password"; | ||
| 8 | static EXPECTED_PASSWORD_PROMPT: &str = "password"; | ||
| 6 | 9 | ||
| 7 | fn standard_login() -> Result<CliTester> { | 10 | fn standard_login() -> Result<CliTester> { |
| 8 | let mut p = CliTester::new(["login"]); | 11 | let mut p = CliTester::new(["login"]); |
| @@ -10,6 +13,10 @@ fn standard_login() -> Result<CliTester> { | |||
| 10 | p.expect_input_eventually(EXPECTED_NSEC_PROMPT)? | 13 | p.expect_input_eventually(EXPECTED_NSEC_PROMPT)? |
| 11 | .succeeds_with(TEST_KEY_1_NSEC)?; | 14 | .succeeds_with(TEST_KEY_1_NSEC)?; |
| 12 | 15 | ||
| 16 | p.expect_password(EXPECTED_SET_PASSWORD_PROMPT)? | ||
| 17 | .with_confirmation(EXPECTED_SET_PASSWORD_CONFIRM_PROMPT)? | ||
| 18 | .succeeds_with(TEST_PASSWORD)?; | ||
| 19 | |||
| 13 | p.expect_end_eventually()?; | 20 | p.expect_end_eventually()?; |
| 14 | Ok(p) | 21 | Ok(p) |
| 15 | } | 22 | } |
| @@ -19,11 +26,10 @@ mod when_first_time_login { | |||
| 19 | 26 | ||
| 20 | #[test] | 27 | #[test] |
| 21 | #[serial] | 28 | #[serial] |
| 22 | fn prompts_for_nsec() -> Result<()> { | 29 | fn prompts_for_nsec_and_password() -> Result<()> { |
| 23 | with_fresh_config(|| { | 30 | before()?; |
| 24 | standard_login()?; | 31 | standard_login()?; |
| 25 | Ok(()) | 32 | after() |
| 26 | }) | ||
| 27 | } | 33 | } |
| 28 | 34 | ||
| 29 | #[test] | 35 | #[test] |
| @@ -35,36 +41,137 @@ mod when_first_time_login { | |||
| 35 | p.expect_input(EXPECTED_NSEC_PROMPT)? | 41 | p.expect_input(EXPECTED_NSEC_PROMPT)? |
| 36 | .succeeds_with(TEST_KEY_1_NSEC)?; | 42 | .succeeds_with(TEST_KEY_1_NSEC)?; |
| 37 | 43 | ||
| 38 | p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NSEC).as_str()) | 44 | p.expect_password(EXPECTED_SET_PASSWORD_PROMPT)? |
| 45 | .with_confirmation(EXPECTED_SET_PASSWORD_CONFIRM_PROMPT)? | ||
| 46 | .succeeds_with(TEST_PASSWORD)?; | ||
| 47 | |||
| 48 | p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str()) | ||
| 39 | }) | 49 | }) |
| 40 | } | 50 | } |
| 41 | 51 | ||
| 42 | #[test] | 52 | #[test] |
| 43 | #[serial] | 53 | #[serial] |
| 44 | fn next_time_returns_logged_in_as_npub() -> Result<()> { | 54 | fn succeeds_with_hex_secret_key_in_place_of_nsec() -> Result<()> { |
| 55 | with_fresh_config(|| { | ||
| 56 | let mut p = CliTester::new(["login"]); | ||
| 57 | |||
| 58 | p.expect_input(EXPECTED_NSEC_PROMPT)? | ||
| 59 | .succeeds_with(TEST_KEY_1_SK_HEX)?; | ||
| 60 | |||
| 61 | p.expect_password(EXPECTED_SET_PASSWORD_PROMPT)? | ||
| 62 | .with_confirmation(EXPECTED_SET_PASSWORD_CONFIRM_PROMPT)? | ||
| 63 | .succeeds_with(TEST_PASSWORD)?; | ||
| 64 | |||
| 65 | p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str()) | ||
| 66 | }) | ||
| 67 | } | ||
| 68 | |||
| 69 | mod when_invalid_nsec { | ||
| 70 | use super::*; | ||
| 71 | |||
| 72 | #[test] | ||
| 73 | #[serial] | ||
| 74 | fn prompts_for_nsec_until_valid() -> Result<()> { | ||
| 75 | with_fresh_config(|| { | ||
| 76 | let invalid_nsec_response = | ||
| 77 | "invalid nsec. try again with nsec (or hex private key)"; | ||
| 78 | |||
| 79 | let mut p = CliTester::new(["login"]); | ||
| 80 | |||
| 81 | p.expect_input(EXPECTED_NSEC_PROMPT)? | ||
| 82 | // this behaviour is intentional. rejecting the response with dialoguer hides | ||
| 83 | // the original input from the user so they cannot see the | ||
| 84 | // mistake they made. | ||
| 85 | .succeeds_with(TEST_INVALID_NSEC)?; | ||
| 86 | |||
| 87 | p.expect_input(invalid_nsec_response)? | ||
| 88 | .succeeds_with(TEST_INVALID_NSEC)?; | ||
| 89 | |||
| 90 | p.expect_input(invalid_nsec_response)? | ||
| 91 | .succeeds_with(TEST_KEY_1_NSEC)?; | ||
| 92 | |||
| 93 | p.expect_password(EXPECTED_SET_PASSWORD_PROMPT)? | ||
| 94 | .with_confirmation(EXPECTED_SET_PASSWORD_CONFIRM_PROMPT)? | ||
| 95 | .succeeds_with(TEST_PASSWORD)?; | ||
| 96 | |||
| 97 | p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str()) | ||
| 98 | }) | ||
| 99 | } | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | mod when_second_time_login { | ||
| 104 | use super::*; | ||
| 105 | |||
| 106 | #[test] | ||
| 107 | #[serial] | ||
| 108 | fn prints_login_as_npub() -> Result<()> { | ||
| 45 | with_fresh_config(|| { | 109 | with_fresh_config(|| { |
| 46 | standard_login()?.exit()?; | 110 | standard_login()?.exit()?; |
| 47 | 111 | ||
| 48 | CliTester::new(["login"]) | 112 | CliTester::new(["login"]) |
| 49 | .expect(format!("logged in as {}\r\n", TEST_KEY_1_NSEC).as_str())? | 113 | .expect(format!("login as {}\r\n", TEST_KEY_1_NPUB).as_str())? |
| 50 | .exit() | 114 | .exit() |
| 51 | }) | 115 | }) |
| 52 | } | 116 | } |
| 117 | |||
| 118 | #[test] | ||
| 119 | #[serial] | ||
| 120 | fn prompts_for_password_and_succeeds_with_logged_in_as_npub() -> Result<()> { | ||
| 121 | with_fresh_config(|| { | ||
| 122 | standard_login()?.exit()?; | ||
| 123 | |||
| 124 | let mut p = CliTester::new(["login"]); | ||
| 125 | |||
| 126 | p.expect(format!("login as {}\r\n", TEST_KEY_1_NPUB).as_str())? | ||
| 127 | .expect_password(EXPECTED_PASSWORD_PROMPT)? | ||
| 128 | .succeeds_with(TEST_PASSWORD)?; | ||
| 129 | |||
| 130 | p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str()) | ||
| 131 | }) | ||
| 132 | } | ||
| 133 | |||
| 134 | #[test] | ||
| 135 | #[serial] | ||
| 136 | fn when_invalid_password_exit_with_error() -> Result<()> { | ||
| 137 | with_fresh_config(|| { | ||
| 138 | standard_login()?.exit()?; | ||
| 139 | |||
| 140 | let mut p = CliTester::new(["login"]); | ||
| 141 | |||
| 142 | p.expect(format!("login as {}\r\n", TEST_KEY_1_NPUB).as_str())? | ||
| 143 | .expect_password(EXPECTED_PASSWORD_PROMPT)? | ||
| 144 | .succeeds_with(TEST_INVALID_PASSWORD)?; | ||
| 145 | p.expect_end_with(format!("Error: failed to log in as {}\r\n\r\nCaused by:\r\n 0: failed to decrypt key with provided password\r\n 1: failed to decrypt\r\n", TEST_KEY_1_NPUB).as_str()) | ||
| 146 | }) | ||
| 147 | } | ||
| 53 | } | 148 | } |
| 54 | 149 | ||
| 55 | mod when_called_with_nsec_parameter { | 150 | mod when_called_with_nsec_parameter_only { |
| 56 | use super::*; | 151 | use super::*; |
| 57 | 152 | ||
| 58 | #[test] | 153 | #[test] |
| 59 | #[serial] | 154 | #[serial] |
| 60 | fn valid_nsec_param_succeeds_without_prompts() -> Result<()> { | 155 | fn valid_nsec_param_succeeds_without_prompts() -> Result<()> { |
| 61 | with_fresh_config(|| { | 156 | with_fresh_config(|| { |
| 62 | CliTester::new(["--nsec", TEST_KEY_2_NSEC, "login"]) | 157 | CliTester::new(["login", "--nsec", TEST_KEY_1_NSEC]) |
| 63 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_2_NSEC).as_str())?; | 158 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str()) |
| 159 | }) | ||
| 160 | } | ||
| 161 | |||
| 162 | #[test] | ||
| 163 | #[serial] | ||
| 164 | fn forgets_identity() -> Result<()> { | ||
| 165 | with_fresh_config(|| { | ||
| 166 | CliTester::new(["login", "--nsec", TEST_KEY_1_NSEC]) | ||
| 167 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())?; | ||
| 64 | 168 | ||
| 65 | CliTester::new(["login"]) | 169 | let mut p = CliTester::new(["login"]); |
| 66 | .expect(format!("logged in as {}\r\n", TEST_KEY_2_NSEC).as_str())? | 170 | |
| 67 | .exit() | 171 | p.expect_input(EXPECTED_NSEC_PROMPT)? |
| 172 | .succeeds_with(TEST_KEY_1_NSEC)?; | ||
| 173 | |||
| 174 | p.exit() | ||
| 68 | }) | 175 | }) |
| 69 | } | 176 | } |
| 70 | 177 | ||
| @@ -77,69 +184,212 @@ mod when_called_with_nsec_parameter { | |||
| 77 | with_fresh_config(|| { | 184 | with_fresh_config(|| { |
| 78 | standard_login()?.exit()?; | 185 | standard_login()?.exit()?; |
| 79 | 186 | ||
| 80 | CliTester::new(["--nsec", TEST_KEY_2_NSEC, "login"]) | 187 | CliTester::new(["login", "--nsec", TEST_KEY_2_NSEC]) |
| 81 | .expect(format!("logged in as {}\r\n", TEST_KEY_1_NSEC).as_str())? | 188 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_2_NPUB).as_str()) |
| 82 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_2_NSEC).as_str())?; | ||
| 83 | |||
| 84 | CliTester::new(["login"]) | ||
| 85 | .expect(format!("logged in as {}\r\n", TEST_KEY_2_NSEC).as_str())? | ||
| 86 | .exit() | ||
| 87 | }) | 189 | }) |
| 88 | } | 190 | } |
| 89 | } | 191 | } |
| 192 | #[test] | ||
| 193 | #[serial] | ||
| 194 | fn invalid_nsec_param_fails_without_prompts() -> Result<()> { | ||
| 195 | with_fresh_config(|| { | ||
| 196 | CliTester::new(["login", "--nsec", TEST_INVALID_NSEC]).expect_end_with( | ||
| 197 | "Error: invalid nsec parameter\r\n\r\nCaused by:\r\n Invalid secret key\r\n", | ||
| 198 | ) | ||
| 199 | }) | ||
| 200 | } | ||
| 90 | } | 201 | } |
| 91 | 202 | ||
| 92 | mod when_logged_in { | 203 | mod when_called_with_nsec_and_password_parameter { |
| 93 | use super::*; | 204 | use super::*; |
| 94 | 205 | ||
| 95 | #[test] | 206 | #[test] |
| 96 | #[serial] | 207 | #[serial] |
| 97 | fn returns_logged_in_as_npub() -> Result<()> { | 208 | fn valid_nsec_param_succeeds_without_prompts() -> Result<()> { |
| 98 | with_fresh_config(|| { | 209 | with_fresh_config(|| { |
| 99 | standard_login()?.exit()?; | 210 | CliTester::new([ |
| 211 | "login", | ||
| 212 | "--nsec", | ||
| 213 | TEST_KEY_1_NSEC, | ||
| 214 | "--password", | ||
| 215 | TEST_PASSWORD, | ||
| 216 | ]) | ||
| 217 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str()) | ||
| 218 | }) | ||
| 219 | } | ||
| 220 | |||
| 221 | #[test] | ||
| 222 | #[serial] | ||
| 223 | fn remembers_identity() -> Result<()> { | ||
| 224 | with_fresh_config(|| { | ||
| 225 | CliTester::new([ | ||
| 226 | "login", | ||
| 227 | "--nsec", | ||
| 228 | TEST_KEY_1_NSEC, | ||
| 229 | "--password", | ||
| 230 | TEST_PASSWORD, | ||
| 231 | ]) | ||
| 232 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())?; | ||
| 100 | 233 | ||
| 101 | CliTester::new(["login"]) | 234 | CliTester::new(["login"]) |
| 102 | .expect(format!("logged in as {}\r\n", TEST_KEY_1_NSEC).as_str())? | 235 | .expect(format!("login as {}\r\n", TEST_KEY_1_NPUB).as_str())? |
| 103 | .exit() | 236 | .exit() |
| 104 | }) | 237 | }) |
| 105 | } | 238 | } |
| 106 | 239 | ||
| 107 | #[test] | 240 | #[test] |
| 108 | #[serial] | 241 | #[serial] |
| 109 | fn prompts_to_log_in_with_different_nsec() -> Result<()> { | 242 | fn parameters_can_be_called_globally() -> Result<()> { |
| 110 | with_fresh_config(|| { | 243 | with_fresh_config(|| { |
| 111 | standard_login()?.exit()?; | 244 | CliTester::new([ |
| 112 | 245 | "--nsec", | |
| 113 | let mut p = CliTester::new(["login"]); | 246 | TEST_KEY_1_NSEC, |
| 114 | p.expect(format!("logged in as {}\r\n", TEST_KEY_1_NSEC).as_str())?; | 247 | "--password", |
| 115 | 248 | TEST_PASSWORD, | |
| 116 | p.expect_input(EXPECTED_NSEC_PROMPT)? | 249 | "login", |
| 117 | .succeeds_with(TEST_KEY_2_NSEC)?; | 250 | ]) |
| 118 | 251 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str()) | |
| 119 | p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_2_NSEC).as_str()) | ||
| 120 | }) | 252 | }) |
| 121 | } | 253 | } |
| 254 | |||
| 122 | mod when_logging_in_as_different_nsec { | 255 | mod when_logging_in_as_different_nsec { |
| 123 | use super::*; | 256 | use super::*; |
| 124 | 257 | ||
| 125 | #[test] | 258 | #[test] |
| 126 | #[serial] | 259 | #[serial] |
| 127 | fn confirmed_as_logged_in_as_additional_user() -> Result<()> { | 260 | fn valid_nsec_param_succeeds_without_prompts_and_logs_in() -> Result<()> { |
| 128 | with_fresh_config(|| { | 261 | with_fresh_config(|| { |
| 129 | standard_login()?.exit()?; | 262 | standard_login()?.exit()?; |
| 130 | 263 | ||
| 131 | let mut p = CliTester::new(["login"]); | 264 | CliTester::new([ |
| 132 | p.expect(format!("logged in as {}\r\n", TEST_KEY_1_NSEC).as_str())?; | 265 | "login", |
| 266 | "--nsec", | ||
| 267 | TEST_KEY_2_NSEC, | ||
| 268 | "--password", | ||
| 269 | TEST_PASSWORD, | ||
| 270 | ]) | ||
| 271 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_2_NPUB).as_str()) | ||
| 272 | }) | ||
| 273 | } | ||
| 133 | 274 | ||
| 134 | p.expect_input(EXPECTED_NSEC_PROMPT)? | 275 | #[test] |
| 135 | .succeeds_with(TEST_KEY_2_NSEC)?; | 276 | #[serial] |
| 277 | fn remembers_identity() -> Result<()> { | ||
| 278 | with_fresh_config(|| { | ||
| 279 | standard_login()?.exit()?; | ||
| 136 | 280 | ||
| 137 | p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_2_NSEC).as_str())?; | 281 | CliTester::new([ |
| 282 | "login", | ||
| 283 | "--nsec", | ||
| 284 | TEST_KEY_2_NSEC, | ||
| 285 | "--password", | ||
| 286 | TEST_PASSWORD, | ||
| 287 | ]) | ||
| 288 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_2_NPUB).as_str())?; | ||
| 138 | 289 | ||
| 139 | CliTester::new(["login"]) | 290 | CliTester::new(["login"]) |
| 140 | .expect(format!("logged in as {}\r\n", TEST_KEY_2_NSEC).as_str())? | 291 | .expect(format!("login as {}\r\n", TEST_KEY_2_NPUB).as_str())? |
| 141 | .exit() | 292 | .exit() |
| 142 | }) | 293 | }) |
| 143 | } | 294 | } |
| 144 | } | 295 | } |
| 296 | |||
| 297 | mod when_provided_with_new_password { | ||
| 298 | use super::*; | ||
| 299 | |||
| 300 | #[test] | ||
| 301 | #[serial] | ||
| 302 | fn password_changes() -> Result<()> { | ||
| 303 | with_fresh_config(|| { | ||
| 304 | standard_login()?.exit()?; | ||
| 305 | |||
| 306 | CliTester::new([ | ||
| 307 | "login", | ||
| 308 | "--nsec", | ||
| 309 | TEST_KEY_1_NSEC, | ||
| 310 | "--password", | ||
| 311 | TEST_INVALID_PASSWORD, | ||
| 312 | ]) | ||
| 313 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())?; | ||
| 314 | |||
| 315 | CliTester::new(["--password", TEST_INVALID_PASSWORD, "login"]) | ||
| 316 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str()) | ||
| 317 | }) | ||
| 318 | } | ||
| 319 | } | ||
| 320 | |||
| 321 | #[test] | ||
| 322 | #[serial] | ||
| 323 | fn invalid_nsec_param_fails_without_prompts() -> Result<()> { | ||
| 324 | with_fresh_config(|| { | ||
| 325 | CliTester::new([ | ||
| 326 | "login", | ||
| 327 | "--nsec", | ||
| 328 | TEST_INVALID_NSEC, | ||
| 329 | "--password", | ||
| 330 | TEST_PASSWORD, | ||
| 331 | ]) | ||
| 332 | .expect_end_with( | ||
| 333 | "Error: invalid nsec parameter\r\n\r\nCaused by:\r\n Invalid secret key\r\n", | ||
| 334 | ) | ||
| 335 | }) | ||
| 336 | } | ||
| 337 | } | ||
| 338 | |||
| 339 | mod when_called_with_password_parameter_only { | ||
| 340 | use super::*; | ||
| 341 | |||
| 342 | #[test] | ||
| 343 | #[serial] | ||
| 344 | fn when_nsec_stored_logs_in_without_prompts() -> Result<()> { | ||
| 345 | with_fresh_config(|| { | ||
| 346 | standard_login()?.exit()?; | ||
| 347 | |||
| 348 | CliTester::new(["login", "--password", TEST_PASSWORD]) | ||
| 349 | .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str()) | ||
| 350 | }) | ||
| 351 | } | ||
| 352 | |||
| 353 | #[test] | ||
| 354 | #[serial] | ||
| 355 | fn when_no_nsec_stored_logs_error() -> Result<()> { | ||
| 356 | with_fresh_config(|| { | ||
| 357 | CliTester::new(["login", "--password", TEST_PASSWORD]) | ||
| 358 | .expect_end_with("Error: no nsec available to decrypt with specified password\r\n") | ||
| 359 | }) | ||
| 360 | } | ||
| 361 | } | ||
| 362 | |||
| 363 | mod when_weak_password { | ||
| 364 | use super::*; | ||
| 365 | |||
| 366 | #[test] | ||
| 367 | #[serial] | ||
| 368 | // combined into a single test as it is computationally expensive to run | ||
| 369 | fn warns_it_might_take_a_few_seconds_then_succeeds_then_second_login_prompts_for_password_then_warns_again_then_succeeds() | ||
| 370 | -> Result<()> { | ||
| 371 | with_fresh_config(|| { | ||
| 372 | let mut p = CliTester::new_with_timeout(10000, ["login"]); | ||
| 373 | p.expect_input(EXPECTED_NSEC_PROMPT)? | ||
| 374 | .succeeds_with(TEST_KEY_1_NSEC)?; | ||
| 375 | |||
| 376 | p.expect_password(EXPECTED_SET_PASSWORD_PROMPT)? | ||
| 377 | .with_confirmation(EXPECTED_SET_PASSWORD_CONFIRM_PROMPT)? | ||
| 378 | .succeeds_with(TEST_WEAK_PASSWORD)?; | ||
| 379 | |||
| 380 | p.expect("this may take a few seconds...\r\n")?; | ||
| 381 | |||
| 382 | p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str())?; | ||
| 383 | |||
| 384 | p = CliTester::new_with_timeout(10000, ["login"]); | ||
| 385 | |||
| 386 | p.expect(format!("login as {}\r\n", TEST_KEY_1_NPUB).as_str())? | ||
| 387 | .expect_password(EXPECTED_PASSWORD_PROMPT)? | ||
| 388 | .succeeds_with(TEST_WEAK_PASSWORD)?; | ||
| 389 | |||
| 390 | p.expect("this may take a few seconds...\r\n")?; | ||
| 391 | |||
| 392 | p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NPUB).as_str()) | ||
| 393 | }) | ||
| 394 | } | ||
| 145 | } | 395 | } |