use std::{
os::raw::{c_uint, c_void},
pin::Pin,
sync::{Arc, Mutex},
time::Duration,
};
use ffi_sdk::BoxedLoginProvider;
use crate::{auth::authenticator::DittoAuthenticator, utils::prelude::*};
pub trait DittoAuthenticationEventHandler: Send + Sync {
fn authentication_required(&self, auth: DittoAuthenticator);
fn authentication_expiring_soon(&self, auth: DittoAuthenticator, seconds_remaining: Duration);
}
pub(crate) struct LoginProvider {
pub(crate) _provider: BoxedLoginProvider,
pub(crate) ctx: Arc<Mutex<LoginProviderCtx>>,
}
pub(crate) struct LoginProviderCtx {
auth_event_handler: Pin<Box<dyn DittoAuthenticationEventHandler + 'static>>,
authenticator: Option<DittoAuthenticator>,
#[allow(dead_code)] cached_expiry_time: Option<u32>,
}
impl LoginProvider {
pub fn new(handler: Box<dyn DittoAuthenticationEventHandler + 'static>) -> Self {
let ctx = LoginProviderCtx {
auth_event_handler: handler.into(),
authenticator: None,
cached_expiry_time: None,
};
let arc_ctx = Arc::new(Mutex::new(ctx));
let raw_context = Arc::as_ptr(&arc_ctx) as *mut c_void;
let c_provider = unsafe {
ffi_sdk::ditto_auth_client_make_login_provider(
raw_context,
Some(LoginProviderCtx::retain),
Some(LoginProviderCtx::release),
LoginProviderCtx::authentication_expiring,
)
};
LoginProvider {
_provider: c_provider,
ctx: arc_ctx,
}
}
}
impl LoginProviderCtx {
pub(crate) extern "C" fn retain(ctx: *mut c_void) {
let ptr = ctx.cast::<Mutex<LoginProviderCtx>>();
unsafe { Arc::increment_strong_count(ptr) }; }
pub(crate) extern "C" fn release(ctx: *mut c_void) {
let ptr = ctx.cast::<Mutex<LoginProviderCtx>>();
unsafe {
Arc::decrement_strong_count(ptr);
} }
pub(crate) extern "C" fn authentication_expiring(ctx: *mut c_void, seconds_remaining: c_uint) {
let ctx_ptr: *const Mutex<LoginProviderCtx> = ctx.cast();
let arc_ctx: &Mutex<LoginProviderCtx> = unsafe { &*ctx_ptr }; let mut ctx_ref = arc_ctx.lock().expect("LoginProvider Mutex is poisoned"); match &ctx_ref.authenticator {
Some(authn) => {
if seconds_remaining == 0 {
ctx_ref
.auth_event_handler
.authentication_required(authn.retain());
} else {
let duration = Duration::from_secs(seconds_remaining.into());
ctx_ref
.auth_event_handler
.authentication_expiring_soon(authn.retain(), duration);
}
}
None => ctx_ref.cached_expiry_time = Some(seconds_remaining),
}
}
pub(crate) fn set_authenticator(&mut self, authenticator: DittoAuthenticator) {
self.authenticator = Some(authenticator);
if let Some(authn) = &self.authenticator {
if let Some(time) = self.cached_expiry_time {
if time == 0 {
self.auth_event_handler
.authentication_required(authn.retain());
} else {
let duration = Duration::from_secs(time.into());
self.auth_event_handler
.authentication_expiring_soon(authn.retain(), duration);
}
self.cached_expiry_time = None; }
}
}
}