diff --git a/apt-auth-config/src/lib.rs b/apt-auth-config/src/lib.rs index 886ce997a..e0d0de382 100644 --- a/apt-auth-config/src/lib.rs +++ b/apt-auth-config/src/lib.rs @@ -21,72 +21,85 @@ pub enum AuthConfigError { ParseError(#[from] netrc::Error), } -#[derive(Debug, Eq)] +#[derive(Debug, PartialEq, Eq)] pub struct AuthUrl { schema: Option, - host_and_path: String, -} - -impl AuthUrl { - fn drop_suffix(&self) -> &str { - let mut res = self.host_and_path.as_str(); - - while let Some(x) = res.strip_suffix('/') { - res = x; - } - - res - } + host: String, + port: Option, + path: Option, } impl From<&str> for AuthUrl { fn from(value: &str) -> Self { - if let Ok(url) = Url::parse(value) { - AuthUrl { - schema: Some(url.scheme().to_string()), - host_and_path: { - let mut s = String::new(); - if let Some(host) = url.host_str() { - s.push_str(host); - } - s.push_str(url.path()); - s - }, - } - } else { - AuthUrl { - schema: None, - host_and_path: value.to_string(), + // extract schema + let (schema, host_port_path) = value.split_once("://").unzip(); + let host_port_path = host_port_path.unwrap_or(value); + + // extract path + let (host_port, mut path) = host_port_path.split_once("/").unzip(); + let host_port = host_port.unwrap_or(host_port_path); + + // extract port + let (host, port) = host_port.split_once(":").unzip(); + let host = host.unwrap_or(host_port); + let (host, port) = port + .and_then(|port| port.parse::().ok()) + .map(|port| (host, Some(port))) + .unwrap_or((host_port, None)); + + // strip suffix + if let Some(path) = &mut path { + while let Some(x) = path.strip_suffix('/') { + *path = x; } } + + Self { + schema: schema.map(|schema| schema.to_string()), + host: host.to_string(), + port, + path: path.map(|path| path.to_string()), + } } } impl From<&Url> for AuthUrl { fn from(value: &Url) -> Self { - let mut host_and_path = String::new(); - let schema = value.scheme().to_string(); - - if let Some(host) = value.host_str() { - host_and_path.push_str(host); + let mut path = value.path(); + while let Some(x) = path.strip_suffix('/') { + path = x; } - host_and_path.push_str(value.path()); - AuthUrl { - schema: Some(schema), - host_and_path, + schema: Some(value.scheme().to_string()), + host: value + .host() + .map(|host| host.to_string()) + .unwrap_or_default(), + port: value.port_or_known_default(), + path: Some(path.to_string()), } } } -impl PartialEq for AuthUrl { - fn eq(&self, other: &Self) -> bool { - if let Some((a, b)) = self.schema.as_ref().zip(other.schema.as_ref()) { - return a == b && self.drop_suffix() == other.drop_suffix(); +impl AuthUrl { + fn test(&self, other: &Self) -> bool { + if let Some(a) = &other.schema { + if let Some(b) = &self.schema { + if a != b { + return false; + } + } else if a != "https" && a != "tor+https" { + return false; + } } - - self.drop_suffix() == other.drop_suffix() + if self.port.is_some() && other.port.is_some() && self.port != other.port { + return false; + } + if self.path.is_some() && other.path.is_some() && self.path != other.path { + return false; + } + self.host == other.host } } @@ -139,7 +152,91 @@ impl AuthConfig { pub fn find(&self, url: &str) -> Option<&Authenticator> { self.0 .iter() - .find(|x| AuthUrl::from(url) == x.0) + .find(|(x, _)| x.test(&AuthUrl::from(url))) .map(|x| &x.1) } } + +#[cfg(test)] +mod test { + use crate::*; + + #[test] + fn test_auth_parse() { + assert_eq!( + AuthUrl::from("localhost"), + AuthUrl { + schema: None, + host: "localhost".to_string(), + port: None, + path: None + } + ); + assert_eq!( + AuthUrl::from("localhost:1234"), + AuthUrl { + schema: None, + host: "localhost".to_string(), + port: Some(1234), + path: None + } + ); + assert_eq!( + AuthUrl::from("ftp://localhost"), + AuthUrl { + schema: Some("ftp".to_string()), + host: "localhost".to_string(), + port: None, + path: None + } + ); + assert_eq!( + AuthUrl::from("ftp://localhost:123/something"), + AuthUrl { + schema: Some("ftp".to_string()), + host: "localhost".to_string(), + port: Some(123), + path: Some("something".to_string()) + } + ); + assert_eq!( + AuthUrl::from("ftp://localhost:123/something///"), + AuthUrl { + schema: Some("ftp".to_string()), + host: "localhost".to_string(), + port: Some(123), + path: Some("something".to_string()) + } + ); + } + + #[test] + fn test_auth_match() { + assert!(AuthUrl::from("localhost").test(&AuthUrl::from("localhost"))); + assert!(!AuthUrl::from("localhost").test(&AuthUrl::from("ten.avaj"))); + + assert!(AuthUrl::from("localhost").test(&AuthUrl::from("https://localhost"))); + assert!(AuthUrl::from("https://localhost").test(&AuthUrl::from("https://localhost"))); + assert!(AuthUrl::from("localhost").test(&AuthUrl::from("tor+https://localhost"))); + assert!(!AuthUrl::from("localhost") + .test(&AuthUrl::from("aosctexttransferprotocol://localhost"))); + assert!(AuthUrl::from("attp://localhost").test(&AuthUrl::from("attp://localhost"))); + assert!(!AuthUrl::from("attp://localhost").test(&AuthUrl::from("http://localhost"))); + + assert!(AuthUrl::from("localhost").test(&AuthUrl::from("https://localhost:456"))); + assert!(!AuthUrl::from("localhost:123").test(&AuthUrl::from("https://localhost:456"))); + assert!(AuthUrl::from("localhost:123").test(&AuthUrl::from("https://localhost:123"))); + + assert!( + AuthUrl::from("localhost:123/foo").test(&AuthUrl::from("https://localhost:123/foo")) + ); + assert!( + AuthUrl::from("localhost:123/bar").test(&AuthUrl::from("https://localhost:123/bar")) + ); + assert!( + !AuthUrl::from("localhost:123/foo").test(&AuthUrl::from("https://localhost:123/bar")) + ); + assert!(AuthUrl::from("localhost:123").test(&AuthUrl::from("https://localhost:123/bar"))); + assert!(AuthUrl::from("localhost:123").test(&AuthUrl::from("https://localhost:123/foo"))); + } +}