Skip to content

Commit ea8b4bd

Browse files
committed
apt-auth-config: make path and port optional
in compliance with APT_AUTH.CONF(5).
1 parent e70dbf4 commit ea8b4bd

File tree

1 file changed

+143
-46
lines changed

1 file changed

+143
-46
lines changed

apt-auth-config/src/lib.rs

+143-46
Original file line numberDiff line numberDiff line change
@@ -21,72 +21,85 @@ pub enum AuthConfigError {
2121
ParseError(#[from] netrc::Error),
2222
}
2323

24-
#[derive(Debug, Eq)]
24+
#[derive(Debug, PartialEq, Eq)]
2525
pub struct AuthUrl {
2626
schema: Option<String>,
27-
host_and_path: String,
28-
}
29-
30-
impl AuthUrl {
31-
fn drop_suffix(&self) -> &str {
32-
let mut res = self.host_and_path.as_str();
33-
34-
while let Some(x) = res.strip_suffix('/') {
35-
res = x;
36-
}
37-
38-
res
39-
}
27+
host: String,
28+
port: Option<u16>,
29+
path: Option<String>,
4030
}
4131

4232
impl From<&str> for AuthUrl {
4333
fn from(value: &str) -> Self {
44-
if let Ok(url) = Url::parse(value) {
45-
AuthUrl {
46-
schema: Some(url.scheme().to_string()),
47-
host_and_path: {
48-
let mut s = String::new();
49-
if let Some(host) = url.host_str() {
50-
s.push_str(host);
51-
}
52-
s.push_str(url.path());
53-
s
54-
},
55-
}
56-
} else {
57-
AuthUrl {
58-
schema: None,
59-
host_and_path: value.to_string(),
34+
// extract schema
35+
let (schema, host_port_path) = value.split_once("://").unzip();
36+
let host_port_path = host_port_path.unwrap_or(value);
37+
38+
// extract path
39+
let (host_port, mut path) = host_port_path.split_once("/").unzip();
40+
let host_port = host_port.unwrap_or(host_port_path);
41+
42+
// extract port
43+
let (host, port) = host_port.split_once(":").unzip();
44+
let host = host.unwrap_or(host_port);
45+
let (host, port) = port
46+
.and_then(|port| port.parse::<u16>().ok())
47+
.map(|port| (host, Some(port)))
48+
.unwrap_or((host_port, None));
49+
50+
// strip suffix
51+
if let Some(path) = &mut path {
52+
while let Some(x) = path.strip_suffix('/') {
53+
*path = x;
6054
}
6155
}
56+
57+
Self {
58+
schema: schema.map(|schema| schema.to_string()),
59+
host: host.to_string(),
60+
port,
61+
path: path.map(|path| path.to_string()),
62+
}
6263
}
6364
}
6465

6566
impl From<&Url> for AuthUrl {
6667
fn from(value: &Url) -> Self {
67-
let mut host_and_path = String::new();
68-
let schema = value.scheme().to_string();
69-
70-
if let Some(host) = value.host_str() {
71-
host_and_path.push_str(host);
68+
let mut path = value.path();
69+
while let Some(x) = path.strip_suffix('/') {
70+
path = x;
7271
}
7372

74-
host_and_path.push_str(value.path());
75-
7673
AuthUrl {
77-
schema: Some(schema),
78-
host_and_path,
74+
schema: Some(value.scheme().to_string()),
75+
host: value
76+
.host()
77+
.map(|host| host.to_string())
78+
.unwrap_or_default(),
79+
port: value.port_or_known_default(),
80+
path: Some(path.to_string()),
7981
}
8082
}
8183
}
8284

83-
impl PartialEq for AuthUrl {
84-
fn eq(&self, other: &Self) -> bool {
85-
if let Some((a, b)) = self.schema.as_ref().zip(other.schema.as_ref()) {
86-
return a == b && self.drop_suffix() == other.drop_suffix();
85+
impl AuthUrl {
86+
fn test(&self, other: &Self) -> bool {
87+
if let Some(a) = &other.schema {
88+
if let Some(b) = &self.schema {
89+
if a != b {
90+
return false;
91+
}
92+
} else if a != "https" && a != "tor+https" {
93+
return false;
94+
}
8795
}
88-
89-
self.drop_suffix() == other.drop_suffix()
96+
if self.port.is_some() && other.port.is_some() && self.port != other.port {
97+
return false;
98+
}
99+
if self.path.is_some() && other.path.is_some() && self.path != other.path {
100+
return false;
101+
}
102+
self.host == other.host
90103
}
91104
}
92105

@@ -139,7 +152,91 @@ impl AuthConfig {
139152
pub fn find(&self, url: &str) -> Option<&Authenticator> {
140153
self.0
141154
.iter()
142-
.find(|x| AuthUrl::from(url) == x.0)
155+
.find(|(x, _)| x.test(&AuthUrl::from(url)))
143156
.map(|x| &x.1)
144157
}
145158
}
159+
160+
#[cfg(test)]
161+
mod test {
162+
use crate::*;
163+
164+
#[test]
165+
fn test_auth_parse() {
166+
assert_eq!(
167+
AuthUrl::from("localhost"),
168+
AuthUrl {
169+
schema: None,
170+
host: "localhost".to_string(),
171+
port: None,
172+
path: None
173+
}
174+
);
175+
assert_eq!(
176+
AuthUrl::from("localhost:1234"),
177+
AuthUrl {
178+
schema: None,
179+
host: "localhost".to_string(),
180+
port: Some(1234),
181+
path: None
182+
}
183+
);
184+
assert_eq!(
185+
AuthUrl::from("ftp://localhost"),
186+
AuthUrl {
187+
schema: Some("ftp".to_string()),
188+
host: "localhost".to_string(),
189+
port: None,
190+
path: None
191+
}
192+
);
193+
assert_eq!(
194+
AuthUrl::from("ftp://localhost:123/something"),
195+
AuthUrl {
196+
schema: Some("ftp".to_string()),
197+
host: "localhost".to_string(),
198+
port: Some(123),
199+
path: Some("something".to_string())
200+
}
201+
);
202+
assert_eq!(
203+
AuthUrl::from("ftp://localhost:123/something///"),
204+
AuthUrl {
205+
schema: Some("ftp".to_string()),
206+
host: "localhost".to_string(),
207+
port: Some(123),
208+
path: Some("something".to_string())
209+
}
210+
);
211+
}
212+
213+
#[test]
214+
fn test_auth_match() {
215+
assert!(AuthUrl::from("localhost").test(&AuthUrl::from("localhost")));
216+
assert!(!AuthUrl::from("localhost").test(&AuthUrl::from("ten.avaj")));
217+
218+
assert!(AuthUrl::from("localhost").test(&AuthUrl::from("https://localhost")));
219+
assert!(AuthUrl::from("https://localhost").test(&AuthUrl::from("https://localhost")));
220+
assert!(AuthUrl::from("localhost").test(&AuthUrl::from("tor+https://localhost")));
221+
assert!(!AuthUrl::from("localhost")
222+
.test(&AuthUrl::from("aosctexttransferprotocol://localhost")));
223+
assert!(AuthUrl::from("attp://localhost").test(&AuthUrl::from("attp://localhost")));
224+
assert!(!AuthUrl::from("attp://localhost").test(&AuthUrl::from("http://localhost")));
225+
226+
assert!(AuthUrl::from("localhost").test(&AuthUrl::from("https://localhost:456")));
227+
assert!(!AuthUrl::from("localhost:123").test(&AuthUrl::from("https://localhost:456")));
228+
assert!(AuthUrl::from("localhost:123").test(&AuthUrl::from("https://localhost:123")));
229+
230+
assert!(
231+
AuthUrl::from("localhost:123/foo").test(&AuthUrl::from("https://localhost:123/foo"))
232+
);
233+
assert!(
234+
AuthUrl::from("localhost:123/bar").test(&AuthUrl::from("https://localhost:123/bar"))
235+
);
236+
assert!(
237+
!AuthUrl::from("localhost:123/foo").test(&AuthUrl::from("https://localhost:123/bar"))
238+
);
239+
assert!(AuthUrl::from("localhost:123").test(&AuthUrl::from("https://localhost:123/bar")));
240+
assert!(AuthUrl::from("localhost:123").test(&AuthUrl::from("https://localhost:123/foo")));
241+
}
242+
}

0 commit comments

Comments
 (0)