Skip to main content

amq_protocol/
auth.rs

1pub use crate::uri::SASLMechanism;
2use crate::{
3    types::{AMQPValue, FieldTable, LongString, generation::gen_field_table},
4    uri::AMQPUserInfo,
5};
6
7/// Structure holding the username and password for authentication
8#[derive(Clone, Debug, PartialEq, Eq)]
9pub struct Credentials {
10    username: LongString,
11    password: LongString,
12}
13
14impl Credentials {
15    /// Create a new Credentials instance with the given username and password
16    pub fn new(username: LongString, password: LongString) -> Self {
17        Self { username, password }
18    }
19
20    /// Get the username
21    pub fn username(&self) -> &LongString {
22        &self.username
23    }
24
25    /// Get the password
26    pub fn password(&self) -> &LongString {
27        &self.password
28    }
29
30    /// Get the SASL authentication String for the given SASL mechanism
31    pub fn sasl_auth_string(&self, mechanism: SASLMechanism) -> LongString {
32        match mechanism {
33            SASLMechanism::AMQPlain => self.amqplain_auth_string(),
34            SASLMechanism::Anonymous | SASLMechanism::External => LongString::default(),
35            SASLMechanism::Plain => format!("\0{}\0{}", self.username, self.password).into(),
36            SASLMechanism::RabbitCrDemo => self.username.clone(),
37        }
38    }
39
40    /// Get the expected challenge for RabbitCrDemo mechanism
41    pub fn rabbit_cr_demo_challenge(&self) -> &'static str {
42        "Please tell me your password"
43    }
44
45    /// Get the answer we need to give to the server for the RabbitCrDemo mechanism
46    pub fn rabbit_cr_demo_answer(&self) -> LongString {
47        format!("My password is {}", self.password).into()
48    }
49
50    fn amqplain_auth_string(&self) -> LongString {
51        let mut table = FieldTable::default();
52        table.insert("LOGIN".into(), AMQPValue::LongString(self.username.clone()));
53        table.insert(
54            "PASSWORD".into(),
55            AMQPValue::LongString(self.password.clone()),
56        );
57        let (buf, _) = gen_field_table(&table)(Vec::new().into())
58            .expect("failed to serialize AMQPLAIN auth string")
59            .into_inner();
60        // skip the FieldTable length prefix (4 bytes)
61        buf[4..].to_vec().into()
62    }
63}
64
65impl Default for Credentials {
66    fn default() -> Self {
67        Self::new("guest".into(), "guest".into())
68    }
69}
70
71impl From<AMQPUserInfo> for Credentials {
72    fn from(user_info: AMQPUserInfo) -> Self {
73        Self {
74            username: user_info.username.into(),
75            password: user_info.password.into(),
76        }
77    }
78}
79
80#[cfg(test)]
81mod test {
82    use super::*;
83
84    #[test]
85    fn test_amqplain() {
86        assert_eq!(
87            Credentials::default().amqplain_auth_string(),
88            "\u{5}LOGINS\u{0}\u{0}\u{0}\u{5}guest\u{8}PASSWORDS\u{0}\u{0}\u{0}\u{5}guest".into()
89        );
90    }
91}