use std::path::PathBuf;
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum AuthResult {
Accept,
#[default]
Reject,
Partial {
remaining_methods: Vec<String>,
},
}
impl AuthResult {
pub fn is_accepted(&self) -> bool {
matches!(self, AuthResult::Accept)
}
pub fn is_rejected(&self) -> bool {
matches!(self, AuthResult::Reject)
}
pub fn is_partial(&self) -> bool {
matches!(self, AuthResult::Partial { .. })
}
pub fn partial<I, S>(methods: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
AuthResult::Partial {
remaining_methods: methods.into_iter().map(Into::into).collect(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UserInfo {
pub username: String,
pub home_dir: PathBuf,
pub shell: PathBuf,
pub uid: Option<u32>,
pub gid: Option<u32>,
pub groups: Vec<u32>,
pub display_name: Option<String>,
}
impl UserInfo {
pub fn new(username: impl Into<String>) -> Self {
let username = username.into();
#[cfg(target_os = "macos")]
let (home_dir, shell) = (
PathBuf::from(format!("/Users/{username}")),
PathBuf::from("/bin/zsh"),
);
#[cfg(target_os = "linux")]
let (home_dir, shell) = (
PathBuf::from(format!("/home/{username}")),
PathBuf::from("/bin/sh"),
);
#[cfg(target_os = "windows")]
let (home_dir, shell) = (
PathBuf::from(format!("C:\\Users\\{username}")),
PathBuf::from("cmd.exe"),
);
#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
let (home_dir, shell) = (
PathBuf::from(format!("/home/{username}")),
PathBuf::from("/bin/sh"),
);
Self {
username,
home_dir,
shell,
uid: None,
gid: None,
groups: Vec::new(),
display_name: None,
}
}
pub fn with_home_dir(mut self, path: impl Into<PathBuf>) -> Self {
self.home_dir = path.into();
self
}
pub fn with_shell(mut self, path: impl Into<PathBuf>) -> Self {
self.shell = path.into();
self
}
pub fn with_uid(mut self, uid: u32) -> Self {
self.uid = Some(uid);
self
}
pub fn with_gid(mut self, gid: u32) -> Self {
self.gid = Some(gid);
self
}
pub fn with_groups(mut self, groups: impl Into<Vec<u32>>) -> Self {
self.groups = groups.into();
self
}
pub fn with_display_name(mut self, name: impl Into<String>) -> Self {
self.display_name = Some(name.into());
self
}
}
pub mod auth_method_names {
pub const PASSWORD: &str = "password";
pub const PUBLICKEY: &str = "publickey";
pub const KEYBOARD_INTERACTIVE: &str = "keyboard-interactive";
pub const HOSTBASED: &str = "hostbased";
pub const NONE: &str = "none";
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum ServerCheckMethod {
NoCheck,
PublicKey(String),
PublicKeyFile(String),
#[default]
DefaultKnownHostsFile,
KnownHostsFile(String),
}
impl ServerCheckMethod {
pub fn with_public_key(key: impl Into<String>) -> Self {
Self::PublicKey(key.into())
}
pub fn with_public_key_file(path: impl Into<String>) -> Self {
Self::PublicKeyFile(path.into())
}
pub fn with_known_hosts_file(path: impl Into<String>) -> Self {
Self::KnownHostsFile(path.into())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_auth_result_states() {
let accept = AuthResult::Accept;
assert!(accept.is_accepted());
assert!(!accept.is_rejected());
assert!(!accept.is_partial());
let reject = AuthResult::Reject;
assert!(!reject.is_accepted());
assert!(reject.is_rejected());
assert!(!reject.is_partial());
let partial = AuthResult::partial(["password", "publickey"]);
assert!(!partial.is_accepted());
assert!(!partial.is_rejected());
assert!(partial.is_partial());
}
#[test]
fn test_user_info_builder() {
let user = UserInfo::new("testuser")
.with_home_dir("/custom/home")
.with_shell("/bin/zsh")
.with_uid(1001)
.with_gid(1001)
.with_groups(vec![100, 101])
.with_display_name("Test User");
assert_eq!(user.username, "testuser");
assert_eq!(user.home_dir, PathBuf::from("/custom/home"));
assert_eq!(user.shell, PathBuf::from("/bin/zsh"));
assert_eq!(user.uid, Some(1001));
assert_eq!(user.gid, Some(1001));
assert_eq!(user.groups, vec![100, 101]);
assert_eq!(user.display_name, Some("Test User".to_string()));
}
#[test]
fn test_server_check_method() {
let default = ServerCheckMethod::default();
assert_eq!(default, ServerCheckMethod::DefaultKnownHostsFile);
let key = ServerCheckMethod::with_public_key("ssh-rsa AAAA...");
assert!(matches!(key, ServerCheckMethod::PublicKey(_)));
let file = ServerCheckMethod::with_known_hosts_file("/path/to/known_hosts");
assert!(matches!(file, ServerCheckMethod::KnownHostsFile(_)));
}
}