bitwarden_emergency_access/
get_grantor_policies.rs1use bitwarden_api_api::models::PolicyType as ApiPolicyType;
2use bitwarden_core::ApiError;
3use bitwarden_error::bitwarden_error;
4use bitwarden_policies::{Policy, PolicyParseError};
5use thiserror::Error;
6
7use crate::{EmergencyAccessClient, EmergencyAccessId};
8
9#[bitwarden_error(flat)]
11#[derive(Debug, Error)]
12pub enum EmergencyAccessGetGrantorPoliciesError {
13 #[error(transparent)]
15 Api(#[from] ApiError),
16 #[error(transparent)]
18 PolicyParse(#[from] PolicyParseError),
19}
20
21#[bitwarden_ffi::wasm_export]
22impl EmergencyAccessClient {
23 pub async fn get_grantor_policies(
31 &self,
32 emergency_access_id: EmergencyAccessId,
33 ) -> Result<Vec<Policy>, EmergencyAccessGetGrantorPoliciesError> {
34 let response = self
35 .api_configurations
36 .api_client
37 .emergency_access_api()
38 .policies(emergency_access_id.into())
39 .await?;
40
41 Ok(response
44 .data
45 .unwrap_or_default()
46 .into_iter()
47 .filter(|policy| !matches!(policy.r#type, Some(ApiPolicyType::__Unknown(_))))
48 .map(Policy::try_from)
49 .collect::<Result<_, _>>()?)
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use bitwarden_api_api::models::{PolicyResponseModel, PolicyResponseModelListResponseModel};
56 use bitwarden_core::client::test_accounts::test_bitwarden_com_account_v2;
57 use bitwarden_policies::PolicyType;
58
59 use super::*;
60 use crate::{
61 EmergencyAccessClientExt,
62 test_support::{api_error, is_test_id, test_client, test_id},
63 };
64
65 const TEST_POLICY_ID: &str = "0ae2ad8e-2f1c-4d1b-9d7c-7a4c3f4b8a11";
66 const TEST_ORGANIZATION_ID: &str = "5b9e0a0c-7f3e-4b8a-9c1d-2e6f4a8b0c22";
67
68 fn master_password_policy() -> PolicyResponseModel {
69 PolicyResponseModel {
70 id: Some(TEST_POLICY_ID.parse().unwrap()),
71 organization_id: Some(TEST_ORGANIZATION_ID.parse().unwrap()),
72 r#type: Some(ApiPolicyType::MasterPassword),
73 data: Some(serde_json::json!({ "minLength": 12 })),
74 enabled: Some(true),
75 ..Default::default()
76 }
77 }
78
79 async fn policies(
80 data: Option<Vec<PolicyResponseModel>>,
81 ) -> Result<Vec<Policy>, EmergencyAccessGetGrantorPoliciesError> {
82 let response = PolicyResponseModelListResponseModel {
83 data,
84 ..Default::default()
85 };
86
87 let client = test_client(test_bitwarden_com_account_v2(), move |mock| {
88 mock.emergency_access_api
89 .expect_policies()
90 .withf(is_test_id)
91 .returning(move |_| Ok(response.clone()))
92 .once();
93 })
94 .await;
95
96 client
97 .emergency_access()
98 .get_grantor_policies(test_id())
99 .await
100 }
101
102 #[tokio::test]
103 async fn maps_policies() {
104 let result = policies(Some(vec![master_password_policy()]))
105 .await
106 .unwrap();
107
108 assert_eq!(result.len(), 1);
109 assert_eq!(result[0].id.to_string(), TEST_POLICY_ID);
110 assert_eq!(result[0].organization_id.to_string(), TEST_ORGANIZATION_ID);
111 assert_eq!(result[0].r#type, PolicyType::MasterPassword);
112 assert_eq!(result[0].data.as_deref(), Some(r#"{"minLength":12}"#));
113 assert!(result[0].enabled);
114 }
115
116 #[tokio::test]
117 async fn returns_empty_list_when_data_is_missing() {
118 assert!(policies(None).await.unwrap().is_empty());
119 }
120
121 #[tokio::test]
122 async fn maps_enabled_and_disabled_policies() {
123 let disabled = PolicyResponseModel {
125 enabled: Some(false),
126 ..master_password_policy()
127 };
128
129 let result = policies(Some(vec![master_password_policy(), disabled]))
130 .await
131 .unwrap();
132
133 let enabled: Vec<bool> = result.iter().map(|policy| policy.enabled).collect();
134 assert_eq!(enabled, vec![true, false]);
135 }
136
137 #[tokio::test]
138 async fn skips_unknown_policy_types() {
139 let policy = |r#type| PolicyResponseModel {
142 r#type: Some(r#type),
143 ..master_password_policy()
144 };
145
146 let result = policies(Some(vec![
147 policy(ApiPolicyType::__Unknown(998)),
148 policy(ApiPolicyType::MasterPassword),
149 policy(ApiPolicyType::TwoFactorAuthentication),
150 policy(ApiPolicyType::__Unknown(999)),
151 policy(ApiPolicyType::ResetPassword),
152 ]))
153 .await
154 .unwrap();
155
156 let types: Vec<PolicyType> = result.into_iter().map(|policy| policy.r#type).collect();
157 assert_eq!(
158 types,
159 vec![
160 PolicyType::MasterPassword,
161 PolicyType::TwoFactorAuthentication,
162 PolicyType::ResetPassword,
163 ]
164 );
165 }
166
167 #[tokio::test]
168 async fn fails_when_request_fails() {
169 let client = test_client(test_bitwarden_com_account_v2(), |mock| {
170 mock.emergency_access_api
171 .expect_policies()
172 .returning(|_| api_error())
173 .once();
174 })
175 .await;
176
177 let result = client
178 .emergency_access()
179 .get_grantor_policies(test_id())
180 .await;
181
182 assert!(matches!(
183 result,
184 Err(EmergencyAccessGetGrantorPoliciesError::Api(_))
185 ));
186 }
187}