typed_headers/impls/
credentials.rs

1use base64;
2use std::fmt;
3use std::str::FromStr;
4
5use crate::Error;
6use super::{AuthScheme, Token68};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9enum Info {
10    None,
11    Token68(Token68),
12    // AuthParams(Vec<(String, String)>),
13}
14
15/// Authentication credentials, as described in [RFC7235].
16///
17/// [RFC7235]: https://tools.ietf.org/html/rfc7235#section-2.1
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct Credentials {
20    scheme: AuthScheme,
21    info: Info,
22}
23
24impl Credentials {
25    /// Creates credentials from just an auth-scheme.
26    #[inline]
27    pub fn from_auth_scheme(scheme: AuthScheme) -> Credentials {
28        Credentials {
29            scheme,
30            info: Info::None,
31        }
32    }
33
34    /// Creates credentials from an auth-scheme and token68 data.
35    #[inline]
36    pub fn from_token68(scheme: AuthScheme, token: Token68) -> Credentials {
37        Credentials {
38            scheme,
39            info: Info::Token68(token),
40        }
41    }
42
43    /// Creates Bearer authentication credentials as described in [RFC6750].
44    ///
45    /// [RFC6750]: https://tools.ietf.org/html/rfc6750
46    #[inline]
47    pub fn bearer(token: Token68) -> Credentials {
48        Credentials::from_token68(AuthScheme::BEARER, token)
49    }
50
51    /// Creates Basic authentication credentials as described in [RFC7617].
52    ///
53    /// [RFC7671]: https://tools.ietf.org/html/rfc7617
54    #[inline]
55    pub fn basic(user_id: &str, password: &str) -> Result<Credentials, Error> {
56        if user_id.contains(':') || has_ctr(user_id) {
57            return Err(Error::invalid_value());
58        }
59
60        if has_ctr(password) {
61            return Err(Error::invalid_value());
62        }
63
64        let token = format!("{}:{}", user_id, password);
65        let token = base64::encode(token.as_bytes());
66        let token = Token68(token);
67
68        Ok(Credentials::from_token68(AuthScheme::BASIC, token))
69    }
70
71    /// Returns the auth-scheme associated with the credentials.
72    #[inline]
73    pub fn scheme(&self) -> &AuthScheme {
74        &self.scheme
75    }
76
77    /// Returns the token68 value associated with the credentials if present.
78    #[inline]
79    pub fn token68(&self) -> Option<&Token68> {
80        match self.info {
81            Info::None => None,
82            Info::Token68(ref token) => Some(token),
83        }
84    }
85
86    /// Returns the bearer token if this contains Bearer credentials.
87    #[inline]
88    pub fn as_bearer(&self) -> Option<&Token68> {
89        if self.scheme != AuthScheme::BEARER {
90            return None;
91        }
92
93        self.token68()
94    }
95}
96
97impl fmt::Display for Credentials {
98    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
99        match self.info {
100            Info::None => fmt.write_str(self.scheme.as_str()),
101            Info::Token68(ref token) => write!(fmt, "{} {}", self.scheme, token),
102        }
103    }
104}
105
106impl FromStr for Credentials {
107    type Err = Error;
108
109    fn from_str(s: &str) -> Result<Credentials, Error> {
110        let mut it = s.splitn(2, ' ');
111        let auth_scheme = it
112            .next()
113            .unwrap()
114            .parse::<AuthScheme>()
115            .map_err(|_| Error::invalid_value())?;
116
117        let info = match it.next() {
118            Some(info) => info,
119            None => return Ok(Credentials::from_auth_scheme(auth_scheme)),
120        };
121
122        let info = info.trim_start_matches(' ');
123
124        match info.parse::<Token68>() {
125            Ok(token) => Ok(Credentials::from_token68(auth_scheme, token)),
126            // FIXME parse out auth-params
127            Err(_) => return Err(Error::invalid_value()),
128        }
129    }
130}
131
132fn has_ctr(s: &str) -> bool {
133    s.as_bytes().iter().any(u8::is_ascii_control)
134}