Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for netrc files as a secondary fallback tier #395

Merged
merged 10 commits into from
Jan 3, 2024
1 change: 1 addition & 0 deletions crates/rattler_networking/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ dirs = "5.0.1"
keyring = "2.0.5"
lazy_static = "1.4.0"
libc = "0.2.148"
netrc-rs = "0.1.2"
reqwest = { version = "0.11.22", default-features = false}
retry-policies = { version = "0.2.0", default-features = false }
serde = "1.0.188"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! This module provides a way to store and retrieve authentication information for a given host.
pub mod authentication;
pub mod fallback_storage;
pub mod netrc_storage;
pub mod storage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//! Fallback storage for passwords.
use netrc_rs::{Machine, Netrc};
use serde::de::value;
use std::{
collections::HashMap,
env,
io::Read,
path::PathBuf,
sync::{Arc, Mutex},
};

/// A struct that implements storage and access of authentication
/// information backed by a on-disk JSON file
#[derive(Clone)]
pub struct NetRcStorage {
/// The path to the JSON file
pub path: PathBuf,
mariusvniekerk marked this conversation as resolved.
Show resolved Hide resolved

/// A mutex to ensure that only one thread accesses the file at a time
mutex: Arc<Mutex<()>>,

/// The netrc file contents
machines: HashMap<String, Machine>,
}

/// An error that can occur when accessing the fallback storage
#[derive(thiserror::Error, Debug)]
pub enum NetRcStorageError {
/// An IO error occurred when accessing the fallback storage
#[error("IO error: {0}")]
IOError(#[from] std::io::Error),
// /// An error occurred when (de)serializing the credentials
// #[error("JSON error: {0}")]
// JSONError(#[from] serde_json::Error),
}

impl NetRcStorage {
/// Create a new fallback storage with the given path
pub fn new() -> Self {
mariusvniekerk marked this conversation as resolved.
Show resolved Hide resolved
// Get the path to the netrc file
let path = match env::var("NETRC") {
Ok(val) => PathBuf::from(val),
Err(_) => match dirs::home_dir() {
Some(mut path) => {
path.push(".netrc");
path
}
None => PathBuf::from(".netrc"),
},
};

Self {
path: path.clone(),
mutex: Arc::new(Mutex::new(())),
machines: if path.exists() {
match std::fs::File::open(&path) {
mariusvniekerk marked this conversation as resolved.
Show resolved Hide resolved
Ok(file) => {
let mut reader = std::io::BufReader::new(file);
let mut content = String::new();
match reader.read_to_string(&mut content) {
Ok(_) => match Netrc::parse(&content, false) {
Ok(netrc) => netrc
.machines
.into_iter()
.map(|m| (m.name.clone(), m))
.filter_map(|(name, value)| name.map(|n| (n, value)))
.collect(),
Err(_) => HashMap::new(),
},
Err(_) => HashMap::new(),
}
}
Err(_) => HashMap::new(),
}
} else {
HashMap::new()
},
}
}

/// Retrieve the authentication information for the given host
pub fn get_password(&self, host: &str) -> Result<Option<String>, NetRcStorageError> {
let _lock = self.mutex.lock().unwrap();
mariusvniekerk marked this conversation as resolved.
Show resolved Hide resolved
match self.machines.get(host) {
Some(machine) => Ok(machine.password.clone()),
None => Ok(None),
}
}
}
20 changes: 19 additions & 1 deletion crates/rattler_networking/src/authentication_storage/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::{
use keyring::Entry;
use reqwest::{IntoUrl, Url};

use super::netrc_storage;
use super::{authentication::Authentication, fallback_storage};

/// A struct that implements storage and access of authentication
Expand All @@ -24,16 +25,21 @@ pub struct AuthenticationStorage {

/// A cache so that we don't have to access the keyring all the time
cache: Arc<Mutex<HashMap<String, Option<Authentication>>>>,

/// Netrc Storage that will be used if the is no key store application available.
pub netrc_storage: netrc_storage::NetRcStorage,
}

impl AuthenticationStorage {
/// Create a new authentication storage with the given store key
pub fn new(store_key: &str, fallback_folder: &Path) -> AuthenticationStorage {
let fallback_location = fallback_folder.join(format!("{}_auth_store.json", store_key));

AuthenticationStorage {
store_key: store_key.to_string(),
fallback_storage: fallback_storage::FallbackStorage::new(fallback_location),
cache: Arc::new(Mutex::new(HashMap::new())),
netrc_storage: netrc_storage::NetRcStorage::new(),
}
}
}
Expand Down Expand Up @@ -112,7 +118,19 @@ impl AuthenticationStorage {
self.fallback_storage.path.display()
);
match self.fallback_storage.get_password(host)? {
None => return Ok(None),
None => {
// Fallback to netrc file
tracing::debug!(
"Unable to retrieve credentials for from fallback {}: {}, using netrc credential storage",
host,
e,
);
match self.netrc_storage.get_password(host) {
Ok(None) => return Ok(None),
Ok(Some(password)) => password,
Err(_) => return Ok(None),
}
}
Some(password) => password,
}
}
Expand Down