Mercurial > crates > nonstick
view testharness/src/bin/testharness.rs @ 171:e27c5c667a5a
Create full new types for return code and flags, separate end to end.
This plumbs the ReturnCode and RawFlags types through the places where
we call into or are called from PAM.
Also adds Sun documentation to the project.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Fri, 25 Jul 2025 20:52:14 -0400 |
parents | 0cabe7b94a4f |
children | 6727cbe56f4a |
line wrap: on
line source
//! The actual program which runs the tests. use nonstick::conv::Exchange; use nonstick::items::Items; use nonstick::libpam::TransactionBuilder; use nonstick::{ AuthnFlags, AuthtokFlags, Conversation, ErrorCode, LibPamTransaction, PamShared, Transaction, }; use std::cell::Cell; use std::ffi::OsString; use std::os::unix::ffi::OsStrExt; fn main() { test_wrong_user(); test_wrong_password(); test_correct(); } #[derive(Debug, Default)] struct TestHarness { username_requested: Cell<bool>, wrong_username: bool, wrong_password: bool, changing_password: Cell<bool>, change_prompt_count: Cell<usize>, } impl Conversation for &TestHarness { fn communicate(&self, messages: &[Exchange]) { if let [only_msg] = messages { match only_msg { Exchange::Prompt(p) => { if self.username_requested.get() { panic!("username already requested!") } if self.wrong_username { p.set_answer(Ok(OsString::from("not-right"))) } else { p.set_answer(Ok(OsString::from("initial"))) } self.username_requested.set(true) } Exchange::MaskedPrompt(p) => { let answer = if self.changing_password.get() { let prompt_count = self.change_prompt_count.get(); eprintln!("CHANGING PASSWORD PROMPT {prompt_count}"); eprintln!("-> {p:?}"); self.change_prompt_count.set(prompt_count + 1); // When changing passwords after logging in, Sun PAM // uses the existing authtok that was just entered as // the old_authtok. Other PAMs prompt the user to enter // their existing password again. let responses: &[&str] = if cfg!(pam_impl = "Sun") { &["mistake", "mismatch", "acceptable", "acceptable"] } else { &[ "old token!", "mistake", "mismatch", "old token!", "acceptable", "acceptable", ] }; responses[prompt_count] } else if self.wrong_password { "bogus" } else { "valid" }; p.set_answer(Ok(OsString::from(answer))); } Exchange::Error(e) if self.changing_password.get() => e.set_answer(Ok(())), other => panic!("Unknown message {other:?}!"), } } else { for msg in messages { match msg { Exchange::Info(i) => i.set_answer(Ok(())), Exchange::Error(e) => e.set_answer(Ok(())), Exchange::Prompt(p) => match p.question().as_bytes() { b"How many?" => p.set_answer(Ok(OsString::from("123"))), _ => p.set_answer(Err(ErrorCode::ConversationError)), }, Exchange::MaskedPrompt(p) => match p.question().as_bytes() { b"Where?" => p.set_answer(Ok(OsString::from("abc"))), _ => p.set_answer(Err(ErrorCode::ConversationError)), }, other => other.set_error(ErrorCode::Abort), } } } } } impl TestHarness { fn start(&self) -> LibPamTransaction<&Self> { TransactionBuilder::new_with_service("nonstick-testharness") .build(self) .expect("expected build success") } } fn test_wrong_user() { let harness = TestHarness { wrong_username: true, ..Default::default() }; let mut tx = harness.start(); let auth = tx.authenticate(AuthnFlags::empty()); assert_eq!(auth, Err(ErrorCode::UserUnknown)); } fn test_wrong_password() { let harness = TestHarness { wrong_password: true, ..Default::default() }; let mut tx = harness.start(); let auth = tx.authenticate(AuthnFlags::empty()); assert_eq!(auth, Err(ErrorCode::AuthenticationError)); } fn test_correct() { let harness = TestHarness::default(); let mut tx = harness.start(); tx.authenticate(AuthnFlags::empty()).unwrap(); assert_eq!(tx.items().user().unwrap().unwrap(), "updated-in-process"); let result = tx.account_management(AuthnFlags::empty()); assert_eq!(result, Err(ErrorCode::NewAuthTokRequired)); harness.changing_password.set(true); let change = tx.change_authtok(AuthtokFlags::CHANGE_EXPIRED_AUTHTOK); assert_eq!(change, Err(ErrorCode::TryAgain)); tx.change_authtok(AuthtokFlags::CHANGE_EXPIRED_AUTHTOK) .unwrap(); }