Skip to content

Commit d457c2c

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

File tree

1 file changed

+145
-46
lines changed

1 file changed

+145
-46
lines changed

apt-auth-config/src/lib.rs

Lines changed: 145 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -21,72 +21,87 @@ 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 {
93+
if a != "https" && a != "tor+https" {
94+
return false;
95+
}
96+
}
8797
}
88-
89-
self.drop_suffix() == other.drop_suffix()
98+
if self.port.is_some() && other.port.is_some() && self.port != other.port {
99+
return false;
100+
}
101+
if self.path.is_some() && other.path.is_some() && self.path != other.path {
102+
return false;
103+
}
104+
self.host == other.host
90105
}
91106
}
92107

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

0 commit comments

Comments
 (0)