1use crate::sasl;
2
3use anyhow::{anyhow, Result};
4
5pub const EXTERNAL: &str = "EXTERNAL";
7
8pub struct ExternalClient {
13 identity: String,
14}
15
16impl ExternalClient {
17 pub fn new(identity: String) -> Self {
18 Self {
19 identity,
20 }
21 }
22}
23
24impl sasl::Client for ExternalClient {
25 fn start(&mut self) -> Result<(String, Vec<u8>)> {
26 Ok((
27 EXTERNAL.to_string(),
28 self.identity.clone().into_bytes(),
29 ))
30 }
31
32 fn next(&mut self, _challenge: &[u8]) -> Result<Vec<u8>> {
33 Err(anyhow!(sasl::ERR_UNEXPECTED_SERVER_CHALLENGE))
34 }
35}
36
37pub type ExternalAuthenticator = Box<dyn Fn(&str) -> Result<()> + Send>;
42
43pub struct ExternalServer {
46 done: bool,
47 authenticator: ExternalAuthenticator,
48}
49
50impl ExternalServer {
51 pub fn new<F>(authenticator: ExternalAuthenticator) -> Self {
52 Self {
53 done: false,
54 authenticator,
55 }
56 }
57}
58
59impl sasl::Server for ExternalServer {
60 fn next(&mut self, response: Option<&[u8]>) -> Result<(Vec<u8>, bool)> {
61 if self.done {
62 return Err(anyhow!(sasl::ERR_UNEXPECTED_CLIENT_RESPONSE));
63 }
64
65 if response.is_none() {
66 return Ok((Vec::new(), false));
67 }
68 let response = response.unwrap();
69
70 self.done = true;
71
72 if response.contains(&b'\x00') {
73 return Err(anyhow!("identity contains a NUL character"));
74 }
75
76 (self.authenticator)(std::str::from_utf8(response)?)?;
77 Ok((Vec::new(), true))
78 }
79}