http_authentication/
header_utils.rs

1use alloc::vec::Vec;
2
3use http::{
4    header::{AUTHORIZATION, PROXY_AUTHENTICATE, PROXY_AUTHORIZATION, WWW_AUTHENTICATE},
5    HeaderMap,
6};
7
8use crate::{
9    challenge::Challenge,
10    challenges::{Challenges, ChallengesParseError, ChallengesWithSlice},
11    credentials::{Credentials, CredentialsParseError},
12};
13
14//
15//
16//
17pub fn get_authorization(
18    header_map: &HeaderMap,
19) -> Option<Result<Credentials, CredentialsParseError>> {
20    header_map
21        .get(AUTHORIZATION)
22        .map(|x| Credentials::from_bytes(x.as_bytes()))
23}
24
25pub fn get_proxy_authorization(
26    header_map: &HeaderMap,
27) -> Option<Result<Credentials, CredentialsParseError>> {
28    header_map
29        .get(PROXY_AUTHORIZATION)
30        .map(|x| Credentials::from_bytes(x.as_bytes()))
31}
32
33//
34pub fn set_authorization(
35    header_map: &mut HeaderMap,
36    credentials: &Credentials,
37) -> Result<(), http::header::InvalidHeaderValue> {
38    use alloc::string::ToString as _;
39
40    header_map.remove(AUTHORIZATION);
41    header_map.append(
42        AUTHORIZATION,
43        http::HeaderValue::from_str(credentials.to_string().as_str())?,
44    );
45    Ok(())
46}
47
48pub fn set_proxy_authorization(
49    header_map: &mut HeaderMap,
50    credentials: &Credentials,
51) -> Result<(), http::header::InvalidHeaderValue> {
52    use alloc::string::ToString as _;
53
54    header_map.remove(PROXY_AUTHORIZATION);
55    header_map.append(
56        PROXY_AUTHORIZATION,
57        http::HeaderValue::from_str(credentials.to_string().as_str())?,
58    );
59    Ok(())
60}
61
62#[cfg(feature = "scheme-basic")]
63pub fn set_authorization_with_basic(
64    header_map: &mut HeaderMap,
65    user_id: impl AsRef<str>,
66    password: impl AsRef<str>,
67) -> Result<(), http::header::InvalidHeaderValue> {
68    set_authorization(header_map, &Credentials::basic(user_id, password))
69}
70
71#[cfg(feature = "scheme-basic")]
72pub fn set_proxy_authorization_with_basic(
73    header_map: &mut HeaderMap,
74    user_id: impl AsRef<str>,
75    password: impl AsRef<str>,
76) -> Result<(), http::header::InvalidHeaderValue> {
77    set_proxy_authorization(header_map, &Credentials::basic(user_id, password))
78}
79
80//
81#[cfg(feature = "scheme-bearer")]
82pub fn set_authorization_with_bearer(
83    header_map: &mut HeaderMap,
84    token: impl AsRef<str>,
85) -> Result<(), http::header::InvalidHeaderValue> {
86    set_authorization(header_map, &Credentials::bearer(token))
87}
88
89#[cfg(feature = "scheme-bearer")]
90pub fn set_proxy_authorization_with_bearer(
91    header_map: &mut HeaderMap,
92    token: impl AsRef<str>,
93) -> Result<(), http::header::InvalidHeaderValue> {
94    set_proxy_authorization(header_map, &Credentials::bearer(token))
95}
96
97//
98//
99//
100pub fn get_www_authenticate(header_map: &HeaderMap) -> Result<Challenges, ChallengesParseError> {
101    let list = header_map
102        .get_all(WWW_AUTHENTICATE)
103        .into_iter()
104        .map(|x| Challenges::from_bytes(x.as_bytes()))
105        .collect::<Result<Vec<_>, _>>()?;
106    let list = list.into_iter().flat_map(|x| x.0).collect::<Vec<_>>();
107    Ok(Challenges::new(list))
108}
109
110pub fn get_proxy_authenticate(header_map: &HeaderMap) -> Result<Challenges, ChallengesParseError> {
111    let list = header_map
112        .get_all(PROXY_AUTHENTICATE)
113        .into_iter()
114        .map(|x| Challenges::from_bytes(x.as_bytes()))
115        .collect::<Result<Vec<_>, _>>()?;
116    let list = list.into_iter().flat_map(|x| x.0).collect::<Vec<_>>();
117    Ok(Challenges::new(list))
118}
119
120//
121pub fn append_www_authenticate(
122    header_map: &mut HeaderMap,
123    challenge: &Challenge,
124) -> Result<(), http::header::InvalidHeaderValue> {
125    use alloc::string::ToString as _;
126
127    header_map.append(
128        WWW_AUTHENTICATE,
129        http::HeaderValue::from_str(challenge.to_string().as_str())?,
130    );
131    Ok(())
132}
133
134pub fn append_www_authenticate_with_multiple(
135    header_map: &mut HeaderMap,
136    challenges: &[Challenge],
137) -> Result<(), http::header::InvalidHeaderValue> {
138    use alloc::string::ToString as _;
139
140    header_map.append(
141        WWW_AUTHENTICATE,
142        http::HeaderValue::from_str(ChallengesWithSlice::new(challenges).to_string().as_str())?,
143    );
144    Ok(())
145}
146
147pub fn append_proxy_authenticate(
148    header_map: &mut HeaderMap,
149    challenge: &Challenge,
150) -> Result<(), http::header::InvalidHeaderValue> {
151    use alloc::string::ToString as _;
152
153    header_map.append(
154        PROXY_AUTHENTICATE,
155        http::HeaderValue::from_str(challenge.to_string().as_str())?,
156    );
157    Ok(())
158}
159
160pub fn append_proxy_authenticate_with_multiple(
161    header_map: &mut HeaderMap,
162    challenges: &[Challenge],
163) -> Result<(), http::header::InvalidHeaderValue> {
164    use alloc::string::ToString as _;
165
166    header_map.append(
167        PROXY_AUTHENTICATE,
168        http::HeaderValue::from_str(ChallengesWithSlice::new(challenges).to_string().as_str())?,
169    );
170    Ok(())
171}
172
173#[cfg(test)]
174mod tests {
175    use super::*;
176
177    #[cfg(feature = "scheme-basic")]
178    #[test]
179    fn test_get_set_authorization() {
180        use crate::schemes::basic::{
181            DEMO_CREDENTIALS_PASSWORD_STR, DEMO_CREDENTIALS_STR, DEMO_CREDENTIALS_USER_ID_STR,
182        };
183
184        //
185        let mut map = HeaderMap::new();
186        assert!(get_authorization(&map).is_none());
187        map.append(AUTHORIZATION, DEMO_CREDENTIALS_STR.parse().unwrap());
188        let c = get_authorization(&map).map(|x| x.unwrap()).unwrap();
189        match c {
190            Credentials::Basic(c) => {
191                assert_eq!(c.user_id, DEMO_CREDENTIALS_USER_ID_STR.into());
192                assert_eq!(c.password, DEMO_CREDENTIALS_PASSWORD_STR.into());
193            }
194            x => panic!("{x:?}"),
195        }
196
197        //
198        let mut map = HeaderMap::new();
199        set_authorization_with_basic(
200            &mut map,
201            DEMO_CREDENTIALS_USER_ID_STR,
202            DEMO_CREDENTIALS_PASSWORD_STR,
203        )
204        .unwrap();
205        assert_eq!(map.get(AUTHORIZATION).unwrap(), DEMO_CREDENTIALS_STR);
206    }
207
208    #[cfg(feature = "scheme-basic")]
209    #[test]
210    fn test_get_set_proxy_authorization() {
211        use crate::schemes::basic::{
212            DEMO_CREDENTIALS_PASSWORD_STR, DEMO_CREDENTIALS_STR, DEMO_CREDENTIALS_USER_ID_STR,
213        };
214
215        //
216        let mut map = HeaderMap::new();
217        assert!(get_proxy_authorization(&map).is_none());
218        map.append(PROXY_AUTHORIZATION, DEMO_CREDENTIALS_STR.parse().unwrap());
219        let c = get_proxy_authorization(&map).map(|x| x.unwrap()).unwrap();
220        match c {
221            Credentials::Basic(c) => {
222                assert_eq!(c.user_id, DEMO_CREDENTIALS_USER_ID_STR.into());
223                assert_eq!(c.password, DEMO_CREDENTIALS_PASSWORD_STR.into());
224            }
225            x => panic!("{x:?}"),
226        }
227
228        //
229        let mut map = HeaderMap::new();
230        set_proxy_authorization_with_basic(
231            &mut map,
232            DEMO_CREDENTIALS_USER_ID_STR,
233            DEMO_CREDENTIALS_PASSWORD_STR,
234        )
235        .unwrap();
236        assert_eq!(map.get(PROXY_AUTHORIZATION).unwrap(), DEMO_CREDENTIALS_STR);
237    }
238
239    #[cfg(all(feature = "scheme-basic", feature = "scheme-bearer"))]
240    #[test]
241    fn test_get_append_www_authenticate() {
242        //
243        let mut map = HeaderMap::new();
244        assert!(get_www_authenticate(&map).unwrap().is_empty());
245
246        //
247        map.append(
248            WWW_AUTHENTICATE,
249            r#"Basic realm="foo", charset="UTF-8", Bearer realm="bar", scope="openid profile email""#
250                .parse()
251                .unwrap(),
252        );
253        let c = get_www_authenticate(&map).unwrap();
254        for (i, c) in c.iter().enumerate() {
255            match i {
256                0 => {
257                    let c = c.as_basic().unwrap();
258                    assert_eq!(c.realm, "foo".into());
259                    assert_eq!(c.charset, Some("UTF-8".into()));
260                }
261                1 => {
262                    let c = c.as_bearer().unwrap();
263                    assert_eq!(c.realm, "bar".into());
264                    assert_eq!(c.scope, Some("openid profile email".into()));
265                }
266                i => panic!("{i} {c:?}"),
267            }
268        }
269
270        //
271        map.clear();
272        map.append(
273            WWW_AUTHENTICATE,
274            r#"Basic realm="foo", charset="UTF-8""#.parse().unwrap(),
275        );
276        map.append(
277            WWW_AUTHENTICATE,
278            r#"Bearer realm="bar", scope="openid profile email""#
279                .parse()
280                .unwrap(),
281        );
282        let c = get_www_authenticate(&map).unwrap();
283        for (i, c) in c.iter().enumerate() {
284            match i {
285                0 => {
286                    let c = c.as_basic().unwrap();
287                    assert_eq!(c.realm, "foo".into());
288                    assert_eq!(c.charset, Some("UTF-8".into()));
289                }
290                1 => {
291                    let c = c.as_bearer().unwrap();
292                    assert_eq!(c.realm, "bar".into());
293                    assert_eq!(c.scope, Some("openid profile email".into()));
294                }
295                i => panic!("{i} {c:?}"),
296            }
297        }
298
299        //
300        map.clear();
301        append_www_authenticate(
302            &mut map,
303            &crate::schemes::basic::Challenge::new("foo").into(),
304        )
305        .unwrap();
306        let list = map
307            .get_all(WWW_AUTHENTICATE)
308            .into_iter()
309            .collect::<Vec<_>>();
310        assert_eq!(list.len(), 1);
311        assert_eq!(list[0], r#"Basic realm="foo""#);
312
313        //
314        map.clear();
315        append_www_authenticate_with_multiple(
316            &mut map,
317            &[
318                crate::schemes::basic::Challenge::new("foo").into(),
319                crate::schemes::bearer::Challenge::new("bar").into(),
320            ],
321        )
322        .unwrap();
323        let list = map
324            .get_all(WWW_AUTHENTICATE)
325            .into_iter()
326            .collect::<Vec<_>>();
327        assert_eq!(list.len(), 1);
328        assert_eq!(list[0], r#"Basic realm="foo", Bearer realm="bar""#);
329    }
330
331    #[cfg(all(feature = "scheme-basic", feature = "scheme-bearer"))]
332    #[test]
333    fn test_get_append_proxy_authenticate() {
334        //
335        let mut map = HeaderMap::new();
336        assert!(get_proxy_authenticate(&map).unwrap().is_empty());
337
338        //
339        map.append(
340            PROXY_AUTHENTICATE,
341            r#"Basic realm="foo", charset="UTF-8", Bearer realm="bar", scope="openid profile email""#
342                .parse()
343                .unwrap(),
344        );
345        let c = get_proxy_authenticate(&map).unwrap();
346        for (i, c) in c.iter().enumerate() {
347            match i {
348                0 => {
349                    let c = c.as_basic().unwrap();
350                    assert_eq!(c.realm, "foo".into());
351                    assert_eq!(c.charset, Some("UTF-8".into()));
352                }
353                1 => {
354                    let c = c.as_bearer().unwrap();
355                    assert_eq!(c.realm, "bar".into());
356                    assert_eq!(c.scope, Some("openid profile email".into()));
357                }
358                i => panic!("{i} {c:?}"),
359            }
360        }
361
362        //
363        map.clear();
364        map.append(
365            PROXY_AUTHENTICATE,
366            r#"Basic realm="foo", charset="UTF-8""#.parse().unwrap(),
367        );
368        map.append(
369            PROXY_AUTHENTICATE,
370            r#"Bearer realm="bar", scope="openid profile email""#
371                .parse()
372                .unwrap(),
373        );
374        let c = get_proxy_authenticate(&map).unwrap();
375        for (i, c) in c.iter().enumerate() {
376            match i {
377                0 => {
378                    let c = c.as_basic().unwrap();
379                    assert_eq!(c.realm, "foo".into());
380                    assert_eq!(c.charset, Some("UTF-8".into()));
381                }
382                1 => {
383                    let c = c.as_bearer().unwrap();
384                    assert_eq!(c.realm, "bar".into());
385                    assert_eq!(c.scope, Some("openid profile email".into()));
386                }
387                i => panic!("{i} {c:?}"),
388            }
389        }
390
391        //
392        map.clear();
393        append_proxy_authenticate(
394            &mut map,
395            &crate::schemes::basic::Challenge::new("foo").into(),
396        )
397        .unwrap();
398        let list = map
399            .get_all(PROXY_AUTHENTICATE)
400            .into_iter()
401            .collect::<Vec<_>>();
402        assert_eq!(list.len(), 1);
403        assert_eq!(list[0], r#"Basic realm="foo""#);
404
405        //
406        map.clear();
407        append_proxy_authenticate_with_multiple(
408            &mut map,
409            &[
410                crate::schemes::basic::Challenge::new("foo").into(),
411                crate::schemes::bearer::Challenge::new("bar").into(),
412            ],
413        )
414        .unwrap();
415        let list = map
416            .get_all(PROXY_AUTHENTICATE)
417            .into_iter()
418            .collect::<Vec<_>>();
419        assert_eq!(list.len(), 1);
420        assert_eq!(list[0], r#"Basic realm="foo", Bearer realm="bar""#);
421    }
422}