Simplified keychain management on non-Windows platforms
This commit is contained in:
@@ -187,31 +187,41 @@ pub fn save_auth_pass(auth_pass: &AuthPass) -> Result<(), OAuthError> {
|
|||||||
let encoded = URL_SAFE_NO_PAD.encode(&compressed);
|
let encoded = URL_SAFE_NO_PAD.encode(&compressed);
|
||||||
info!("Encoded length: {}", encoded.len());
|
info!("Encoded length: {}", encoded.len());
|
||||||
|
|
||||||
// Windows keyring has a 2560-byte UTF-16 limit, which means 1280 chars max
|
#[cfg(target_os = "windows")]
|
||||||
// Split into chunks of 1200 chars to be safe
|
{
|
||||||
const CHUNK_SIZE: usize = 1200;
|
// Windows keyring has a 2560-byte UTF-16 limit, which means 1280 chars max
|
||||||
let chunks: Vec<&str> = encoded
|
// Split into chunks of 1200 chars to be safe
|
||||||
.as_bytes()
|
const CHUNK_SIZE: usize = 1200;
|
||||||
.chunks(CHUNK_SIZE)
|
let chunks: Vec<&str> = encoded
|
||||||
.map(|chunk| std::str::from_utf8(chunk).unwrap())
|
.as_bytes()
|
||||||
.collect();
|
.chunks(CHUNK_SIZE)
|
||||||
|
.map(|chunk| std::str::from_utf8(chunk).unwrap())
|
||||||
|
.collect();
|
||||||
|
|
||||||
info!("Splitting auth pass into {} chunks", chunks.len());
|
info!("Splitting auth pass into {} chunks", chunks.len());
|
||||||
|
|
||||||
// Save chunk count
|
// Save chunk count
|
||||||
let count_entry = Entry::new(SERVICE_NAME, "auth_pass_count")?;
|
let count_entry = Entry::new(SERVICE_NAME, "auth_pass_count")?;
|
||||||
count_entry.set_password(&chunks.len().to_string())?;
|
count_entry.set_password(&chunks.len().to_string())?;
|
||||||
|
|
||||||
// Save each chunk
|
// Save each chunk
|
||||||
for (i, chunk) in chunks.iter().enumerate() {
|
for (i, chunk) in chunks.iter().enumerate() {
|
||||||
let entry = Entry::new(SERVICE_NAME, &format!("auth_pass_{}", i))?;
|
let entry = Entry::new(SERVICE_NAME, &format!("auth_pass_{}", i))?;
|
||||||
entry.set_password(chunk)?;
|
entry.set_password(chunk)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"Auth pass saved to keyring successfully in {} chunks",
|
||||||
|
chunks.len()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
info!(
|
#[cfg(not(target_os = "windows"))]
|
||||||
"Auth pass saved to keyring successfully in {} chunks",
|
{
|
||||||
chunks.len()
|
let entry = Entry::new(SERVICE_NAME, "auth_pass")?;
|
||||||
);
|
entry.set_password(&encoded)?;
|
||||||
|
info!("Auth pass saved to keyring successfully");
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,40 +229,60 @@ pub fn save_auth_pass(auth_pass: &AuthPass) -> Result<(), OAuthError> {
|
|||||||
pub fn load_auth_pass() -> Result<Option<AuthPass>, OAuthError> {
|
pub fn load_auth_pass() -> Result<Option<AuthPass>, OAuthError> {
|
||||||
info!("Reading credentials from keyring");
|
info!("Reading credentials from keyring");
|
||||||
|
|
||||||
// Get chunk count
|
#[cfg(target_os = "windows")]
|
||||||
let count_entry = Entry::new(SERVICE_NAME, "auth_pass_count")?;
|
let encoded = {
|
||||||
let chunk_count = match count_entry.get_password() {
|
// Get chunk count
|
||||||
Ok(count_str) => match count_str.parse::<usize>() {
|
let count_entry = Entry::new(SERVICE_NAME, "auth_pass_count")?;
|
||||||
Ok(count) => count,
|
let chunk_count = match count_entry.get_password() {
|
||||||
Err(_) => {
|
Ok(count_str) => match count_str.parse::<usize>() {
|
||||||
error!("Invalid chunk count in keyring");
|
Ok(count) => count,
|
||||||
|
Err(_) => {
|
||||||
|
error!("Invalid chunk count in keyring");
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(keyring::Error::NoEntry) => {
|
||||||
|
info!("No auth pass found in keyring");
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
},
|
Err(e) => {
|
||||||
Err(keyring::Error::NoEntry) => {
|
error!("Failed to load chunk count from keyring");
|
||||||
info!("No auth pass found in keyring");
|
return Err(OAuthError::KeyringError(e));
|
||||||
return Ok(None);
|
}
|
||||||
}
|
};
|
||||||
Err(e) => {
|
|
||||||
error!("Failed to load chunk count from keyring");
|
info!("Loading {} auth pass chunks from keyring", chunk_count);
|
||||||
return Err(OAuthError::KeyringError(e));
|
|
||||||
|
// Reassemble chunks
|
||||||
|
let mut encoded = String::new();
|
||||||
|
for i in 0..chunk_count {
|
||||||
|
let entry = Entry::new(SERVICE_NAME, &format!("auth_pass_{}", i))?;
|
||||||
|
match entry.get_password() {
|
||||||
|
Ok(chunk) => encoded.push_str(&chunk),
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to load chunk {} from keyring", i);
|
||||||
|
return Err(OAuthError::KeyringError(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
encoded
|
||||||
};
|
};
|
||||||
|
|
||||||
info!("Loading {} auth pass chunks from keyring", chunk_count);
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
let encoded = {
|
||||||
// Reassemble chunks
|
let entry = Entry::new(SERVICE_NAME, "auth_pass")?;
|
||||||
let mut encoded = String::new();
|
|
||||||
for i in 0..chunk_count {
|
|
||||||
let entry = Entry::new(SERVICE_NAME, &format!("auth_pass_{}", i))?;
|
|
||||||
match entry.get_password() {
|
match entry.get_password() {
|
||||||
Ok(chunk) => encoded.push_str(&chunk),
|
Ok(pass) => pass,
|
||||||
|
Err(keyring::Error::NoEntry) => {
|
||||||
|
info!("No auth pass found in keyring");
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Failed to load chunk {} from keyring", i);
|
error!("Failed to load auth pass from keyring");
|
||||||
return Err(OAuthError::KeyringError(e));
|
return Err(OAuthError::KeyringError(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
info!("Reassembled encoded length: {}", encoded.len());
|
info!("Reassembled encoded length: {}", encoded.len());
|
||||||
|
|
||||||
@@ -288,21 +318,30 @@ pub fn load_auth_pass() -> Result<Option<AuthPass>, OAuthError> {
|
|||||||
|
|
||||||
/// Clear auth_pass from secure storage and app state.
|
/// Clear auth_pass from secure storage and app state.
|
||||||
pub fn clear_auth_pass() -> Result<(), OAuthError> {
|
pub fn clear_auth_pass() -> Result<(), OAuthError> {
|
||||||
// Try to get chunk count
|
#[cfg(target_os = "windows")]
|
||||||
let count_entry = Entry::new(SERVICE_NAME, "auth_pass_count")?;
|
{
|
||||||
let chunk_count = match count_entry.get_password() {
|
// Try to get chunk count
|
||||||
Ok(count_str) => count_str.parse::<usize>().unwrap_or(0),
|
let count_entry = Entry::new(SERVICE_NAME, "auth_pass_count")?;
|
||||||
Err(_) => 0,
|
let chunk_count = match count_entry.get_password() {
|
||||||
};
|
Ok(count_str) => count_str.parse::<usize>().unwrap_or(0),
|
||||||
|
Err(_) => 0,
|
||||||
|
};
|
||||||
|
|
||||||
// Delete all chunks
|
// Delete all chunks
|
||||||
for i in 0..chunk_count {
|
for i in 0..chunk_count {
|
||||||
let entry = Entry::new(SERVICE_NAME, &format!("auth_pass_{}", i))?;
|
let entry = Entry::new(SERVICE_NAME, &format!("auth_pass_{}", i))?;
|
||||||
let _ = entry.delete_credential();
|
let _ = entry.delete_credential();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete chunk count
|
||||||
|
let _ = count_entry.delete_credential();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete chunk count
|
#[cfg(not(target_os = "windows"))]
|
||||||
let _ = count_entry.delete_credential();
|
{
|
||||||
|
let entry = Entry::new(SERVICE_NAME, "auth_pass")?;
|
||||||
|
let _ = entry.delete_credential();
|
||||||
|
}
|
||||||
|
|
||||||
info!("Auth pass cleared from keyring successfully");
|
info!("Auth pass cleared from keyring successfully");
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
Reference in New Issue
Block a user