use async_trait::async_trait;
use crate::error::CassandraResult;
#[async_trait]
pub trait SaslMechanism: Send + Sync + std::fmt::Debug {
fn name(&self) -> &str;
async fn initial_response(&self) -> CassandraResult<Vec<u8>>;
async fn evaluate(&self, challenge: &[u8]) -> CassandraResult<Vec<u8>>;
}
#[derive(Debug, Clone)]
pub struct PlainSasl {
pub username: String,
pub password: String,
}
impl PlainSasl {
pub fn new(username: impl Into<String>, password: impl Into<String>) -> Self {
Self {
username: username.into(),
password: password.into(),
}
}
}
#[async_trait]
impl SaslMechanism for PlainSasl {
fn name(&self) -> &str {
"PLAIN"
}
async fn initial_response(&self) -> CassandraResult<Vec<u8>> {
let mut buf = Vec::with_capacity(2 + self.username.len() + self.password.len());
buf.push(0);
buf.extend_from_slice(self.username.as_bytes());
buf.push(0);
buf.extend_from_slice(self.password.as_bytes());
Ok(buf)
}
async fn evaluate(&self, _challenge: &[u8]) -> CassandraResult<Vec<u8>> {
Ok(Vec::new())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_plain_sasl_initial_response_format() {
let sasl = PlainSasl::new("alice", "s3cret");
let response = sasl.initial_response().await.unwrap();
let expected: Vec<u8> = b"\0alice\0s3cret".to_vec();
assert_eq!(response, expected);
}
#[tokio::test]
async fn test_plain_sasl_evaluate_returns_empty() {
let sasl = PlainSasl::new("alice", "s3cret");
let response = sasl.evaluate(b"challenge").await.unwrap();
assert!(response.is_empty());
}
#[test]
fn test_plain_sasl_name() {
let sasl = PlainSasl::new("u", "p");
assert_eq!(sasl.name(), "PLAIN");
}
}