use acid_io::Write;
use stringprep::saslprep;
use super::mechinfo::PlainError;
use crate::context::{Demand, DemandReply, Provider};
use crate::error::SessionError;
use crate::mechanism::Authentication;
use crate::property::{AuthId, AuthzId, Password};
use crate::session::{MechanismData, MessageSent, State};
#[derive(Debug)]
pub struct PlainProvider<'a> {
pub authcid: &'a str,
pub authzid: &'a str,
pub password: &'a [u8],
}
impl<'a> Provider<'a> for PlainProvider<'a> {
fn provide(&self, req: &mut Demand<'a>) -> DemandReply<()> {
req.provide_ref::<AuthId>(self.authcid)?
.provide_ref::<Password>(self.password)?
.provide_ref::<AuthzId>(self.authzid)?
.done()
}
}
pub struct Plain;
impl Authentication for Plain {
#[allow(clippy::similar_names)]
fn step(
&mut self,
session: &mut MechanismData,
input: Option<&[u8]>,
_writer: &mut dyn Write,
) -> Result<State, SessionError> {
let input = input.ok_or(SessionError::InputDataRequired)?;
if input.len() < 4 {
return Err(PlainError::BadFormat.into());
}
let mut split = input.split(|byte| *byte == 0);
let authzid: &[u8] = split.next().ok_or(PlainError::BadFormat)?;
let authcid: &[u8] = split.next().ok_or(PlainError::BadFormat)?;
let password: &[u8] = split.next().ok_or(PlainError::BadFormat)?;
if split.next().is_some() {
return Err(PlainError::BadFormat.into());
}
let authzid = core::str::from_utf8(authzid).map_err(PlainError::BadAuthzid)?;
let authcid = core::str::from_utf8(authcid).map_err(PlainError::BadAuthcid)?;
let authcid = saslprep(authcid).map_err(PlainError::Saslprep)?;
if authcid.is_empty() {
return Err(PlainError::Empty.into());
}
if let Ok(password) = core::str::from_utf8(password) {
let password = saslprep(password).map_err(PlainError::Saslprep)?;
if password.is_empty() {
return Err(PlainError::Empty.into());
}
let provider = PlainProvider {
authzid,
authcid: authcid.as_ref(),
password: password.as_bytes(),
};
session.validate(&provider)?;
} else {
if password.is_empty() {
return Err(PlainError::Empty.into());
}
let provider = PlainProvider {
authzid,
authcid: authcid.as_ref(),
password,
};
session.validate(&provider)?;
};
Ok(State::Finished(MessageSent::No))
}
}