diff testharness/src/lib.rs @ 163:a75a66cb4181

Add end-to-end tests; fix issues found by tests. - Create tests and installer/remover shell script - Fix Pointer/pointee problems - Add Debug formatting - Misc cleanup
author Paul Fisher <paul@pfish.zone>
date Mon, 14 Jul 2025 17:40:11 -0400
parents 634cd5f2ac8b
children 2f5913131295
line wrap: on
line diff
--- a/testharness/src/lib.rs	Mon Jul 14 15:07:16 2025 -0400
+++ b/testharness/src/lib.rs	Mon Jul 14 17:40:11 2025 -0400
@@ -1,26 +1,103 @@
 //! The nonstick library
+
+use crate::nonstick::items::ItemsMut;
+use std::cell::Cell;
 extern crate nonstick;
 
-use nonstick::{pam_hooks, Flags, ModuleClient, PamModule};
-use std::ffi::CStr;
+use nonstick::conv::{ErrorMsg, InfoMsg, MaskedQAndA, QAndA};
+use nonstick::{error, info, pam_hooks, ErrorCode, Flags, ModuleClient, PamModule};
+use std::ffi::{CStr, OsString};
+use std::os::unix::ffi::OsStrExt;
 
 struct TestHarness;
 
 impl<M: ModuleClient> PamModule<M> for TestHarness {
-    fn authenticate(_handle: &mut M, _args: Vec<&CStr>, _flags: Flags) -> nonstick::Result<()> {
-        Ok(())
+    fn authenticate(handle: &mut M, args: Vec<&CStr>, _: Flags) -> nonstick::Result<()> {
+        let strings: Vec<_> = args.iter().map(|&a| Vec::from(a.to_bytes())).collect();
+        if strings != vec![Vec::from(b"param"), Vec::from(b"param2")] {
+            return Err(ErrorCode::SystemError);
+        }
+        let username = handle.username(None)?;
+        if username != "initial" {
+            return Err(ErrorCode::UserUnknown);
+        }
+        handle
+            .items_mut()
+            .set_user(Some("updated-in-process".as_ref()))?;
+        handle.set_module_data("florgus", Cell::new(99))?;
+        let authtok = handle.authtok(Some("custom".as_ref()))?;
+        if authtok.as_bytes() != b"valid" {
+            return Err(ErrorCode::AuthenticationError);
+        }
+        let info = InfoMsg::new("Watch out!".as_ref());
+        let err = ErrorMsg::new("It's broken!".as_ref());
+        let public = QAndA::new("How many?".as_ref());
+        let private = MaskedQAndA::new("Where?".as_ref());
+        let msgs = &[
+            info.exchange(),
+            err.exchange(),
+            public.exchange(),
+            private.exchange(),
+        ];
+        handle.communicate(msgs);
+        let public = public.answer()?;
+        info!(handle, "public question: {:?}", public);
+        let private = private.answer()?;
+        info!(handle, "private question: {:?}", private);
+        if public.as_bytes() == b"123" && private.as_bytes() == b"abc" {
+            Ok(())
+        } else {
+            Err(ErrorCode::Abort)
+        }
     }
 
-    fn account_management(
-        _handle: &mut M,
-        _args: Vec<&CStr>,
-        _flags: Flags,
-    ) -> nonstick::Result<()> {
-        Ok(())
+    fn account_management(handle: &mut M, _: Vec<&CStr>, _: Flags) -> nonstick::Result<()> {
+        let value: &Cell<i32> = match handle.username(None)?.as_bytes() {
+            b"initial" => return Err(ErrorCode::AccountExpired),
+            b"updated-in-process" => handle.get_module_data("florgus"),
+            _ => return Err(ErrorCode::UserUnknown),
+        }
+        .ok_or(ErrorCode::SessionError)?;
+        let florgus_str: Option<&i32> = handle.get_module_data("florgus");
+        if let Some(s) = florgus_str {
+            error!(
+                handle,
+                "module_data type mismatch: florgus = <{s}> but should not be set"
+            )
+        }
+        if value.get() != 99 {
+            error!(handle, "wrong value! {}", value.get());
+            return Err(ErrorCode::AuthTokError);
+        }
+        let password = handle.authtok(None)?;
+        if password.as_bytes() == b"valid" {
+            Err(ErrorCode::NewAuthTokRequired)
+        } else {
+            Ok(())
+        }
     }
 
-    fn change_authtok(_handle: &mut M, _args: Vec<&CStr>, _flags: Flags) -> nonstick::Result<()> {
-        todo!()
+    fn change_authtok(handle: &mut M, _: Vec<&CStr>, flags: Flags) -> nonstick::Result<()> {
+        if flags.contains(Flags::PRELIMINARY_CHECK) {
+            let password = handle.authtok(None)?;
+            if password.as_bytes() != b"acceptable" {
+                return Err(ErrorCode::PermissionDenied);
+            }
+            handle.set_module_data("checked_pass", password)
+        } else if flags.contains(Flags::UPDATE_AUTHTOK) {
+            let password = handle.authtok(None)?;
+            let checked: &OsString = handle
+                .get_module_data("checked_pass")
+                .ok_or(ErrorCode::SystemError)?;
+            if password != *checked {
+                error!(handle, "password mismatch? {password:?} {checked:?}");
+                return Err(ErrorCode::AuthenticationError);
+            }
+            Ok(())
+        } else {
+            error!(handle, "invalid flag state: {flags:?}");
+            Err(ErrorCode::SystemError)
+        }
     }
 }