dittolive_ditto/identity/
mod.rs

1//! # Ditto needs an `Identity` to start syncing with other peers.
2//!
3//! The various identity configurations that you can use when initializing a `Ditto` instance.
4//!
5//! - [`OfflinePlayground`]: Develop peer-to-peer apps with no cloud connection. This mode offers no
6//!   security and must only be used for development.
7//! - [`OnlineWithAuthentication`]: Run Ditto in secure production mode, logging on to Ditto Cloud
8//!   or on on-premises authentication server. User permissions are centrally managed.
9//! - [`OnlinePlayground`]: Test a Ditto Cloud app with weak shared token authentication
10//!   ("Playground mode"). This mode is not secure and must only be used for development.
11//! - [`SharedKey`]: A mode where any device is trusted provided they  know the secret key. This is
12//!   a simplistic authentication model normally only suitable for private apps where users and
13//!   devices are both trusted.
14//! - [`Manual`]: A Ditto peer identity that was created manually rather than generated by an
15//!   identity service. This is a text bundle beginning with the line `-----BEGIN DITTO
16//!   IDENTITY-----`. To learn how to create and use Manual Identities please refer to Ditto's
17//!   online documentation.
18
19use std::sync::{Arc, Mutex};
20
21use ffi_sdk::BoxedIdentityConfig;
22
23use crate::{
24    ditto::AppId,
25    error::{DittoError, ErrorKind},
26};
27use_prelude!();
28
29// TODO(v5): Remove pub
30#[doc(hidden)]
31pub mod auth;
32
33use self::auth::LoginProvider;
34pub use self::auth::{
35    AuthenticationClientFeedback, DittoAuthenticationEventHandler, DittoAuthenticator,
36};
37
38// Identity is pub because it is part of the Builder's public interface (and technically
39// this very path: `::dittolive_ditto::identity::Identity`).
40// It is therefore Sealed to prevent downstream implementations
41#[doc(hidden)]
42#[deprecated(
43    note = "Identity types are removed in SDK 5.0. Use `DittoConfig` with `Ditto::open(config)` \
44            or `Ditto::open_sync(config)` instead."
45)]
46pub trait Identity: private::Sealed + Send + Sync {
47    /// Consumes the identity's built IdentityConfig
48    fn identity_config(&self) -> Option<BoxedIdentityConfig>;
49
50    /// Returns whether a `DittoAuthenticator` is to be involved.
51    fn involves_authenticator(&self) -> bool {
52        false
53    }
54
55    /// REFACTOR(Ham): This is necessary for a couple of reasons:
56    ///
57    /// we only want to pass down our login provider down into the Rust core once we've had a
58    /// chance to make the FFI `Ditto` object and store a reference to it in the
59    /// `DittoAuthenticator`
60    fn set_login_provider_with_ditto_pointer(
61        &self,
62        _ditto_fields: &Arc<crate::ditto::DittoFields>,
63    ) {
64    }
65
66    /// Indicates if cloud sync should be enabled by default
67    fn is_cloud_sync_enabled(&self) -> bool;
68
69    /// Returns the configured URL for Auth
70    fn auth_url(&self) -> Result<String, DittoError>;
71
72    // TODO(v5): This shouldn't be necessary now that we're using the "transports behind core"
73    // implementation to manage transports
74    /// Returns the configured URL for WebSocket sync
75    fn sync_url(&self) -> Result<String, DittoError>;
76
77    /// Indicates whether the specific Identity type requires an offline only license token to be
78    /// set.
79    fn requires_offline_only_license_token(&self) -> bool;
80}
81
82/// Run Ditto in secure production mode, logging on to Ditto Cloud or an
83/// on-premises authentication server. User permissions are centrally managed.
84/// Sync will not work until a successful login has occurred.
85///
86/// The only required configuration is the application's UUID, which can be
87/// found on the Ditto portal where the app is registered.
88///
89/// By default cloud sync is enabled. This means the SDK will sync to a Big Peer
90/// in Ditto's cloud when an internet connection is available. This is
91/// controlled by the `enable_cloud_sync` parameter. If `true` (default), a
92/// suitable wss:// URL will be added to the [`TransportConfig`].
93///
94/// To prevent cloud sync, or to specify your own URL later, pass `false`.
95///
96/// Authentication requests are handled by Ditto's cloud by default, configured
97/// in the portal at `<https://portal.ditto.live>`.
98///
99/// To use a different or on-premises authentication service, pass a custom
100/// HTTPS base URL as the * `auth_url` parameter.
101#[deprecated(
102    note = "Identity types are removed in SDK 5.0. Use `DittoConfig` with `Ditto::open(config)` \
103            or `Ditto::open_sync(config)` instead."
104)]
105pub struct OnlineWithAuthentication {
106    app_id: AppId,
107    enable_cloud_sync: bool,
108    auth_url: String,
109    identity_config: Mutex<Option<BoxedIdentityConfig>>,
110    auth_event_handler: Arc<dyn 'static + DittoAuthenticationEventHandler>,
111}
112
113#[doc(hidden)]
114#[deprecated(note = "use OnlinePlayground instead")]
115#[allow(deprecated)]
116pub type OnlinePlaygroundV2 = OnlinePlayground;
117
118/// Test a Ditto Cloud app with a simple shared token ("Playground mode"). This
119/// mode offers no security and must only be used for development. Other
120/// behavior mirrors the [`OnlineWithAuthentication`] identity.
121#[deprecated(
122    note = "Identity types are removed in SDK 5.0. Use `DittoConfig` with `Ditto::open(config)` \
123            or `Ditto::open_sync(config)` instead."
124)]
125pub struct OnlinePlayground {
126    app_id: AppId,
127    enable_cloud_sync: bool,
128    auth_url: String,
129    identity_config: Mutex<Option<BoxedIdentityConfig>>,
130}
131
132/// Develop peer-to-peer apps with no cloud connection. This mode offers no
133/// security and must only be used for development. In this mode, any string can
134/// be used as the name of the app.
135#[deprecated(
136    note = "Identity types are removed in SDK 5.0. Use `DittoConfig` with `Ditto::open(config)` \
137            or `Ditto::open_sync(config)` instead."
138)]
139pub struct OfflinePlayground {
140    identity_config: Mutex<Option<BoxedIdentityConfig>>,
141}
142
143/// An identity where any device is trusted provided they know the secret key.
144/// This is a simplistic authentication model normally only suitable for private
145/// apps where users and devices are both trusted. In this mode, any string may
146/// be used as the app id.
147#[deprecated(
148    note = "Identity types are removed in SDK 5.0. Use `DittoConfig` with `Ditto::open(config)` \
149            or `Ditto::open_sync(config)` instead."
150)]
151pub struct SharedKey {
152    identity_config: Mutex<Option<BoxedIdentityConfig>>,
153}
154
155/// An identity where devices are manually configured with a x509 certificate
156/// bundle
157#[deprecated(
158    note = "Identity types are removed in SDK 5.0. Use `DittoConfig` with `Ditto::open(config)` \
159            or `Ditto::open_sync(config)` instead."
160)]
161pub struct Manual {
162    identity_config: Mutex<Option<BoxedIdentityConfig>>,
163}
164
165#[allow(deprecated)]
166impl OnlineWithAuthentication {
167    /// Construct an OnlineWithAuthentication Identity.
168    ///
169    /// - `ditto_root`: The directory containing ditto local storage
170    /// - `app_id`: The unique identifier of the application, usually a UUIDv4 string
171    /// - `auth_event_handler`: A user created type which implements the
172    ///   `DittoAuthenticationEventHandler` trait
173    /// - `enable_cloud_sync`: whether cloud sync should be started by default
174    /// - `custom_auth_url`: an optional custom authentication URL; default is is `cloud.ditto.live`
175    pub fn new(
176        _ditto_root: Arc<dyn DittoRoot>, // TODO: Remove this in a future major release (v5)
177        app_id: AppId,
178        auth_event_handler: impl DittoAuthenticationEventHandler + 'static,
179        enable_cloud_sync: bool,
180        custom_auth_url: Option<&str>,
181    ) -> Result<Self, DittoError> {
182        let auth_url = match custom_auth_url {
183            Some(url) => url.to_owned(),
184            None => app_id.default_auth_url(),
185        };
186        let c_app_id = char_p::new(app_id.to_string());
187        let c_auth_url = char_p::new(auth_url.as_str());
188
189        let identity_config = Mutex::new(Some(
190            ffi_sdk::ditto_identity_config_make_online_with_authentication(
191                c_app_id.as_ref(),
192                c_auth_url.as_ref(),
193            )
194            .ok_or(ErrorKind::Config)?,
195        ));
196
197        Ok(OnlineWithAuthentication {
198            app_id,
199            enable_cloud_sync,
200            auth_url,
201            identity_config,
202            auth_event_handler: Arc::new(auth_event_handler),
203        })
204    }
205}
206
207#[allow(deprecated)]
208impl Identity for OnlineWithAuthentication {
209    fn identity_config(&self) -> Option<BoxedIdentityConfig> {
210        let mut config = self.identity_config.lock().unwrap();
211        config.take()
212    }
213
214    fn involves_authenticator(&self) -> bool {
215        true
216    }
217
218    fn set_login_provider_with_ditto_pointer(&self, ditto_fields: &Arc<crate::ditto::DittoFields>) {
219        let authenticator = DittoAuthenticator {
220            ditto_fields: Arc::downgrade(ditto_fields),
221        };
222        let auth_event_handler = self.auth_event_handler.retain();
223        let LoginProvider(c_provider) = LoginProvider::new(auth_event_handler, authenticator);
224        ffi_sdk::ditto_auth_set_login_provider(&ditto_fields.ditto, Some(c_provider));
225    }
226
227    fn is_cloud_sync_enabled(&self) -> bool {
228        self.enable_cloud_sync
229    }
230
231    fn auth_url(&self) -> Result<String, DittoError> {
232        Ok(self.auth_url.to_owned())
233    }
234
235    fn sync_url(&self) -> Result<String, DittoError> {
236        Ok(self.app_id.default_sync_url())
237    }
238
239    fn requires_offline_only_license_token(&self) -> bool {
240        false
241    }
242}
243
244#[allow(deprecated)]
245impl OnlinePlayground {
246    /// Construct a new `OnlinePlayground` identity.
247    ///
248    /// - `ditto_root`: Instance of DittoRoot indicating local storage directory
249    /// - `app_id`: A unique AppId which must be a valid UUIDv4
250    /// - `shared_token`: A shared token used to set up the `OnlinePlayground` session. This token
251    ///   is provided by the portal when setting up the application.
252    /// - `enable_cloud_sync`: Should WebSocket sync with `wss://<app_id>.cloud.ditto.live` be
253    ///   enabled by default. Do not enable this if you want to provide a custom sync URL later
254    /// - `custom_auth_url`: An optional alternative URL for authentication requests. Defaults to `https://<app_id>.cloud.ditto.live/`
255    pub fn new(
256        _ditto_root: Arc<dyn DittoRoot>, // TODO: Remove this in a future major release (v5)
257        app_id: AppId,
258        shared_token: String,
259        enable_cloud_sync: bool,
260        custom_auth_url: Option<&str>,
261    ) -> Result<Self, DittoError> {
262        let c_app_id = app_id.to_c_string();
263        let c_shared_token = char_p::new(shared_token.as_str());
264        let auth_url = match custom_auth_url {
265            Some(url) => url.to_owned(),
266            None => app_id.default_auth_url(),
267        };
268        let c_auth_url = char_p::new(auth_url.as_str());
269        let identity_config = Mutex::new(Some(
270            ffi_sdk::ditto_identity_config_make_online_playground(
271                c_app_id.as_ref(),
272                c_shared_token.as_ref(),
273                c_auth_url.as_ref(),
274            )
275            .ok_or(ErrorKind::Config)?,
276        ));
277
278        Ok(Self {
279            app_id,
280            enable_cloud_sync,
281            auth_url,
282            identity_config,
283        })
284    }
285}
286
287#[allow(deprecated)]
288impl Identity for OnlinePlayground {
289    fn identity_config(&self) -> Option<BoxedIdentityConfig> {
290        let mut config = self.identity_config.lock().unwrap();
291        config.take()
292    }
293
294    fn involves_authenticator(&self) -> bool {
295        true
296    }
297
298    fn auth_url(&self) -> Result<String, DittoError> {
299        Ok(self.auth_url.to_owned())
300    }
301
302    fn sync_url(&self) -> Result<String, DittoError> {
303        Ok(self.app_id.default_sync_url())
304    }
305
306    fn is_cloud_sync_enabled(&self) -> bool {
307        self.enable_cloud_sync
308    }
309
310    fn requires_offline_only_license_token(&self) -> bool {
311        false
312    }
313}
314
315#[allow(deprecated)]
316impl OfflinePlayground {
317    /// Construct an `OfflinePlayground` identity.
318    ///
319    /// - `ditto_root`: Location for the local database
320    /// - `app_id`: A unique string identifying the App. For an `OfflinePlayground` identity this
321    ///   does not need to be a UUIDv4.
322    // TODO: Remove `ditto_root` in a future major release (v5)
323    pub fn new(_ditto_root: Arc<dyn DittoRoot>, app_id: AppId) -> Result<Self, DittoError> {
324        let c_app_id = app_id.to_c_string();
325        let identity_config = Mutex::new(Some(
326            ffi_sdk::ditto_identity_config_make_offline_playground(c_app_id.as_ref(), 0)
327                .ok_or(ErrorKind::Config)?,
328        ));
329        Ok(Self { identity_config })
330    }
331
332    /// Generate an `OfflinePlayground` identity with a random AppId for testing
333    pub fn random(ditto_root: Arc<dyn DittoRoot>) -> Result<Self, DittoError> {
334        let app_id = AppId::generate();
335        Self::new(ditto_root, app_id)
336    }
337}
338
339#[allow(deprecated)]
340impl Identity for OfflinePlayground {
341    fn identity_config(&self) -> Option<BoxedIdentityConfig> {
342        let mut config = self.identity_config.lock().unwrap();
343        config.take()
344    }
345
346    fn is_cloud_sync_enabled(&self) -> bool {
347        false
348    }
349
350    fn auth_url(&self) -> Result<String, DittoError> {
351        Err(DittoError::new(
352            ErrorKind::InvalidInput,
353            "Cloud Auth is not enabled for OfflinePlayground Identities".to_string(),
354        ))
355    }
356    fn sync_url(&self) -> Result<String, DittoError> {
357        Err(DittoError::new(
358            ErrorKind::InvalidInput,
359            "Cloud sync is not enabled for OfflinePlayground Identities".to_string(),
360        ))
361    }
362
363    fn requires_offline_only_license_token(&self) -> bool {
364        true
365    }
366}
367
368#[allow(deprecated)]
369impl SharedKey {
370    /// Construct a `SharedKey` identity.
371    ///
372    /// - `ditto_root`: An instance of `DittoRoot` for local storage
373    /// - `app_id`: The unique UUIDv4 identifying this app; must be shared across all instances
374    /// - `key_der_b64`: A pre-shared key for securing peer-to-peer communication encoded as a
375    ///   Base64 string
376    pub fn new(
377        _ditto_root: Arc<dyn DittoRoot>, // TODO: Remove this in a future major release (v5)
378        app_id: AppId,
379        key_der_b64: &str,
380    ) -> Result<Self, DittoError> {
381        let c_app_id = app_id.to_c_string();
382        let c_key_der = char_p::new(key_der_b64);
383        let identity_config = Mutex::new(Some(
384            ffi_sdk::ditto_identity_config_make_shared_key(
385                c_app_id.as_ref(),
386                c_key_der.as_ref(),
387                0,
388            )
389            .ok_or(ErrorKind::Config)?,
390        ));
391        Ok(Self { identity_config })
392    }
393}
394
395#[allow(deprecated)]
396impl Identity for SharedKey {
397    fn identity_config(&self) -> Option<BoxedIdentityConfig> {
398        let mut config = self.identity_config.lock().unwrap();
399        config.take()
400    }
401
402    fn auth_url(&self) -> Result<String, DittoError> {
403        let msg = "SharedKey Identities don't support auth urls".to_owned();
404        Err(DittoError::new(ErrorKind::Config, msg))
405    }
406
407    fn sync_url(&self) -> Result<String, DittoError> {
408        let msg = "SharedKey Identities to support cloud sync".to_owned();
409        Err(DittoError::new(ErrorKind::Config, msg))
410    }
411
412    fn is_cloud_sync_enabled(&self) -> bool {
413        false
414    }
415
416    fn requires_offline_only_license_token(&self) -> bool {
417        true
418    }
419}
420
421#[allow(deprecated)]
422impl Manual {
423    /// Construct a `Manual` identity.
424    ///
425    /// A Ditto peer identity that was created manually rather than generated by an identity
426    /// service. This is a text bundle beginning with the line "-----BEGIN DITTO IDENTITY-----".
427    /// To learn how to create and use Manual Identities please refer to Ditto's online
428    /// documentation.
429    ///
430    /// - `manual_identity`: a multiline string containing a Manual Identity bundle
431    pub fn new(manual_identity: impl Into<String>) -> Result<Self, DittoError> {
432        let manual_identity_str = char_p::new(manual_identity.into());
433        let identity_config = Mutex::new(Some(
434            ffi_sdk::ditto_identity_config_make_manual(manual_identity_str.as_ref())
435                .ok_or(ErrorKind::Config)?,
436        ));
437        Ok(Self { identity_config })
438    }
439}
440
441#[allow(deprecated)]
442impl Identity for Manual {
443    fn identity_config(&self) -> Option<BoxedIdentityConfig> {
444        let mut config = self.identity_config.lock().unwrap();
445        config.take()
446    }
447
448    fn auth_url(&self) -> Result<String, DittoError> {
449        let msg = "Manual Identities don't support auth urls".to_owned();
450        Err(DittoError::new(ErrorKind::Config, msg))
451    }
452
453    fn sync_url(&self) -> Result<String, DittoError> {
454        let msg = "Manual Identities to support cloud sync".to_owned();
455        Err(DittoError::new(ErrorKind::Config, msg))
456    }
457
458    fn is_cloud_sync_enabled(&self) -> bool {
459        false
460    }
461
462    fn requires_offline_only_license_token(&self) -> bool {
463        true
464    }
465}
466
467mod private {
468    use super::*;
469    pub trait Sealed {}
470    #[allow(deprecated)]
471    impl Sealed for OnlineWithAuthentication {}
472    #[allow(deprecated)]
473    impl Sealed for OnlinePlayground {}
474    #[allow(deprecated)]
475    impl Sealed for OfflinePlayground {}
476    #[allow(deprecated)]
477    impl Sealed for SharedKey {}
478    #[allow(deprecated)]
479    impl Sealed for Manual {}
480}