organization.rs (29835B)
1 use super::{CollectionUser, OrgPolicy, OrgPolicyType, User}; 2 use crate::db::models::{Collection, TwoFactorType}; 3 use crate::error::Error; 4 use crate::util; 5 use core::cmp::Ordering; 6 use diesel::result::{self, DatabaseErrorKind}; 7 use serde_json::Value; 8 use std::collections::HashMap; 9 10 db_object! { 11 #[derive(AsChangeset, Insertable, Queryable)] 12 #[diesel(table_name = organizations)] 13 pub struct Organization { 14 pub uuid: String, 15 pub name: String, 16 pub billing_email: String, 17 pub private_key: Option<String>, 18 pub public_key: Option<String>, 19 } 20 21 #[derive(AsChangeset, Insertable, Queryable)] 22 #[diesel(table_name = users_organizations)] 23 pub struct UserOrganization { 24 pub uuid: String, 25 pub user_uuid: String, 26 pub org_uuid: String, 27 pub access_all: bool, 28 pub akey: String, 29 pub status: i32, 30 pub atype: i32, 31 pub reset_password_key: Option<String>, 32 pub external_id: Option<String>, 33 } 34 } 35 pub enum UserOrgStatus { 36 Revoked = -1, 37 Invited = 0, 38 Accepted = 1, 39 Confirmed = 2, 40 } 41 impl From<UserOrgStatus> for i32 { 42 fn from(value: UserOrgStatus) -> Self { 43 match value { 44 UserOrgStatus::Revoked => -1i32, 45 UserOrgStatus::Invited => 0i32, 46 UserOrgStatus::Accepted => 1i32, 47 UserOrgStatus::Confirmed => 2i32, 48 } 49 } 50 } 51 52 #[derive(Clone, Copy, Eq, PartialEq)] 53 pub enum UserOrgType { 54 Owner = 0, 55 Admin = 1, 56 User = 2, 57 Manager = 3, 58 } 59 impl From<UserOrgType> for i32 { 60 fn from(value: UserOrgType) -> Self { 61 match value { 62 UserOrgType::Owner => 0i32, 63 UserOrgType::Admin => 1i32, 64 UserOrgType::User => 2i32, 65 UserOrgType::Manager => 3i32, 66 } 67 } 68 } 69 impl TryFrom<i32> for UserOrgType { 70 type Error = Error; 71 fn try_from(value: i32) -> Result<Self, Error> { 72 match value { 73 0i32 => Ok(Self::Owner), 74 1i32 => Ok(Self::Admin), 75 2i32 => Ok(Self::User), 76 3i32 => Ok(Self::Manager), 77 _ => { 78 const MSG: &str = "i32 is not valid UserOrgType"; 79 Err(Error::new(MSG, MSG)) 80 } 81 } 82 } 83 } 84 impl From<UserOrgType> for usize { 85 fn from(value: UserOrgType) -> Self { 86 match value { 87 UserOrgType::Owner => 0, 88 UserOrgType::Admin => 1, 89 UserOrgType::User => 2, 90 UserOrgType::Manager => 3, 91 } 92 } 93 } 94 95 impl UserOrgType { 96 pub fn from_str(s: &str) -> Option<Self> { 97 match s { 98 "0" | "Owner" => Some(Self::Owner), 99 "1" | "Admin" => Some(Self::Admin), 100 "2" | "User" => Some(Self::User), 101 "3" | "Manager" => Some(Self::Manager), 102 _ => None, 103 } 104 } 105 } 106 107 impl Ord for UserOrgType { 108 fn cmp(&self, other: &Self) -> Ordering { 109 match *self { 110 Self::Owner => match *other { 111 Self::Owner => Ordering::Equal, 112 Self::Admin | Self::User | Self::Manager => Ordering::Greater, 113 }, 114 Self::Admin => match *other { 115 Self::Owner => Ordering::Less, 116 Self::Admin => Ordering::Equal, 117 Self::User | Self::Manager => Ordering::Greater, 118 }, 119 Self::User => match *other { 120 Self::User => Ordering::Equal, 121 Self::Owner | Self::Admin | Self::Manager => Ordering::Less, 122 }, 123 Self::Manager => match *other { 124 Self::Owner | Self::Admin => Ordering::Less, 125 Self::User => Ordering::Greater, 126 Self::Manager => Ordering::Equal, 127 }, 128 } 129 } 130 } 131 132 impl PartialOrd for UserOrgType { 133 fn partial_cmp(&self, other: &Self) -> Option<Ordering> { 134 Some(self.cmp(other)) 135 } 136 } 137 138 impl PartialEq<i32> for UserOrgType { 139 fn eq(&self, other: &i32) -> bool { 140 *other == i32::from(*self) 141 } 142 } 143 144 impl PartialOrd<i32> for UserOrgType { 145 fn partial_cmp(&self, other: &i32) -> Option<Ordering> { 146 if let Ok(other) = Self::try_from(*other) { 147 return Some(self.cmp(&other)); 148 } 149 None 150 } 151 152 fn gt(&self, other: &i32) -> bool { 153 matches!(self.partial_cmp(other), Some(Ordering::Greater)) 154 } 155 156 fn ge(&self, other: &i32) -> bool { 157 matches!( 158 self.partial_cmp(other), 159 Some(Ordering::Greater | Ordering::Equal) 160 ) 161 } 162 } 163 164 impl PartialEq<UserOrgType> for i32 { 165 fn eq(&self, other: &UserOrgType) -> bool { 166 *self == Self::from(*other) 167 } 168 } 169 170 impl PartialOrd<UserOrgType> for i32 { 171 fn partial_cmp(&self, other: &UserOrgType) -> Option<Ordering> { 172 if let Ok(self_type) = UserOrgType::try_from(*self) { 173 return Some(self_type.cmp(other)); 174 } 175 None 176 } 177 178 fn lt(&self, other: &UserOrgType) -> bool { 179 matches!(self.partial_cmp(other), Some(Ordering::Less) | None) 180 } 181 182 fn le(&self, other: &UserOrgType) -> bool { 183 matches!( 184 self.partial_cmp(other), 185 Some(Ordering::Less | Ordering::Equal) | None 186 ) 187 } 188 } 189 190 /// Local methods 191 impl Organization { 192 // https://github.com/bitwarden/server/blob/13d1e74d6960cf0d042620b72d85bf583a4236f7/src/Api/Models/Response/Organizations/OrganizationResponseModel.cs 193 pub fn to_json(&self) -> Value { 194 json!({ 195 "id": self.uuid, 196 "identifier": null, // not supported by us 197 "name": self.name, 198 "seats": null, 199 "maxCollections": null, 200 "maxStorageGb": i16::MAX, 201 "use2fa": true, 202 "useCustomPermissions": false, 203 "useDirectory": false, // Is supported, but this value isn't checked anywhere (yet) 204 "useEvents": false, 205 "useGroups": false, 206 "useTotp": true, 207 "usePolicies": true, 208 "useSso": false, // Not supported 209 "selfHost": true, 210 "useApi": true, 211 "hasPublicAndPrivateKeys": self.private_key.is_some() && self.public_key.is_some(), 212 "useResetPassword": false, 213 "businessName": null, 214 "businessAddress1": null, 215 "businessAddress2": null, 216 "businessAddress3": null, 217 "businessCountry": null, 218 "businessTaxNumber": null, 219 "billingEmail": self.billing_email, 220 "planType": 6, 221 "usersGetPremium": true, 222 "object": "organization", 223 }) 224 } 225 } 226 // Used to either subtract or add to the current status 227 // The number 128 should be fine, it is well within the range of an i32 228 // The same goes for the database where we only use INTEGER (the same as an i32) 229 // It should also provide enough room for 100+ types, which i doubt will ever happen. 230 static ACTIVATE_REVOKE_DIFF: i32 = 128i32; 231 232 impl UserOrganization { 233 pub fn new(user_uuid: String, org_uuid: String) -> Self { 234 Self { 235 uuid: util::get_uuid(), 236 user_uuid, 237 org_uuid, 238 access_all: false, 239 akey: String::new(), 240 status: i32::from(UserOrgStatus::Accepted), 241 atype: i32::from(UserOrgType::User), 242 reset_password_key: None, 243 external_id: None, 244 } 245 } 246 247 pub fn restore(&mut self) -> bool { 248 if self.status < i32::from(UserOrgStatus::Invited) { 249 self.status = self 250 .status 251 .checked_add(ACTIVATE_REVOKE_DIFF) 252 .expect("i32 add overflowed"); 253 true 254 } else { 255 false 256 } 257 } 258 259 pub fn revoke(&mut self) -> bool { 260 if self.status > i32::from(UserOrgStatus::Revoked) { 261 self.status = self 262 .status 263 .checked_sub(ACTIVATE_REVOKE_DIFF) 264 .expect("i32 sub underflowed"); 265 true 266 } else { 267 false 268 } 269 } 270 271 /// Return the status of the user in an unrevoked state 272 pub fn get_unrevoked_status(&self) -> i32 { 273 if self.status <= i32::from(UserOrgStatus::Revoked) { 274 return self 275 .status 276 .checked_add(ACTIVATE_REVOKE_DIFF) 277 .unwrap_or_else(|| panic!("overflow")); 278 } 279 self.status 280 } 281 282 pub fn set_external_id(&mut self, external_id: Option<String>) -> bool { 283 //Check if external id is empty. We don't want to have 284 //empty strings in the database 285 if self.external_id != external_id { 286 self.external_id = match external_id { 287 Some(external_id) if !external_id.is_empty() => Some(external_id), 288 _ => None, 289 }; 290 return true; 291 } 292 false 293 } 294 } 295 296 use crate::api::EmptyResult; 297 use crate::db::DbConn; 298 use crate::error::MapResult; 299 300 /// Database methods 301 impl Organization { 302 pub async fn save(&self, conn: &DbConn) -> EmptyResult { 303 for user_org in &UserOrganization::find_by_org(&self.uuid, conn).await { 304 User::update_uuid_revision(&user_org.user_uuid, conn).await; 305 } 306 db_run! { conn: 307 { 308 match diesel::replace_into(organizations::table) 309 .values(OrganizationDb::to_db(self)) 310 .execute(conn) 311 { 312 Ok(_) => Ok(()), 313 // Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first. 314 Err(result::Error::DatabaseError(DatabaseErrorKind::ForeignKeyViolation, _)) => { 315 diesel::update(organizations::table) 316 .filter(organizations::uuid.eq(&self.uuid)) 317 .set(OrganizationDb::to_db(self)) 318 .execute(conn) 319 .map_res("Error saving organization") 320 } 321 Err(e) => Err(e.into()), 322 }.map_res("Error saving organization") 323 } 324 } 325 } 326 327 pub async fn delete(self, conn: &DbConn) -> EmptyResult { 328 use super::{Cipher, Collection}; 329 Cipher::delete_all_by_organization(&self.uuid, conn).await?; 330 Collection::delete_all_by_organization(&self.uuid, conn).await?; 331 UserOrganization::delete_all_by_organization(&self.uuid, conn).await?; 332 OrgPolicy::delete_all_by_organization(&self.uuid, conn).await?; 333 db_run! { conn: { 334 diesel::delete(organizations::table.filter(organizations::uuid.eq(self.uuid))) 335 .execute(conn) 336 .map_res("Error saving organization") 337 }} 338 } 339 340 pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> { 341 db_run! { conn: { 342 organizations::table 343 .filter(organizations::uuid.eq(uuid)) 344 .first::<OrganizationDb>(conn) 345 .ok().from_db() 346 }} 347 } 348 } 349 350 impl UserOrganization { 351 pub async fn to_json(&self, conn: &DbConn) -> Value { 352 let org = Organization::find_by_uuid(&self.org_uuid, conn) 353 .await 354 .unwrap(); 355 let permissions = json!({ 356 // TODO: Add support for Custom User Roles 357 // See: https://bitwarden.com/help/article/user-types-access-control/#custom-role 358 "accessEventLogs": false, 359 "accessImportExport": false, 360 "accessReports": false, 361 "createNewCollections": false, 362 "editAnyCollection": false, 363 "deleteAnyCollection": false, 364 "editAssignedCollections": false, 365 "deleteAssignedCollections": false, 366 "manageGroups": false, 367 "managePolicies": false, 368 "manageSso": false, // Not supported 369 "manageUsers": false, 370 "manageResetPassword": false, 371 "manageScim": false // Not supported (Not AGPLv3 Licensed) 372 }); 373 // https://github.com/bitwarden/server/blob/13d1e74d6960cf0d042620b72d85bf583a4236f7/src/Api/Models/Response/ProfileOrganizationResponseModel.cs 374 json!({ 375 "id": self.org_uuid, 376 "identifier": null, // Not supported 377 "name": org.name, 378 "seats": null, 379 "maxCollections": null, 380 "usersGetPremium": true, 381 "use2fa": true, 382 "useDirectory": false, // Is supported, but this value isn't checked anywhere (yet) 383 "useEvents": false, 384 "useGroups": false, 385 "useTotp": true, 386 "useScim": false, // Not supported (Not AGPLv3 Licensed) 387 "usePolicies": true, 388 "useApi": false, 389 "selfHost": true, 390 "hasPublicAndPrivateKeys": org.private_key.is_some() && org.public_key.is_some(), 391 "resetPasswordEnrolled": self.reset_password_key.is_some(), 392 "useResetPassword": false, 393 "ssoBound": false, // Not supported 394 "useSso": false, // Not supported 395 "useKeyConnector": false, 396 "useSecretsManager": false, 397 "usePasswordManager": true, 398 "useCustomPermissions": false, 399 "useActivateAutofillPolicy": false, 400 "organizationUserId": self.uuid, 401 "providerId": null, 402 "providerName": null, 403 "providerType": null, 404 "familySponsorshipFriendlyName": null, 405 "familySponsorshipAvailable": false, 406 "planProductType": 3i32, 407 "productTierType": 3i32, 408 "keyConnectorEnabled": false, 409 "keyConnectorUrl": null, 410 "familySponsorshipLastSyncDate": null, 411 "familySponsorshipValidUntil": null, 412 "familySponsorshipToDelete": null, 413 "accessSecretsManager": false, 414 "limitCollectionCreationDeletion": false, // This should be set to true only when we can handle roles like createNewCollections 415 "allowAdminAccessToAllCollectionItems": true, 416 "flexibleCollections": false, 417 "permissions": permissions, 418 "maxStorageGb": i16::MAX, 419 "userId": self.user_uuid, 420 "key": self.akey, 421 "status": self.status, 422 "type": self.atype, 423 "enabled": true, 424 "object": "profileOrganization", 425 }) 426 } 427 pub async fn to_json_user_details(&self, include_collections: bool, conn: &DbConn) -> Value { 428 let user = User::find_by_uuid(&self.user_uuid, conn).await.unwrap(); 429 // Because BitWarden want the status to be -1 for revoked users we need to catch that here. 430 // We subtract/add a number so we can restore/activate the user to it's previous state again. 431 let status = if self.status < i32::from(UserOrgStatus::Revoked) { 432 i32::from(UserOrgStatus::Revoked) 433 } else { 434 self.status 435 }; 436 437 let twofactor_enabled = TwoFactorType::has_twofactor(&user.uuid, conn) 438 .await 439 .expect("unable to get two factor information"); 440 let groups: Vec<String> = Vec::new(); 441 let collections: Vec<Value> = if include_collections { 442 // Get all collections for the user here already to prevent more queries 443 let cu: HashMap<String, CollectionUser> = 444 CollectionUser::find_by_organization_and_user_uuid( 445 &self.org_uuid, 446 &self.user_uuid, 447 conn, 448 ) 449 .await 450 .into_iter() 451 .map(|cu| (cu.collection_uuid.clone(), cu)) 452 .collect(); 453 Collection::find_by_organization_and_user_uuid(&self.org_uuid, &self.user_uuid, conn) 454 .await 455 .into_iter() 456 .map(|c| { 457 let (read_only, hide_passwords, can_manage) = if self.has_full_access() { 458 (false, false, self.atype >= UserOrgType::Manager) 459 } else if let Some(cu) = cu.get(&c.uuid) { 460 ( 461 cu.read_only, 462 cu.hide_passwords, 463 self.atype == UserOrgType::Manager 464 && !cu.read_only 465 && !cu.hide_passwords, 466 ) 467 } else { 468 (true, true, false) 469 }; 470 471 json!({ 472 "id": c.uuid, 473 "readOnly": read_only, 474 "hidePasswords": hide_passwords, 475 "manage": can_manage, 476 }) 477 }) 478 .collect() 479 } else { 480 Vec::new() 481 }; 482 let permissions = json!({ 483 // TODO: Add support for Custom User Roles 484 // See: https://bitwarden.com/help/article/user-types-access-control/#custom-role 485 "accessEventLogs": false, 486 "accessImportExport": false, 487 "accessReports": false, 488 "createNewCollections": false, 489 "editAnyCollection": false, 490 "deleteAnyCollection": false, 491 "editAssignedCollections": false, 492 "deleteAssignedCollections": false, 493 "manageGroups": false, 494 "managePolicies": false, 495 "manageSso": false, // Not supported 496 "manageUsers": false, 497 "manageResetPassword": false, 498 "manageScim": false // Not supported (Not AGPLv3 Licensed) 499 }); 500 json!({ 501 "id": self.uuid, 502 "userId": self.user_uuid, 503 "name": if self.get_unrevoked_status() >= i32::from(UserOrgStatus::Accepted) { Some(user.name) } else { None }, 504 "email": user.email, 505 "externalId": self.external_id, 506 "avatarColor": user.avatar_color, 507 "groups": groups, 508 "collections": collections, 509 "status": status, 510 "type": self.atype, 511 "accessAll": self.access_all, 512 "twoFactorEnabled": twofactor_enabled, 513 "resetPasswordEnrolled": self.reset_password_key.is_some(), 514 "hasMasterPassword": !user.password_hash.is_empty(), 515 "permissions": permissions, 516 "ssoBound": false, // Not supported 517 "usesKeyConnector": false, // Not supported 518 "accessSecretsManager": false, // Not supported (Not AGPLv3 Licensed) 519 "object": "organizationUserUserDetails", 520 }) 521 } 522 523 pub fn to_json_user_access_restrictions(&self, col_user: &CollectionUser) -> Value { 524 json!({ 525 "id": self.uuid, 526 "readOnly": col_user.read_only, 527 "hidePasswords": col_user.hide_passwords, 528 }) 529 } 530 531 pub async fn save(&self, conn: &DbConn) -> EmptyResult { 532 User::update_uuid_revision(&self.user_uuid, conn).await; 533 db_run! { conn: 534 { 535 match diesel::replace_into(users_organizations::table) 536 .values(UserOrganizationDb::to_db(self)) 537 .execute(conn) 538 { 539 Ok(_) => Ok(()), 540 // Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first. 541 Err(result::Error::DatabaseError(DatabaseErrorKind::ForeignKeyViolation, _)) => { 542 diesel::update(users_organizations::table) 543 .filter(users_organizations::uuid.eq(&self.uuid)) 544 .set(UserOrganizationDb::to_db(self)) 545 .execute(conn) 546 .map_res("Error adding user to organization") 547 }, 548 Err(e) => Err(e.into()), 549 }.map_res("Error adding user to organization") 550 } 551 } 552 } 553 554 pub async fn delete(self, conn: &DbConn) -> EmptyResult { 555 User::update_uuid_revision(&self.user_uuid, conn).await; 556 CollectionUser::delete_all_by_user_and_org(&self.user_uuid, &self.org_uuid, conn).await?; 557 db_run! { conn: { 558 diesel::delete(users_organizations::table.filter(users_organizations::uuid.eq(self.uuid))) 559 .execute(conn) 560 .map_res("Error removing user from organization") 561 }} 562 } 563 564 async fn delete_all_by_organization(org_uuid: &str, conn: &DbConn) -> EmptyResult { 565 for user_org in Self::find_by_org(org_uuid, conn).await { 566 user_org.delete(conn).await?; 567 } 568 Ok(()) 569 } 570 571 pub async fn delete_all_by_user(user_uuid: &str, conn: &DbConn) -> EmptyResult { 572 for user_org in Self::find_any_state_by_user(user_uuid, conn).await { 573 user_org.delete(conn).await?; 574 } 575 Ok(()) 576 } 577 578 pub async fn find_by_email_and_org(email: &str, org_id: &str, conn: &DbConn) -> Option<Self> { 579 if let Some(user) = super::User::find_by_mail(email, conn).await { 580 if let Some(user_org) = Self::find_by_user_and_org(&user.uuid, org_id, conn).await { 581 return Some(user_org); 582 } 583 } 584 585 None 586 } 587 588 pub fn has_status(&self, status: UserOrgStatus) -> bool { 589 self.status == i32::from(status) 590 } 591 592 pub fn has_full_access(&self) -> bool { 593 (self.access_all || self.atype >= UserOrgType::Admin) 594 && self.has_status(UserOrgStatus::Confirmed) 595 } 596 597 pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> { 598 db_run! { conn: { 599 users_organizations::table 600 .filter(users_organizations::uuid.eq(uuid)) 601 .first::<UserOrganizationDb>(conn) 602 .ok().from_db() 603 }} 604 } 605 606 pub async fn find_by_uuid_and_org(uuid: &str, org_uuid: &str, conn: &DbConn) -> Option<Self> { 607 db_run! { conn: { 608 users_organizations::table 609 .filter(users_organizations::uuid.eq(uuid)) 610 .filter(users_organizations::org_uuid.eq(org_uuid)) 611 .first::<UserOrganizationDb>(conn) 612 .ok().from_db() 613 }} 614 } 615 616 pub async fn find_confirmed_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> { 617 db_run! { conn: { 618 users_organizations::table 619 .filter(users_organizations::user_uuid.eq(user_uuid)) 620 .filter(users_organizations::status.eq(i32::from(UserOrgStatus::Confirmed))) 621 .load::<UserOrganizationDb>(conn) 622 .unwrap_or_default().from_db() 623 }} 624 } 625 626 pub async fn find_invited_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> { 627 db_run! { conn: { 628 users_organizations::table 629 .filter(users_organizations::user_uuid.eq(user_uuid)) 630 .filter(users_organizations::status.eq(i32::from(UserOrgStatus::Invited))) 631 .load::<UserOrganizationDb>(conn) 632 .unwrap_or_default().from_db() 633 }} 634 } 635 636 async fn find_any_state_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> { 637 db_run! { conn: { 638 users_organizations::table 639 .filter(users_organizations::user_uuid.eq(user_uuid)) 640 .load::<UserOrganizationDb>(conn) 641 .unwrap_or_default().from_db() 642 }} 643 } 644 645 pub async fn count_accepted_and_confirmed_by_user(user_uuid: &str, conn: &DbConn) -> u64 { 646 u64::try_from({ 647 db_run! { conn: { 648 users_organizations::table 649 .filter(users_organizations::user_uuid.eq(user_uuid)) 650 .filter(users_organizations::status.eq(i32::from(UserOrgStatus::Accepted)) 651 .or(users_organizations::status.eq(i32::from(UserOrgStatus::Confirmed)))) 652 .count() 653 .first::<i64>(conn) 654 .unwrap_or(0) 655 }} 656 }) 657 .expect("underflow") 658 } 659 660 pub async fn find_by_org(org_uuid: &str, conn: &DbConn) -> Vec<Self> { 661 db_run! { conn: { 662 users_organizations::table 663 .filter(users_organizations::org_uuid.eq(org_uuid)) 664 .load::<UserOrganizationDb>(conn) 665 .expect("Error loading user organizations").from_db() 666 }} 667 } 668 669 pub async fn find_by_org_and_type( 670 org_uuid: &str, 671 atype: UserOrgType, 672 conn: &DbConn, 673 ) -> Vec<Self> { 674 db_run! { conn: { 675 users_organizations::table 676 .filter(users_organizations::org_uuid.eq(org_uuid)) 677 .filter(users_organizations::atype.eq(i32::from(atype))) 678 .load::<UserOrganizationDb>(conn) 679 .expect("Error loading user organizations").from_db() 680 }} 681 } 682 683 pub async fn count_confirmed_by_org_and_type( 684 org_uuid: &str, 685 atype: UserOrgType, 686 conn: &DbConn, 687 ) -> u64 { 688 u64::try_from({ 689 db_run! { conn: { 690 users_organizations::table 691 .filter(users_organizations::org_uuid.eq(org_uuid)) 692 .filter(users_organizations::atype.eq(i32::from(atype))) 693 .filter(users_organizations::status.eq(i32::from(UserOrgStatus::Confirmed))) 694 .count() 695 .first::<i64>(conn) 696 .unwrap_or(0) 697 }} 698 }) 699 .expect("underflow") 700 } 701 702 pub async fn find_by_user_and_org( 703 user_uuid: &str, 704 org_uuid: &str, 705 conn: &DbConn, 706 ) -> Option<Self> { 707 db_run! { conn: { 708 users_organizations::table 709 .filter(users_organizations::user_uuid.eq(user_uuid)) 710 .filter(users_organizations::org_uuid.eq(org_uuid)) 711 .first::<UserOrganizationDb>(conn) 712 .ok().from_db() 713 }} 714 } 715 716 pub async fn find_confirmed_by_user_and_org( 717 user_uuid: &str, 718 org_uuid: &str, 719 conn: &DbConn, 720 ) -> Option<Self> { 721 db_run! { conn: { 722 users_organizations::table 723 .filter(users_organizations::user_uuid.eq(user_uuid)) 724 .filter(users_organizations::org_uuid.eq(org_uuid)) 725 .filter( 726 users_organizations::status.eq(i32::from(UserOrgStatus::Confirmed)) 727 ) 728 .first::<UserOrganizationDb>(conn) 729 .ok().from_db() 730 }} 731 } 732 733 pub async fn find_by_user(user_uuid: &str, conn: &DbConn) -> Vec<Self> { 734 db_run! { conn: { 735 users_organizations::table 736 .filter(users_organizations::user_uuid.eq(user_uuid)) 737 .load::<UserOrganizationDb>(conn) 738 .expect("Error loading user organizations").from_db() 739 }} 740 } 741 742 pub async fn find_by_user_and_policy( 743 user_uuid: &str, 744 policy_type: OrgPolicyType, 745 conn: &DbConn, 746 ) -> Vec<Self> { 747 db_run! { conn: { 748 users_organizations::table 749 .inner_join( 750 org_policies::table.on( 751 org_policies::org_uuid.eq(users_organizations::org_uuid) 752 .and(users_organizations::user_uuid.eq(user_uuid)) 753 .and(org_policies::atype.eq(i32::from(policy_type))) 754 .and(org_policies::enabled.eq(true))) 755 ) 756 .filter( 757 users_organizations::status.eq(i32::from(UserOrgStatus::Confirmed)) 758 ) 759 .select(users_organizations::all_columns) 760 .load::<UserOrganizationDb>(conn) 761 .unwrap_or_default().from_db() 762 }} 763 } 764 765 pub async fn find_by_cipher_and_org( 766 cipher_uuid: &str, 767 org_uuid: &str, 768 conn: &DbConn, 769 ) -> Vec<Self> { 770 db_run! { conn: { 771 users_organizations::table 772 .filter(users_organizations::org_uuid.eq(org_uuid)) 773 .left_join(users_collections::table.on( 774 users_collections::user_uuid.eq(users_organizations::user_uuid) 775 )) 776 .left_join(ciphers_collections::table.on( 777 ciphers_collections::collection_uuid.eq(users_collections::collection_uuid).and( 778 ciphers_collections::cipher_uuid.eq(&cipher_uuid) 779 ) 780 )) 781 .filter( 782 users_organizations::access_all.eq(true).or( // AccessAll.. 783 ciphers_collections::cipher_uuid.eq(&cipher_uuid) // ..or access to collection with cipher 784 ) 785 ) 786 .select(users_organizations::all_columns) 787 .distinct() 788 .load::<UserOrganizationDb>(conn).expect("Error loading user organizations").from_db() 789 }} 790 } 791 792 pub async fn find_by_collection_and_org( 793 collection_uuid: &str, 794 org_uuid: &str, 795 conn: &DbConn, 796 ) -> Vec<Self> { 797 db_run! { conn: { 798 users_organizations::table 799 .filter(users_organizations::org_uuid.eq(org_uuid)) 800 .left_join(users_collections::table.on( 801 users_collections::user_uuid.eq(users_organizations::user_uuid) 802 )) 803 .filter( 804 users_organizations::access_all.eq(true).or( // AccessAll.. 805 users_collections::collection_uuid.eq(&collection_uuid) // ..or access to collection with cipher 806 ) 807 ) 808 .select(users_organizations::all_columns) 809 .load::<UserOrganizationDb>(conn).expect("Error loading user organizations").from_db() 810 }} 811 } 812 }