view testharness/tests/end2end.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 13b4d2a19674
children
line wrap: on
line source

#![cfg(feature = "test-install")]

use std::any::Any;
use std::convert::Infallible;
use std::error::Error;
use std::io;
use std::panic::UnwindSafe;
use std::path::{Path, PathBuf};
use std::{fs, panic};

const PAM_CONFIG: &str = "\
auth required pam_testharness.so
account required pam_testharness.so
password required pam_testharness.so
session required pam_testharness.so
";
const PAM_CONFIG_PATH: &str = "/etc/pam.d/testharness";
const PAM_MODULE_PATH: &str = "/lib/security/pam_testharness.so";

#[derive(Debug, thiserror::Error)]
enum TestError {
    #[error("error in test harness: {0}")]
    HarnessError(#[from] io::Error),
    #[error("panic in test: {0:?}")]
    Panic(Box<dyn Any + Send>),
    #[error(transparent)]
    TestError(anyhow::Error),
}

#[test]
fn test_auth_only() -> Result<(), TestError> {
    harness(|| Ok::<(), Infallible>(()))
}

fn harness<E: Error + Send + Sync + 'static>(
    test: impl Fn() -> Result<(), E> + UnwindSafe,
) -> Result<(), TestError> {
    let dylib_path = test_cdylib::build_current_project();
    let module_path = Path::new(PAM_MODULE_PATH);
    let config_path = Path::new(PAM_CONFIG_PATH);
    let parent = module_path.parent().unwrap();
    fs::create_dir_all(parent)?;
    fs::copy(dylib_path, module_path)?;
    fs::write(config_path, PAM_CONFIG)?;
    panic::catch_unwind(test)
        .map_err(TestError::Panic)?
        .map_err(|e| TestError::TestError(e.into()))?;
    fs::remove_file(module_path)?;
    fs::remove_file(config_path)?;
    // If the /lib/security directory can't be removed, that's OK.
    let _ = fs::remove_dir(parent);
    Ok(())
}