use std::sync::{Arc, Mutex};
use ffi_sdk::BoxedIdentityConfig;
use crate::{
auth::login_provider::{DittoAuthenticationEventHandler, LoginProvider},
ditto::AppId,
error::{DittoError, ErrorKind},
};
use_prelude!();
pub(crate) type SharedIdentity = Arc<dyn Identity>;
#[doc(hidden)]
pub trait Identity: private::Sealed + Send + Sync {
fn identity_config(&self) -> Option<BoxedIdentityConfig>;
fn authenticator(&self) -> Option<DittoAuthenticator> {
None
}
fn set_login_provider(&self, _authenticator: Option<DittoAuthenticator>) {}
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 {
app_id: AppId,
enable_cloud_sync: bool,
auth_url: String,
identity_config: Mutex<Option<BoxedIdentityConfig>>,
authenticator: DittoAuthenticator,
auth_event_handler: Mutex<Option<Box<dyn DittoAuthenticationEventHandler + 'static>>>,
}
#[deprecated(note = "use OnlinePlayground instead")]
pub type OnlinePlaygroundV2 = OnlinePlayground;
pub struct OnlinePlayground {
app_id: AppId,
enable_cloud_sync: bool,
auth_url: String,
identity_config: Mutex<Option<BoxedIdentityConfig>>,
authenticator: DittoAuthenticator,
}
pub struct OfflinePlayground {
identity_config: Mutex<Option<BoxedIdentityConfig>>,
}
pub struct SharedKey {
identity_config: Mutex<Option<BoxedIdentityConfig>>,
}
pub struct Manual {
identity_config: Mutex<Option<BoxedIdentityConfig>>,
}
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_app_id = char_p::new(app_id.to_string());
let c_auth_url = char_p::new(auth_url.as_str());
let identity_config = Mutex::new(Some(
ffi_sdk::ditto_identity_config_make_online_with_authentication(
c_app_id.as_ref(),
c_auth_url.as_ref(),
)
.ok_or(ErrorKind::Config)?,
));
let authenticator = DittoAuthenticator::new();
Ok(OnlineWithAuthentication {
app_id,
enable_cloud_sync,
auth_url,
identity_config,
authenticator,
auth_event_handler: Mutex::new(Some(Box::new(auth_event_handler))),
})
}
}
impl Identity for OnlineWithAuthentication {
fn identity_config(&self) -> Option<BoxedIdentityConfig> {
let mut config = self.identity_config.lock().unwrap();
config.take()
}
fn authenticator(&self) -> Option<DittoAuthenticator> {
Some(self.authenticator.retain())
}
fn set_login_provider(&self, authenticator: Option<DittoAuthenticator>) {
let mut auth_event_handler = self.auth_event_handler.lock().unwrap();
let auth_event_handler = auth_event_handler
.take()
.expect("auth event handler is some");
let login_provider = LoginProvider::new(auth_event_handler);
let LoginProvider {
_provider: c_provider,
ctx,
} = login_provider;
let authenticator = authenticator.expect("authenticator is some");
let ditto = authenticator
.ditto_fields
.upgrade()
.expect("ditto fields is alive")
.ditto
.retain();
ctx.lock().unwrap().set_authenticator(authenticator);
ffi_sdk::ditto_auth_set_login_provider(&ditto, c_provider);
}
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 requires_offline_only_license_token(&self) -> bool {
false
}
}
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_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 identity_config = Mutex::new(Some(
ffi_sdk::ditto_identity_config_make_online_playground(
c_app_id.as_ref(),
c_shared_token.as_ref(),
c_auth_url.as_ref(),
)
.ok_or(ErrorKind::Config)?,
));
let authenticator = DittoAuthenticator::new();
Ok(Self {
app_id,
enable_cloud_sync,
auth_url,
identity_config,
authenticator,
})
}
}
impl Identity for OnlinePlayground {
fn identity_config(&self) -> Option<BoxedIdentityConfig> {
let mut config = self.identity_config.lock().unwrap();
config.take()
}
fn authenticator(&self) -> Option<DittoAuthenticator> {
Some(self.authenticator.retain())
}
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 is_cloud_sync_enabled(&self) -> bool {
self.enable_cloud_sync
}
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_app_id = app_id.to_c_string();
let identity_config = Mutex::new(Some(
ffi_sdk::ditto_identity_config_make_offline_playground(c_app_id.as_ref(), 0)
.ok_or(ErrorKind::Config)?,
));
Ok(Self { identity_config })
}
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 identity_config(&self) -> Option<BoxedIdentityConfig> {
let mut config = self.identity_config.lock().unwrap();
config.take()
}
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 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_app_id = app_id.to_c_string();
let c_key_der = char_p::new(key_der_b64);
let identity_config = Mutex::new(Some(
ffi_sdk::ditto_identity_config_make_shared_key(
c_app_id.as_ref(),
c_key_der.as_ref(),
0,
)
.ok_or(ErrorKind::Config)?,
));
Ok(Self { identity_config })
}
}
impl Identity for SharedKey {
fn identity_config(&self) -> Option<BoxedIdentityConfig> {
let mut config = self.identity_config.lock().unwrap();
config.take()
}
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 is_cloud_sync_enabled(&self) -> bool {
false
}
fn requires_offline_only_license_token(&self) -> bool {
true
}
}
impl Manual {
pub fn new(manual_identity: impl Into<String>) -> Result<Self, DittoError> {
let manual_identity_str = char_p::new(manual_identity.into());
let identity_config = Mutex::new(Some(
ffi_sdk::ditto_identity_config_make_manual(manual_identity_str.as_ref())
.ok_or(ErrorKind::Config)?,
));
Ok(Self { identity_config })
}
}
impl Identity for Manual {
fn identity_config(&self) -> Option<BoxedIdentityConfig> {
let mut config = self.identity_config.lock().unwrap();
config.take()
}
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 is_cloud_sync_enabled(&self) -> bool {
false
}
fn requires_offline_only_license_token(&self) -> bool {
true
}
}
mod private {
use super::*;
pub trait Sealed {}
impl Sealed for OnlineWithAuthentication {}
impl Sealed for OnlinePlayground {}
impl Sealed for OfflinePlayground {}
impl Sealed for SharedKey {}
impl Sealed for Manual {}
}