imap_types/
auth.rs

1//! Authentication-related types.
2
3use std::{
4    borrow::Cow,
5    fmt::{Display, Formatter},
6};
7
8#[cfg(feature = "arbitrary")]
9use arbitrary::Arbitrary;
10#[cfg(feature = "bounded-static")]
11use bounded_static::ToStatic;
12#[cfg(feature = "serde")]
13use serde::{Deserialize, Serialize};
14
15use crate::{
16    core::{impl_try_from, Atom},
17    secret::Secret,
18};
19
20/// Authentication mechanism.
21#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
22#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
23#[derive(Debug, Clone, PartialEq, Eq, Hash)]
24#[non_exhaustive]
25pub enum AuthMechanism<'a> {
26    /// The PLAIN SASL mechanism.
27    ///
28    /// ```imap
29    /// AUTH=PLAIN
30    /// ```
31    ///
32    /// ```text
33    /// base64(b"<authenticate-id>\x00<authorize-id>\x00<password>")
34    /// ```
35    ///
36    /// # Reference(s):
37    ///
38    /// * RFC4616: The PLAIN Simple Authentication and Security Layer (SASL) Mechanism
39    Plain,
40
41    /// The (non-standardized and slow) LOGIN SASL mechanism.
42    ///
43    /// ```imap
44    /// AUTH=LOGIN
45    /// ```
46    ///
47    /// ```text
48    /// base64(b"<username>")
49    /// base64(b"<password>")
50    /// ```
51    ///
52    /// # Reference(s):
53    ///
54    /// + draft-murchison-sasl-login-00: The LOGIN SASL Mechanism
55    Login,
56
57    /// Google's OAuth 2.0 mechanism.
58    ///
59    /// ```imap
60    /// AUTH=XOAUTH2
61    /// ```
62    ///
63    /// ```text
64    /// base64(b"user=<user>\x01auth=Bearer <token>\x01\x01")
65    /// ```
66    ///
67    /// # Reference(s):
68    ///
69    /// * <https://developers.google.com/gmail/imap/xoauth2-protocol>
70    XOAuth2,
71
72    /// Some other (unknown) mechanism.
73    Other(AuthMechanismOther<'a>),
74}
75
76impl_try_from!(Atom<'a>, 'a, &'a [u8], AuthMechanism<'a>);
77impl_try_from!(Atom<'a>, 'a, Vec<u8>, AuthMechanism<'a>);
78impl_try_from!(Atom<'a>, 'a, &'a str, AuthMechanism<'a>);
79impl_try_from!(Atom<'a>, 'a, String, AuthMechanism<'a>);
80impl_try_from!(Atom<'a>, 'a, Cow<'a, str>, AuthMechanism<'a>);
81
82impl<'a> From<Atom<'a>> for AuthMechanism<'a> {
83    fn from(atom: Atom<'a>) -> Self {
84        match atom.as_ref().to_ascii_uppercase().as_str() {
85            "PLAIN" => Self::Plain,
86            "LOGIN" => Self::Login,
87            "XOAUTH2" => Self::XOAuth2,
88            _ => Self::Other(AuthMechanismOther(atom)),
89        }
90    }
91}
92
93impl<'a> Display for AuthMechanism<'a> {
94    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
95        f.write_str(match self {
96            Self::Plain => "PLAIN",
97            Self::Login => "LOGIN",
98            Self::XOAuth2 => "XOAUTH2",
99            Self::Other(other) => other.0.as_ref(),
100        })
101    }
102}
103
104/// An (unknown) authentication mechanism.
105///
106/// It's guaranteed that this type can't represent any mechanism from [`AuthMechanism`].
107#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
108#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
109#[derive(Debug, Clone, PartialEq, Eq, Hash)]
110pub struct AuthMechanismOther<'a>(Atom<'a>);
111
112/// Data line used, e.g., during AUTHENTICATE.
113///
114/// Holds the raw binary data, i.e., a `Vec<u8>`, *not* the BASE64 string.
115#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
116#[cfg_attr(feature = "bounded-static", derive(ToStatic))]
117#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
118#[derive(Debug, Clone, PartialEq, Eq, Hash)]
119pub struct AuthenticateData(pub Secret<Vec<u8>>);
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124
125    #[test]
126    fn test_conversion() {
127        assert!(AuthMechanism::try_from("plain").is_ok());
128        assert!(AuthMechanism::try_from("login").is_ok());
129        assert!(AuthMechanism::try_from("xoauth2").is_ok());
130        assert!(AuthMechanism::try_from("xxxplain").is_ok());
131        assert!(AuthMechanism::try_from("xxxlogin").is_ok());
132        assert!(AuthMechanism::try_from("xxxxoauth2").is_ok());
133    }
134}