use crate::alloc::{boxed::Box, string::String};
use crate::callback::SessionCallback;
use crate::error::SASLError;
use crate::registry::{Mechanism, MechanismIter};
use crate::session::SessionData;
use alloc::sync::Arc;
use core::fmt;
#[doc(inline)]
#[cfg(feature = "config_builder")]
pub use crate::builder::ConfigBuilder;
use crate::mechanism::Authentication;
use crate::mechname::Mechname;
trait ConfigInstance: fmt::Debug + Send + Sync {
fn get_mech_iter<'a>(&self) -> MechanismIter<'a>;
fn get_callback(&self) -> &dyn SessionCallback;
fn select<'a>(
&self,
cb: bool,
offered: &mut dyn Iterator<Item = &'a Mechname>,
) -> Result<(Box<dyn Authentication>, &'static Mechanism), SASLError>;
}
#[repr(transparent)]
pub struct SASLConfig {
inner: dyn ConfigInstance + Send + Sync,
}
impl fmt::Debug for SASLConfig {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}
#[cfg(any(test, feature = "provider", feature = "testutils"))]
mod provider {
use super::{Box, Mechanism, SASLConfig, SASLError, SessionCallback};
use crate::mechanism::Authentication;
use crate::mechname::Mechname;
impl SASLConfig {
#[inline(always)]
pub(crate) fn select_mechanism<'a>(
&self,
offered: impl IntoIterator<Item = &'a Mechname>,
) -> Result<(Box<dyn Authentication>, &'static Mechanism), SASLError> {
let cb = self.get_callback().enable_channel_binding();
self.inner.select(cb, &mut offered.into_iter())
}
#[inline(always)]
pub(crate) fn get_callback(&self) -> &dyn SessionCallback {
self.inner.get_callback()
}
}
}
impl SASLConfig {
#[inline(always)]
pub(crate) fn mech_list<'a>(&self) -> impl Iterator<Item = &'a Mechanism> {
self.inner.get_mech_iter()
}
}
#[cfg(feature = "config_builder")]
mod instance {
use super::{
fmt, Arc, Box, ConfigInstance, Mechanism, MechanismIter, SASLConfig, SASLError,
SessionCallback, SessionData, String,
};
pub use crate::builder::ConfigBuilder;
use crate::callback::Request;
use crate::context::Context;
use crate::error::SessionError;
use crate::mechanism::Authentication;
use crate::mechname::Mechname;
use crate::property::{AuthId, AuthzId, Password};
use crate::registry::Registry;
impl SASLConfig {
fn cast(arc: Arc<dyn ConfigInstance>) -> Arc<Self> {
unsafe { core::mem::transmute(arc) }
}
pub(crate) fn new<CB: SessionCallback + 'static>(
callback: CB,
mechanisms: Registry,
) -> Result<Arc<Self>, SASLError> {
let inner = Inner::new(callback, mechanisms)?;
let outer = Arc::new(inner) as Arc<dyn ConfigInstance>;
Ok(Self::cast(outer))
}
#[must_use]
pub const fn builder() -> ConfigBuilder {
ConfigBuilder::new()
}
#[allow(clippy::similar_names)]
pub fn with_credentials(
authzid: Option<String>,
authid: String,
password: String,
) -> Result<Arc<Self>, SASLError> {
struct CredentialsProvider {
authid: String,
password: String,
authzid: Option<String>,
}
impl SessionCallback for CredentialsProvider {
fn callback(
&self,
_session_data: &SessionData,
_context: &Context,
request: &mut Request<'_>,
) -> Result<(), SessionError> {
request
.satisfy::<AuthId>(self.authid.as_str())?
.satisfy::<Password>(self.password.as_bytes())?;
if let Some(authzid) = self.authzid.as_deref() {
request.satisfy::<AuthzId>(authzid)?;
}
Ok(())
}
}
let has_authzid = authzid.is_some();
let callback = CredentialsProvider {
authid,
password,
authzid,
};
Self::builder()
.with_credentials_mechanisms(has_authzid)
.with_callback(callback)
}
}
struct Inner {
cb: bool,
callback: Box<dyn SessionCallback>,
mechanisms: Registry,
}
impl fmt::Debug for Inner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SASLConfig")
.field("channel_binding", &self.cb)
.field("mechanisms", &self.mechanisms)
.finish()
}
}
#[allow(clippy::unnecessary_wraps)]
#[cfg(any(feature = "config_builder", feature = "testutils"))]
impl Inner {
pub(crate) fn new<CB: SessionCallback + 'static>(
callback: CB,
mechanisms: Registry,
) -> Result<Self, SASLError> {
Ok(Self {
cb: false, callback: Box::new(callback),
mechanisms,
})
}
}
impl ConfigInstance for Inner {
fn get_mech_iter<'a>(&self) -> MechanismIter<'a> {
self.mechanisms.get_mechanisms()
}
fn get_callback(&self) -> &dyn SessionCallback {
self.callback.as_ref()
}
fn select<'a>(
&self,
cb: bool,
offered: &mut dyn Iterator<Item = &'a Mechname>,
) -> Result<(Box<dyn Authentication>, &'static Mechanism), SASLError> {
let callback = self.get_callback();
self.mechanisms.select(cb | self.cb, offered, |acc, mech| {
callback.prefer(acc, mech)
})
}
}
}
#[cfg(test)]
mod tests {
use super::*;
static_assertions::assert_impl_all!(SASLConfig: Send, Sync);
}