lychee_lib/basic_auth/
mod.rs1use regex::RegexSet;
2use thiserror::Error;
3
4use crate::{BasicAuthCredentials, BasicAuthSelector, Uri};
5
6#[derive(Debug, Error)]
7pub enum BasicAuthExtractorError {
8 #[error("RegexSet error")]
9 RegexSetError(#[from] regex::Error),
10}
11
12#[derive(Debug, Clone)]
16pub struct BasicAuthExtractor {
17 credentials: Vec<BasicAuthCredentials>,
18 regex_set: RegexSet,
19}
20
21impl BasicAuthExtractor {
22 pub fn new<T: AsRef<[BasicAuthSelector]>>(
43 selectors: T,
44 ) -> Result<Self, BasicAuthExtractorError> {
45 let mut raw_uri_regexes = Vec::new();
46 let mut credentials = Vec::new();
47
48 for selector in selectors.as_ref() {
49 raw_uri_regexes.push(selector.raw_uri_regex.clone());
50 credentials.push(selector.credentials.clone());
51 }
52
53 let regex_set = RegexSet::new(raw_uri_regexes)?;
54
55 Ok(Self {
56 credentials,
57 regex_set,
58 })
59 }
60
61 pub(crate) fn matches(&self, uri: &Uri) -> Option<BasicAuthCredentials> {
66 let matches: Vec<_> = self.regex_set.matches(uri.as_str()).into_iter().collect();
67
68 if matches.is_empty() {
69 return None;
70 }
71
72 Some(self.credentials[matches[0]].clone())
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use std::str::FromStr;
79
80 use super::*;
81
82 #[test]
83 fn test_basic_auth_extractor_new() {
84 let selector_str = "http://example.com foo:bar";
85 let selector = BasicAuthSelector::from_str(selector_str).unwrap();
86 let extractor = BasicAuthExtractor::new([selector]).unwrap();
87
88 assert_eq!(extractor.credentials.len(), 1);
89 assert_eq!(extractor.credentials[0].username, "foo");
90 assert_eq!(extractor.credentials[0].password, "bar");
91 }
92
93 #[test]
94 fn test_basic_auth_extractor_matches() {
95 let selector_str = "http://example.com foo:bar";
96 let selector = BasicAuthSelector::from_str(selector_str).unwrap();
97 let extractor = BasicAuthExtractor::new([selector]).unwrap();
98
99 let uri = Uri::try_from("http://example.com").unwrap();
100 let credentials = extractor.matches(&uri).unwrap();
101
102 assert_eq!(credentials.username, "foo");
103 assert_eq!(credentials.password, "bar");
104 }
105
106 #[test]
107 fn test_basic_auth_extractor_matches_multiple() {
108 let example_com = BasicAuthSelector::from_str("http://example.com foo1:bar1").unwrap();
109 let example_org = BasicAuthSelector::from_str("http://example.org foo2:bar2").unwrap();
110 let extractor = BasicAuthExtractor::new([example_com, example_org]).unwrap();
111
112 let uri = Uri::try_from("http://example.org").unwrap();
113 let credentials = extractor.matches(&uri).unwrap();
114
115 assert_eq!(credentials.username, "foo2");
116 assert_eq!(credentials.password, "bar2");
117 }
118
119 #[test]
120 fn test_basic_auth_regex_match() {
121 let selector_str = "https?://example.com/(.*)/bar foo:bar";
122 let selector = BasicAuthSelector::from_str(selector_str).unwrap();
123 let extractor = BasicAuthExtractor::new([selector]).unwrap();
124
125 let uri = Uri::try_from("http://example.com/foo/bar").unwrap();
126 let credentials = extractor.matches(&uri).unwrap();
127
128 assert_eq!(credentials.username, "foo");
129 assert_eq!(credentials.password, "bar");
130
131 let uri = Uri::try_from("https://example.com/baz/bar").unwrap();
132 let credentials = extractor.matches(&uri).unwrap();
133
134 assert_eq!(credentials.username, "foo");
135 assert_eq!(credentials.password, "bar");
136 }
137
138 #[test]
139 fn test_basic_auth_first_match_wins() {
140 let example_com = BasicAuthSelector::from_str("http://example.com foo1:bar1").unwrap();
141 let example_org = BasicAuthSelector::from_str("http://example.com foo2:bar2").unwrap();
142 let extractor = BasicAuthExtractor::new([example_com, example_org]).unwrap();
143
144 let uri = Uri::try_from("http://example.com").unwrap();
145 let credentials = extractor.matches(&uri).unwrap();
146
147 assert_eq!(credentials.username, "foo1");
148 assert_eq!(credentials.password, "bar1");
149 }
150
151 #[test]
152 fn test_basic_auth_extractor_no_match() {
153 let selector_str = "http://example.com foo:bar";
154 let selector = BasicAuthSelector::from_str(selector_str).unwrap();
155 let extractor = BasicAuthExtractor::new([selector]).unwrap();
156
157 let uri = Uri::try_from("http://test.com").unwrap();
158 let credentials = extractor.matches(&uri);
159
160 assert!(credentials.is_none());
161 }
162}