1use bitwarden_core::{OrganizationId, UserId};
2use chrono::{DateTime, Utc};
3use serde::{Deserialize, Serialize};
4use uuid::Uuid;
5
6use crate::{
7 MemberDecryptionType, OrganizationUserId, OrganizationUserStatusType, OrganizationUserType,
8 Permissions, ProductTierType, ProviderId, ProviderType,
9};
10
11#[derive(Default, PartialEq, Serialize, Deserialize, Debug, Clone)]
16#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
17#[bitwarden_ffi::wasm_record]
18#[serde(rename_all = "camelCase")]
19pub struct ProfileOrganization {
20 pub details: OrganizationDetails,
22 pub plan: OrganizationPlan,
24 pub membership: OrganizationMembership,
26 pub collection_management: CollectionManagementSettings,
28 pub provider: Option<OrganizationProvider>,
30 pub sso: SsoSettings,
32 pub family_sponsorship: FamilySponsorship,
34}
35
36bitwarden_state::register_repository_item!(OrganizationId => ProfileOrganization, "ProfileOrganization");
37
38#[derive(PartialEq, Serialize, Deserialize, Debug, Clone)]
40#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
41#[bitwarden_ffi::wasm_record]
42#[serde(rename_all = "camelCase")]
43pub struct OrganizationDetails {
44 pub id: OrganizationId,
46 pub name: String,
48 pub enabled: bool,
50 pub has_public_and_private_keys: bool,
52}
53
54impl Default for OrganizationDetails {
55 fn default() -> Self {
56 OrganizationDetails {
57 id: OrganizationId::new(Uuid::nil()),
58 name: String::new(),
59 enabled: true,
60 has_public_and_private_keys: false,
61 }
62 }
63}
64
65#[derive(PartialEq, Serialize, Deserialize, Debug, Clone)]
67#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
68#[bitwarden_ffi::wasm_record]
69#[serde(rename_all = "camelCase")]
70pub struct OrganizationPlan {
71 pub product_tier_type: ProductTierType,
73 pub seats: Option<u32>,
75 pub max_collections: Option<u32>,
77 pub max_storage_gb: Option<u32>,
79 pub self_host: bool,
81 pub users_get_premium: bool,
83 pub use_policies: bool,
85 pub use_groups: bool,
87 pub use_directory: bool,
89 pub use_events: bool,
91 pub use_totp: bool,
93 pub use_2fa: bool,
95 pub use_api: bool,
97 pub use_sso: bool,
99 pub use_organization_domains: bool,
101 pub use_key_connector: bool,
103 pub use_scim: bool,
105 pub use_custom_permissions: bool,
107 pub use_reset_password: bool,
109 pub use_secrets_manager: bool,
111 pub use_password_manager: bool,
113 pub use_pam: bool,
115 pub use_activate_autofill_policy: bool,
117 pub use_automatic_user_confirmation: bool,
120 pub use_access_intelligence: bool,
122 pub use_admin_sponsored_families: bool,
124 #[serde(rename = "useDisableSMAdsForUsers")]
126 pub use_disable_sm_ads_for_users: bool,
127 pub use_phishing_blocker: bool,
129 pub use_my_items: bool,
133 pub use_invite_links: bool,
135}
136
137impl Default for OrganizationPlan {
138 fn default() -> Self {
139 OrganizationPlan {
140 product_tier_type: ProductTierType::Free,
141 seats: Some(10),
142 max_collections: None,
143 max_storage_gb: None,
144 self_host: false,
145 users_get_premium: false,
146 use_policies: false,
147 use_groups: false,
148 use_directory: false,
149 use_events: false,
150 use_totp: false,
151 use_2fa: false,
152 use_api: false,
153 use_sso: false,
154 use_organization_domains: false,
155 use_key_connector: false,
156 use_scim: false,
157 use_custom_permissions: false,
158 use_reset_password: false,
159 use_secrets_manager: false,
160 use_password_manager: false,
161 use_pam: false,
162 use_activate_autofill_policy: false,
163 use_automatic_user_confirmation: false,
164 use_access_intelligence: false,
165 use_admin_sponsored_families: false,
166 use_disable_sm_ads_for_users: false,
167 use_phishing_blocker: false,
168 use_my_items: false,
169 use_invite_links: false,
170 }
171 }
172}
173
174#[derive(PartialEq, Serialize, Deserialize, Debug, Clone)]
176#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
177#[bitwarden_ffi::wasm_record]
178#[serde(rename_all = "camelCase")]
179pub struct OrganizationMembership {
180 pub status: OrganizationUserStatusType,
182 pub r#type: OrganizationUserType,
184 pub permissions: Permissions,
187 pub user_id: UserId,
189 pub organization_user_id: Option<OrganizationUserId>,
192 pub is_member: bool,
195 pub is_provider_user: bool,
197 pub reset_password_enrolled: bool,
199 pub sso_bound: bool,
201 pub user_is_claimed_by_organization: bool,
203 pub access_secrets_manager: bool,
205}
206
207impl Default for OrganizationMembership {
208 fn default() -> Self {
209 OrganizationMembership {
210 status: OrganizationUserStatusType::Confirmed,
211 r#type: OrganizationUserType::User,
212 permissions: Permissions::default(),
213 user_id: UserId::new(Uuid::nil()),
214 organization_user_id: None,
215 is_member: true,
216 is_provider_user: false,
217 reset_password_enrolled: false,
218 sso_bound: false,
219 user_is_claimed_by_organization: false,
220 access_secrets_manager: false,
221 }
222 }
223}
224
225#[derive(Default, PartialEq, Serialize, Deserialize, Debug, Clone)]
228#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
229#[bitwarden_ffi::wasm_record]
230#[serde(rename_all = "camelCase")]
231pub struct CollectionManagementSettings {
232 pub limit_collection_creation: bool,
237 pub limit_collection_deletion: bool,
241 pub limit_item_deletion: bool,
245 pub allow_admin_access_to_all_collection_items: bool,
251}
252
253#[derive(PartialEq, Serialize, Deserialize, Debug, Clone)]
255#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
256#[bitwarden_ffi::wasm_record]
257#[serde(rename_all = "camelCase")]
258pub struct OrganizationProvider {
259 pub id: ProviderId,
261 pub r#type: ProviderType,
263 pub name: Option<String>,
266}
267
268#[derive(Default, PartialEq, Serialize, Deserialize, Debug, Clone)]
274#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
275#[bitwarden_ffi::wasm_record]
276#[serde(rename_all = "camelCase")]
277pub struct SsoSettings {
278 pub enabled: bool,
280 pub identifier: Option<String>,
282 pub member_decryption_type: Option<MemberDecryptionType>,
285 pub key_connector_enabled: bool,
287 pub key_connector_url: Option<String>,
290}
291
292#[derive(Default, PartialEq, Serialize, Deserialize, Debug, Clone)]
294#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
295#[bitwarden_ffi::wasm_record]
296#[serde(rename_all = "camelCase")]
297pub struct FamilySponsorship {
298 pub available: bool,
301 pub friendly_name: Option<String>,
304 pub valid_until: Option<DateTime<Utc>>,
307 pub last_sync_date: Option<DateTime<Utc>>,
309 pub to_delete: Option<bool>,
311 pub is_admin_initiated: bool,
313}
314
315#[cfg(test)]
316mod tests {
317 use serde_json::json;
318
319 use super::*;
320
321 fn organization_json() -> serde_json::Value {
323 serde_json::from_str(
324 r#"{
325 "details": {
326 "id": "0b5a2d2c-6b39-4c7e-9f4a-0a1b2c3d4e5f",
327 "name": "Test Org",
328 "enabled": true,
329 "hasPublicAndPrivateKeys": true
330 },
331 "plan": {
332 "productTierType": 3,
333 "seats": 25,
334 "maxCollections": null,
335 "maxStorageGb": 5,
336 "selfHost": false,
337 "usersGetPremium": true,
338 "usePolicies": true,
339 "useGroups": true,
340 "useDirectory": false,
341 "useEvents": true,
342 "useTotp": true,
343 "use2fa": true,
344 "useApi": false,
345 "useSso": true,
346 "useOrganizationDomains": false,
347 "useKeyConnector": false,
348 "useScim": false,
349 "useCustomPermissions": true,
350 "useResetPassword": true,
351 "useSecretsManager": false,
352 "usePasswordManager": true,
353 "usePam": false,
354 "useActivateAutofillPolicy": false,
355 "useAutomaticUserConfirmation": false,
356 "useAccessIntelligence": false,
357 "useAdminSponsoredFamilies": false,
358 "useDisableSMAdsForUsers": false,
359 "usePhishingBlocker": false,
360 "useMyItems": false,
361 "useInviteLinks": true
362 },
363 "membership": {
364 "status": 2,
365 "type": 4,
366 "permissions": {
367 "accessEventLogs": true,
368 "accessImportExport": false,
369 "accessReports": true,
370 "createNewCollections": false,
371 "editAnyCollection": false,
372 "deleteAnyCollection": false,
373 "manageGroups": true,
374 "manageSso": false,
375 "managePolicies": false,
376 "manageUsers": true,
377 "manageResetPassword": false,
378 "manageScim": false
379 },
380 "userId": "1c6b3e3d-7c4a-4d8f-8a5b-1b2c3d4e5f60",
381 "organizationUserId": "2d7c4f4e-8d5b-4e9a-9b6c-2c3d4e5f6071",
382 "isMember": true,
383 "isProviderUser": false,
384 "resetPasswordEnrolled": true,
385 "ssoBound": false,
386 "userIsClaimedByOrganization": false,
387 "accessSecretsManager": false
388 },
389 "collectionManagement": {
390 "limitCollectionCreation": true,
391 "limitCollectionDeletion": true,
392 "limitItemDeletion": false,
393 "allowAdminAccessToAllCollectionItems": true
394 },
395 "provider": null,
396 "sso": {
397 "enabled": true,
398 "identifier": "test-org",
399 "memberDecryptionType": 0,
400 "keyConnectorEnabled": false,
401 "keyConnectorUrl": null
402 },
403 "familySponsorship": {
404 "available": false,
405 "friendlyName": null,
406 "validUntil": null,
407 "lastSyncDate": "2024-01-02T03:04:05Z",
408 "toDelete": null,
409 "isAdminInitiated": false
410 }
411 }"#,
412 )
413 .unwrap()
414 }
415
416 #[test]
417 fn round_trips_organization_json() {
418 let json = organization_json();
419
420 let organization: ProfileOrganization = serde_json::from_value(json.clone()).unwrap();
421 assert_eq!(
422 organization.details.id,
423 OrganizationId::new("0b5a2d2c-6b39-4c7e-9f4a-0a1b2c3d4e5f".parse().unwrap())
424 );
425 assert!(organization.plan.use_invite_links);
426 assert_eq!(organization.membership.r#type, OrganizationUserType::Custom);
427
428 assert_eq!(serde_json::to_value(&organization).unwrap(), json);
429 }
430
431 #[test]
432 fn deserializes_js_iso_date_strings() {
433 let mut json = organization_json();
434 json["familySponsorship"]["lastSyncDate"] = json!("2024-01-02T03:04:05.000Z");
435
436 let organization: ProfileOrganization = serde_json::from_value(json).unwrap();
437 assert_eq!(
438 organization.family_sponsorship.last_sync_date,
439 Some("2024-01-02T03:04:05Z".parse().unwrap())
440 );
441 }
442
443 #[test]
444 fn round_trips_provider_without_name() {
445 let mut json = organization_json();
446 json["provider"] = json!({
447 "id": "3e8d5a5f-9e6c-4fab-8c7d-3d4e5f607182",
448 "type": 2,
449 "name": null
450 });
451
452 let organization: ProfileOrganization = serde_json::from_value(json.clone()).unwrap();
453 assert_eq!(
454 organization.provider,
455 Some(OrganizationProvider {
456 id: "3e8d5a5f-9e6c-4fab-8c7d-3d4e5f607182".parse().unwrap(),
457 r#type: ProviderType::BusinessUnit,
458 name: None,
459 })
460 );
461
462 assert_eq!(serde_json::to_value(&organization).unwrap(), json);
463 }
464}