Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 143 additions & 46 deletions apt-auth-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,72 +21,85 @@ pub enum AuthConfigError {
ParseError(#[from] netrc::Error),
}

#[derive(Debug, Eq)]
#[derive(Debug, PartialEq, Eq)]
pub struct AuthUrl {
schema: Option<String>,
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<u16>,
path: Option<String>,
}

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::<u16>().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
}
}

Expand Down Expand Up @@ -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")));
}
}
Loading