Skip to main content

bitwarden_emergency_access/
emergency_access.rs

1use bitwarden_api_api::models::{
2    EmergencyAccessGranteeDetailsResponseModel, EmergencyAccessGrantorDetailsResponseModel,
3    EmergencyAccessStatusType as ApiEmergencyAccessStatus,
4    EmergencyAccessType as ApiEmergencyAccessType,
5};
6use bitwarden_core::{MissingFieldError, UserId, require};
7use bitwarden_uuid::uuid_newtype;
8use serde::{Deserialize, Serialize};
9use serde_repr::{Deserialize_repr, Serialize_repr};
10
11uuid_newtype!(pub EmergencyAccessId);
12
13/// What the grantee may do once access is granted.
14#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Copy, PartialEq, Eq)]
15#[bitwarden_ffi::wasm_object]
16#[repr(u8)]
17pub enum EmergencyAccessType {
18    /// The grantee may view the grantor's vault.
19    View = 0,
20    /// The grantee may reset the grantor's master password and take over the account.
21    Takeover = 1,
22}
23
24/// Where an emergency access is in its lifecycle.
25#[derive(Serialize_repr, Deserialize_repr, Debug, Clone, Copy, PartialEq, Eq)]
26#[bitwarden_ffi::wasm_object]
27#[repr(u8)]
28pub enum EmergencyAccessStatus {
29    /// The grantor invited the grantee.
30    Invited = 0,
31    /// The grantee accepted the invite.
32    Accepted = 1,
33    /// The grantor shared their user key with the grantee.
34    Confirmed = 2,
35    /// The grantee requested access and is waiting for approval or the wait time to pass.
36    RecoveryInitiated = 3,
37    /// Access was granted to the grantee.
38    RecoveryApproved = 4,
39}
40
41/// An emergency access seen by the grantor, describing the trusted grantee.
42#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
43#[serde(rename_all = "camelCase")]
44#[bitwarden_ffi::wasm_record]
45pub struct GranteeEmergencyAccess {
46    /// The emergency access ID.
47    pub id: EmergencyAccessId,
48    /// The grantee's user ID, absent until the grantee accepted the invite.
49    pub grantee_id: Option<UserId>,
50    /// The grantee's name.
51    pub name: Option<String>,
52    /// The grantee's email.
53    pub email: Option<String>,
54    /// What the grantee may do once access is granted.
55    pub r#type: EmergencyAccessType,
56    /// Where the emergency access is in its lifecycle.
57    pub status: EmergencyAccessStatus,
58    /// Days after a request until access is granted automatically.
59    pub wait_time_days: i32,
60    /// The grantee's avatar color.
61    pub avatar_color: Option<String>,
62}
63
64/// An emergency access seen by the grantee, describing the grantor who trusts them.
65#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
66#[serde(rename_all = "camelCase")]
67#[bitwarden_ffi::wasm_record]
68pub struct GrantorEmergencyAccess {
69    /// The emergency access ID.
70    pub id: EmergencyAccessId,
71    /// The grantor's user ID.
72    pub grantor_id: UserId,
73    /// The grantor's name.
74    pub name: Option<String>,
75    /// The grantor's email.
76    pub email: Option<String>,
77    /// What the current user may do once access is granted.
78    pub r#type: EmergencyAccessType,
79    /// Where the emergency access is in its lifecycle.
80    pub status: EmergencyAccessStatus,
81    /// Days after a request until access is granted automatically.
82    pub wait_time_days: i32,
83    /// The grantor's avatar color.
84    pub avatar_color: Option<String>,
85}
86
87impl TryFrom<ApiEmergencyAccessType> for EmergencyAccessType {
88    type Error = MissingFieldError;
89
90    fn try_from(value: ApiEmergencyAccessType) -> Result<Self, Self::Error> {
91        Ok(match value {
92            ApiEmergencyAccessType::View => Self::View,
93            ApiEmergencyAccessType::Takeover => Self::Takeover,
94            ApiEmergencyAccessType::__Unknown(_) => return Err(MissingFieldError("type")),
95        })
96    }
97}
98
99impl From<EmergencyAccessType> for ApiEmergencyAccessType {
100    fn from(value: EmergencyAccessType) -> Self {
101        match value {
102            EmergencyAccessType::View => Self::View,
103            EmergencyAccessType::Takeover => Self::Takeover,
104        }
105    }
106}
107
108impl TryFrom<ApiEmergencyAccessStatus> for EmergencyAccessStatus {
109    type Error = MissingFieldError;
110
111    fn try_from(value: ApiEmergencyAccessStatus) -> Result<Self, Self::Error> {
112        Ok(match value {
113            ApiEmergencyAccessStatus::Invited => Self::Invited,
114            ApiEmergencyAccessStatus::Accepted => Self::Accepted,
115            ApiEmergencyAccessStatus::Confirmed => Self::Confirmed,
116            ApiEmergencyAccessStatus::RecoveryInitiated => Self::RecoveryInitiated,
117            ApiEmergencyAccessStatus::RecoveryApproved => Self::RecoveryApproved,
118            ApiEmergencyAccessStatus::__Unknown(_) => return Err(MissingFieldError("status")),
119        })
120    }
121}
122
123impl TryFrom<EmergencyAccessGranteeDetailsResponseModel> for GranteeEmergencyAccess {
124    type Error = MissingFieldError;
125
126    fn try_from(response: EmergencyAccessGranteeDetailsResponseModel) -> Result<Self, Self::Error> {
127        Ok(Self {
128            id: EmergencyAccessId::new(require!(response.id)),
129            grantee_id: response.grantee_id.map(UserId::new),
130            name: response.name,
131            email: response.email,
132            r#type: require!(response.r#type).try_into()?,
133            status: require!(response.status).try_into()?,
134            wait_time_days: require!(response.wait_time_days),
135            avatar_color: response.avatar_color,
136        })
137    }
138}
139
140impl TryFrom<EmergencyAccessGrantorDetailsResponseModel> for GrantorEmergencyAccess {
141    type Error = MissingFieldError;
142
143    fn try_from(response: EmergencyAccessGrantorDetailsResponseModel) -> Result<Self, Self::Error> {
144        Ok(Self {
145            id: EmergencyAccessId::new(require!(response.id)),
146            grantor_id: UserId::new(require!(response.grantor_id)),
147            name: response.name,
148            email: response.email,
149            r#type: require!(response.r#type).try_into()?,
150            status: require!(response.status).try_into()?,
151            wait_time_days: require!(response.wait_time_days),
152            avatar_color: response.avatar_color,
153        })
154    }
155}