http_authentication/
credentials.rs

1use alloc::vec::Vec;
2use core::str::FromStr;
3
4use crate::{
5    schemes::{NAME_BASIC, NAME_BEARER, NAME_DIGEST},
6    SP,
7};
8
9//
10#[derive(Debug, Clone)]
11pub enum Credentials {
12    #[cfg(feature = "scheme-basic")]
13    Basic(crate::schemes::basic::Credentials),
14    #[cfg(feature = "scheme-bearer")]
15    Bearer(crate::schemes::bearer::Credentials),
16}
17
18impl Credentials {
19    //
20    #[cfg(feature = "scheme-basic")]
21    pub fn basic(user_id: impl AsRef<str>, password: impl AsRef<str>) -> Self {
22        Self::Basic(crate::schemes::basic::Credentials::new(user_id, password))
23    }
24
25    #[cfg(feature = "scheme-basic")]
26    pub fn as_basic(&self) -> Option<&crate::schemes::basic::Credentials> {
27        match self {
28            Self::Basic(c) => Some(c),
29            #[allow(unreachable_patterns)]
30            _ => None,
31        }
32    }
33
34    //
35    #[cfg(feature = "scheme-bearer")]
36    pub fn bearer(token: impl AsRef<str>) -> Self {
37        Self::Bearer(crate::schemes::bearer::Credentials::new(token))
38    }
39
40    #[cfg(feature = "scheme-bearer")]
41    pub fn as_bearer(&self) -> Option<&crate::schemes::bearer::Credentials> {
42        match self {
43            Self::Bearer(c) => Some(c),
44            #[allow(unreachable_patterns)]
45            _ => None,
46        }
47    }
48
49    //
50    pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, CredentialsParseError> {
51        let bytes = bytes.as_ref();
52
53        let scheme = bytes
54            .iter()
55            .take_while(|x| **x != SP as u8)
56            .cloned()
57            .collect::<Vec<_>>();
58        match scheme {
59            x if x.eq_ignore_ascii_case(NAME_BASIC.as_bytes()) => {
60                #[cfg(feature = "scheme-basic")]
61                {
62                    crate::schemes::basic::Credentials::from_bytes(bytes)
63                        .map(Self::Basic)
64                        .map_err(CredentialsParseError::Basic)
65                }
66                #[cfg(not(feature = "scheme-basic"))]
67                {
68                    Err(CredentialsParseError::SchemeUnsupported(
69                        "Require feature scheme-basic",
70                    ))
71                }
72            }
73            x if x.eq_ignore_ascii_case(NAME_BEARER.as_bytes()) => {
74                #[cfg(feature = "scheme-bearer")]
75                {
76                    crate::schemes::bearer::Credentials::from_bytes(bytes)
77                        .map(Self::Bearer)
78                        .map_err(CredentialsParseError::Bearer)
79                }
80                #[cfg(not(feature = "scheme-bearer"))]
81                {
82                    Err(CredentialsParseError::SchemeUnsupported(
83                        "Require feature scheme-bearer",
84                    ))
85                }
86            }
87            x if x.eq_ignore_ascii_case(NAME_DIGEST.as_bytes()) => {
88                Err(CredentialsParseError::SchemeUnsupported("Unimplemented"))
89            }
90            _ => Err(CredentialsParseError::SchemeUnknown),
91        }
92    }
93}
94
95//
96#[derive(Debug)]
97pub enum CredentialsParseError {
98    #[cfg(feature = "scheme-basic")]
99    Basic(crate::schemes::basic::CredentialsParseError),
100    #[cfg(feature = "scheme-bearer")]
101    Bearer(crate::schemes::bearer::CredentialsParseError),
102    SchemeUnknown,
103    SchemeUnsupported(&'static str),
104}
105
106impl core::fmt::Display for CredentialsParseError {
107    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
108        write!(f, "{self:?}")
109    }
110}
111
112#[cfg(feature = "std")]
113impl std::error::Error for CredentialsParseError {}
114
115//
116#[allow(unused_variables)]
117impl core::fmt::Display for Credentials {
118    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
119        match self {
120            #[cfg(feature = "scheme-basic")]
121            Self::Basic(c) => c.fmt(f),
122            #[cfg(feature = "scheme-bearer")]
123            Self::Bearer(c) => c.fmt(f),
124            #[allow(unreachable_patterns)]
125            _ => unimplemented!(),
126        }
127    }
128}
129
130//
131impl FromStr for Credentials {
132    type Err = CredentialsParseError;
133
134    fn from_str(s: &str) -> Result<Self, Self::Err> {
135        Self::from_bytes(s.as_bytes())
136    }
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[allow(unused_imports)]
144    use alloc::string::ToString as _;
145
146    #[test]
147    fn test_parse_and_render() {
148        //
149        #[cfg(feature = "scheme-basic")]
150        {
151            use crate::schemes::basic::{
152                DEMO_CREDENTIALS_PASSWORD_STR, DEMO_CREDENTIALS_STR, DEMO_CREDENTIALS_USER_ID_STR,
153            };
154
155            match DEMO_CREDENTIALS_STR.parse::<Credentials>() {
156                Ok(c) => {
157                    let c = c.as_basic().unwrap();
158                    assert_eq!(c.user_id, DEMO_CREDENTIALS_USER_ID_STR.into());
159                    assert_eq!(c.password, DEMO_CREDENTIALS_PASSWORD_STR.into());
160                    assert_eq!(c.to_string(), DEMO_CREDENTIALS_STR);
161                }
162                x => panic!("{x:?}"),
163            }
164        }
165        #[cfg(not(feature = "scheme-basic"))]
166        {
167            match "Basic bar".parse::<Credentials>() {
168                Err(CredentialsParseError::SchemeUnsupported(_)) => {}
169                x => panic!("{x:?}"),
170            }
171        }
172
173        //
174        #[cfg(feature = "scheme-bearer")]
175        {
176            use crate::schemes::bearer::{DEMO_CREDENTIALS_STR, DEMO_CREDENTIALS_TOKEN_STR};
177
178            match DEMO_CREDENTIALS_STR.parse::<Credentials>() {
179                Ok(c) => {
180                    let c = c.as_bearer().unwrap();
181                    assert_eq!(c.token, DEMO_CREDENTIALS_TOKEN_STR.into());
182                    assert_eq!(c.to_string(), DEMO_CREDENTIALS_STR);
183                }
184                x => panic!("{x:?}"),
185            }
186        }
187        #[cfg(not(feature = "scheme-bearer"))]
188        {
189            match "Bearer bar".parse::<Credentials>() {
190                Err(CredentialsParseError::SchemeUnsupported(_)) => {}
191                x => panic!("{x:?}"),
192            }
193        }
194
195        //
196        match Credentials::from_str("") {
197            Err(CredentialsParseError::SchemeUnknown) => {}
198            x => panic!("{x:?}"),
199        }
200
201        match Credentials::from_str("Foo bar") {
202            Err(CredentialsParseError::SchemeUnknown) => {}
203            x => panic!("{x:?}"),
204        }
205    }
206}