use std::sync::{Arc, RwLock};
use ffi_sdk::BoxedAuthClient;
use tokio::runtime::Handle;
use crate::{
auth::{DittoAuthenticationEventHandler, DittoAuthenticator, LoginProvider, ValidityListener},
ditto::AppId,
error::{DittoError, ErrorKind},
transport::TransportSync,
};
use_prelude!();
pub type SharedIdentity = Arc<dyn Identity>;
pub trait Identity: private::Sealed + Send + Sync {
fn site_id(&self) -> SiteId {
unsafe { ffi_sdk::ditto_auth_client_get_site_id(&self.auth_client()) }
}
fn auth_client(&self) -> Arc<BoxedAuthClient>;
fn authenticator(&self) -> Option<DittoAuthenticator>;
fn make_listener(
&self,
transports: Arc<RwLock<TransportSync>>,
) -> Option<Arc<ValidityListener>>;
fn app_id(&self) -> AppId {
AppId(unsafe { ffi_sdk::ditto_auth_client_get_app_id(&self.auth_client()) }.into_string())
}
fn is_web_valid(&self) -> bool;
fn is_x509_valid(&self) -> bool;
fn is_cloud_sync_enabled(&self) -> bool;
fn auth_url(&self) -> Result<String, DittoError>;
fn sync_url(&self) -> Result<String, DittoError>;
fn requires_offline_only_license_token(&self) -> bool;
}
pub struct OnlineWithAuthentication {
auth: DittoAuthenticator,
app_id: AppId,
enable_cloud_sync: bool,
auth_url: String,
}
#[deprecated(note = "use OnlinePlayground instead")]
pub type OnlinePlaygroundV2 = OnlinePlayground;
pub struct OnlinePlayground {
auth: DittoAuthenticator,
app_id: AppId,
enable_cloud_sync: bool,
auth_url: String,
}
pub struct SharedKey {
auth: DittoAuthenticator,
}
pub struct Manual {
auth: DittoAuthenticator,
}
pub struct OfflinePlayground {
auth: DittoAuthenticator,
}
impl OnlineWithAuthentication {
pub fn new(
ditto_root: Arc<dyn DittoRoot>,
app_id: AppId,
auth_event_handler: impl DittoAuthenticationEventHandler + 'static,
enable_cloud_sync: bool,
custom_auth_url: Option<&str>,
) -> Result<Self, DittoError> {
let auth_url = match custom_auth_url {
Some(url) => url.to_owned(),
None => app_id.default_auth_url(),
};
let c_working_dir = ditto_root.data_dir_to_c_str()?;
let c_app_id = char_p::new(app_id.to_string());
let c_auth_url = char_p::new(auth_url.as_str());
let login_provider = LoginProvider::new(auth_event_handler);
let LoginProvider {
_provider: c_provider,
ctx,
} = login_provider;
let auth_client = unsafe {
let executor = make_executor();
ffi_sdk::ditto_auth_client_make_with_web_with_executor(
c_working_dir.as_ref(),
c_app_id.as_ref(),
c_auth_url.as_ref(),
Some(c_provider),
executor,
)
.ok_or(ErrorKind::Config)?
};
let authenticator = DittoAuthenticator::new(Arc::new(auth_client));
ctx.lock()
.unwrap()
.set_authenticator(authenticator.retain());
Ok(OnlineWithAuthentication {
app_id,
auth: authenticator,
enable_cloud_sync,
auth_url,
})
}
}
impl Identity for OnlineWithAuthentication {
fn auth_client(&self) -> Arc<BoxedAuthClient> {
self.auth.auth_client()
}
fn is_web_valid(&self) -> bool {
unsafe { ffi_sdk::ditto_auth_client_is_web_valid(&self.auth_client()) != 0 }
}
fn is_x509_valid(&self) -> bool {
unsafe { ffi_sdk::ditto_auth_client_is_x509_valid(&self.auth_client()) != 0 }
}
fn is_cloud_sync_enabled(&self) -> bool {
self.enable_cloud_sync
}
fn auth_url(&self) -> Result<String, DittoError> {
Ok(self.auth_url.to_owned())
}
fn sync_url(&self) -> Result<String, DittoError> {
Ok(self.app_id.default_sync_url())
}
fn authenticator(&self) -> Option<DittoAuthenticator> {
Some(self.auth.retain())
}
fn make_listener(
&self,
transports: Arc<RwLock<TransportSync>>,
) -> Option<Arc<ValidityListener>> {
Some(ValidityListener::new(transports, self.auth.auth_client()))
}
fn requires_offline_only_license_token(&self) -> bool {
false
}
}
impl OfflinePlayground {
pub fn new(ditto_root: Arc<dyn DittoRoot>, app_id: AppId) -> Result<Self, DittoError> {
let c_data_dir = ditto_root.data_dir_to_c_str()?;
let c_app_id = app_id.to_c_string();
let auth_client = unsafe {
let executor = make_executor();
ffi_sdk::ditto_auth_client_make_for_development_with_executor(
Some(c_data_dir.as_ref()),
c_app_id.as_ref(),
0,
executor,
)
.ok_or(ErrorKind::Config)?
};
let authenticator = DittoAuthenticator::new(Arc::new(auth_client));
Ok(Self {
auth: authenticator,
})
}
pub fn random(ditto_root: Arc<dyn DittoRoot>) -> Result<Self, DittoError> {
let app_id = AppId::generate();
Self::new(ditto_root, app_id)
}
}
impl Identity for OfflinePlayground {
fn site_id(&self) -> SiteId {
unsafe { ffi_sdk::ditto_auth_client_get_site_id(&self.auth_client()) }
}
fn auth_client(&self) -> Arc<BoxedAuthClient> {
self.auth.auth_client()
}
fn is_web_valid(&self) -> bool {
unsafe { ffi_sdk::ditto_auth_client_is_web_valid(&self.auth_client()) != 0 }
}
fn is_x509_valid(&self) -> bool {
unsafe { ffi_sdk::ditto_auth_client_is_x509_valid(&self.auth_client()) != 0 }
}
fn is_cloud_sync_enabled(&self) -> bool {
false
}
fn auth_url(&self) -> Result<String, DittoError> {
Err(DittoError::new(
ErrorKind::InvalidInput,
"Cloud Auth is not enabled for OfflinePlayground Identities".to_string(),
))
}
fn sync_url(&self) -> Result<String, DittoError> {
Err(DittoError::new(
ErrorKind::InvalidInput,
"Cloud sync is not enabled for OfflinePlayground Identities".to_string(),
))
}
fn authenticator(&self) -> Option<DittoAuthenticator> {
Some(self.auth.retain())
}
fn make_listener(
&self,
transports: Arc<RwLock<TransportSync>>,
) -> Option<Arc<ValidityListener>> {
Some(ValidityListener::new(transports, self.auth.auth_client()))
}
fn requires_offline_only_license_token(&self) -> bool {
true
}
}
impl SharedKey {
pub fn new(
ditto_root: Arc<dyn DittoRoot>,
app_id: AppId,
key_der_b64: &str,
) -> Result<Self, DittoError> {
let c_data_dir = ditto_root.data_dir_to_c_str()?;
let c_app_id = app_id.to_c_string();
let c_key_der = char_p::new(key_der_b64);
let auth_client = unsafe {
let executor = make_executor();
ffi_sdk::ditto_auth_client_make_with_shared_key_with_executor(
Some(c_data_dir.as_ref()),
c_app_id.as_ref(),
c_key_der.as_ref(),
0,
executor,
)
.ok_or(ErrorKind::Config)?
};
let authenticator = DittoAuthenticator::new(Arc::new(auth_client));
Ok(Self {
auth: authenticator,
})
}
}
impl Identity for SharedKey {
fn site_id(&self) -> SiteId {
unsafe { ffi_sdk::ditto_auth_client_get_site_id(&self.auth_client()) }
}
fn auth_client(&self) -> Arc<BoxedAuthClient> {
self.auth.auth_client()
}
fn is_web_valid(&self) -> bool {
unsafe { ffi_sdk::ditto_auth_client_is_web_valid(&self.auth_client()) != 0 }
}
fn is_x509_valid(&self) -> bool {
unsafe { ffi_sdk::ditto_auth_client_is_x509_valid(&self.auth_client()) != 0 }
}
fn auth_url(&self) -> Result<String, DittoError> {
let msg = "SharedKey Identities don't support auth urls".to_owned();
Err(DittoError::new(ErrorKind::Config, msg))
}
fn sync_url(&self) -> Result<String, DittoError> {
let msg = "SharedKey Identities to support cloud sync".to_owned();
Err(DittoError::new(ErrorKind::Config, msg))
}
fn authenticator(&self) -> Option<DittoAuthenticator> {
None
}
fn make_listener(
&self,
_transports: Arc<RwLock<TransportSync>>,
) -> Option<Arc<ValidityListener>> {
None
}
fn is_cloud_sync_enabled(&self) -> bool {
false
}
fn requires_offline_only_license_token(&self) -> bool {
true
}
}
impl Manual {
pub fn new(certificate_config_b64: &str) -> Result<Self, DittoError> {
let certificate_config = char_p::new(certificate_config_b64);
let auth_client = unsafe {
let executor = make_executor();
ffi_sdk::ditto_auth_client_make_with_static_x509_with_executor(
certificate_config.as_ref(),
executor,
)
.ok_or(ErrorKind::Config)?
};
let authenticator = DittoAuthenticator::new(Arc::new(auth_client));
Ok(Self {
auth: authenticator,
})
}
}
impl Identity for Manual {
fn site_id(&self) -> SiteId {
unsafe { ffi_sdk::ditto_auth_client_get_site_id(&self.auth_client()) }
}
fn auth_client(&self) -> Arc<BoxedAuthClient> {
self.auth.auth_client()
}
fn is_web_valid(&self) -> bool {
unsafe { ffi_sdk::ditto_auth_client_is_web_valid(&self.auth_client()) != 0 }
}
fn is_x509_valid(&self) -> bool {
unsafe { ffi_sdk::ditto_auth_client_is_x509_valid(&self.auth_client()) != 0 }
}
fn auth_url(&self) -> Result<String, DittoError> {
let msg = "Manual Identities don't support auth urls".to_owned();
Err(DittoError::new(ErrorKind::Config, msg))
}
fn sync_url(&self) -> Result<String, DittoError> {
let msg = "Manual Identities to support cloud sync".to_owned();
Err(DittoError::new(ErrorKind::Config, msg))
}
fn authenticator(&self) -> Option<DittoAuthenticator> {
None
}
fn make_listener(
&self,
_transports: Arc<RwLock<TransportSync>>,
) -> Option<Arc<ValidityListener>> {
None
}
fn is_cloud_sync_enabled(&self) -> bool {
false
}
fn requires_offline_only_license_token(&self) -> bool {
true
}
}
impl OnlinePlayground {
pub fn new(
ditto_root: Arc<dyn DittoRoot>,
app_id: AppId,
shared_token: String,
enable_cloud_sync: bool,
custom_auth_url: Option<&str>,
) -> Result<Self, DittoError> {
let c_data_dir = ditto_root.data_dir_to_c_str()?;
let c_app_id = app_id.to_c_string();
let c_shared_token = char_p::new(shared_token.as_str());
let auth_url = match custom_auth_url {
Some(url) => url.to_owned(),
None => app_id.default_auth_url(),
};
let c_auth_url = char_p::new(auth_url.as_str());
let auth_client = unsafe {
let executor = make_executor();
ffi_sdk::ditto_auth_client_make_anonymous_client_with_executor(
c_data_dir.as_ref(),
c_app_id.as_ref(),
c_shared_token.as_ref(),
c_auth_url.as_ref(),
executor,
)
.ok_or(ErrorKind::Config)?
};
let authenticator = DittoAuthenticator::new(Arc::new(auth_client));
Ok(Self {
app_id,
auth: authenticator,
enable_cloud_sync,
auth_url,
})
}
}
impl Identity for OnlinePlayground {
fn auth_client(&self) -> Arc<BoxedAuthClient> {
self.auth.auth_client()
}
fn is_web_valid(&self) -> bool {
unsafe { ffi_sdk::ditto_auth_client_is_web_valid(&self.auth_client()) != 0 }
}
fn is_x509_valid(&self) -> bool {
unsafe { ffi_sdk::ditto_auth_client_is_x509_valid(&self.auth_client()) != 0 }
}
fn auth_url(&self) -> Result<String, DittoError> {
Ok(self.auth_url.to_owned())
}
fn sync_url(&self) -> Result<String, DittoError> {
Ok(self.app_id.default_sync_url())
}
fn authenticator(&self) -> Option<DittoAuthenticator> {
Some(self.auth.retain())
}
fn make_listener(
&self,
transports: Arc<RwLock<TransportSync>>,
) -> Option<Arc<ValidityListener>> {
Some(ValidityListener::new(transports, self.auth.auth_client()))
}
fn is_cloud_sync_enabled(&self) -> bool {
self.enable_cloud_sync
}
fn requires_offline_only_license_token(&self) -> bool {
false
}
}
mod private {
use super::*;
pub trait Sealed {}
impl Sealed for OnlinePlayground {}
impl Sealed for Manual {}
impl Sealed for OnlineWithAuthentication {}
impl Sealed for OfflinePlayground {}
impl Sealed for SharedKey {}
}
pub(crate) fn make_executor() -> repr_c::Box<ffi_sdk::Executor> {
unsafe {
if let Ok(handle) = Handle::try_current() {
let handle = Box::new(handle);
ffi_sdk::ditto_make_executor_from_handle(handle)
} else {
ffi_sdk::ditto_make_executor_new_runtime()
}
}
}