use secrecy::SecretString;
#[derive(Debug, Clone)]
pub struct WinrmConfig {
pub port: u16,
pub use_tls: bool,
pub accept_invalid_certs: bool,
pub connect_timeout_secs: u64,
pub operation_timeout_secs: u64,
pub auth_method: AuthMethod,
pub max_envelope_size: u32,
pub max_retries: u32,
pub client_cert_pem: Option<String>,
pub client_key_pem: Option<String>,
pub proxy: Option<String>,
pub codepage: u32,
pub working_directory: Option<String>,
pub env_vars: Vec<(String, String)>,
pub encryption: EncryptionMode,
pub user_agent: Option<String>,
pub idle_timeout_secs: Option<u64>,
}
impl Default for WinrmConfig {
fn default() -> Self {
Self {
port: 5985,
use_tls: false,
accept_invalid_certs: false,
connect_timeout_secs: 30,
operation_timeout_secs: 60,
auth_method: AuthMethod::Ntlm,
max_envelope_size: 153_600,
max_retries: 0,
client_cert_pem: None,
client_key_pem: None,
proxy: None,
encryption: EncryptionMode::Auto,
user_agent: None,
codepage: 65001,
working_directory: None,
env_vars: Vec::new(),
idle_timeout_secs: None,
}
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub enum EncryptionMode {
#[default]
Auto,
Always,
Never,
}
#[derive(Debug, Clone)]
pub enum AuthMethod {
Basic,
Ntlm,
Kerberos,
Certificate,
CredSsp,
}
#[derive(Clone)]
pub struct WinrmCredentials {
pub username: String,
pub password: SecretString,
pub domain: String,
}
impl WinrmCredentials {
pub fn new(
username: impl Into<String>,
password: impl Into<String>,
domain: impl Into<String>,
) -> Self {
Self {
username: username.into(),
password: SecretString::from(password.into()),
domain: domain.into(),
}
}
}
impl std::fmt::Debug for WinrmCredentials {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("WinrmCredentials")
.field("username", &self.username)
.field("password", &"[REDACTED]")
.field("domain", &self.domain)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use secrecy::ExposeSecret;
#[test]
fn default_config_uses_http_and_ntlm() {
let config = WinrmConfig::default();
assert_eq!(config.port, 5985);
assert!(!config.use_tls);
assert!(matches!(config.auth_method, AuthMethod::Ntlm));
}
#[test]
fn default_config_max_envelope_size() {
let config = WinrmConfig::default();
assert_eq!(config.max_envelope_size, 153_600);
}
#[test]
fn custom_max_envelope_size() {
let config = WinrmConfig {
max_envelope_size: 512_000,
..Default::default()
};
assert_eq!(config.max_envelope_size, 512_000);
}
#[test]
fn default_config_max_retries_is_zero() {
let config = WinrmConfig::default();
assert_eq!(config.max_retries, 0);
}
#[test]
fn default_config_has_no_proxy() {
let config = WinrmConfig::default();
assert!(config.proxy.is_none());
}
#[test]
fn default_config_has_no_cert_paths() {
let config = WinrmConfig::default();
assert!(config.client_cert_pem.is_none());
assert!(config.client_key_pem.is_none());
}
#[test]
fn credentials_new_constructor() {
let creds = WinrmCredentials::new("admin", "s3cret", "DOMAIN");
assert_eq!(creds.username, "admin");
assert_eq!(creds.password.expose_secret(), "s3cret");
assert_eq!(creds.domain, "DOMAIN");
}
#[test]
fn credentials_debug_redacts_password() {
let creds = WinrmCredentials::new("admin", "super-secret-password", "DOM");
let debug_output = format!("{creds:?}");
assert!(debug_output.contains("admin"));
assert!(debug_output.contains("DOM"));
assert!(debug_output.contains("[REDACTED]"));
assert!(!debug_output.contains("super-secret-password"));
}
#[test]
fn credentials_expose_secret_works() {
let creds = WinrmCredentials::new("user", "my-password", "");
let exposed: &str = creds.password.expose_secret();
assert_eq!(exposed, "my-password");
}
#[test]
fn auth_method_kerberos_variant_exists() {
let method = AuthMethod::Kerberos;
let debug = format!("{method:?}");
assert!(debug.contains("Kerberos"));
}
#[test]
fn auth_method_certificate_variant_exists() {
let method = AuthMethod::Certificate;
let debug = format!("{method:?}");
assert!(debug.contains("Certificate"));
}
}