Skip to main content

zookeeper_client/sasl/
digest_md5.rs

1use std::borrow::Cow;
2
3use rsasl::callback::{Context, Request, SessionCallback, SessionData};
4use rsasl::prelude::*;
5use rsasl::property::{AuthId, Password, Realm};
6
7use super::{Result, SaslInitiator, SaslInnerOptions, SaslOptions, SaslSession};
8
9/// DIGEST-MD5 SASL options.
10///
11/// Uses [SaslOptions::digest_md5] to construct one.
12#[derive(Clone, Debug)]
13pub struct DigestMd5SaslOptions {
14    realm: Option<Cow<'static, str>>,
15    username: Cow<'static, str>,
16    password: Cow<'static, str>,
17}
18
19impl DigestMd5SaslOptions {
20    fn realm(&self) -> Option<&str> {
21        self.realm.as_ref().map(|s| s.as_ref())
22    }
23
24    pub(crate) fn new(username: impl Into<Cow<'static, str>>, password: impl Into<Cow<'static, str>>) -> Self {
25        Self { realm: None, username: username.into(), password: password.into() }
26    }
27
28    /// Specifies the client chosen realm.
29    #[cfg(test)]
30    pub fn with_realm(self, realm: impl Into<Cow<'static, str>>) -> Self {
31        Self { realm: Some(realm.into()), ..self }
32    }
33}
34
35impl From<DigestMd5SaslOptions> for SaslOptions {
36    fn from(options: DigestMd5SaslOptions) -> Self {
37        Self(SaslInnerOptions::DigestMd5(options))
38    }
39}
40
41struct DigestSessionCallback {
42    options: DigestMd5SaslOptions,
43}
44
45impl SessionCallback for DigestSessionCallback {
46    fn callback(
47        &self,
48        _session_data: &SessionData,
49        _context: &Context,
50        request: &mut Request<'_>,
51    ) -> Result<(), SessionError> {
52        if request.is::<Realm>() {
53            if let Some(realm) = self.options.realm() {
54                request.satisfy::<Realm>(realm)?;
55            }
56        } else if request.is::<AuthId>() {
57            request.satisfy::<AuthId>(&self.options.username)?;
58        } else if request.is::<Password>() {
59            request.satisfy::<Password>(self.options.password.as_bytes())?;
60        }
61        Ok(())
62    }
63}
64
65impl SaslInitiator for DigestMd5SaslOptions {
66    fn new_session(&self, _hostname: &str) -> Result<SaslSession> {
67        let callback = DigestSessionCallback { options: self.clone() };
68        let config = SASLConfig::builder().with_defaults().with_callback(callback).unwrap();
69        let client = SASLClient::new(config);
70        let session = client.start_suggested(&[Mechname::parse(b"DIGEST-MD5").unwrap()]).unwrap();
71        SaslSession::new(session)
72    }
73}