keychain.rs (2880B)
1 //! Credential storage module 2 //! 3 //! Uses file-based storage in the user's app data directory. 4 //! Note: For production, consider using the system keychain with proper code signing. 5 6 use crate::types::KeychainError; 7 use std::fs; 8 use std::path::PathBuf; 9 10 /// Get the path to the token file 11 fn token_file_path() -> Result<PathBuf, KeychainError> { 12 let home = std::env::var("HOME").map_err(|_| KeychainError::NotFound)?; 13 let app_dir = PathBuf::from(home).join(".config").join("beegram"); 14 Ok(app_dir.join("auth_token")) 15 } 16 17 /// Ensure the app config directory exists 18 fn ensure_config_dir() -> Result<(), KeychainError> { 19 let path = token_file_path()?; 20 if let Some(parent) = path.parent() { 21 fs::create_dir_all(parent).map_err(|e| { 22 tracing::error!("Failed to create config directory: {:?}", e); 23 KeychainError::NotFound 24 })?; 25 } 26 Ok(()) 27 } 28 29 /// Save authentication token to file 30 pub fn save_token(token: &str) -> Result<(), KeychainError> { 31 ensure_config_dir()?; 32 let path = token_file_path()?; 33 tracing::info!("Saving token to {:?}", path); 34 35 fs::write(&path, token).map_err(|e| { 36 tracing::error!("Failed to save token: {:?}", e); 37 KeychainError::NotFound 38 })?; 39 40 // Set file permissions to owner-only (Unix) 41 #[cfg(unix)] 42 { 43 use std::os::unix::fs::PermissionsExt; 44 let perms = fs::Permissions::from_mode(0o600); 45 let _ = fs::set_permissions(&path, perms); 46 } 47 48 tracing::info!("Token saved successfully"); 49 Ok(()) 50 } 51 52 /// Load authentication token from file 53 pub fn load_token() -> Result<String, KeychainError> { 54 let path = token_file_path()?; 55 tracing::info!("Loading token from {:?}", path); 56 57 match fs::read_to_string(&path) { 58 Ok(token) if !token.is_empty() => { 59 tracing::info!("Token loaded (length={})", token.len()); 60 Ok(token) 61 } 62 Ok(_) => { 63 tracing::info!("Empty token file"); 64 Err(KeychainError::NotFound) 65 } 66 Err(e) if e.kind() == std::io::ErrorKind::NotFound => { 67 tracing::info!("No token file found"); 68 Err(KeychainError::NotFound) 69 } 70 Err(e) => { 71 tracing::error!("Failed to read token: {:?}", e); 72 Err(KeychainError::NotFound) 73 } 74 } 75 } 76 77 /// Delete authentication token file 78 pub fn delete_token() -> Result<(), KeychainError> { 79 let path = token_file_path()?; 80 match fs::remove_file(&path) { 81 Ok(()) => { 82 tracing::info!("Token deleted"); 83 Ok(()) 84 } 85 Err(e) if e.kind() == std::io::ErrorKind::NotFound => { 86 tracing::debug!("No token to delete"); 87 Ok(()) 88 } 89 Err(e) => { 90 tracing::warn!("Failed to delete token: {:?}", e); 91 Err(KeychainError::NotFound) 92 } 93 } 94 }