use crate::{Authentication, Error, Stage};
use nisshi_sans_io::{ApiKey, ErrorCode, SaslHandshakeRequest, SaslHandshakeResponse};
use rama::{Context, Service};
use rsasl::prelude::Mechname;
use tracing::{debug, instrument};
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct SaslHandshakeService;
impl ApiKey for SaslHandshakeService {
const KEY: i16 = SaslHandshakeRequest::KEY;
}
impl<S> Service<S, SaslHandshakeRequest> for SaslHandshakeService
where
S: Send + Sync + 'static,
{
type Response = SaslHandshakeResponse;
type Error = Error;
#[instrument(skip(self, ctx), ret)]
async fn serve(
&self,
ctx: Context<S>,
req: SaslHandshakeRequest,
) -> Result<Self::Response, Self::Error> {
if let Some(authentication) = ctx.get::<Authentication>().cloned() {
authentication.stage
.lock()
.map_err(Into::into)
.and_then(|mut guard| {
if guard.as_ref().is_none_or(|guard|!matches!(guard, Stage::Server(_))) {
debug!(?guard);
_ = guard.replace(authentication.fresh_server());
}
if let Some(Stage::Server(server)) = guard.take() && let Ok(mechanism) = Mechname::parse(req.mechanism.as_bytes()) {
debug!(available = ?server.get_available().into_iter().map(|mechanism|mechanism.mechanism.as_str()).collect::<Vec<_>>());
server
.start_suggested(mechanism)
.inspect_err(|err| debug!(?err, ?mechanism))
.map_err(Into::into)
.map(|session| {
let mechanisms = [session.get_mechname().to_string()];
_ = guard.replace(Stage::Session(session));
SaslHandshakeResponse::default()
.error_code(ErrorCode::None.into())
.mechanisms(Some(mechanisms.into()))
})
} else {
Ok(SaslHandshakeResponse::default()
.error_code(ErrorCode::UnsupportedSaslMechanism.into())
.mechanisms(Some([req.mechanism].into())))
}
})
} else {
Ok(SaslHandshakeResponse::default()
.error_code(ErrorCode::UnsupportedSaslMechanism.into())
.mechanisms(Some([req.mechanism].into())))
}
}
}