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