zookeeper_client/sasl/
mod.rs1#[allow(unused_imports)]
2use std::borrow::Cow;
3
4#[cfg(feature = "sasl-gssapi")]
5mod gssapi;
6#[cfg(feature = "sasl-gssapi")]
7pub use gssapi::*;
8
9#[cfg(feature = "sasl-digest-md5")]
10mod digest_md5;
11#[cfg(feature = "sasl-digest-md5")]
12pub use digest_md5::*;
13
14#[cfg(feature = "sasl-digest-md5")]
15mod mechanisms {
16 mod digest_md5;
17}
18
19use rsasl::prelude::*;
20
21use crate::error::Error;
22
23pub(crate) type Result<T, E = crate::error::Error> = std::result::Result<T, E>;
24
25pub(crate) trait SaslInitiator {
26 fn new_session(&self, hostname: &str) -> Result<SaslSession>;
27}
28
29#[derive(Clone, Debug)]
31pub struct SaslOptions(SaslInnerOptions);
32
33#[derive(Clone, Debug)]
34enum SaslInnerOptions {
35 #[cfg(feature = "sasl-gssapi")]
36 Gssapi(GssapiSaslOptions),
37 #[cfg(feature = "sasl-digest-md5")]
38 DigestMd5(DigestMd5SaslOptions),
39}
40
41impl SaslOptions {
42 #[cfg(feature = "sasl-gssapi")]
48 #[cfg_attr(docsrs, doc(cfg(any(feature = "sasl", feature = "sasl-gssapi"))))]
49 pub fn gssapi() -> GssapiSaslOptions {
50 GssapiSaslOptions::new()
51 }
52
53 #[cfg(feature = "sasl-digest-md5")]
55 #[cfg_attr(docsrs, doc(cfg(any(feature = "sasl", feature = "sasl-digest-md5"))))]
56 pub fn digest_md5(
57 username: impl Into<Cow<'static, str>>,
58 password: impl Into<Cow<'static, str>>,
59 ) -> DigestMd5SaslOptions {
60 DigestMd5SaslOptions::new(username, password)
61 }
62}
63
64impl SaslInitiator for SaslOptions {
65 fn new_session(&self, hostname: &str) -> Result<SaslSession> {
66 match &self.0 {
67 #[cfg(feature = "sasl-digest-md5")]
68 SaslInnerOptions::DigestMd5(options) => options.new_session(hostname),
69 #[cfg(feature = "sasl-gssapi")]
70 SaslInnerOptions::Gssapi(options) => options.new_session(hostname),
71 }
72 }
73}
74
75pub struct SaslSession {
76 output: Vec<u8>,
77 session: Session,
78 finished: bool,
79}
80
81impl SaslSession {
82 fn new(session: Session) -> Result<Self> {
83 let mut session = Self { session, output: Default::default(), finished: false };
84 if session.session.are_we_first() {
85 session.step(Default::default())?;
86 }
87 Ok(session)
88 }
89
90 pub fn name(&self) -> &str {
91 self.session.get_mechname().as_str()
92 }
93
94 pub fn initial(&self) -> &[u8] {
95 &self.output
96 }
97
98 pub fn step(&mut self, challenge: &[u8]) -> Result<Option<&[u8]>> {
99 if self.finished {
100 return Err(Error::UnexpectedError(format!("SASL {} session already finished", self.name())));
101 }
102 self.output.clear();
103 match self.session.step(Some(challenge), &mut self.output).map_err(Error::other)? {
104 State::Running => Ok(Some(&self.output)),
105 State::Finished(MessageSent::Yes) => {
106 self.finished = true;
107 Ok(Some(&self.output))
108 },
109 State::Finished(MessageSent::No) => {
110 self.finished = true;
111 Ok(None)
112 },
113 }
114 }
115}