Skip to main content

bitwarden_emergency_access/
invite.rs

1use bitwarden_api_api::models::EmergencyAccessInviteRequestModel;
2use bitwarden_core::ApiError;
3use bitwarden_error::bitwarden_error;
4use thiserror::Error;
5
6use crate::{EmergencyAccessClient, EmergencyAccessType};
7
8/// Errors returned when inviting an emergency contact.
9#[bitwarden_error(flat)]
10#[derive(Debug, Error)]
11pub enum EmergencyAccessInviteError {
12    /// The request to the server failed.
13    #[error(transparent)]
14    Api(#[from] ApiError),
15}
16
17#[bitwarden_ffi::wasm_export]
18impl EmergencyAccessClient {
19    /// Invites `grantee_email` to become a trusted emergency contact. Step 1 of the setup; the
20    /// grantee accepts next.
21    ///
22    /// Called by the grantor.
23    pub async fn invite(
24        &self,
25        grantee_email: String,
26        r#type: EmergencyAccessType,
27        wait_time_days: i32,
28    ) -> Result<(), EmergencyAccessInviteError> {
29        let request = EmergencyAccessInviteRequestModel {
30            email: grantee_email.trim().to_owned(),
31            r#type: r#type.into(),
32            wait_time_days,
33        };
34
35        self.api_configurations
36            .api_client
37            .emergency_access_api()
38            .invite(Some(request))
39            .await?;
40
41        Ok(())
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use bitwarden_api_api::models::EmergencyAccessType as ApiEmergencyAccessType;
48    use bitwarden_core::client::test_accounts::test_bitwarden_com_account;
49
50    use super::*;
51    use crate::{
52        EmergencyAccessClientExt,
53        test_support::{api_error, test_client},
54    };
55
56    const TEST_EMAIL: &str = "[email protected]";
57    const TEST_WAIT_TIME_DAYS: i32 = 7;
58
59    #[tokio::test]
60    async fn sends_trimmed_invite() {
61        let client = test_client(test_bitwarden_com_account(), |mock| {
62            mock.emergency_access_api
63                .expect_invite()
64                .withf(|request| {
65                    *request
66                        == Some(EmergencyAccessInviteRequestModel {
67                            email: TEST_EMAIL.to_owned(),
68                            r#type: ApiEmergencyAccessType::Takeover,
69                            wait_time_days: TEST_WAIT_TIME_DAYS,
70                        })
71                })
72                .returning(|_| Ok(()))
73                .once();
74        })
75        .await;
76
77        client
78            .emergency_access()
79            .invite(
80                format!("  {TEST_EMAIL} "),
81                EmergencyAccessType::Takeover,
82                TEST_WAIT_TIME_DAYS,
83            )
84            .await
85            .unwrap();
86    }
87
88    #[tokio::test]
89    async fn fails_when_request_fails() {
90        let client = test_client(test_bitwarden_com_account(), |mock| {
91            mock.emergency_access_api
92                .expect_invite()
93                .returning(|_| api_error())
94                .once();
95        })
96        .await;
97
98        let result = client
99            .emergency_access()
100            .invite(
101                TEST_EMAIL.to_owned(),
102                EmergencyAccessType::View,
103                TEST_WAIT_TIME_DAYS,
104            )
105            .await;
106
107        assert!(matches!(result, Err(EmergencyAccessInviteError::Api(_))));
108    }
109}