Skip to main content

ironoxide/
blocking.rs

1//! Blocking variant of IronOxide
2//!
3//! These synchronous functions will block the current thread to execute instead
4//! of returning futures that need to be executed on a runtime. In every other way,
5//! they are identical to their asynchronous counterparts.
6//!
7//! # Optional
8//! This requires the optional `blocking` feature to be enabled.
9
10#[doc(no_inline)]
11use crate::prelude::*;
12
13use crate::{
14    InitAndRotationCheck::{NoRotationNeeded, RotationNeeded},
15    Result,
16};
17use std::{collections::HashMap, sync::Arc};
18
19#[cfg(feature = "beta")]
20use crate::search::{BlindIndexSearchInitialize, EncryptedBlindIndexSalt};
21use tokio::runtime::Runtime;
22
23/// Struct that is used to hold the regular DeviceContext as well as a runtime that will be used
24/// when initializing a BlockingIronOxide. This was added to fix a bug where initializing multiple
25/// SDK instances with a single device would hang indefinitely (as each initialization call would
26/// create its own runtime but share a request client)
27#[derive(Clone, Debug)]
28pub struct BlockingDeviceContext {
29    pub device: DeviceContext,
30    pub(crate) rt: Arc<Runtime>,
31}
32
33impl From<DeviceAddResult> for BlockingDeviceContext {
34    fn from(value: DeviceAddResult) -> Self {
35        Self {
36            device: value.into(),
37            rt: Arc::new(create_runtime()),
38        }
39    }
40}
41
42impl BlockingDeviceContext {
43    pub fn new(device: DeviceContext) -> Self {
44        Self {
45            device,
46            rt: Arc::new(create_runtime()),
47        }
48    }
49    /// ID of the device's owner
50    pub fn account_id(&self) -> &UserId {
51        &self.device.auth().account_id()
52    }
53    /// ID of the segment
54    pub fn segment_id(&self) -> usize {
55        self.device.auth().segment_id()
56    }
57    /// Private signing key of the device
58    pub fn signing_private_key(&self) -> &DeviceSigningKeyPair {
59        &self.device.auth().signing_private_key()
60    }
61    /// Private encryption key of the device
62    pub fn device_private_key(&self) -> &PrivateKey {
63        &self.device.device_private_key()
64    }
65}
66
67/// Struct that is used to make authenticated requests to the IronCore API. Instantiated with the details
68/// of an account's various ids, device, and signing keys. Once instantiated all operations will be
69/// performed in the context of the account provided. Identical to IronOxide but also contains a Runtime.
70#[derive(Debug)]
71pub struct BlockingIronOxide {
72    pub(crate) ironoxide: IronOxide,
73    pub(crate) runtime: Arc<tokio::runtime::Runtime>,
74}
75
76impl BlockingIronOxide {
77    /// Get the `DeviceContext` instance that was used to create this SDK instance
78    pub fn device(&self) -> &DeviceContext {
79        &self.ironoxide.device
80    }
81
82    /// See [ironoxide::IronOxide::clear_policy_cache](../struct.IronOxide.html#method.clear_policy_cache)
83    pub fn clear_policy_cache(&self) -> usize {
84        self.ironoxide.clear_policy_cache()
85    }
86
87    /// See [ironoxide::IronOxide::create_blind_index](../struct.IronOxide.html#method.create_blind_index)
88    #[cfg(feature = "beta")]
89    pub fn create_blind_index(&self, group_id: &GroupId) -> Result<EncryptedBlindIndexSalt> {
90        self.runtime
91            .block_on(self.ironoxide.create_blind_index(group_id))
92    }
93
94    /// See [ironoxide::IronOxide::rotate_all](../struct.IronOxide.html#method.rotate_all)
95    pub fn rotate_all(
96        &self,
97        rotations: &PrivateKeyRotationCheckResult,
98        password: &str,
99        timeout: Option<std::time::Duration>,
100    ) -> Result<(
101        Option<UserUpdatePrivateKeyResult>,
102        Option<Vec<GroupUpdatePrivateKeyResult>>,
103    )> {
104        self.runtime
105            .block_on(self.ironoxide.rotate_all(rotations, password, timeout))
106    }
107
108    /// See [ironoxide::document::DocumentOps::document_list](trait.DocumentOps.html#tymethod.document_list)
109    pub fn document_list(&self) -> Result<DocumentListResult> {
110        self.runtime.block_on(self.ironoxide.document_list())
111    }
112    /// See [ironoxide::document::DocumentOps::document_get_metadata](trait.DocumentOps.html#tymethod.document_get_metadata)
113    pub fn document_get_metadata(&self, id: &DocumentId) -> Result<DocumentMetadataResult> {
114        self.runtime
115            .block_on(self.ironoxide.document_get_metadata(id))
116    }
117    /// See [ironoxide::document::DocumentOps::document_get_id_from_bytes](trait.DocumentOps.html#tymethod.document_get_id_from_bytes)
118    pub fn document_get_id_from_bytes(&self, encrypted_document: &[u8]) -> Result<DocumentId> {
119        self.ironoxide
120            .document_get_id_from_bytes(encrypted_document)
121    }
122    /// See [ironoxide::document::DocumentOps::document_encrypt](trait.DocumentOps.html#tymethod.document_encrypt)
123    pub fn document_encrypt(
124        &self,
125        document_data: Vec<u8>,
126        encrypt_opts: &DocumentEncryptOpts,
127    ) -> Result<DocumentEncryptResult> {
128        self.runtime
129            .block_on(self.ironoxide.document_encrypt(document_data, encrypt_opts))
130    }
131    /// See [ironoxide::document::DocumentOps::document_update_bytes](trait.DocumentOps.html#tymethod.document_update_bytes)
132    pub fn document_update_bytes(
133        &self,
134        id: &DocumentId,
135        new_document_data: Vec<u8>,
136    ) -> Result<DocumentEncryptResult> {
137        self.runtime
138            .block_on(self.ironoxide.document_update_bytes(id, new_document_data))
139    }
140    /// See [ironoxide::document::DocumentOps::document_decrypt](trait.DocumentOps.html#tymethod.document_decrypt)
141    pub fn document_decrypt(&self, encrypted_document: &[u8]) -> Result<DocumentDecryptResult> {
142        self.runtime
143            .block_on(self.ironoxide.document_decrypt(encrypted_document))
144    }
145    /// See [ironoxide::document::DocumentOps::document_update_name](trait.DocumentOps.html#tymethod.document_update_name)
146    pub fn document_update_name(
147        &self,
148        id: &DocumentId,
149        name: Option<&DocumentName>,
150    ) -> Result<DocumentMetadataResult> {
151        self.runtime
152            .block_on(self.ironoxide.document_update_name(id, name))
153    }
154    /// See [ironoxide::document::DocumentOps::document_grant_access](trait.DocumentOps.html#tymethod.document_grant_access)
155    pub fn document_grant_access(
156        &self,
157        id: &DocumentId,
158        grant_list: &Vec<UserOrGroup>,
159    ) -> Result<DocumentAccessResult> {
160        self.runtime
161            .block_on(self.ironoxide.document_grant_access(id, grant_list))
162    }
163    /// See [ironoxide::document::DocumentOps::document_revoke_access](trait.DocumentOps.html#tymethod.document_revoke_access)
164    pub fn document_revoke_access(
165        &self,
166        id: &DocumentId,
167        revoke_list: &Vec<UserOrGroup>,
168    ) -> Result<DocumentAccessResult> {
169        self.runtime
170            .block_on(self.ironoxide.document_revoke_access(id, revoke_list))
171    }
172    /// See [ironoxide::document::advanced::DocumentAdvancedOps::document_encrypt_unmanaged](trait.DocumentAdvancedOps.html#tymethod.document_encrypt_unmanaged)
173    pub fn document_encrypt_unmanaged(
174        &self,
175        data: Vec<u8>,
176        encrypt_opts: &DocumentEncryptOpts,
177    ) -> Result<DocumentEncryptUnmanagedResult> {
178        self.runtime.block_on(
179            self.ironoxide
180                .document_encrypt_unmanaged(data, encrypt_opts),
181        )
182    }
183    /// See [ironoxide::document::advanced::DocumentAdvancedOps::document_decrypt_unmanaged](trait.DocumentAdvancedOps.html#tymethod.document_decrypt_unmanaged)
184    pub fn document_decrypt_unmanaged(
185        &self,
186        encrypted_data: &[u8],
187        encrypted_deks: &[u8],
188    ) -> Result<DocumentDecryptUnmanagedResult> {
189        self.runtime.block_on(
190            self.ironoxide
191                .document_decrypt_unmanaged(encrypted_data, encrypted_deks),
192        )
193    }
194    /// See [ironoxide::document::advanced::DocumentAdvancedOps::document_get_metadata_unmanaged](trait.DocumentAdvancedOps.html#tymethod.document_get_metadata_unmanaged)
195    pub fn document_get_metadata_unmanaged(
196        &self,
197        encrypted_deks: &[u8],
198    ) -> Result<DocumentMetadataUnmanagedResult> {
199        self.ironoxide
200            .document_get_metadata_unmanaged(encrypted_deks)
201    }
202    /// See [ironoxide::document::advanced::DocumentAdvancedOps::document_get_id_from_bytes_unmanaged](trait.DocumentAdvancedOps.html#tymethod.document_get_id_from_bytes_unmanaged)
203    pub fn document_get_id_from_bytes_unmanaged(
204        &self,
205        encrypted_document: &[u8],
206    ) -> Result<DocumentId> {
207        self.ironoxide
208            .document_get_id_from_bytes_unmanaged(encrypted_document)
209    }
210    /// See [ironoxide::document::advanced::DocumentAdvancedOps::document_get_id_from_edeks_unmanaged](trait.DocumentAdvancedOps.html#tymethod.document_get_id_from_edeks_unmanaged)
211    pub fn document_get_id_from_edeks_unmanaged(&self, edeks: &[u8]) -> Result<DocumentId> {
212        self.ironoxide.document_get_id_from_edeks_unmanaged(edeks)
213    }
214    /// See [ironoxide::document::advanced::DocumentAdvancedOps::document_grant_access_unmanaged](trait.DocumentAdvancedOps.html#tymethod.document_grant_access_unmanaged)
215    pub fn document_grant_access_unmanaged(
216        &self,
217        edeks: &[u8],
218        grant_list: &[UserOrGroup],
219    ) -> Result<DocumentAccessUnmanagedResult> {
220        self.runtime.block_on(
221            self.ironoxide
222                .document_grant_access_unmanaged(edeks, grant_list),
223        )
224    }
225    /// See [ironoxide::document::advanced::DocumentAdvancedOps::document_revoke_access_unmanaged](trait.DocumentAdvancedOps.html#tymethod.document_revoke_access_unmanaged)
226    pub fn document_revoke_access_unmanaged(
227        &self,
228        edeks: &[u8],
229        revoke_list: &[UserOrGroup],
230    ) -> Result<DocumentAccessUnmanagedResult> {
231        self.ironoxide
232            .document_revoke_access_unmanaged(edeks, revoke_list)
233    }
234
235    /// See [ironoxide::document::file::DocumentFileOps::document_file_encrypt](trait.DocumentFileOps.html#tymethod.document_file_encrypt)
236    pub fn document_file_encrypt(
237        &self,
238        source_path: &str,
239        destination_path: &str,
240        opts: &DocumentEncryptOpts,
241    ) -> Result<DocumentFileEncryptResult> {
242        self.runtime.block_on(self.ironoxide.document_file_encrypt(
243            source_path,
244            destination_path,
245            opts,
246        ))
247    }
248
249    /// See [ironoxide::document::file::DocumentFileOps::document_file_decrypt](trait.DocumentFileOps.html#tymethod.document_file_decrypt)
250    pub fn document_file_decrypt(
251        &self,
252        source_path: &str,
253        destination_path: &str,
254    ) -> Result<DocumentFileDecryptResult> {
255        self.runtime.block_on(
256            self.ironoxide
257                .document_file_decrypt(source_path, destination_path),
258        )
259    }
260
261    /// See [ironoxide::document::file::DocumentFileAdvancedOps::document_file_encrypt_unmanaged](trait.DocumentFileAdvancedOps.html#tymethod.document_file_encrypt_unmanaged)
262    pub fn document_file_encrypt_unmanaged(
263        &self,
264        source_path: &str,
265        destination_path: &str,
266        opts: &DocumentEncryptOpts,
267    ) -> Result<DocumentFileEncryptUnmanagedResult> {
268        self.runtime
269            .block_on(self.ironoxide.document_file_encrypt_unmanaged(
270                source_path,
271                destination_path,
272                opts,
273            ))
274    }
275
276    /// See [ironoxide::document::file::DocumentFileAdvancedOps::document_file_decrypt_unmanaged](trait.DocumentFileAdvancedOps.html#tymethod.document_file_decrypt_unmanaged)
277    pub fn document_file_decrypt_unmanaged(
278        &self,
279        source_path: &str,
280        destination_path: &str,
281        encrypted_deks: &[u8],
282    ) -> Result<DocumentFileDecryptUnmanagedResult> {
283        self.runtime
284            .block_on(self.ironoxide.document_file_decrypt_unmanaged(
285                source_path,
286                destination_path,
287                encrypted_deks,
288            ))
289    }
290
291    /// See [ironoxide::IronOxide::export_public_key_cache](../struct.IronOxide.html#method.export_public_key_cache)
292    pub fn export_public_key_cache(&self) -> Result<Vec<u8>> {
293        self.ironoxide.export_public_key_cache()
294    }
295    /// See [ironoxide::group::GroupOps::group_list](trait.GroupOps.html#tymethod.group_list)
296    pub fn group_list(&self) -> Result<GroupListResult> {
297        self.runtime.block_on(self.ironoxide.group_list())
298    }
299    /// See [ironoxide::group::GroupOps::group_create](trait.GroupOps.html#tymethod.group_create)
300    pub fn group_create(&self, opts: &GroupCreateOpts) -> Result<GroupCreateResult> {
301        self.runtime.block_on(self.ironoxide.group_create(opts))
302    }
303    /// See [ironoxide::group::GroupOps::group_get_metadata](trait.GroupOps.html#tymethod.group_get_metadata)
304    pub fn group_get_metadata(&self, id: &GroupId) -> Result<GroupGetResult> {
305        self.runtime.block_on(self.ironoxide.group_get_metadata(id))
306    }
307    /// See [ironoxide::group::GroupOps::group_delete](trait.GroupOps.html#tymethod.group_delete)
308    pub fn group_delete(&self, id: &GroupId) -> Result<GroupId> {
309        self.runtime.block_on(self.ironoxide.group_delete(id))
310    }
311    /// See [ironoxide::group::GroupOps::group_update_name](trait.GroupOps.html#tymethod.group_update_name)
312    pub fn group_update_name(
313        &self,
314        id: &GroupId,
315        name: Option<&GroupName>,
316    ) -> Result<GroupMetaResult> {
317        self.runtime
318            .block_on(self.ironoxide.group_update_name(id, name))
319    }
320    /// See [ironoxide::group::GroupOps::group_add_members](trait.GroupOps.html#tymethod.group_add_members)
321    pub fn group_add_members(
322        &self,
323        id: &GroupId,
324        grant_list: &[UserId],
325    ) -> Result<GroupAccessEditResult> {
326        self.runtime
327            .block_on(self.ironoxide.group_add_members(id, grant_list))
328    }
329    /// See [ironoxide::group::GroupOps::group_remove_members](trait.GroupOps.html#tymethod.group_remove_members)
330    pub fn group_remove_members(
331        &self,
332        id: &GroupId,
333        revoke_list: &[UserId],
334    ) -> Result<GroupAccessEditResult> {
335        self.runtime
336            .block_on(self.ironoxide.group_remove_members(id, revoke_list))
337    }
338    /// See [ironoxide::group::GroupOps::group_add_admins](trait.GroupOps.html#tymethod.group_add_admins)
339    pub fn group_add_admins(
340        &self,
341        id: &GroupId,
342        users: &[UserId],
343    ) -> Result<GroupAccessEditResult> {
344        self.runtime
345            .block_on(self.ironoxide.group_add_admins(id, users))
346    }
347    /// See [ironoxide::group::GroupOps::group_remove_admins](trait.GroupOps.html#tymethod.group_remove_admins)
348    pub fn group_remove_admins(
349        &self,
350        id: &GroupId,
351        revoke_list: &[UserId],
352    ) -> Result<GroupAccessEditResult> {
353        self.runtime
354            .block_on(self.ironoxide.group_remove_admins(id, revoke_list))
355    }
356    /// See [ironoxide::group::GroupOps::group_rotate_private_key](trait.GroupOps.html#tymethod.group_rotate_private_key)
357    pub fn group_rotate_private_key(&self, id: &GroupId) -> Result<GroupUpdatePrivateKeyResult> {
358        self.runtime
359            .block_on(self.ironoxide.group_rotate_private_key(id))
360    }
361    /// See [ironoxide::user::UserOps::user_create](trait.UserOps.html#tymethod.user_create)
362    pub fn user_create(
363        jwt: &Jwt,
364        password: &str,
365        user_create_opts: &UserCreateOpts,
366        timeout: Option<std::time::Duration>,
367    ) -> Result<UserCreateResult> {
368        let rt = create_runtime();
369        rt.block_on(IronOxide::user_create(
370            jwt,
371            password,
372            user_create_opts,
373            timeout,
374        ))
375    }
376    /// See [ironoxide::user::UserOps::user_list_devices](trait.UserOps.html#tymethod.user_list_devices)
377    pub fn user_list_devices(&self) -> Result<UserDeviceListResult> {
378        self.runtime.block_on(self.ironoxide.user_list_devices())
379    }
380    /// See [ironoxide::user::UserOps::generate_new_device](trait.UserOps.html#tymethod.generate_new_device)
381    pub fn generate_new_device(
382        jwt: &Jwt,
383        password: &str,
384        device_create_options: &DeviceCreateOpts,
385        timeout: Option<std::time::Duration>,
386    ) -> Result<DeviceAddResult> {
387        let rt = create_runtime();
388        rt.block_on(IronOxide::generate_new_device(
389            jwt,
390            password,
391            device_create_options,
392            timeout,
393        ))
394    }
395    /// See [ironoxide::user::UserOps::user_delete_device](trait.UserOps.html#tymethod.user_delete_device)
396    pub fn user_delete_device(&self, device_id: Option<&DeviceId>) -> Result<DeviceId> {
397        self.runtime
398            .block_on(self.ironoxide.user_delete_device(device_id))
399    }
400    /// See [ironoxide::user::UserOps::user_verify](trait.UserOps.html#tymethod.user_verify)
401    pub fn user_verify(
402        jwt: &Jwt,
403        timeout: Option<std::time::Duration>,
404    ) -> Result<Option<UserResult>> {
405        let rt = create_runtime();
406        rt.block_on(IronOxide::user_verify(jwt, timeout))
407    }
408    /// See [ironoxide::user::UserOps::user_get_public_key](trait.UserOps.html#tymethod.user_get_public_key)
409    pub fn user_get_public_key(&self, users: &[UserId]) -> Result<HashMap<UserId, PublicKey>> {
410        self.runtime
411            .block_on(self.ironoxide.user_get_public_key(users))
412    }
413    /// See [ironoxide::user::UserOps::user_rotate_private_key](trait.UserOps.html#tymethod.user_rotate_private_key)
414    pub fn user_rotate_private_key(&self, password: &str) -> Result<UserUpdatePrivateKeyResult> {
415        self.runtime
416            .block_on(self.ironoxide.user_rotate_private_key(password))
417    }
418    /// See [ironoxide::user::UserOps::user_change_password](trait.UserOps.html#tymethod.user_change_password)
419    pub fn user_change_password(
420        &self,
421        current_password: &str,
422        new_password: &str,
423    ) -> Result<UserUpdateResult> {
424        self.runtime.block_on(
425            self.ironoxide
426                .user_change_password(current_password, new_password),
427        )
428    }
429}
430
431/// Creates a tokio runtime on the current thread
432fn create_runtime() -> tokio::runtime::Runtime {
433    tokio::runtime::Builder::new_current_thread()
434        .enable_all() // enable both I/O and time drivers
435        .build()
436        .expect("tokio runtime failed to initialize")
437}
438
439/// Initialize the BlockingIronOxide SDK with a device. Verifies that the provided user/segment exists and the provided device
440/// keys are valid and exist for the provided account. If successful, returns instance of the BlockingIronOxide SDK.
441pub fn initialize(
442    device_context: &BlockingDeviceContext,
443    config: &IronOxideConfig,
444) -> Result<BlockingIronOxide> {
445    let maybe_io = device_context
446        .rt
447        .block_on(crate::initialize(&device_context.device, config));
448    maybe_io.map(|io| BlockingIronOxide {
449        ironoxide: io,
450        runtime: device_context.rt.clone(),
451    })
452}
453
454/// Initialize the BlockingIronOxide SDK with a device and cached public keys, enabling offline encryption immediately.
455///
456/// Verifies that the provided user/segment exists and the provided device keys are valid and
457/// exist for the provided account. Verifies the public key cache has not been tampered with.
458pub fn initialize_with_public_keys(
459    device_context: &BlockingDeviceContext,
460    config: &IronOxideConfig,
461    public_key_cache: Vec<u8>,
462) -> Result<BlockingIronOxide> {
463    let maybe_io = device_context
464        .rt
465        .block_on(crate::initialize_with_public_keys(
466            &device_context.device,
467            config,
468            public_key_cache,
469        ));
470    maybe_io.map(|io| BlockingIronOxide {
471        ironoxide: io,
472        runtime: device_context.rt.clone(),
473    })
474}
475
476/// Initialize the BlockingIronOxide SDK with a device and cached public keys, and check for
477/// necessary private key rotations.
478///
479/// Verifies that the provided user/segment exists and the provided device keys are valid and
480/// exist for the provided account. Verifies the public key cache has not been tampered with.
481/// Also checks if the user or any groups they admin are marked for private key rotation.
482pub fn initialize_with_public_keys_and_check_rotation(
483    device_context: &BlockingDeviceContext,
484    config: &IronOxideConfig,
485    public_key_cache: Vec<u8>,
486) -> Result<InitAndRotationCheck<BlockingIronOxide>> {
487    let maybe_init =
488        device_context
489            .rt
490            .block_on(crate::initialize_with_public_keys_and_check_rotation(
491                &device_context.device,
492                config,
493                public_key_cache,
494            ));
495    maybe_init.map(|init| match init {
496        NoRotationNeeded(io) => NoRotationNeeded(BlockingIronOxide {
497            ironoxide: io,
498            runtime: device_context.rt.clone(),
499        }),
500        RotationNeeded(io, rot) => RotationNeeded(
501            BlockingIronOxide {
502                ironoxide: io,
503                runtime: device_context.rt.clone(),
504            },
505            rot,
506        ),
507    })
508}
509
510/// Initialize the BlockingIronOxide SDK and check to see if the user that owns this `DeviceContext` is
511/// marked for private key rotation, or if any of the groups that the user is an admin of are marked
512/// for private key rotation.
513pub fn initialize_check_rotation(
514    device_context: &BlockingDeviceContext,
515    config: &IronOxideConfig,
516) -> Result<InitAndRotationCheck<BlockingIronOxide>> {
517    let maybe_init = device_context.rt.block_on(crate::initialize_check_rotation(
518        &device_context.device,
519        config,
520    ));
521    maybe_init.map(|init| match init {
522        NoRotationNeeded(io) => NoRotationNeeded(BlockingIronOxide {
523            ironoxide: io,
524            runtime: device_context.rt.clone(),
525        }),
526        RotationNeeded(io, rot) => RotationNeeded(
527            BlockingIronOxide {
528                ironoxide: io,
529                runtime: device_context.rt.clone(),
530            },
531            rot,
532        ),
533    })
534}