From 0b9e3bafd3692c5c247d2fb24083fb9ae3b22b96 Mon Sep 17 00:00:00 2001 From: Stefan Melmuk Date: Fri, 20 Dec 2024 21:04:58 +0100 Subject: [PATCH] rename membership rename UserOrganization to Membership to clarify the relation and prevent confusion whether something refers to a member(ship) or user --- src/api/admin.rs | 43 ++- src/api/core/accounts.rs | 30 +- src/api/core/ciphers.rs | 33 +- src/api/core/emergency_access.rs | 6 +- src/api/core/events.rs | 12 +- src/api/core/organizations.rs | 593 +++++++++++++++---------------- src/api/core/public.rs | 65 ++-- src/api/core/two_factor/mod.rs | 11 +- src/api/identity.rs | 6 +- src/auth.rs | 46 +-- src/db/models/cipher.rs | 35 +- src/db/models/collection.rs | 50 +-- src/db/models/device.rs | 10 +- src/db/models/event.rs | 6 +- src/db/models/group.rs | 26 +- src/db/models/mod.rs | 2 +- src/db/models/org_policy.rs | 24 +- src/db/models/organization.rs | 166 ++++----- src/db/models/user.rs | 14 +- src/mail.rs | 6 +- 20 files changed, 568 insertions(+), 616 deletions(-) diff --git a/src/api/admin.rs b/src/api/admin.rs index f49f6ed3..ddb21f79 100644 --- a/src/api/admin.rs +++ b/src/api/admin.rs @@ -50,7 +50,7 @@ pub fn routes() -> Vec { disable_user, enable_user, remove_2fa, - update_user_org_type, + update_membership_type, update_revision_users, post_config, delete_config, @@ -394,15 +394,15 @@ async fn get_user_json(uuid: &str, _token: AdminToken, mut conn: DbConn) -> Json async fn delete_user(uuid: &str, token: AdminToken, mut conn: DbConn) -> EmptyResult { let user = get_user_or_404(uuid, &mut conn).await?; - // Get the user_org records before deleting the actual user - let user_orgs = UserOrganization::find_any_state_by_user(uuid, &mut conn).await; + // Get the membership records before deleting the actual user + let memberships = Membership::find_any_state_by_user(uuid, &mut conn).await; let res = user.delete(&mut conn).await; - for user_org in user_orgs { + for membership in memberships { log_event( EventType::OrganizationUserRemoved as i32, - &user_org.uuid, - &user_org.org_uuid, + &membership.uuid, + &membership.org_uuid, ACTING_ADMIN_USER, 14, // Use UnknownBrowser type &token.ip.ip, @@ -485,42 +485,41 @@ async fn resend_user_invite(uuid: &str, _token: AdminToken, mut conn: DbConn) -> } #[derive(Debug, Deserialize)] -struct UserOrgTypeData { +struct MembershipTypeData { user_type: NumberOrString, user_uuid: String, org_uuid: String, } #[post("/users/org_type", data = "")] -async fn update_user_org_type(data: Json, token: AdminToken, mut conn: DbConn) -> EmptyResult { - let data: UserOrgTypeData = data.into_inner(); +async fn update_membership_type(data: Json, token: AdminToken, mut conn: DbConn) -> EmptyResult { + let data: MembershipTypeData = data.into_inner(); - let Some(mut user_to_edit) = - UserOrganization::find_by_user_and_org(&data.user_uuid, &data.org_uuid, &mut conn).await + let Some(mut member_to_edit) = Membership::find_by_user_and_org(&data.user_uuid, &data.org_uuid, &mut conn).await else { err!("The specified user isn't member of the organization") }; - let new_type = match UserOrgType::from_str(&data.user_type.into_string()) { + let new_type = match MembershipType::from_str(&data.user_type.into_string()) { Some(new_type) => new_type as i32, None => err!("Invalid type"), }; - if user_to_edit.atype == UserOrgType::Owner && new_type != UserOrgType::Owner { + if member_to_edit.atype == MembershipType::Owner && new_type != MembershipType::Owner { // Removing owner permission, check that there is at least one other confirmed owner - if UserOrganization::count_confirmed_by_org_and_type(&data.org_uuid, UserOrgType::Owner, &mut conn).await <= 1 { + if Membership::count_confirmed_by_org_and_type(&data.org_uuid, MembershipType::Owner, &mut conn).await <= 1 { err!("Can't change the type of the last owner") } } - // This check is also done at api::organizations::{accept_invite(), _confirm_invite, _activate_user(), edit_user()}, update_user_org_type + // This check is also done at api::organizations::{accept_invite(), _confirm_invite, _activate_user(), edit_user()}, update_membership_type // It returns different error messages per function. - if new_type < UserOrgType::Admin { - match OrgPolicy::is_user_allowed(&user_to_edit.user_uuid, &user_to_edit.org_uuid, true, &mut conn).await { + if new_type < MembershipType::Admin { + match OrgPolicy::is_user_allowed(&member_to_edit.user_uuid, &member_to_edit.org_uuid, true, &mut conn).await { Ok(_) => {} Err(OrgPolicyErr::TwoFactorMissing) => { if CONFIG.email_2fa_auto_fallback() { - two_factor::email::find_and_activate_email_2fa(&user_to_edit.user_uuid, &mut conn).await?; + two_factor::email::find_and_activate_email_2fa(&member_to_edit.user_uuid, &mut conn).await?; } else { err!("You cannot modify this user to this type because they have not setup 2FA"); } @@ -533,7 +532,7 @@ async fn update_user_org_type(data: Json, token: AdminToken, mu log_event( EventType::OrganizationUserUpdated as i32, - &user_to_edit.uuid, + &member_to_edit.uuid, &data.org_uuid, ACTING_ADMIN_USER, 14, // Use UnknownBrowser type @@ -542,8 +541,8 @@ async fn update_user_org_type(data: Json, token: AdminToken, mu ) .await; - user_to_edit.atype = new_type; - user_to_edit.save(&mut conn).await + member_to_edit.atype = new_type; + member_to_edit.save(&mut conn).await } #[post("/users/update_revision")] @@ -557,7 +556,7 @@ async fn organizations_overview(_token: AdminToken, mut conn: DbConn) -> ApiResu let mut organizations_json = Vec::with_capacity(organizations.len()); for o in organizations { let mut org = o.to_json(); - org["user_count"] = json!(UserOrganization::count_by_org(&o.uuid, &mut conn).await); + org["user_count"] = json!(Membership::count_by_org(&o.uuid, &mut conn).await); org["cipher_count"] = json!(Cipher::count_by_org(&o.uuid, &mut conn).await); org["collection_count"] = json!(Collection::count_by_org(&o.uuid, &mut conn).await); org["group_count"] = json!(Group::count_by_org(&o.uuid, &mut conn).await); diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index d95a50b8..92d72d57 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -106,15 +106,15 @@ fn enforce_password_hint_setting(password_hint: &Option) -> EmptyResult } Ok(()) } -async fn is_email_2fa_required(org_user_uuid: Option, conn: &mut DbConn) -> bool { +async fn is_email_2fa_required(member_uuid: Option, conn: &mut DbConn) -> bool { if !CONFIG._enable_email_2fa() { return false; } if CONFIG.email_2fa_enforce_on_verified_invite() { return true; } - if org_user_uuid.is_some() { - return OrgPolicy::is_enabled_for_member(&org_user_uuid.unwrap(), OrgPolicyType::TwoFactorAuthentication, conn) + if member_uuid.is_some() { + return OrgPolicy::is_enabled_for_member(&member_uuid.unwrap(), OrgPolicyType::TwoFactorAuthentication, conn) .await; } false @@ -161,9 +161,9 @@ pub async fn _register(data: Json, mut conn: DbConn) -> JsonResult err!("Registration email does not match invite email") } } else if Invitation::take(&email, &mut conn).await { - for user_org in UserOrganization::find_invited_by_user(&user.uuid, &mut conn).await.iter_mut() { - user_org.status = UserOrgStatus::Accepted as i32; - user_org.save(&mut conn).await?; + for membership in Membership::find_invited_by_user(&user.uuid, &mut conn).await.iter_mut() { + membership.status = MembershipStatus::Accepted as i32; + membership.save(&mut conn).await?; } user } else if CONFIG.is_signup_allowed(&email) @@ -484,7 +484,7 @@ fn validate_keydata( existing_ciphers: &[Cipher], existing_folders: &[Folder], existing_emergency_access: &[EmergencyAccess], - existing_user_orgs: &[UserOrganization], + existing_memberships: &[Membership], existing_sends: &[Send], ) -> EmptyResult { // Check that we're correctly rotating all the user's ciphers @@ -516,7 +516,7 @@ fn validate_keydata( } // Check that we're correctly rotating all the user's reset password keys - let existing_reset_password_ids = existing_user_orgs.iter().map(|uo| uo.org_uuid.as_str()).collect::>(); + let existing_reset_password_ids = existing_memberships.iter().map(|m| m.org_uuid.as_str()).collect::>(); let provided_reset_password_ids = data.reset_password_keys.iter().map(|rp| rp.organization_id.as_str()).collect::>(); if !provided_reset_password_ids.is_superset(&existing_reset_password_ids) { @@ -555,9 +555,9 @@ async fn post_rotatekey(data: Json, headers: Headers, mut conn: DbConn, let mut existing_ciphers = Cipher::find_owned_by_user(user_uuid, &mut conn).await; let mut existing_folders = Folder::find_by_user(user_uuid, &mut conn).await; let mut existing_emergency_access = EmergencyAccess::find_all_by_grantor_uuid(user_uuid, &mut conn).await; - let mut existing_user_orgs = UserOrganization::find_by_user(user_uuid, &mut conn).await; + let mut existing_memberships = Membership::find_by_user(user_uuid, &mut conn).await; // We only rotate the reset password key if it is set. - existing_user_orgs.retain(|uo| uo.reset_password_key.is_some()); + existing_memberships.retain(|m| m.reset_password_key.is_some()); let mut existing_sends = Send::find_by_user(user_uuid, &mut conn).await; validate_keydata( @@ -565,7 +565,7 @@ async fn post_rotatekey(data: Json, headers: Headers, mut conn: DbConn, &existing_ciphers, &existing_folders, &existing_emergency_access, - &existing_user_orgs, + &existing_memberships, &existing_sends, )?; @@ -597,14 +597,14 @@ async fn post_rotatekey(data: Json, headers: Headers, mut conn: DbConn, // Update reset password data for reset_password_data in data.reset_password_keys { - let Some(user_org) = - existing_user_orgs.iter_mut().find(|uo| uo.org_uuid == reset_password_data.organization_id) + let Some(membership) = + existing_memberships.iter_mut().find(|m| m.org_uuid == reset_password_data.organization_id) else { err!("Reset password doesn't exist") }; - user_org.reset_password_key = Some(reset_password_data.reset_password_key); - user_org.save(&mut conn).await? + membership.reset_password_key = Some(reset_password_data.reset_password_key); + membership.save(&mut conn).await? } // Update send data diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs index 136b890a..8e1c4f0e 100644 --- a/src/api/core/ciphers.rs +++ b/src/api/core/ciphers.rs @@ -405,11 +405,11 @@ pub async fn update_cipher_from_data( let transfer_cipher = cipher.organization_uuid.is_none() && data.organization_id.is_some(); if let Some(org_id) = data.organization_id { - match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, conn).await { + match Membership::find_by_user_and_org(&headers.user.uuid, &org_id, conn).await { None => err!("You don't have permission to add item to organization"), - Some(org_user) => { + Some(member) => { if shared_to_collections.is_some() - || org_user.has_full_access() + || member.has_full_access() || cipher.is_write_accessible_to_user(&headers.user.uuid, conn).await { cipher.organization_uuid = Some(org_id); @@ -1593,10 +1593,10 @@ async fn delete_all( match organization { Some(org_data) => { // Organization ID in query params, purging organization vault - match UserOrganization::find_by_user_and_org(&user.uuid, &org_data.org_id, &mut conn).await { + match Membership::find_by_user_and_org(&user.uuid, &org_data.org_id, &mut conn).await { None => err!("You don't have permission to purge the organization vault"), - Some(user_org) => { - if user_org.atype == UserOrgType::Owner { + Some(member) => { + if member.atype == MembershipType::Owner { Cipher::delete_all_by_organization(&org_data.org_id, &mut conn).await?; nt.send_user_update(UpdateType::SyncVault, &user).await; @@ -1835,7 +1835,7 @@ pub struct CipherSyncData { pub cipher_folders: HashMap, pub cipher_favorites: HashSet, pub cipher_collections: HashMap>, - pub user_organizations: HashMap, + pub members: HashMap, pub user_collections: HashMap, pub user_collections_groups: HashMap, pub user_group_full_access_for_organizations: HashSet, @@ -1869,8 +1869,8 @@ impl CipherSyncData { } // Generate a list of Cipher UUID's containing a Vec with one or more Attachment records - let user_org_uuids = UserOrganization::get_org_uuid_by_user(user_uuid, conn).await; - let attachments = Attachment::find_all_by_user_and_orgs(user_uuid, &user_org_uuids, conn).await; + let orgs = Membership::get_orgs_by_user(user_uuid, conn).await; + let attachments = Attachment::find_all_by_user_and_orgs(user_uuid, &orgs, conn).await; let mut cipher_attachments: HashMap> = HashMap::with_capacity(attachments.len()); for attachment in attachments { cipher_attachments.entry(attachment.cipher_uuid.clone()).or_default().push(attachment); @@ -1884,12 +1884,9 @@ impl CipherSyncData { cipher_collections.entry(cipher).or_default().push(collection); } - // Generate a HashMap with the Organization UUID as key and the UserOrganization record - let user_organizations: HashMap = UserOrganization::find_by_user(user_uuid, conn) - .await - .into_iter() - .map(|uo| (uo.org_uuid.clone(), uo)) - .collect(); + // Generate a HashMap with the Organization UUID as key and the Membership record + let members: HashMap = + Membership::find_by_user(user_uuid, conn).await.into_iter().map(|m| (m.org_uuid.clone(), m)).collect(); // Generate a HashMap with the User_Collections UUID as key and the CollectionUser record let user_collections: HashMap = CollectionUser::find_by_user(user_uuid, conn) @@ -1909,9 +1906,9 @@ impl CipherSyncData { HashMap::new() }; - // Get all organizations that the user has full access to via group assignment + // Get all organizations that the given user has full access to via group assignment let user_group_full_access_for_organizations: HashSet = if CONFIG.org_groups_enabled() { - Group::gather_user_organizations_full_access(user_uuid, conn).await.into_iter().collect() + Group::get_orgs_by_user_with_full_access(user_uuid, conn).await.into_iter().collect() } else { HashSet::new() }; @@ -1921,7 +1918,7 @@ impl CipherSyncData { cipher_folders, cipher_favorites, cipher_collections, - user_organizations, + members, user_collections, user_collections_groups, user_group_full_access_for_organizations, diff --git a/src/api/core/emergency_access.rs b/src/api/core/emergency_access.rs index 8ed9e87a..6d3d881d 100644 --- a/src/api/core/emergency_access.rs +++ b/src/api/core/emergency_access.rs @@ -662,9 +662,9 @@ async fn password_emergency_access( TwoFactor::delete_all_by_user(&grantor_user.uuid, &mut conn).await?; // Remove grantor from all organisations unless Owner - for user_org in UserOrganization::find_any_state_by_user(&grantor_user.uuid, &mut conn).await { - if user_org.atype != UserOrgType::Owner as i32 { - user_org.delete(&mut conn).await?; + for member in Membership::find_any_state_by_user(&grantor_user.uuid, &mut conn).await { + if member.atype != MembershipType::Owner as i32 { + member.delete(&mut conn).await?; } } Ok(()) diff --git a/src/api/core/events.rs b/src/api/core/events.rs index 484094f5..49ff083f 100644 --- a/src/api/core/events.rs +++ b/src/api/core/events.rs @@ -8,7 +8,7 @@ use crate::{ api::{EmptyResult, JsonResult}, auth::{AdminHeaders, Headers}, db::{ - models::{Cipher, Event, UserOrganization}, + models::{Cipher, Event, Membership}, DbConn, DbPool, }, util::parse_date, @@ -66,7 +66,7 @@ async fn get_cipher_events(cipher_id: &str, data: EventRange, headers: Headers, Vec::with_capacity(0) } else { let mut events_json = Vec::with_capacity(0); - if UserOrganization::user_has_ge_admin_access_to_cipher(&headers.user.uuid, cipher_id, &mut conn).await { + if Membership::user_has_ge_admin_access_to_cipher(&headers.user.uuid, cipher_id, &mut conn).await { let start_date = parse_date(&data.start); let end_date = if let Some(before_date) = &data.continuation_token { parse_date(before_date) @@ -90,10 +90,10 @@ async fn get_cipher_events(cipher_id: &str, data: EventRange, headers: Headers, }))) } -#[get("/organizations//users//events?")] +#[get("/organizations//users//events?")] async fn get_user_events( org_id: &str, - user_org_id: &str, + member_id: &str, data: EventRange, _headers: AdminHeaders, mut conn: DbConn, @@ -110,7 +110,7 @@ async fn get_user_events( parse_date(&data.end) }; - Event::find_by_org_and_user_org(org_id, user_org_id, &start_date, &end_date, &mut conn) + Event::find_by_org_and_member(org_id, member_id, &start_date, &end_date, &mut conn) .await .iter() .map(|e| e.to_json()) @@ -233,7 +233,7 @@ async fn _log_user_event( ip: &IpAddr, conn: &mut DbConn, ) { - let orgs = UserOrganization::get_org_uuid_by_user(user_uuid, conn).await; + let orgs = Membership::get_orgs_by_user(user_uuid, conn).await; let mut events: Vec = Vec::with_capacity(orgs.len() + 1); // We need an event per org and one without an org // Upstream saves the event also without any org_uuid. diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs index f3158536..b5ffe3ed 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -41,7 +41,7 @@ pub fn routes() -> Vec { bulk_delete_organization_collections, post_bulk_collections, get_org_details, - get_org_users, + get_members, send_invite, reinvite_user, bulk_reinvite_user, @@ -50,7 +50,7 @@ pub fn routes() -> Vec { accept_invite, get_user, edit_user, - put_organization_user, + put_membership, delete_user, bulk_delete_user, post_delete_user, @@ -68,14 +68,14 @@ pub fn routes() -> Vec { get_organization_keys, get_organization_public_key, bulk_public_keys, - deactivate_organization_user, - bulk_deactivate_organization_user, - revoke_organization_user, - bulk_revoke_organization_user, - activate_organization_user, - bulk_activate_organization_user, - restore_organization_user, - bulk_restore_organization_user, + deactivate_membership, + bulk_deactivate_membership, + revoke_membership, + bulk_revoke_membership, + activate_membership, + bulk_activate_membership, + restore_membership, + bulk_restore_membership, get_groups, post_groups, get_group, @@ -171,16 +171,16 @@ async fn create_organization(headers: Headers, data: Json, mut conn: Db }; let org = Organization::new(data.name, data.billing_email, private_key, public_key); - let mut user_org = UserOrganization::new(headers.user.uuid, org.uuid.clone()); + let mut member = Membership::new(headers.user.uuid, org.uuid.clone()); let collection = Collection::new(org.uuid.clone(), data.collection_name, None); - user_org.akey = data.key; - user_org.access_all = true; - user_org.atype = UserOrgType::Owner as i32; - user_org.status = UserOrgStatus::Confirmed as i32; + member.akey = data.key; + member.access_all = true; + member.atype = MembershipType::Owner as i32; + member.status = MembershipStatus::Confirmed as i32; org.save(&mut conn).await?; - user_org.save(&mut conn).await?; + member.save(&mut conn).await?; collection.save(&mut conn).await?; Ok(Json(org.to_json())) @@ -215,18 +215,18 @@ async fn post_delete_organization( #[post("/organizations//leave")] async fn leave_organization(org_id: &str, headers: Headers, mut conn: DbConn) -> EmptyResult { - match UserOrganization::find_by_user_and_org(&headers.user.uuid, org_id, &mut conn).await { + match Membership::find_by_user_and_org(&headers.user.uuid, org_id, &mut conn).await { None => err!("User not part of organization"), - Some(user_org) => { - if user_org.atype == UserOrgType::Owner - && UserOrganization::count_confirmed_by_org_and_type(org_id, UserOrgType::Owner, &mut conn).await <= 1 + Some(member) => { + if member.atype == MembershipType::Owner + && Membership::count_confirmed_by_org_and_type(org_id, MembershipType::Owner, &mut conn).await <= 1 { err!("The last owner can't leave") } log_event( EventType::OrganizationUserRemoved as i32, - &user_org.uuid, + &member.uuid, org_id, &headers.user.uuid, headers.device.atype, @@ -235,7 +235,7 @@ async fn leave_organization(org_id: &str, headers: Headers, mut conn: DbConn) -> ) .await; - user_org.delete(&mut conn).await + member.delete(&mut conn).await } } } @@ -317,7 +317,7 @@ async fn get_org_collections(org_id: &str, _headers: ManagerHeadersLoose, mut co async fn get_org_collections_details(org_id: &str, headers: ManagerHeadersLoose, mut conn: DbConn) -> JsonResult { let mut data = Vec::new(); - let Some(user_org) = UserOrganization::find_by_user_and_org(&headers.user.uuid, org_id, &mut conn).await else { + let Some(member) = Membership::find_by_user_and_org(&headers.user.uuid, org_id, &mut conn).await else { err!("User is not part of organization") }; @@ -325,16 +325,15 @@ async fn get_org_collections_details(org_id: &str, headers: ManagerHeadersLoose, let coll_users = CollectionUser::find_by_organization(org_id, &mut conn).await; // check if current user has full access to the organization (either directly or via any group) - let has_full_access_to_org = user_org.access_all - || (CONFIG.org_groups_enabled() - && GroupUser::has_full_access_by_member(org_id, &user_org.uuid, &mut conn).await); + let has_full_access_to_org = member.access_all + || (CONFIG.org_groups_enabled() && GroupUser::has_full_access_by_member(org_id, &member.uuid, &mut conn).await); for col in Collection::find_by_organization(org_id, &mut conn).await { // check whether the current user has access to the given collection let assigned = has_full_access_to_org - || CollectionUser::has_access_to_collection_by_user(&col.uuid, &user_org.user_uuid, &mut conn).await + || CollectionUser::has_access_to_collection_by_user(&col.uuid, &member.user_uuid, &mut conn).await || (CONFIG.org_groups_enabled() - && GroupUser::has_access_to_collection_by_member(&col.uuid, &user_org.uuid, &mut conn).await); + && GroupUser::has_access_to_collection_by_member(&col.uuid, &member.uuid, &mut conn).await); // get the users assigned directly to the given collection let users: Vec = coll_users @@ -410,20 +409,20 @@ async fn post_organization_collections( } for user in data.users { - let Some(org_user) = UserOrganization::find_by_uuid_and_org(&user.id, org_id, &mut conn).await else { + let Some(member) = Membership::find_by_uuid_and_org(&user.id, org_id, &mut conn).await else { err!("User is not part of organization") }; - if org_user.access_all { + if member.access_all { continue; } - CollectionUser::save(&org_user.user_uuid, &collection.uuid, user.read_only, user.hide_passwords, &mut conn) + CollectionUser::save(&member.user_uuid, &collection.uuid, user.read_only, user.hide_passwords, &mut conn) .await?; } - if headers.org_user.atype == UserOrgType::Manager && !headers.org_user.access_all { - CollectionUser::save(&headers.org_user.user_uuid, &collection.uuid, false, false, &mut conn).await?; + if headers.membership.atype == MembershipType::Manager && !headers.membership.access_all { + CollectionUser::save(&headers.membership.user_uuid, &collection.uuid, false, false, &mut conn).await?; } Ok(Json(collection.to_json())) @@ -488,25 +487,25 @@ async fn post_organization_collection_update( CollectionUser::delete_all_by_collection(col_id, &mut conn).await?; for user in data.users { - let Some(org_user) = UserOrganization::find_by_uuid_and_org(&user.id, org_id, &mut conn).await else { + let Some(member) = Membership::find_by_uuid_and_org(&user.id, org_id, &mut conn).await else { err!("User is not part of organization") }; - if org_user.access_all { + if member.access_all { continue; } - CollectionUser::save(&org_user.user_uuid, col_id, user.read_only, user.hide_passwords, &mut conn).await?; + CollectionUser::save(&member.user_uuid, col_id, user.read_only, user.hide_passwords, &mut conn).await?; } Ok(Json(collection.to_json_details(&headers.user.uuid, None, &mut conn).await)) } -#[delete("/organizations//collections//user/")] +#[delete("/organizations//collections//user/")] async fn delete_organization_collection_user( org_id: &str, col_id: &str, - org_user_id: &str, + member_id: &str, _headers: AdminHeaders, mut conn: DbConn, ) -> EmptyResult { @@ -514,10 +513,10 @@ async fn delete_organization_collection_user( err!("Collection not found", "Collection does not exist or does not belong to this organization") }; - match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &mut conn).await { + match Membership::find_by_uuid_and_org(member_id, org_id, &mut conn).await { None => err!("User not found in organization"), - Some(user_org) => { - match CollectionUser::find_by_collection_and_user(&collection.uuid, &user_org.user_uuid, &mut conn).await { + Some(member) => { + match CollectionUser::find_by_collection_and_user(&collection.uuid, &member.user_uuid, &mut conn).await { None => err!("User not assigned to collection"), Some(col_user) => col_user.delete(&mut conn).await, } @@ -525,15 +524,15 @@ async fn delete_organization_collection_user( } } -#[post("/organizations//collections//delete-user/")] +#[post("/organizations//collections//delete-user/")] async fn post_organization_collection_delete_user( org_id: &str, col_id: &str, - org_user_id: &str, + member_id: &str, headers: AdminHeaders, conn: DbConn, ) -> EmptyResult { - delete_organization_collection_user(org_id, col_id, org_user_id, headers, conn).await + delete_organization_collection_user(org_id, col_id, member_id, headers, conn).await } async fn _delete_organization_collection( @@ -626,8 +625,7 @@ async fn get_org_collection_detail( err!("Collection is not owned by organization") } - let Some(user_org) = UserOrganization::find_by_user_and_org(&headers.user.uuid, org_id, &mut conn).await - else { + let Some(member) = Membership::find_by_user_and_org(&headers.user.uuid, org_id, &mut conn).await else { err!("User is not part of organization") }; @@ -646,7 +644,7 @@ async fn get_org_collection_detail( }; let users: Vec = - CollectionUser::find_by_collection_swap_user_uuid_with_org_user_uuid(&collection.uuid, &mut conn) + CollectionUser::find_by_collection_swap_user_uuid_with_member_uuid(&collection.uuid, &mut conn) .await .iter() .map(|collection_user| { @@ -654,7 +652,7 @@ async fn get_org_collection_detail( }) .collect(); - let assigned = Collection::can_access_collection(&user_org, &collection.uuid, &mut conn).await; + let assigned = Collection::can_access_collection(&member, &collection.uuid, &mut conn).await; let mut json_object = collection.to_json_details(&headers.user.uuid, None, &mut conn).await; json_object["assigned"] = json!(assigned); @@ -677,7 +675,7 @@ async fn get_collection_users(org_id: &str, coll_id: &str, _headers: ManagerHead let mut user_list = Vec::new(); for col_user in CollectionUser::find_by_collection(&collection.uuid, &mut conn).await { user_list.push( - UserOrganization::find_by_user_and_org(&col_user.user_uuid, org_id, &mut conn) + Membership::find_by_user_and_org(&col_user.user_uuid, org_id, &mut conn) .await .unwrap() .to_json_user_access_restrictions(&col_user), @@ -705,7 +703,7 @@ async fn put_collection_users( // And then add all the received ones (except if the user has access_all) for d in data.iter() { - let Some(user) = UserOrganization::find_by_uuid_and_org(&d.id, org_id, &mut conn).await else { + let Some(user) = Membership::find_by_uuid_and_org(&d.id, org_id, &mut conn).await else { err!("User is not part of organization") }; @@ -727,9 +725,7 @@ struct OrgIdData { #[get("/ciphers/organization-details?")] async fn get_org_details(data: OrgIdData, headers: Headers, mut conn: DbConn) -> JsonResult { - if UserOrganization::find_confirmed_by_user_and_org(&headers.user.uuid, &data.organization_id, &mut conn) - .await - .is_none() + if Membership::find_confirmed_by_user_and_org(&headers.user.uuid, &data.organization_id, &mut conn).await.is_none() { err_code!("Resource not found.", rocket::http::Status::NotFound.code); } @@ -762,14 +758,14 @@ struct GetOrgUserData { } #[get("/organizations//users?")] -async fn get_org_users( +async fn get_members( data: GetOrgUserData, org_id: &str, _headers: ManagerHeadersLoose, mut conn: DbConn, ) -> Json { let mut users_json = Vec::new(); - for u in UserOrganization::find_by_org(org_id, &mut conn).await { + for u in Membership::find_by_org(org_id, &mut conn).await { users_json.push( u.to_json_user_details( data.include_collections.unwrap_or(false), @@ -836,17 +832,17 @@ struct InviteData { async fn send_invite(org_id: &str, data: Json, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult { let data: InviteData = data.into_inner(); - let new_type = match UserOrgType::from_str(&data.r#type.into_string()) { + let new_type = match MembershipType::from_str(&data.r#type.into_string()) { Some(new_type) => new_type as i32, None => err!("Invalid type"), }; - if new_type != UserOrgType::User && headers.org_user_type != UserOrgType::Owner { + if new_type != MembershipType::User && headers.membership_type != MembershipType::Owner { err!("Only Owners can invite Managers, Admins or Owners") } for email in data.emails.iter() { - let mut user_org_status = UserOrgStatus::Invited as i32; + let mut member_status = MembershipStatus::Invited as i32; let user = match User::find_by_mail(email, &mut conn).await { None => { if !CONFIG.invitations_allowed() { @@ -867,23 +863,23 @@ async fn send_invite(org_id: &str, data: Json, headers: AdminHeaders user } Some(user) => { - if UserOrganization::find_by_user_and_org(&user.uuid, org_id, &mut conn).await.is_some() { + if Membership::find_by_user_and_org(&user.uuid, org_id, &mut conn).await.is_some() { err!(format!("User already in organization: {email}")) } else { // automatically accept existing users if mail is disabled if !CONFIG.mail_enabled() && !user.password_hash.is_empty() { - user_org_status = UserOrgStatus::Accepted as i32; + member_status = MembershipStatus::Accepted as i32; } user } } }; - let mut new_user = UserOrganization::new(user.uuid.clone(), String::from(org_id)); + let mut new_user = Membership::new(user.uuid.clone(), String::from(org_id)); let access_all = data.access_all; new_user.access_all = access_all; new_user.atype = new_type; - new_user.status = user_org_status; + new_user.status = member_status; // If no accessAll, add the collections received if !access_all { @@ -952,8 +948,8 @@ async fn bulk_reinvite_user( let data: OrgBulkIds = data.into_inner(); let mut bulk_response = Vec::new(); - for org_user_id in data.ids { - let err_msg = match _reinvite_user(org_id, &org_user_id, &headers.user.email, &mut conn).await { + for member_id in data.ids { + let err_msg = match _reinvite_user(org_id, &member_id, &headers.user.email, &mut conn).await { Ok(_) => String::new(), Err(e) => format!("{e:?}"), }; @@ -961,7 +957,7 @@ async fn bulk_reinvite_user( bulk_response.push(json!( { "object": "OrganizationBulkConfirmResponseModel", - "id": org_user_id, + "id": member_id, "error": err_msg } )) @@ -974,21 +970,21 @@ async fn bulk_reinvite_user( })) } -#[post("/organizations//users//reinvite")] -async fn reinvite_user(org_id: &str, user_org: &str, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult { - _reinvite_user(org_id, user_org, &headers.user.email, &mut conn).await +#[post("/organizations//users//reinvite")] +async fn reinvite_user(org_id: &str, member: &str, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult { + _reinvite_user(org_id, member, &headers.user.email, &mut conn).await } -async fn _reinvite_user(org_id: &str, user_org: &str, invited_by_email: &str, conn: &mut DbConn) -> EmptyResult { - let Some(user_org) = UserOrganization::find_by_uuid_and_org(user_org, org_id, conn).await else { +async fn _reinvite_user(org_id: &str, member: &str, invited_by_email: &str, conn: &mut DbConn) -> EmptyResult { + let Some(member) = Membership::find_by_uuid_and_org(member, org_id, conn).await else { err!("The user hasn't been invited to the organization.") }; - if user_org.status != UserOrgStatus::Invited as i32 { + if member.status != MembershipStatus::Invited as i32 { err!("The user is already accepted or confirmed to the organization") } - let Some(user) = User::find_by_uuid(&user_org.user_uuid, conn).await else { + let Some(user) = User::find_by_uuid(&member.user_uuid, conn).await else { err!("User not found.") }; @@ -1005,7 +1001,7 @@ async fn _reinvite_user(org_id: &str, user_org: &str, invited_by_email: &str, co mail::send_invite( &user, Some(org_id.to_string()), - Some(user_org.uuid), + Some(member.uuid), &org_name, Some(invited_by_email.to_string()), ) @@ -1015,9 +1011,9 @@ async fn _reinvite_user(org_id: &str, user_org: &str, invited_by_email: &str, co invitation.save(conn).await?; } else { let _ = Invitation::take(&user.email, conn).await; - let mut user_org = user_org; - user_org.status = UserOrgStatus::Accepted as i32; - user_org.save(conn).await?; + let mut member = member; + member.status = MembershipStatus::Accepted as i32; + member.save(conn).await?; } Ok(()) @@ -1030,28 +1026,28 @@ struct AcceptData { reset_password_key: Option, } -#[post("/organizations//users//accept", data = "")] -async fn accept_invite(org_id: &str, org_user_id: &str, data: Json, mut conn: DbConn) -> EmptyResult { - // The web-vault passes org_id and org_user_id in the URL, but we are just reading them from the JWT instead +#[post("/organizations//users//accept", data = "")] +async fn accept_invite(org_id: &str, member_id: &str, data: Json, mut conn: DbConn) -> EmptyResult { + // The web-vault passes org_id and member_id in the URL, but we are just reading them from the JWT instead let data: AcceptData = data.into_inner(); let claims = decode_invite(&data.token)?; - // If a claim does not have a user_org_id or it does not match the one in from the URI, something is wrong. - match &claims.user_org_id { - Some(ou_id) if ou_id.eq(org_user_id) => {} - _ => err!("Error accepting the invitation", "Claim does not match the org_user_id"), + // If a claim does not have a member_id or it does not match the one in from the URI, something is wrong. + match &claims.member_id { + Some(ou_id) if ou_id.eq(member_id) => {} + _ => err!("Error accepting the invitation", "Claim does not match the member_id"), } match User::find_by_mail(&claims.email, &mut conn).await { Some(user) => { Invitation::take(&claims.email, &mut conn).await; - if let (Some(user_org), Some(org)) = (&claims.user_org_id, &claims.org_id) { - let Some(mut user_org) = UserOrganization::find_by_uuid_and_org(user_org, org, &mut conn).await else { + if let (Some(member), Some(org)) = (&claims.member_id, &claims.org_id) { + let Some(mut member) = Membership::find_by_uuid_and_org(member, org, &mut conn).await else { err!("Error accepting the invitation") }; - if user_org.status != UserOrgStatus::Invited as i32 { + if member.status != MembershipStatus::Invited as i32 { err!("User already accepted the invitation") } @@ -1060,10 +1056,10 @@ async fn accept_invite(org_id: &str, org_user_id: &str, data: Json, err!("Reset password key is required, but not provided."); } - // This check is also done at accept_invite(), _confirm_invite, _activate_user(), edit_user(), admin::update_user_org_type + // This check is also done at accept_invite(), _confirm_invite, _activate_user(), edit_user(), admin::update_membership_type // It returns different error messages per function. - if user_org.atype < UserOrgType::Admin { - match OrgPolicy::is_user_allowed(&user_org.user_uuid, org_id, false, &mut conn).await { + if member.atype < MembershipType::Admin { + match OrgPolicy::is_user_allowed(&member.user_uuid, org_id, false, &mut conn).await { Ok(_) => {} Err(OrgPolicyErr::TwoFactorMissing) => { if CONFIG.email_2fa_auto_fallback() { @@ -1078,13 +1074,13 @@ async fn accept_invite(org_id: &str, org_user_id: &str, data: Json, } } - user_org.status = UserOrgStatus::Accepted as i32; + member.status = MembershipStatus::Accepted as i32; if master_password_required { - user_org.reset_password_key = data.reset_password_key; + member.reset_password_key = data.reset_password_key; } - user_org.save(&mut conn).await?; + member.save(&mut conn).await?; } } None => err!("Invited user not found"), @@ -1137,9 +1133,9 @@ async fn bulk_confirm_invite( match data.keys { Some(keys) => { for invite in keys { - let org_user_id = invite.id.unwrap_or_default(); + let member_id = invite.id.unwrap_or_default(); let user_key = invite.key.unwrap_or_default(); - let err_msg = match _confirm_invite(org_id, &org_user_id, &user_key, &headers, &mut conn, &nt).await { + let err_msg = match _confirm_invite(org_id, &member_id, &user_key, &headers, &mut conn, &nt).await { Ok(_) => String::new(), Err(e) => format!("{e:?}"), }; @@ -1147,7 +1143,7 @@ async fn bulk_confirm_invite( bulk_response.push(json!( { "object": "OrganizationBulkConfirmResponseModel", - "id": org_user_id, + "id": member_id, "error": err_msg } )); @@ -1163,10 +1159,10 @@ async fn bulk_confirm_invite( })) } -#[post("/organizations//users//confirm", data = "")] +#[post("/organizations//users//confirm", data = "")] async fn confirm_invite( org_id: &str, - org_user_id: &str, + member_id: &str, data: Json, headers: AdminHeaders, mut conn: DbConn, @@ -1174,41 +1170,41 @@ async fn confirm_invite( ) -> EmptyResult { let data = data.into_inner(); let user_key = data.key.unwrap_or_default(); - _confirm_invite(org_id, org_user_id, &user_key, &headers, &mut conn, &nt).await + _confirm_invite(org_id, member_id, &user_key, &headers, &mut conn, &nt).await } async fn _confirm_invite( org_id: &str, - org_user_id: &str, + member_id: &str, key: &str, headers: &AdminHeaders, conn: &mut DbConn, nt: &Notify<'_>, ) -> EmptyResult { - if key.is_empty() || org_user_id.is_empty() { + if key.is_empty() || member_id.is_empty() { err!("Key or UserId is not set, unable to process request"); } - let Some(mut user_to_confirm) = UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await else { + let Some(mut member_to_confirm) = Membership::find_by_uuid_and_org(member_id, org_id, conn).await else { err!("The specified user isn't a member of the organization") }; - if user_to_confirm.atype != UserOrgType::User && headers.org_user_type != UserOrgType::Owner { + if member_to_confirm.atype != MembershipType::User && headers.membership_type != MembershipType::Owner { err!("Only Owners can confirm Managers, Admins or Owners") } - if user_to_confirm.status != UserOrgStatus::Accepted as i32 { + if member_to_confirm.status != MembershipStatus::Accepted as i32 { err!("User in invalid state") } - // This check is also done at accept_invite(), _confirm_invite, _activate_user(), edit_user(), admin::update_user_org_type + // This check is also done at accept_invite(), _confirm_invite, _activate_user(), edit_user(), admin::update_membership_type // It returns different error messages per function. - if user_to_confirm.atype < UserOrgType::Admin { - match OrgPolicy::is_user_allowed(&user_to_confirm.user_uuid, org_id, true, conn).await { + if member_to_confirm.atype < MembershipType::Admin { + match OrgPolicy::is_user_allowed(&member_to_confirm.user_uuid, org_id, true, conn).await { Ok(_) => {} Err(OrgPolicyErr::TwoFactorMissing) => { if CONFIG.email_2fa_auto_fallback() { - two_factor::email::find_and_activate_email_2fa(&user_to_confirm.user_uuid, conn).await?; + two_factor::email::find_and_activate_email_2fa(&member_to_confirm.user_uuid, conn).await?; } else { err!("You cannot confirm this user because they have not setup 2FA"); } @@ -1219,12 +1215,12 @@ async fn _confirm_invite( } } - user_to_confirm.status = UserOrgStatus::Confirmed as i32; - user_to_confirm.akey = key.to_string(); + member_to_confirm.status = MembershipStatus::Confirmed as i32; + member_to_confirm.akey = key.to_string(); log_event( EventType::OrganizationUserConfirmed as i32, - &user_to_confirm.uuid, + &member_to_confirm.uuid, org_id, &headers.user.uuid, headers.device.atype, @@ -1238,31 +1234,31 @@ async fn _confirm_invite( Some(org) => org.name, None => err!("Error looking up organization."), }; - let address = match User::find_by_uuid(&user_to_confirm.user_uuid, conn).await { + let address = match User::find_by_uuid(&member_to_confirm.user_uuid, conn).await { Some(user) => user.email, None => err!("Error looking up user."), }; mail::send_invite_confirmed(&address, &org_name).await?; } - let save_result = user_to_confirm.save(conn).await; + let save_result = member_to_confirm.save(conn).await; - if let Some(user) = User::find_by_uuid(&user_to_confirm.user_uuid, conn).await { + if let Some(user) = User::find_by_uuid(&member_to_confirm.user_uuid, conn).await { nt.send_user_update(UpdateType::SyncOrgKeys, &user).await; } save_result } -#[get("/organizations//users/?")] +#[get("/organizations//users/?")] async fn get_user( org_id: &str, - org_user_id: &str, + member_id: &str, data: GetOrgUserData, _headers: AdminHeaders, mut conn: DbConn, ) -> JsonResult { - let Some(user) = UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &mut conn).await else { + let Some(user) = Membership::find_by_uuid_and_org(member_id, org_id, &mut conn).await else { err!("The specified user isn't a member of the organization") }; @@ -1284,64 +1280,64 @@ struct EditUserData { access_all: bool, } -#[put("/organizations//users/", data = "", rank = 1)] -async fn put_organization_user( +#[put("/organizations//users/", data = "", rank = 1)] +async fn put_membership( org_id: &str, - org_user_id: &str, + member_id: &str, data: Json, headers: AdminHeaders, conn: DbConn, ) -> EmptyResult { - edit_user(org_id, org_user_id, data, headers, conn).await + edit_user(org_id, member_id, data, headers, conn).await } -#[post("/organizations//users/", data = "", rank = 1)] +#[post("/organizations//users/", data = "", rank = 1)] async fn edit_user( org_id: &str, - org_user_id: &str, + member_id: &str, data: Json, headers: AdminHeaders, mut conn: DbConn, ) -> EmptyResult { let data: EditUserData = data.into_inner(); - let Some(new_type) = UserOrgType::from_str(&data.r#type.into_string()) else { + let Some(new_type) = MembershipType::from_str(&data.r#type.into_string()) else { err!("Invalid type") }; - let Some(mut user_to_edit) = UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &mut conn).await else { + let Some(mut member_to_edit) = Membership::find_by_uuid_and_org(member_id, org_id, &mut conn).await else { err!("The specified user isn't member of the organization") }; - if new_type != user_to_edit.atype - && (user_to_edit.atype >= UserOrgType::Admin || new_type >= UserOrgType::Admin) - && headers.org_user_type != UserOrgType::Owner + if new_type != member_to_edit.atype + && (member_to_edit.atype >= MembershipType::Admin || new_type >= MembershipType::Admin) + && headers.membership_type != MembershipType::Owner { err!("Only Owners can grant and remove Admin or Owner privileges") } - if user_to_edit.atype == UserOrgType::Owner && headers.org_user_type != UserOrgType::Owner { + if member_to_edit.atype == MembershipType::Owner && headers.membership_type != MembershipType::Owner { err!("Only Owners can edit Owner users") } - if user_to_edit.atype == UserOrgType::Owner - && new_type != UserOrgType::Owner - && user_to_edit.status == UserOrgStatus::Confirmed as i32 + if member_to_edit.atype == MembershipType::Owner + && new_type != MembershipType::Owner + && member_to_edit.status == MembershipStatus::Confirmed as i32 { // Removing owner permission, check that there is at least one other confirmed owner - if UserOrganization::count_confirmed_by_org_and_type(org_id, UserOrgType::Owner, &mut conn).await <= 1 { + if Membership::count_confirmed_by_org_and_type(org_id, MembershipType::Owner, &mut conn).await <= 1 { err!("Can't delete the last owner") } } - // This check is also done at accept_invite(), _confirm_invite, _activate_user(), edit_user(), admin::update_user_org_type + // This check is also done at accept_invite(), _confirm_invite, _activate_user(), edit_user(), admin::update_membership_type // It returns different error messages per function. - if new_type < UserOrgType::Admin { - match OrgPolicy::is_user_allowed(&user_to_edit.user_uuid, org_id, true, &mut conn).await { + if new_type < MembershipType::Admin { + match OrgPolicy::is_user_allowed(&member_to_edit.user_uuid, org_id, true, &mut conn).await { Ok(_) => {} Err(OrgPolicyErr::TwoFactorMissing) => { if CONFIG.email_2fa_auto_fallback() { - two_factor::email::find_and_activate_email_2fa(&user_to_edit.user_uuid, &mut conn).await?; + two_factor::email::find_and_activate_email_2fa(&member_to_edit.user_uuid, &mut conn).await?; } else { err!("You cannot modify this user to this type because they have not setup 2FA"); } @@ -1352,11 +1348,11 @@ async fn edit_user( } } - user_to_edit.access_all = data.access_all; - user_to_edit.atype = new_type as i32; + member_to_edit.access_all = data.access_all; + member_to_edit.atype = new_type as i32; // Delete all the odd collections - for c in CollectionUser::find_by_organization_and_user_uuid(org_id, &user_to_edit.user_uuid, &mut conn).await { + for c in CollectionUser::find_by_organization_and_user_uuid(org_id, &member_to_edit.user_uuid, &mut conn).await { c.delete(&mut conn).await?; } @@ -1367,7 +1363,7 @@ async fn edit_user( None => err!("Collection not found in Organization"), Some(collection) => { CollectionUser::save( - &user_to_edit.user_uuid, + &member_to_edit.user_uuid, &collection.uuid, col.read_only, col.hide_passwords, @@ -1379,16 +1375,16 @@ async fn edit_user( } } - GroupUser::delete_all_by_user(&user_to_edit.uuid, &mut conn).await?; + GroupUser::delete_all_by_member(&member_to_edit.uuid, &mut conn).await?; for group in data.groups.iter().flatten() { - let mut group_entry = GroupUser::new(String::from(group), user_to_edit.uuid.clone()); + let mut group_entry = GroupUser::new(String::from(group), member_to_edit.uuid.clone()); group_entry.save(&mut conn).await?; } log_event( EventType::OrganizationUserUpdated as i32, - &user_to_edit.uuid, + &member_to_edit.uuid, org_id, &headers.user.uuid, headers.device.atype, @@ -1397,7 +1393,7 @@ async fn edit_user( ) .await; - user_to_edit.save(&mut conn).await + member_to_edit.save(&mut conn).await } #[delete("/organizations//users", data = "")] @@ -1411,8 +1407,8 @@ async fn bulk_delete_user( let data: OrgBulkIds = data.into_inner(); let mut bulk_response = Vec::new(); - for org_user_id in data.ids { - let err_msg = match _delete_user(org_id, &org_user_id, &headers, &mut conn, &nt).await { + for member_id in data.ids { + let err_msg = match _delete_user(org_id, &member_id, &headers, &mut conn, &nt).await { Ok(_) => String::new(), Err(e) => format!("{e:?}"), }; @@ -1420,7 +1416,7 @@ async fn bulk_delete_user( bulk_response.push(json!( { "object": "OrganizationBulkConfirmResponseModel", - "id": org_user_id, + "id": member_id, "error": err_msg } )) @@ -1433,53 +1429,54 @@ async fn bulk_delete_user( })) } -#[delete("/organizations//users/")] +#[delete("/organizations//users/")] async fn delete_user( org_id: &str, - org_user_id: &str, + member_id: &str, headers: AdminHeaders, mut conn: DbConn, nt: Notify<'_>, ) -> EmptyResult { - _delete_user(org_id, org_user_id, &headers, &mut conn, &nt).await + _delete_user(org_id, member_id, &headers, &mut conn, &nt).await } -#[post("/organizations//users//delete")] +#[post("/organizations//users//delete")] async fn post_delete_user( org_id: &str, - org_user_id: &str, + member_id: &str, headers: AdminHeaders, mut conn: DbConn, nt: Notify<'_>, ) -> EmptyResult { - _delete_user(org_id, org_user_id, &headers, &mut conn, &nt).await + _delete_user(org_id, member_id, &headers, &mut conn, &nt).await } async fn _delete_user( org_id: &str, - org_user_id: &str, + member_id: &str, headers: &AdminHeaders, conn: &mut DbConn, nt: &Notify<'_>, ) -> EmptyResult { - let Some(user_to_delete) = UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await else { + let Some(member_to_delete) = Membership::find_by_uuid_and_org(member_id, org_id, conn).await else { err!("User to delete isn't member of the organization") }; - if user_to_delete.atype != UserOrgType::User && headers.org_user_type != UserOrgType::Owner { + if member_to_delete.atype != MembershipType::User && headers.membership_type != MembershipType::Owner { err!("Only Owners can delete Admins or Owners") } - if user_to_delete.atype == UserOrgType::Owner && user_to_delete.status == UserOrgStatus::Confirmed as i32 { + if member_to_delete.atype == MembershipType::Owner && member_to_delete.status == MembershipStatus::Confirmed as i32 + { // Removing owner, check that there is at least one other confirmed owner - if UserOrganization::count_confirmed_by_org_and_type(org_id, UserOrgType::Owner, conn).await <= 1 { + if Membership::count_confirmed_by_org_and_type(org_id, MembershipType::Owner, conn).await <= 1 { err!("Can't delete the last owner") } } log_event( EventType::OrganizationUserRemoved as i32, - &user_to_delete.uuid, + &member_to_delete.uuid, org_id, &headers.user.uuid, headers.device.atype, @@ -1488,11 +1485,11 @@ async fn _delete_user( ) .await; - if let Some(user) = User::find_by_uuid(&user_to_delete.user_uuid, conn).await { + if let Some(user) = User::find_by_uuid(&member_to_delete.user_uuid, conn).await { nt.send_user_update(UpdateType::SyncOrgKeys, &user).await; } - user_to_delete.delete(conn).await + member_to_delete.delete(conn).await } #[post("/organizations//users/public-keys", data = "")] @@ -1505,23 +1502,23 @@ async fn bulk_public_keys( let data: OrgBulkIds = data.into_inner(); let mut bulk_response = Vec::new(); - // Check all received UserOrg UUID's and find the matching User to retrieve the public-key. - // If the user does not exists, just ignore it, and do not return any information regarding that UserOrg UUID. + // Check all received Membership UUID's and find the matching User to retrieve the public-key. + // If the user does not exists, just ignore it, and do not return any information regarding that Membership UUID. // The web-vault will then ignore that user for the following steps. - for user_org_id in data.ids { - match UserOrganization::find_by_uuid_and_org(&user_org_id, org_id, &mut conn).await { - Some(user_org) => match User::find_by_uuid(&user_org.user_uuid, &mut conn).await { + for member_id in data.ids { + match Membership::find_by_uuid_and_org(&member_id, org_id, &mut conn).await { + Some(member) => match User::find_by_uuid(&member.user_uuid, &mut conn).await { Some(user) => bulk_response.push(json!( { "object": "organizationUserPublicKeyResponseModel", - "id": user_org_id, + "id": member_id, "userId": user.uuid, "key": user.public_key } )), None => debug!("User doesn't exist"), }, - None => debug!("UserOrg doesn't exist"), + None => debug!("Membership doesn't exist"), } } @@ -1797,14 +1794,14 @@ async fn put_policy( // When enabling the SingleOrg policy, remove this org's members that are members of other orgs if pol_type_enum == OrgPolicyType::SingleOrg && data.enabled { - for member in UserOrganization::find_by_org(org_id, &mut conn).await.into_iter() { + for member in Membership::find_by_org(org_id, &mut conn).await.into_iter() { // Policy only applies to non-Owner/non-Admin members who have accepted joining the org // Exclude invited and revoked users when checking for this policy. // Those users will not be allowed to accept or be activated because of the policy checks done there. // We check if the count is larger then 1, because it includes this organization also. - if member.atype < UserOrgType::Admin - && member.status != UserOrgStatus::Invited as i32 - && UserOrganization::count_accepted_and_confirmed_by_user(&member.user_uuid, &mut conn).await > 1 + if member.atype < MembershipType::Admin + && member.status != MembershipStatus::Invited as i32 + && Membership::count_accepted_and_confirmed_by_user(&member.user_uuid, &mut conn).await > 1 { if CONFIG.mail_enabled() { let org = Organization::find_by_uuid(&member.org_uuid, &mut conn).await.unwrap(); @@ -1948,8 +1945,8 @@ async fn import(org_id: &str, data: Json, headers: Headers, mut c // as opposed to upstream which only removes auto-imported users. // User needs to be admin or owner to use the Directory Connector - match UserOrganization::find_by_user_and_org(&headers.user.uuid, org_id, &mut conn).await { - Some(user_org) if user_org.atype >= UserOrgType::Admin => { /* Okay, nothing to do */ } + match Membership::find_by_user_and_org(&headers.user.uuid, org_id, &mut conn).await { + Some(member) if member.atype >= MembershipType::Admin => { /* Okay, nothing to do */ } Some(_) => err!("User has insufficient permissions to use Directory Connector"), None => err!("User not part of organization"), }; @@ -1957,10 +1954,10 @@ async fn import(org_id: &str, data: Json, headers: Headers, mut c for user_data in &data.users { if user_data.deleted { // If user is marked for deletion and it exists, delete it - if let Some(user_org) = UserOrganization::find_by_email_and_org(&user_data.email, org_id, &mut conn).await { + if let Some(member) = Membership::find_by_email_and_org(&user_data.email, org_id, &mut conn).await { log_event( EventType::OrganizationUserRemoved as i32, - &user_org.uuid, + &member.uuid, org_id, &headers.user.uuid, headers.device.atype, @@ -1969,28 +1966,28 @@ async fn import(org_id: &str, data: Json, headers: Headers, mut c ) .await; - user_org.delete(&mut conn).await?; + member.delete(&mut conn).await?; } // If user is not part of the organization, but it exists - } else if UserOrganization::find_by_email_and_org(&user_data.email, org_id, &mut conn).await.is_none() { + } else if Membership::find_by_email_and_org(&user_data.email, org_id, &mut conn).await.is_none() { if let Some(user) = User::find_by_mail(&user_data.email, &mut conn).await { - let user_org_status = if CONFIG.mail_enabled() { - UserOrgStatus::Invited as i32 + let member_status = if CONFIG.mail_enabled() { + MembershipStatus::Invited as i32 } else { - UserOrgStatus::Accepted as i32 // Automatically mark user as accepted if no email invites + MembershipStatus::Accepted as i32 // Automatically mark user as accepted if no email invites }; - let mut new_org_user = UserOrganization::new(user.uuid.clone(), String::from(org_id)); - new_org_user.access_all = false; - new_org_user.atype = UserOrgType::User as i32; - new_org_user.status = user_org_status; + let mut new_member = Membership::new(user.uuid.clone(), String::from(org_id)); + new_member.access_all = false; + new_member.atype = MembershipType::User as i32; + new_member.status = member_status; - new_org_user.save(&mut conn).await?; + new_member.save(&mut conn).await?; log_event( EventType::OrganizationUserInvited as i32, - &new_org_user.uuid, + &new_member.uuid, org_id, &headers.user.uuid, headers.device.atype, @@ -2008,7 +2005,7 @@ async fn import(org_id: &str, data: Json, headers: Headers, mut c mail::send_invite( &user, Some(String::from(org_id)), - Some(new_org_user.uuid), + Some(new_member.uuid), &org_name, Some(headers.user.email.clone()), ) @@ -2020,12 +2017,12 @@ async fn import(org_id: &str, data: Json, headers: Headers, mut c // If this flag is enabled, any user that isn't provided in the Users list will be removed (by default they will be kept unless they have Deleted == true) if data.overwrite_existing { - for user_org in UserOrganization::find_by_org_and_type(org_id, UserOrgType::User, &mut conn).await { - if let Some(user_email) = User::find_by_uuid(&user_org.user_uuid, &mut conn).await.map(|u| u.email) { + for member in Membership::find_by_org_and_type(org_id, MembershipType::User, &mut conn).await { + if let Some(user_email) = User::find_by_uuid(&member.user_uuid, &mut conn).await.map(|u| u.email) { if !data.users.iter().any(|u| u.email == user_email) { log_event( EventType::OrganizationUserRemoved as i32, - &user_org.uuid, + &member.uuid, org_id, &headers.user.uuid, headers.device.atype, @@ -2034,7 +2031,7 @@ async fn import(org_id: &str, data: Json, headers: Headers, mut c ) .await; - user_org.delete(&mut conn).await?; + member.delete(&mut conn).await?; } } } @@ -2044,35 +2041,25 @@ async fn import(org_id: &str, data: Json, headers: Headers, mut c } // Pre web-vault v2022.9.x endpoint -#[put("/organizations//users//deactivate")] -async fn deactivate_organization_user( - org_id: &str, - org_user_id: &str, - headers: AdminHeaders, - mut conn: DbConn, -) -> EmptyResult { - _revoke_organization_user(org_id, org_user_id, &headers, &mut conn).await +#[put("/organizations//users//deactivate")] +async fn deactivate_membership(org_id: &str, member_id: &str, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult { + _revoke_membership(org_id, member_id, &headers, &mut conn).await } // Pre web-vault v2022.9.x endpoint #[put("/organizations//users/deactivate", data = "")] -async fn bulk_deactivate_organization_user( +async fn bulk_deactivate_membership( org_id: &str, data: Json, headers: AdminHeaders, conn: DbConn, ) -> Json { - bulk_revoke_organization_user(org_id, data, headers, conn).await + bulk_revoke_membership(org_id, data, headers, conn).await } -#[put("/organizations//users//revoke")] -async fn revoke_organization_user( - org_id: &str, - org_user_id: &str, - headers: AdminHeaders, - mut conn: DbConn, -) -> EmptyResult { - _revoke_organization_user(org_id, org_user_id, &headers, &mut conn).await +#[put("/organizations//users//revoke")] +async fn revoke_membership(org_id: &str, member_id: &str, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult { + _revoke_membership(org_id, member_id, &headers, &mut conn).await } #[derive(Deserialize, Debug)] @@ -2082,7 +2069,7 @@ struct OrgBulkRevokeData { } #[put("/organizations//users/revoke", data = "")] -async fn bulk_revoke_organization_user( +async fn bulk_revoke_membership( org_id: &str, data: Json, headers: AdminHeaders, @@ -2092,9 +2079,9 @@ async fn bulk_revoke_organization_user( let mut bulk_response = Vec::new(); match data.ids { - Some(org_users) => { - for org_user_id in org_users { - let err_msg = match _revoke_organization_user(org_id, &org_user_id, &headers, &mut conn).await { + Some(members) => { + for member_id in members { + let err_msg = match _revoke_membership(org_id, &member_id, &headers, &mut conn).await { Ok(_) => String::new(), Err(e) => format!("{e:?}"), }; @@ -2102,7 +2089,7 @@ async fn bulk_revoke_organization_user( bulk_response.push(json!( { "object": "OrganizationUserBulkResponseModel", - "id": org_user_id, + "id": member_id, "error": err_msg } )); @@ -2118,32 +2105,27 @@ async fn bulk_revoke_organization_user( })) } -async fn _revoke_organization_user( - org_id: &str, - org_user_id: &str, - headers: &AdminHeaders, - conn: &mut DbConn, -) -> EmptyResult { - match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await { - Some(mut user_org) if user_org.status > UserOrgStatus::Revoked as i32 => { - if user_org.user_uuid == headers.user.uuid { +async fn _revoke_membership(org_id: &str, member_id: &str, headers: &AdminHeaders, conn: &mut DbConn) -> EmptyResult { + match Membership::find_by_uuid_and_org(member_id, org_id, conn).await { + Some(mut member) if member.status > MembershipStatus::Revoked as i32 => { + if member.user_uuid == headers.user.uuid { err!("You cannot revoke yourself") } - if user_org.atype == UserOrgType::Owner && headers.org_user_type != UserOrgType::Owner { + if member.atype == MembershipType::Owner && headers.membership_type != MembershipType::Owner { err!("Only owners can revoke other owners") } - if user_org.atype == UserOrgType::Owner - && UserOrganization::count_confirmed_by_org_and_type(org_id, UserOrgType::Owner, conn).await <= 1 + if member.atype == MembershipType::Owner + && Membership::count_confirmed_by_org_and_type(org_id, MembershipType::Owner, conn).await <= 1 { err!("Organization must have at least one confirmed owner") } - user_org.revoke(); - user_org.save(conn).await?; + member.revoke(); + member.save(conn).await?; log_event( EventType::OrganizationUserRevoked as i32, - &user_org.uuid, + &member.uuid, org_id, &headers.user.uuid, headers.device.atype, @@ -2159,39 +2141,29 @@ async fn _revoke_organization_user( } // Pre web-vault v2022.9.x endpoint -#[put("/organizations//users//activate")] -async fn activate_organization_user( - org_id: &str, - org_user_id: &str, - headers: AdminHeaders, - mut conn: DbConn, -) -> EmptyResult { - _restore_organization_user(org_id, org_user_id, &headers, &mut conn).await +#[put("/organizations//users//activate")] +async fn activate_membership(org_id: &str, member_id: &str, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult { + _restore_membership(org_id, member_id, &headers, &mut conn).await } // Pre web-vault v2022.9.x endpoint #[put("/organizations//users/activate", data = "")] -async fn bulk_activate_organization_user( +async fn bulk_activate_membership( org_id: &str, data: Json, headers: AdminHeaders, conn: DbConn, ) -> Json { - bulk_restore_organization_user(org_id, data, headers, conn).await + bulk_restore_membership(org_id, data, headers, conn).await } -#[put("/organizations//users//restore")] -async fn restore_organization_user( - org_id: &str, - org_user_id: &str, - headers: AdminHeaders, - mut conn: DbConn, -) -> EmptyResult { - _restore_organization_user(org_id, org_user_id, &headers, &mut conn).await +#[put("/organizations//users//restore")] +async fn restore_membership(org_id: &str, member_id: &str, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult { + _restore_membership(org_id, member_id, &headers, &mut conn).await } #[put("/organizations//users/restore", data = "")] -async fn bulk_restore_organization_user( +async fn bulk_restore_membership( org_id: &str, data: Json, headers: AdminHeaders, @@ -2200,8 +2172,8 @@ async fn bulk_restore_organization_user( let data = data.into_inner(); let mut bulk_response = Vec::new(); - for org_user_id in data.ids { - let err_msg = match _restore_organization_user(org_id, &org_user_id, &headers, &mut conn).await { + for member_id in data.ids { + let err_msg = match _restore_membership(org_id, &member_id, &headers, &mut conn).await { Ok(_) => String::new(), Err(e) => format!("{e:?}"), }; @@ -2209,7 +2181,7 @@ async fn bulk_restore_organization_user( bulk_response.push(json!( { "object": "OrganizationUserBulkResponseModel", - "id": org_user_id, + "id": member_id, "error": err_msg } )); @@ -2222,29 +2194,24 @@ async fn bulk_restore_organization_user( })) } -async fn _restore_organization_user( - org_id: &str, - org_user_id: &str, - headers: &AdminHeaders, - conn: &mut DbConn, -) -> EmptyResult { - match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await { - Some(mut user_org) if user_org.status < UserOrgStatus::Accepted as i32 => { - if user_org.user_uuid == headers.user.uuid { +async fn _restore_membership(org_id: &str, member_id: &str, headers: &AdminHeaders, conn: &mut DbConn) -> EmptyResult { + match Membership::find_by_uuid_and_org(member_id, org_id, conn).await { + Some(mut member) if member.status < MembershipStatus::Accepted as i32 => { + if member.user_uuid == headers.user.uuid { err!("You cannot restore yourself") } - if user_org.atype == UserOrgType::Owner && headers.org_user_type != UserOrgType::Owner { + if member.atype == MembershipType::Owner && headers.membership_type != MembershipType::Owner { err!("Only owners can restore other owners") } - // This check is also done at accept_invite(), _confirm_invite, _activate_user(), edit_user(), admin::update_user_org_type + // This check is also done at accept_invite(), _confirm_invite, _activate_user(), edit_user(), admin::update_membership_type // It returns different error messages per function. - if user_org.atype < UserOrgType::Admin { - match OrgPolicy::is_user_allowed(&user_org.user_uuid, org_id, false, conn).await { + if member.atype < MembershipType::Admin { + match OrgPolicy::is_user_allowed(&member.user_uuid, org_id, false, conn).await { Ok(_) => {} Err(OrgPolicyErr::TwoFactorMissing) => { if CONFIG.email_2fa_auto_fallback() { - two_factor::email::find_and_activate_email_2fa(&user_org.user_uuid, conn).await?; + two_factor::email::find_and_activate_email_2fa(&member.user_uuid, conn).await?; } else { err!("You cannot restore this user because they have not setup 2FA"); } @@ -2255,12 +2222,12 @@ async fn _restore_organization_user( } } - user_org.restore(); - user_org.save(conn).await?; + member.restore(); + member.save(conn).await?; log_event( EventType::OrganizationUserRestored as i32, - &user_org.uuid, + &member.uuid, org_id, &headers.user.uuid, headers.device.atype, @@ -2610,7 +2577,7 @@ async fn get_user_groups(org_id: &str, user_id: &str, _headers: AdminHeaders, mu err!("Group support is disabled"); } - if UserOrganization::find_by_uuid_and_org(user_id, org_id, &mut conn).await.is_none() { + if Membership::find_by_uuid_and_org(user_id, org_id, &mut conn).await.is_none() { err!("User could not be found!") }; @@ -2626,21 +2593,21 @@ struct OrganizationUserUpdateGroupsRequest { group_ids: Vec, } -#[post("/organizations//users//groups", data = "")] +#[post("/organizations//users//groups", data = "")] async fn post_user_groups( org_id: &str, - org_user_id: &str, + member_id: &str, data: Json, headers: AdminHeaders, conn: DbConn, ) -> EmptyResult { - put_user_groups(org_id, org_user_id, data, headers, conn).await + put_user_groups(org_id, member_id, data, headers, conn).await } -#[put("/organizations//users//groups", data = "")] +#[put("/organizations//users//groups", data = "")] async fn put_user_groups( org_id: &str, - org_user_id: &str, + member_id: &str, data: Json, headers: AdminHeaders, mut conn: DbConn, @@ -2649,21 +2616,21 @@ async fn put_user_groups( err!("Group support is disabled"); } - if UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &mut conn).await.is_none() { + if Membership::find_by_uuid_and_org(member_id, org_id, &mut conn).await.is_none() { err!("User could not be found or does not belong to the organization."); } - GroupUser::delete_all_by_user(org_user_id, &mut conn).await?; + GroupUser::delete_all_by_member(&member_id, &mut conn).await?; let assigned_group_ids = data.into_inner(); for assigned_group_id in assigned_group_ids.group_ids { - let mut group_user = GroupUser::new(assigned_group_id.clone(), String::from(org_user_id)); + let mut group_user = GroupUser::new(assigned_group_id.clone(), String::from(member_id)); group_user.save(&mut conn).await?; } log_event( EventType::OrganizationUserUpdatedGroups as i32, - org_user_id, + member_id, org_id, &headers.user.uuid, headers.device.atype, @@ -2675,22 +2642,22 @@ async fn put_user_groups( Ok(()) } -#[post("/organizations//groups//delete-user/")] +#[post("/organizations//groups//delete-user/")] async fn post_delete_group_user( org_id: &str, group_id: &str, - org_user_id: &str, + member_id: &str, headers: AdminHeaders, conn: DbConn, ) -> EmptyResult { - delete_group_user(org_id, group_id, org_user_id, headers, conn).await + delete_group_user(org_id, group_id, member_id, headers, conn).await } -#[delete("/organizations//groups//users/")] +#[delete("/organizations//groups//users/")] async fn delete_group_user( org_id: &str, group_id: &str, - org_user_id: &str, + member_id: &str, headers: AdminHeaders, mut conn: DbConn, ) -> EmptyResult { @@ -2698,7 +2665,7 @@ async fn delete_group_user( err!("Group support is disabled"); } - if UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &mut conn).await.is_none() { + if Membership::find_by_uuid_and_org(member_id, org_id, &mut conn).await.is_none() { err!("User could not be found or does not belong to the organization."); } @@ -2708,7 +2675,7 @@ async fn delete_group_user( log_event( EventType::OrganizationUserUpdatedGroups as i32, - org_user_id, + member_id, org_id, &headers.user.uuid, headers.device.atype, @@ -2717,7 +2684,7 @@ async fn delete_group_user( ) .await; - GroupUser::delete_by_group_id_and_user_id(group_id, org_user_id, &mut conn).await + GroupUser::delete_by_group_id_and_user_id(group_id, member_id, &mut conn).await } #[derive(Deserialize)] @@ -2757,10 +2724,10 @@ async fn get_organization_keys(org_id: &str, headers: Headers, conn: DbConn) -> get_organization_public_key(org_id, headers, conn).await } -#[put("/organizations//users//reset-password", data = "")] +#[put("/organizations//users//reset-password", data = "")] async fn put_reset_password( org_id: &str, - org_user_id: &str, + member_id: &str, headers: AdminHeaders, data: Json, mut conn: DbConn, @@ -2770,20 +2737,20 @@ async fn put_reset_password( err!("Required organization not found") }; - let Some(org_user) = UserOrganization::find_by_uuid_and_org(org_user_id, &org.uuid, &mut conn).await else { + let Some(member) = Membership::find_by_uuid_and_org(member_id, &org.uuid, &mut conn).await else { err!("User to reset isn't member of required organization") }; - let Some(user) = User::find_by_uuid(&org_user.user_uuid, &mut conn).await else { + let Some(user) = User::find_by_uuid(&member.user_uuid, &mut conn).await else { err!("User not found") }; - check_reset_password_applicable_and_permissions(org_id, org_user_id, &headers, &mut conn).await?; + check_reset_password_applicable_and_permissions(org_id, member_id, &headers, &mut conn).await?; - if org_user.reset_password_key.is_none() { + if member.reset_password_key.is_none() { err!("Password reset not or not correctly enrolled"); } - if org_user.status != (UserOrgStatus::Confirmed as i32) { + if member.status != (MembershipStatus::Confirmed as i32) { err!("Organization user must be confirmed for password reset functionality"); } @@ -2803,7 +2770,7 @@ async fn put_reset_password( log_event( EventType::OrganizationUserAdminResetPassword as i32, - org_user_id, + member_id, org_id, &headers.user.uuid, headers.device.atype, @@ -2815,10 +2782,10 @@ async fn put_reset_password( Ok(()) } -#[get("/organizations//users//reset-password-details")] +#[get("/organizations//users//reset-password-details")] async fn get_reset_password_details( org_id: &str, - org_user_id: &str, + member_id: &str, headers: AdminHeaders, mut conn: DbConn, ) -> JsonResult { @@ -2826,15 +2793,15 @@ async fn get_reset_password_details( err!("Required organization not found") }; - let Some(org_user) = UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &mut conn).await else { + let Some(member) = Membership::find_by_uuid_and_org(member_id, org_id, &mut conn).await else { err!("User to reset isn't member of required organization") }; - let Some(user) = User::find_by_uuid(&org_user.user_uuid, &mut conn).await else { + let Some(user) = User::find_by_uuid(&member.user_uuid, &mut conn).await else { err!("User not found") }; - check_reset_password_applicable_and_permissions(org_id, org_user_id, &headers, &mut conn).await?; + check_reset_password_applicable_and_permissions(org_id, member_id, &headers, &mut conn).await?; // https://github.com/bitwarden/server/blob/3b50ccb9f804efaacdc46bed5b60e5b28eddefcf/src/Api/Models/Response/Organizations/OrganizationUserResponseModel.cs#L111 Ok(Json(json!({ @@ -2843,7 +2810,7 @@ async fn get_reset_password_details( "kdfIterations":user.client_kdf_iter, "kdfMemory":user.client_kdf_memory, "kdfParallelism":user.client_kdf_parallelism, - "resetPasswordKey":org_user.reset_password_key, + "resetPasswordKey":member.reset_password_key, "encryptedPrivateKey":org.private_key, }))) @@ -2851,20 +2818,20 @@ async fn get_reset_password_details( async fn check_reset_password_applicable_and_permissions( org_id: &str, - org_user_id: &str, + member_id: &str, headers: &AdminHeaders, conn: &mut DbConn, ) -> EmptyResult { check_reset_password_applicable(org_id, conn).await?; - let Some(target_user) = UserOrganization::find_by_uuid_and_org(org_user_id, org_id, conn).await else { + let Some(target_user) = Membership::find_by_uuid_and_org(member_id, org_id, conn).await else { err!("Reset target user not found") }; // Resetting user must be higher/equal to user to reset - match headers.org_user_type { - UserOrgType::Owner => Ok(()), - UserOrgType::Admin if target_user.atype <= UserOrgType::Admin => Ok(()), + match headers.membership_type { + MembershipType::Owner => Ok(()), + MembershipType::Admin if target_user.atype <= MembershipType::Admin => Ok(()), _ => err!("No permission to reset this user's password"), } } @@ -2885,15 +2852,15 @@ async fn check_reset_password_applicable(org_id: &str, conn: &mut DbConn) -> Emp Ok(()) } -#[put("/organizations//users//reset-password-enrollment", data = "")] +#[put("/organizations//users//reset-password-enrollment", data = "")] async fn put_reset_password_enrollment( org_id: &str, - org_user_id: &str, + member_id: &str, headers: Headers, data: Json, mut conn: DbConn, ) -> EmptyResult { - let Some(mut org_user) = UserOrganization::find_by_user_and_org(&headers.user.uuid, org_id, &mut conn).await else { + let Some(mut member) = Membership::find_by_user_and_org(&headers.user.uuid, org_id, &mut conn).await else { err!("User to enroll isn't member of required organization") }; @@ -2916,16 +2883,16 @@ async fn put_reset_password_enrollment( .await?; } - org_user.reset_password_key = reset_request.reset_password_key; - org_user.save(&mut conn).await?; + member.reset_password_key = reset_request.reset_password_key; + member.save(&mut conn).await?; - let log_id = if org_user.reset_password_key.is_some() { + let log_id = if member.reset_password_key.is_some() { EventType::OrganizationUserResetPasswordEnroll as i32 } else { EventType::OrganizationUserResetPasswordWithdraw as i32 }; - log_event(log_id, org_user_id, org_id, &headers.user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await; + log_event(log_id, member_id, org_id, &headers.user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await; Ok(()) } diff --git a/src/api/core/public.rs b/src/api/core/public.rs index 3b3e74cb..1480cef0 100644 --- a/src/api/core/public.rs +++ b/src/api/core/public.rs @@ -54,38 +54,33 @@ async fn ldap_import(data: Json, token: PublicToken, mut conn: Db for user_data in &data.members { if user_data.deleted { // If user is marked for deletion and it exists, revoke it - if let Some(mut user_org) = - UserOrganization::find_by_email_and_org(&user_data.email, &org_id, &mut conn).await - { + if let Some(mut member) = Membership::find_by_email_and_org(&user_data.email, &org_id, &mut conn).await { // Only revoke a user if it is not the last confirmed owner - let revoked = if user_org.atype == UserOrgType::Owner - && user_org.status == UserOrgStatus::Confirmed as i32 + let revoked = if member.atype == MembershipType::Owner + && member.status == MembershipStatus::Confirmed as i32 { - if UserOrganization::count_confirmed_by_org_and_type(&org_id, UserOrgType::Owner, &mut conn).await - <= 1 + if Membership::count_confirmed_by_org_and_type(&org_id, MembershipType::Owner, &mut conn).await <= 1 { warn!("Can't revoke the last owner"); false } else { - user_org.revoke() + member.revoke() } } else { - user_org.revoke() + member.revoke() }; - let ext_modified = user_org.set_external_id(Some(user_data.external_id.clone())); + let ext_modified = member.set_external_id(Some(user_data.external_id.clone())); if revoked || ext_modified { - user_org.save(&mut conn).await?; + member.save(&mut conn).await?; } } // If user is part of the organization, restore it - } else if let Some(mut user_org) = - UserOrganization::find_by_email_and_org(&user_data.email, &org_id, &mut conn).await - { - let restored = user_org.restore(); - let ext_modified = user_org.set_external_id(Some(user_data.external_id.clone())); + } else if let Some(mut member) = Membership::find_by_email_and_org(&user_data.email, &org_id, &mut conn).await { + let restored = member.restore(); + let ext_modified = member.set_external_id(Some(user_data.external_id.clone())); if restored || ext_modified { - user_org.save(&mut conn).await?; + member.save(&mut conn).await?; } } else { // If user is not part of the organization @@ -103,19 +98,19 @@ async fn ldap_import(data: Json, token: PublicToken, mut conn: Db new_user } }; - let user_org_status = if CONFIG.mail_enabled() || user.password_hash.is_empty() { - UserOrgStatus::Invited as i32 + let member_status = if CONFIG.mail_enabled() || user.password_hash.is_empty() { + MembershipStatus::Invited as i32 } else { - UserOrgStatus::Accepted as i32 // Automatically mark user as accepted if no email invites + MembershipStatus::Accepted as i32 // Automatically mark user as accepted if no email invites }; - let mut new_org_user = UserOrganization::new(user.uuid.clone(), org_id.clone()); - new_org_user.set_external_id(Some(user_data.external_id.clone())); - new_org_user.access_all = false; - new_org_user.atype = UserOrgType::User as i32; - new_org_user.status = user_org_status; + let mut new_member = Membership::new(user.uuid.clone(), org_id.clone()); + new_member.set_external_id(Some(user_data.external_id.clone())); + new_member.access_all = false; + new_member.atype = MembershipType::User as i32; + new_member.status = member_status; - new_org_user.save(&mut conn).await?; + new_member.save(&mut conn).await?; if CONFIG.mail_enabled() { let (org_name, org_email) = match Organization::find_by_uuid(&org_id, &mut conn).await { @@ -123,7 +118,7 @@ async fn ldap_import(data: Json, token: PublicToken, mut conn: Db None => err!("Error looking up organization"), }; - mail::send_invite(&user, Some(org_id.clone()), Some(new_org_user.uuid), &org_name, Some(org_email)) + mail::send_invite(&user, Some(org_id.clone()), Some(new_member.uuid), &org_name, Some(org_email)) .await?; } } @@ -149,9 +144,8 @@ async fn ldap_import(data: Json, token: PublicToken, mut conn: Db GroupUser::delete_all_by_group(&group_uuid, &mut conn).await?; for ext_id in &group_data.member_external_ids { - if let Some(user_org) = UserOrganization::find_by_external_id_and_org(ext_id, &org_id, &mut conn).await - { - let mut group_user = GroupUser::new(group_uuid.clone(), user_org.uuid.clone()); + if let Some(member) = Membership::find_by_external_id_and_org(ext_id, &org_id, &mut conn).await { + let mut group_user = GroupUser::new(group_uuid.clone(), member.uuid.clone()); group_user.save(&mut conn).await?; } } @@ -164,20 +158,19 @@ async fn ldap_import(data: Json, token: PublicToken, mut conn: Db if data.overwrite_existing { // Generate a HashSet to quickly verify if a member is listed or not. let sync_members: HashSet = data.members.into_iter().map(|m| m.external_id).collect(); - for user_org in UserOrganization::find_by_org(&org_id, &mut conn).await { - if let Some(ref user_external_id) = user_org.external_id { + for member in Membership::find_by_org(&org_id, &mut conn).await { + if let Some(ref user_external_id) = member.external_id { if !sync_members.contains(user_external_id) { - if user_org.atype == UserOrgType::Owner && user_org.status == UserOrgStatus::Confirmed as i32 { + if member.atype == MembershipType::Owner && member.status == MembershipStatus::Confirmed as i32 { // Removing owner, check that there is at least one other confirmed owner - if UserOrganization::count_confirmed_by_org_and_type(&org_id, UserOrgType::Owner, &mut conn) - .await + if Membership::count_confirmed_by_org_and_type(&org_id, MembershipType::Owner, &mut conn).await <= 1 { warn!("Can't delete the last owner"); continue; } } - user_org.delete(&mut conn).await?; + member.delete(&mut conn).await?; } } } diff --git a/src/api/core/two_factor/mod.rs b/src/api/core/two_factor/mod.rs index 486b526a..756ae836 100644 --- a/src/api/core/two_factor/mod.rs +++ b/src/api/core/two_factor/mod.rs @@ -178,12 +178,11 @@ pub async fn enforce_2fa_policy( ip: &std::net::IpAddr, conn: &mut DbConn, ) -> EmptyResult { - for member in UserOrganization::find_by_user_and_policy(&user.uuid, OrgPolicyType::TwoFactorAuthentication, conn) - .await - .into_iter() + for member in + Membership::find_by_user_and_policy(&user.uuid, OrgPolicyType::TwoFactorAuthentication, conn).await.into_iter() { // Policy only applies to non-Owner/non-Admin members who have accepted joining the org - if member.atype < UserOrgType::Admin { + if member.atype < MembershipType::Admin { if CONFIG.mail_enabled() { let org = Organization::find_by_uuid(&member.org_uuid, conn).await.unwrap(); mail::send_2fa_removed_from_org(&user.email, &org.name).await?; @@ -216,9 +215,9 @@ pub async fn enforce_2fa_policy_for_org( conn: &mut DbConn, ) -> EmptyResult { let org = Organization::find_by_uuid(org_uuid, conn).await.unwrap(); - for member in UserOrganization::find_confirmed_by_org(org_uuid, conn).await.into_iter() { + for member in Membership::find_confirmed_by_org(org_uuid, conn).await.into_iter() { // Don't enforce the policy for Admins and Owners. - if member.atype < UserOrgType::Admin && TwoFactor::find_by_user(&member.user_uuid, conn).await.is_empty() { + if member.atype < MembershipType::Admin && TwoFactor::find_by_user(&member.user_uuid, conn).await.is_empty() { if CONFIG.mail_enabled() { let user = User::find_by_uuid(&member.user_uuid, conn).await.unwrap(); mail::send_2fa_removed_from_org(&user.email, &org.name).await?; diff --git a/src/api/identity.rs b/src/api/identity.rs index 13248f0a..1d111987 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -111,7 +111,7 @@ async fn _refresh_login(data: ConnectData, conn: &mut DbConn) -> JsonResult { // Because this might get used in the future, and is add by the Bitwarden Server, lets keep it, but then commented out // See: https://github.com/dani-garcia/vaultwarden/issues/4156 // --- - // let orgs = UserOrganization::find_confirmed_by_user(&user.uuid, conn).await; + // let members = Membership::find_confirmed_by_user(&user.uuid, conn).await; let (access_token, expires_in) = device.refresh_tokens(&user, scope_vec); device.save(conn).await?; @@ -291,7 +291,7 @@ async fn _password_login( // Because this might get used in the future, and is add by the Bitwarden Server, lets keep it, but then commented out // See: https://github.com/dani-garcia/vaultwarden/issues/4156 // --- - // let orgs = UserOrganization::find_confirmed_by_user(&user.uuid, conn).await; + // let members = Membership::find_confirmed_by_user(&user.uuid, conn).await; let (access_token, expires_in) = device.refresh_tokens(&user, scope_vec); device.save(conn).await?; @@ -440,7 +440,7 @@ async fn _user_api_key_login( // Because this might get used in the future, and is add by the Bitwarden Server, lets keep it, but then commented out // See: https://github.com/dani-garcia/vaultwarden/issues/4156 // --- - // let orgs = UserOrganization::find_confirmed_by_user(&user.uuid, conn).await; + // let members = Membership::find_confirmed_by_user(&user.uuid, conn).await; let (access_token, expires_in) = device.refresh_tokens(&user, scope_vec); device.save(conn).await?; diff --git a/src/auth.rs b/src/auth.rs index 15910679..fda4cb02 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -191,7 +191,7 @@ pub struct InviteJwtClaims { pub email: String, pub org_id: Option, - pub user_org_id: Option, + pub member_id: Option, pub invited_by_email: Option, } @@ -199,7 +199,7 @@ pub fn generate_invite_claims( uuid: String, email: String, org_id: Option, - user_org_id: Option, + member_id: Option, invited_by_email: Option, ) -> InviteJwtClaims { let time_now = Utc::now(); @@ -211,7 +211,7 @@ pub fn generate_invite_claims( sub: uuid, email, org_id, - user_org_id, + member_id, invited_by_email, } } @@ -371,7 +371,7 @@ use rocket::{ }; use crate::db::{ - models::{Collection, Device, User, UserOrgStatus, UserOrgType, UserOrganization, UserStampException}, + models::{Collection, Device, Membership, MembershipStatus, MembershipType, User, UserStampException}, DbConn, }; @@ -534,8 +534,8 @@ pub struct OrgHeaders { pub host: String, pub device: Device, pub user: User, - pub org_user_type: UserOrgType, - pub org_user: UserOrganization, + pub membership_type: MembershipType, + pub membership: Membership, pub ip: ClientIp, } @@ -574,10 +574,10 @@ impl<'r> FromRequest<'r> for OrgHeaders { }; let user = headers.user; - let org_user = match UserOrganization::find_by_user_and_org(&user.uuid, org_id, &mut conn).await { - Some(user) => { - if user.status == UserOrgStatus::Confirmed as i32 { - user + let membership = match Membership::find_by_user_and_org(&user.uuid, org_id, &mut conn).await { + Some(member) => { + if member.status == MembershipStatus::Confirmed as i32 { + member } else { err_handler!("The current user isn't confirmed member of the organization") } @@ -589,15 +589,15 @@ impl<'r> FromRequest<'r> for OrgHeaders { host: headers.host, device: headers.device, user, - org_user_type: { - if let Some(org_usr_type) = UserOrgType::from_i32(org_user.atype) { + membership_type: { + if let Some(org_usr_type) = MembershipType::from_i32(membership.atype) { org_usr_type } else { // This should only happen if the DB is corrupted err_handler!("Unknown user type in the database") } }, - org_user, + membership, ip: headers.ip, }) } @@ -610,7 +610,7 @@ pub struct AdminHeaders { pub host: String, pub device: Device, pub user: User, - pub org_user_type: UserOrgType, + pub membership_type: MembershipType, pub ip: ClientIp, } @@ -620,12 +620,12 @@ impl<'r> FromRequest<'r> for AdminHeaders { async fn from_request(request: &'r Request<'_>) -> Outcome { let headers = try_outcome!(OrgHeaders::from_request(request).await); - if headers.org_user_type >= UserOrgType::Admin { + if headers.membership_type >= MembershipType::Admin { Outcome::Success(Self { host: headers.host, device: headers.device, user: headers.user, - org_user_type: headers.org_user_type, + membership_type: headers.membership_type, ip: headers.ip, }) } else { @@ -680,7 +680,7 @@ impl<'r> FromRequest<'r> for ManagerHeaders { async fn from_request(request: &'r Request<'_>) -> Outcome { let headers = try_outcome!(OrgHeaders::from_request(request).await); - if headers.org_user_type >= UserOrgType::Manager { + if headers.membership_type >= MembershipType::Manager { match get_col_id(request) { Some(col_id) => { let mut conn = match DbConn::from_request(request).await { @@ -688,7 +688,7 @@ impl<'r> FromRequest<'r> for ManagerHeaders { _ => err_handler!("Error getting DB"), }; - if !Collection::can_access_collection(&headers.org_user, &col_id, &mut conn).await { + if !Collection::can_access_collection(&headers.membership, &col_id, &mut conn).await { err_handler!("The current user isn't a manager for this collection") } } @@ -724,7 +724,7 @@ pub struct ManagerHeadersLoose { pub host: String, pub device: Device, pub user: User, - pub org_user: UserOrganization, + pub membership: Membership, pub ip: ClientIp, } @@ -734,12 +734,12 @@ impl<'r> FromRequest<'r> for ManagerHeadersLoose { async fn from_request(request: &'r Request<'_>) -> Outcome { let headers = try_outcome!(OrgHeaders::from_request(request).await); - if headers.org_user_type >= UserOrgType::Manager { + if headers.membership_type >= MembershipType::Manager { Outcome::Success(Self { host: headers.host, device: headers.device, user: headers.user, - org_user: headers.org_user, + membership: headers.membership, ip: headers.ip, }) } else { @@ -769,7 +769,7 @@ impl ManagerHeaders { if uuid::Uuid::parse_str(col_id).is_err() { err!("Collection Id is malformed!"); } - if !Collection::can_access_collection(&h.org_user, col_id, conn).await { + if !Collection::can_access_collection(&h.membership, col_id, conn).await { err!("You don't have access to all collections!"); } } @@ -795,7 +795,7 @@ impl<'r> FromRequest<'r> for OwnerHeaders { async fn from_request(request: &'r Request<'_>) -> Outcome { let headers = try_outcome!(OrgHeaders::from_request(request).await); - if headers.org_user_type == UserOrgType::Owner { + if headers.membership_type == MembershipType::Owner { Outcome::Success(Self { device: headers.device, user: headers.user, diff --git a/src/db/models/cipher.rs b/src/db/models/cipher.rs index 048cb4d1..c4bd05db 100644 --- a/src/db/models/cipher.rs +++ b/src/db/models/cipher.rs @@ -4,7 +4,7 @@ use chrono::{NaiveDateTime, TimeDelta, Utc}; use serde_json::Value; use super::{ - Attachment, CollectionCipher, Favorite, FolderCipher, Group, User, UserOrgStatus, UserOrgType, UserOrganization, + Attachment, CollectionCipher, Favorite, FolderCipher, Group, Membership, MembershipStatus, MembershipType, User, }; use crate::api::core::{CipherData, CipherSyncData, CipherSyncType}; @@ -367,17 +367,16 @@ impl Cipher { // Belongs to Organization, need to update affected users if let Some(ref org_uuid) = self.organization_uuid { // users having access to the collection - let mut collection_users = - UserOrganization::find_by_cipher_and_org(&self.uuid, org_uuid, conn).await; + let mut collection_users = Membership::find_by_cipher_and_org(&self.uuid, org_uuid, conn).await; if CONFIG.org_groups_enabled() { // members of a group having access to the collection let group_users = - UserOrganization::find_by_cipher_and_org_with_group(&self.uuid, org_uuid, conn).await; + Membership::find_by_cipher_and_org_with_group(&self.uuid, org_uuid, conn).await; collection_users.extend(group_users); } - for user_org in collection_users { - User::update_uuid_revision(&user_org.user_uuid, conn).await; - user_uuids.push(user_org.user_uuid.clone()) + for member in collection_users { + User::update_uuid_revision(&member.user_uuid, conn).await; + user_uuids.push(member.user_uuid.clone()) } } } @@ -502,11 +501,11 @@ impl Cipher { ) -> bool { if let Some(ref org_uuid) = self.organization_uuid { if let Some(cipher_sync_data) = cipher_sync_data { - if let Some(cached_user_org) = cipher_sync_data.user_organizations.get(org_uuid) { - return cached_user_org.has_full_access(); + if let Some(cached_member) = cipher_sync_data.members.get(org_uuid) { + return cached_member.has_full_access(); } - } else if let Some(user_org) = UserOrganization::find_by_user_and_org(user_uuid, org_uuid, conn).await { - return user_org.has_full_access(); + } else if let Some(member) = Membership::find_by_user_and_org(user_uuid, org_uuid, conn).await { + return member.has_full_access(); } } false @@ -721,7 +720,7 @@ impl Cipher { .left_join(users_organizations::table.on( ciphers::organization_uuid.eq(users_organizations::org_uuid.nullable()) .and(users_organizations::user_uuid.eq(user_uuid)) - .and(users_organizations::status.eq(UserOrgStatus::Confirmed as i32)) + .and(users_organizations::status.eq(MembershipStatus::Confirmed as i32)) )) .left_join(users_collections::table.on( ciphers_collections::collection_uuid.eq(users_collections::collection_uuid) @@ -748,7 +747,7 @@ impl Cipher { if !visible_only { query = query.or_filter( - users_organizations::atype.le(UserOrgType::Admin as i32) // Org admin/owner + users_organizations::atype.le(MembershipType::Admin as i32) // Org admin/owner ); } @@ -766,7 +765,7 @@ impl Cipher { .left_join(users_organizations::table.on( ciphers::organization_uuid.eq(users_organizations::org_uuid.nullable()) .and(users_organizations::user_uuid.eq(user_uuid)) - .and(users_organizations::status.eq(UserOrgStatus::Confirmed as i32)) + .and(users_organizations::status.eq(MembershipStatus::Confirmed as i32)) )) .left_join(users_collections::table.on( ciphers_collections::collection_uuid.eq(users_collections::collection_uuid) @@ -780,7 +779,7 @@ impl Cipher { if !visible_only { query = query.or_filter( - users_organizations::atype.le(UserOrgType::Admin as i32) // Org admin/owner + users_organizations::atype.le(MembershipType::Admin as i32) // Org admin/owner ); } @@ -946,7 +945,7 @@ impl Cipher { .or(groups::access_all.eq(true)) // Access via groups .or(collections_groups::collections_uuid.is_not_null() // Access via groups .and(collections_groups::read_only.eq(false))) - .or(users_organizations::atype.le(UserOrgType::Admin as i32)) // User is admin or owner + .or(users_organizations::atype.le(MembershipType::Admin as i32)) // User is admin or owner ) .select(ciphers_collections::collection_uuid) .load::(conn).unwrap_or_default() @@ -969,7 +968,7 @@ impl Cipher { .filter(users_organizations::access_all.eq(true) // User has access all .or(users_collections::user_uuid.eq(user_id) // User has access to collection .and(users_collections::read_only.eq(false))) - .or(users_organizations::atype.le(UserOrgType::Admin as i32)) // User is admin or owner + .or(users_organizations::atype.le(MembershipType::Admin as i32)) // User is admin or owner ) .select(ciphers_collections::collection_uuid) .load::(conn).unwrap_or_default() @@ -1008,7 +1007,7 @@ impl Cipher { )) .or_filter(users_collections::user_uuid.eq(user_id)) // User has access to collection .or_filter(users_organizations::access_all.eq(true)) // User has access all - .or_filter(users_organizations::atype.le(UserOrgType::Admin as i32)) // User is admin or owner + .or_filter(users_organizations::atype.le(MembershipType::Admin as i32)) // User is admin or owner .or_filter(groups::access_all.eq(true)) //Access via group .or_filter(collections_groups::collections_uuid.is_not_null()) //Access via group .select(ciphers_collections::all_columns) diff --git a/src/db/models/collection.rs b/src/db/models/collection.rs index a26f22c7..d8aceba3 100644 --- a/src/db/models/collection.rs +++ b/src/db/models/collection.rs @@ -1,6 +1,6 @@ use serde_json::Value; -use super::{CollectionGroup, GroupUser, User, UserOrgStatus, UserOrgType, UserOrganization}; +use super::{CollectionGroup, GroupUser, Membership, MembershipStatus, MembershipType, User}; use crate::CONFIG; db_object! { @@ -79,13 +79,13 @@ impl Collection { conn: &mut DbConn, ) -> Value { let (read_only, hide_passwords, can_manage) = if let Some(cipher_sync_data) = cipher_sync_data { - match cipher_sync_data.user_organizations.get(&self.org_uuid) { + match cipher_sync_data.members.get(&self.org_uuid) { // Only for Manager types Bitwarden returns true for the can_manage option // Owners and Admins always have true - Some(uo) if uo.has_full_access() => (false, false, uo.atype >= UserOrgType::Manager), - Some(uo) => { + Some(m) if m.has_full_access() => (false, false, m.atype >= MembershipType::Manager), + Some(m) => { // Only let a manager manage collections when the have full read/write access - let is_manager = uo.atype == UserOrgType::Manager; + let is_manager = m.atype == MembershipType::Manager; if let Some(uc) = cipher_sync_data.user_collections.get(&self.uuid) { (uc.read_only, uc.hide_passwords, is_manager && !uc.read_only && !uc.hide_passwords) } else if let Some(cg) = cipher_sync_data.user_collections_groups.get(&self.uuid) { @@ -97,10 +97,10 @@ impl Collection { _ => (true, true, false), } } else { - match UserOrganization::find_confirmed_by_user_and_org(user_uuid, &self.org_uuid, conn).await { - Some(ou) if ou.has_full_access() => (false, false, ou.atype >= UserOrgType::Manager), - Some(ou) => { - let is_manager = ou.atype == UserOrgType::Manager; + match Membership::find_confirmed_by_user_and_org(user_uuid, &self.org_uuid, conn).await { + Some(m) if m.has_full_access() => (false, false, m.atype >= MembershipType::Manager), + Some(m) => { + let is_manager = m.atype == MembershipType::Manager; let read_only = !self.is_writable_by_user(user_uuid, conn).await; let hide_passwords = self.hide_passwords_for_user(user_uuid, conn).await; (read_only, hide_passwords, is_manager && !read_only && !hide_passwords) @@ -121,13 +121,13 @@ impl Collection { json_object } - pub async fn can_access_collection(org_user: &UserOrganization, col_id: &str, conn: &mut DbConn) -> bool { - org_user.has_status(UserOrgStatus::Confirmed) - && (org_user.has_full_access() - || CollectionUser::has_access_to_collection_by_user(col_id, &org_user.user_uuid, conn).await + pub async fn can_access_collection(member: &Membership, col_id: &str, conn: &mut DbConn) -> bool { + member.has_status(MembershipStatus::Confirmed) + && (member.has_full_access() + || CollectionUser::has_access_to_collection_by_user(col_id, &member.user_uuid, conn).await || (CONFIG.org_groups_enabled() - && (GroupUser::has_full_access_by_member(&org_user.org_uuid, &org_user.uuid, conn).await - || GroupUser::has_access_to_collection_by_member(col_id, &org_user.uuid, conn).await))) + && (GroupUser::has_full_access_by_member(&member.org_uuid, &member.uuid, conn).await + || GroupUser::has_access_to_collection_by_member(col_id, &member.uuid, conn).await))) } } @@ -193,8 +193,8 @@ impl Collection { } pub async fn update_users_revision(&self, conn: &mut DbConn) { - for user_org in UserOrganization::find_by_collection_and_org(&self.uuid, &self.org_uuid, conn).await.iter() { - User::update_uuid_revision(&user_org.user_uuid, conn).await; + for member in Membership::find_by_collection_and_org(&self.uuid, &self.org_uuid, conn).await.iter() { + User::update_uuid_revision(&member.user_uuid, conn).await; } } @@ -234,7 +234,7 @@ impl Collection { ) )) .filter( - users_organizations::status.eq(UserOrgStatus::Confirmed as i32) + users_organizations::status.eq(MembershipStatus::Confirmed as i32) ) .filter( users_collections::user_uuid.eq(user_uuid).or( // Directly accessed collection @@ -265,7 +265,7 @@ impl Collection { ) )) .filter( - users_organizations::status.eq(UserOrgStatus::Confirmed as i32) + users_organizations::status.eq(MembershipStatus::Confirmed as i32) ) .filter( users_collections::user_uuid.eq(user_uuid).or( // Directly accessed collection @@ -349,7 +349,7 @@ impl Collection { .filter( users_collections::collection_uuid.eq(uuid).or( // Directly accessed collection users_organizations::access_all.eq(true).or( // access_all in Organization - users_organizations::atype.le(UserOrgType::Admin as i32) // Org admin or owner + users_organizations::atype.le(MembershipType::Admin as i32) // Org admin or owner )).or( groups::access_all.eq(true) // access_all in groups ).or( // access via groups @@ -378,7 +378,7 @@ impl Collection { .filter( users_collections::collection_uuid.eq(uuid).or( // Directly accessed collection users_organizations::access_all.eq(true).or( // access_all in Organization - users_organizations::atype.le(UserOrgType::Admin as i32) // Org admin or owner + users_organizations::atype.le(MembershipType::Admin as i32) // Org admin or owner )) ).select(collections::all_columns) .first::(conn).ok() @@ -411,7 +411,7 @@ impl Collection { collections_groups::groups_uuid.eq(groups_users::groups_uuid) .and(collections_groups::collections_uuid.eq(collections::uuid)) )) - .filter(users_organizations::atype.le(UserOrgType::Admin as i32) // Org admin or owner + .filter(users_organizations::atype.le(MembershipType::Admin as i32) // Org admin or owner .or(users_organizations::access_all.eq(true)) // access_all via membership .or(users_collections::collection_uuid.eq(&self.uuid) // write access given to collection .and(users_collections::read_only.eq(false))) @@ -436,7 +436,7 @@ impl Collection { users_collections::collection_uuid.eq(collections::uuid) .and(users_collections::user_uuid.eq(user_uuid)) )) - .filter(users_organizations::atype.le(UserOrgType::Admin as i32) // Org admin or owner + .filter(users_organizations::atype.le(MembershipType::Admin as i32) // Org admin or owner .or(users_organizations::access_all.eq(true)) // access_all via membership .or(users_collections::collection_uuid.eq(&self.uuid) // write access given to collection .and(users_collections::read_only.eq(false))) @@ -478,7 +478,7 @@ impl Collection { .filter( users_collections::collection_uuid.eq(&self.uuid).and(users_collections::hide_passwords.eq(true)).or(// Directly accessed collection users_organizations::access_all.eq(true).or( // access_all in Organization - users_organizations::atype.le(UserOrgType::Admin as i32) // Org admin or owner + users_organizations::atype.le(MembershipType::Admin as i32) // Org admin or owner )).or( groups::access_all.eq(true) // access_all in groups ).or( // access via groups @@ -607,7 +607,7 @@ impl CollectionUser { }} } - pub async fn find_by_collection_swap_user_uuid_with_org_user_uuid( + pub async fn find_by_collection_swap_user_uuid_with_member_uuid( collection_uuid: &str, conn: &mut DbConn, ) -> Vec { diff --git a/src/db/models/device.rs b/src/db/models/device.rs index 8feab49d..efb3380d 100644 --- a/src/db/models/device.rs +++ b/src/db/models/device.rs @@ -75,12 +75,12 @@ impl Device { // Also These key/value pairs are not used anywhere by either Vaultwarden or Bitwarden Clients // Because these might get used in the future, and they are added by the Bitwarden Server, lets keep it, but then commented out // --- - // fn arg: orgs: Vec, + // fn arg: members: Vec, // --- - // let orgowner: Vec<_> = orgs.iter().filter(|o| o.atype == 0).map(|o| o.org_uuid.clone()).collect(); - // let orgadmin: Vec<_> = orgs.iter().filter(|o| o.atype == 1).map(|o| o.org_uuid.clone()).collect(); - // let orguser: Vec<_> = orgs.iter().filter(|o| o.atype == 2).map(|o| o.org_uuid.clone()).collect(); - // let orgmanager: Vec<_> = orgs.iter().filter(|o| o.atype == 3).map(|o| o.org_uuid.clone()).collect(); + // let orgowner: Vec<_> = members.iter().filter(|m| m.atype == 0).map(|o| o.org_uuid.clone()).collect(); + // let orgadmin: Vec<_> = members.iter().filter(|m| m.atype == 1).map(|o| o.org_uuid.clone()).collect(); + // let orguser: Vec<_> = members.iter().filter(|m| m.atype == 2).map(|o| o.org_uuid.clone()).collect(); + // let orgmanager: Vec<_> = members.iter().filter(|m| m.atype == 3).map(|o| o.org_uuid.clone()).collect(); // Create the JWT claims struct, to send to the client use crate::auth::{encode_jwt, LoginJwtClaims, DEFAULT_VALIDITY, JWT_LOGIN_ISSUER}; diff --git a/src/db/models/event.rs b/src/db/models/event.rs index 22d8fb00..0f9e0e10 100644 --- a/src/db/models/event.rs +++ b/src/db/models/event.rs @@ -274,16 +274,16 @@ impl Event { }} } - pub async fn find_by_org_and_user_org( + pub async fn find_by_org_and_member( org_uuid: &str, - user_org_uuid: &str, + member_uuid: &str, start: &NaiveDateTime, end: &NaiveDateTime, conn: &mut DbConn, ) -> Vec { db_run! { conn: { event::table - .inner_join(users_organizations::table.on(users_organizations::uuid.eq(user_org_uuid))) + .inner_join(users_organizations::table.on(users_organizations::uuid.eq(member_uuid))) .filter(event::org_uuid.eq(org_uuid)) .filter(event::event_date.between(start, end)) .filter(event::user_uuid.eq(users_organizations::user_uuid.nullable()).or(event::act_user_uuid.eq(users_organizations::user_uuid.nullable()))) diff --git a/src/db/models/group.rs b/src/db/models/group.rs index d9a08970..06ae4246 100644 --- a/src/db/models/group.rs +++ b/src/db/models/group.rs @@ -1,4 +1,4 @@ -use super::{User, UserOrganization}; +use super::{Membership, User}; use crate::api::EmptyResult; use crate::db::DbConn; use crate::error::MapResult; @@ -213,7 +213,7 @@ impl Group { }} } //Returns all organizations the user has full access to - pub async fn gather_user_organizations_full_access(user_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn get_orgs_by_user_with_full_access(user_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { groups_users::table .inner_join(users_organizations::table.on( @@ -520,9 +520,9 @@ impl GroupUser { } pub async fn update_user_revision(&self, conn: &mut DbConn) { - match UserOrganization::find_by_uuid(&self.users_organizations_uuid, conn).await { - Some(user) => User::update_uuid_revision(&user.user_uuid, conn).await, - None => warn!("User could not be found!"), + match Membership::find_by_uuid(&self.users_organizations_uuid, conn).await { + Some(member) => User::update_uuid_revision(&member.user_uuid, conn).await, + None => warn!("Member could not be found!"), } } @@ -531,9 +531,9 @@ impl GroupUser { users_organizations_uuid: &str, conn: &mut DbConn, ) -> EmptyResult { - match UserOrganization::find_by_uuid(users_organizations_uuid, conn).await { - Some(user) => User::update_uuid_revision(&user.user_uuid, conn).await, - None => warn!("User could not be found!"), + match Membership::find_by_uuid(users_organizations_uuid, conn).await { + Some(member) => User::update_uuid_revision(&member.user_uuid, conn).await, + None => warn!("Member could not be found!"), }; db_run! { conn: { @@ -559,15 +559,15 @@ impl GroupUser { }} } - pub async fn delete_all_by_user(users_organizations_uuid: &str, conn: &mut DbConn) -> EmptyResult { - match UserOrganization::find_by_uuid(users_organizations_uuid, conn).await { - Some(user) => User::update_uuid_revision(&user.user_uuid, conn).await, - None => warn!("User could not be found!"), + pub async fn delete_all_by_member(member_uuid: &str, conn: &mut DbConn) -> EmptyResult { + match Membership::find_by_uuid(member_uuid, conn).await { + Some(member) => User::update_uuid_revision(&member.user_uuid, conn).await, + None => warn!("Member could not be found!"), } db_run! { conn: { diesel::delete(groups_users::table) - .filter(groups_users::users_organizations_uuid.eq(users_organizations_uuid)) + .filter(groups_users::users_organizations_uuid.eq(member_uuid)) .execute(conn) .map_res("Error deleting user groups") }} diff --git a/src/db/models/mod.rs b/src/db/models/mod.rs index 43595bee..b42ed605 100644 --- a/src/db/models/mod.rs +++ b/src/db/models/mod.rs @@ -27,7 +27,7 @@ pub use self::favorite::Favorite; pub use self::folder::{Folder, FolderCipher}; pub use self::group::{CollectionGroup, Group, GroupUser}; pub use self::org_policy::{OrgPolicy, OrgPolicyErr, OrgPolicyType}; -pub use self::organization::{Organization, OrganizationApiKey, UserOrgStatus, UserOrgType, UserOrganization}; +pub use self::organization::{Membership, MembershipStatus, MembershipType, Organization, OrganizationApiKey}; pub use self::send::{Send, SendType}; pub use self::two_factor::{TwoFactor, TwoFactorType}; pub use self::two_factor_duo_context::TwoFactorDuoContext; diff --git a/src/db/models/org_policy.rs b/src/db/models/org_policy.rs index 14447c05..d5cb372c 100644 --- a/src/db/models/org_policy.rs +++ b/src/db/models/org_policy.rs @@ -5,7 +5,7 @@ use crate::api::EmptyResult; use crate::db::DbConn; use crate::error::MapResult; -use super::{TwoFactor, UserOrgStatus, UserOrgType, UserOrganization}; +use super::{Membership, MembershipStatus, MembershipType, TwoFactor}; db_object! { #[derive(Identifiable, Queryable, Insertable, AsChangeset)] @@ -161,7 +161,7 @@ impl OrgPolicy { .and(users_organizations::user_uuid.eq(user_uuid))) ) .filter( - users_organizations::status.eq(UserOrgStatus::Confirmed as i32) + users_organizations::status.eq(MembershipStatus::Confirmed as i32) ) .select(org_policies::all_columns) .load::(conn) @@ -202,10 +202,10 @@ impl OrgPolicy { .and(users_organizations::user_uuid.eq(user_uuid))) ) .filter( - users_organizations::status.eq(UserOrgStatus::Accepted as i32) + users_organizations::status.eq(MembershipStatus::Accepted as i32) ) .or_filter( - users_organizations::status.eq(UserOrgStatus::Confirmed as i32) + users_organizations::status.eq(MembershipStatus::Confirmed as i32) ) .filter(org_policies::atype.eq(policy_type as i32)) .filter(org_policies::enabled.eq(true)) @@ -229,7 +229,7 @@ impl OrgPolicy { .and(users_organizations::user_uuid.eq(user_uuid))) ) .filter( - users_organizations::status.eq(UserOrgStatus::Confirmed as i32) + users_organizations::status.eq(MembershipStatus::Confirmed as i32) ) .filter(org_policies::atype.eq(policy_type as i32)) .filter(org_policies::enabled.eq(true)) @@ -257,8 +257,8 @@ impl OrgPolicy { continue; } - if let Some(user) = UserOrganization::find_by_user_and_org(user_uuid, &policy.org_uuid, conn).await { - if user.atype < UserOrgType::Admin { + if let Some(user) = Membership::find_by_user_and_org(user_uuid, &policy.org_uuid, conn).await { + if user.atype < MembershipType::Admin { return true; } } @@ -316,8 +316,8 @@ impl OrgPolicy { for policy in OrgPolicy::find_confirmed_by_user_and_active_policy(user_uuid, OrgPolicyType::SendOptions, conn).await { - if let Some(user) = UserOrganization::find_by_user_and_org(user_uuid, &policy.org_uuid, conn).await { - if user.atype < UserOrgType::Admin { + if let Some(user) = Membership::find_by_user_and_org(user_uuid, &policy.org_uuid, conn).await { + if user.atype < MembershipType::Admin { match serde_json::from_str::(&policy.data) { Ok(opts) => { if opts.disable_hide_email { @@ -332,9 +332,9 @@ impl OrgPolicy { false } - pub async fn is_enabled_for_member(org_user_uuid: &str, policy_type: OrgPolicyType, conn: &mut DbConn) -> bool { - if let Some(membership) = UserOrganization::find_by_uuid(org_user_uuid, conn).await { - if let Some(policy) = OrgPolicy::find_by_org_and_type(&membership.org_uuid, policy_type, conn).await { + pub async fn is_enabled_for_member(member_uuid: &str, policy_type: OrgPolicyType, conn: &mut DbConn) -> bool { + if let Some(member) = Membership::find_by_uuid(member_uuid, conn).await { + if let Some(policy) = OrgPolicy::find_by_org_and_type(&member.org_uuid, policy_type, conn).await { return policy.enabled; } } diff --git a/src/db/models/organization.rs b/src/db/models/organization.rs index 15f00991..7eacf573 100644 --- a/src/db/models/organization.rs +++ b/src/db/models/organization.rs @@ -25,7 +25,7 @@ db_object! { #[derive(Identifiable, Queryable, Insertable, AsChangeset)] #[diesel(table_name = users_organizations)] #[diesel(primary_key(uuid))] - pub struct UserOrganization { + pub struct Membership { pub uuid: String, pub user_uuid: String, pub org_uuid: String, @@ -51,7 +51,7 @@ db_object! { } // https://github.com/bitwarden/server/blob/b86a04cef9f1e1b82cf18e49fc94e017c641130c/src/Core/Enums/OrganizationUserStatusType.cs -pub enum UserOrgStatus { +pub enum MembershipStatus { Revoked = -1, Invited = 0, Accepted = 1, @@ -59,27 +59,27 @@ pub enum UserOrgStatus { } #[derive(Copy, Clone, PartialEq, Eq, num_derive::FromPrimitive)] -pub enum UserOrgType { +pub enum MembershipType { Owner = 0, Admin = 1, User = 2, Manager = 3, } -impl UserOrgType { +impl MembershipType { pub fn from_str(s: &str) -> Option { match s { - "0" | "Owner" => Some(UserOrgType::Owner), - "1" | "Admin" => Some(UserOrgType::Admin), - "2" | "User" => Some(UserOrgType::User), - "3" | "Manager" => Some(UserOrgType::Manager), + "0" | "Owner" => Some(MembershipType::Owner), + "1" | "Admin" => Some(MembershipType::Admin), + "2" | "User" => Some(MembershipType::User), + "3" | "Manager" => Some(MembershipType::Manager), _ => None, } } } -impl Ord for UserOrgType { - fn cmp(&self, other: &UserOrgType) -> Ordering { +impl Ord for MembershipType { + fn cmp(&self, other: &MembershipType) -> Ordering { // For easy comparison, map each variant to an access level (where 0 is lowest). static ACCESS_LEVEL: [i32; 4] = [ 3, // Owner @@ -91,19 +91,19 @@ impl Ord for UserOrgType { } } -impl PartialOrd for UserOrgType { - fn partial_cmp(&self, other: &UserOrgType) -> Option { +impl PartialOrd for MembershipType { + fn partial_cmp(&self, other: &MembershipType) -> Option { Some(self.cmp(other)) } } -impl PartialEq for UserOrgType { +impl PartialEq for MembershipType { fn eq(&self, other: &i32) -> bool { *other == *self as i32 } } -impl PartialOrd for UserOrgType { +impl PartialOrd for MembershipType { fn partial_cmp(&self, other: &i32) -> Option { if let Some(other) = Self::from_i32(*other) { return Some(self.cmp(&other)); @@ -120,25 +120,25 @@ impl PartialOrd for UserOrgType { } } -impl PartialEq for i32 { - fn eq(&self, other: &UserOrgType) -> bool { +impl PartialEq for i32 { + fn eq(&self, other: &MembershipType) -> bool { *self == *other as i32 } } -impl PartialOrd for i32 { - fn partial_cmp(&self, other: &UserOrgType) -> Option { - if let Some(self_type) = UserOrgType::from_i32(*self) { +impl PartialOrd for i32 { + fn partial_cmp(&self, other: &MembershipType) -> Option { + if let Some(self_type) = MembershipType::from_i32(*self) { return Some(self_type.cmp(other)); } None } - fn lt(&self, other: &UserOrgType) -> bool { + fn lt(&self, other: &MembershipType) -> bool { matches!(self.partial_cmp(other), Some(Ordering::Less) | None) } - fn le(&self, other: &UserOrgType) -> bool { + fn le(&self, other: &MembershipType) -> bool { matches!(self.partial_cmp(other), Some(Ordering::Less | Ordering::Equal) | None) } } @@ -199,7 +199,7 @@ impl Organization { // It should also provide enough room for 100+ types, which i doubt will ever happen. static ACTIVATE_REVOKE_DIFF: i32 = 128; -impl UserOrganization { +impl Membership { pub fn new(user_uuid: String, org_uuid: String) -> Self { Self { uuid: crate::util::get_uuid(), @@ -209,15 +209,15 @@ impl UserOrganization { access_all: false, akey: String::new(), - status: UserOrgStatus::Accepted as i32, - atype: UserOrgType::User as i32, + status: MembershipStatus::Accepted as i32, + atype: MembershipType::User as i32, reset_password_key: None, external_id: None, } } pub fn restore(&mut self) -> bool { - if self.status < UserOrgStatus::Invited as i32 { + if self.status < MembershipStatus::Invited as i32 { self.status += ACTIVATE_REVOKE_DIFF; return true; } @@ -225,7 +225,7 @@ impl UserOrganization { } pub fn revoke(&mut self) -> bool { - if self.status > UserOrgStatus::Revoked as i32 { + if self.status > MembershipStatus::Revoked as i32 { self.status -= ACTIVATE_REVOKE_DIFF; return true; } @@ -234,7 +234,7 @@ impl UserOrganization { /// Return the status of the user in an unrevoked state pub fn get_unrevoked_status(&self) -> i32 { - if self.status <= UserOrgStatus::Revoked as i32 { + if self.status <= MembershipStatus::Revoked as i32 { return self.status + ACTIVATE_REVOKE_DIFF; } self.status @@ -283,8 +283,8 @@ impl Organization { err!(format!("BillingEmail {} is not a valid email address", self.billing_email.trim())) } - for user_org in UserOrganization::find_by_org(&self.uuid, conn).await.iter() { - User::update_uuid_revision(&user_org.user_uuid, conn).await; + for member in Membership::find_by_org(&self.uuid, conn).await.iter() { + User::update_uuid_revision(&member.user_uuid, conn).await; } db_run! { conn: @@ -324,7 +324,7 @@ impl Organization { Cipher::delete_all_by_organization(&self.uuid, conn).await?; Collection::delete_all_by_organization(&self.uuid, conn).await?; - UserOrganization::delete_all_by_organization(&self.uuid, conn).await?; + Membership::delete_all_by_organization(&self.uuid, conn).await?; OrgPolicy::delete_all_by_organization(&self.uuid, conn).await?; Group::delete_all_by_organization(&self.uuid, conn).await?; OrganizationApiKey::delete_all_by_organization(&self.uuid, conn).await?; @@ -352,7 +352,7 @@ impl Organization { } } -impl UserOrganization { +impl Membership { pub async fn to_json(&self, conn: &mut DbConn) -> Value { let org = Organization::find_by_uuid(&self.org_uuid, conn).await.unwrap(); @@ -446,8 +446,8 @@ impl UserOrganization { // Because BitWarden want the status to be -1 for revoked users we need to catch that here. // We subtract/add a number so we can restore/activate the user to it's previous state again. - let status = if self.status < UserOrgStatus::Revoked as i32 { - UserOrgStatus::Revoked as i32 + let status = if self.status < MembershipStatus::Revoked as i32 { + MembershipStatus::Revoked as i32 } else { self.status }; @@ -489,12 +489,12 @@ impl UserOrganization { .into_iter() .filter_map(|c| { let (read_only, hide_passwords, can_manage) = if self.has_full_access() { - (false, false, self.atype >= UserOrgType::Manager) + (false, false, self.atype >= MembershipType::Manager) } else if let Some(cu) = cu.get(&c.uuid) { ( cu.read_only, cu.hide_passwords, - self.atype == UserOrgType::Manager && !cu.read_only && !cu.hide_passwords, + self.atype == MembershipType::Manager && !cu.read_only && !cu.hide_passwords, ) // If previous checks failed it might be that this user has access via a group, but we should not return those elements here // Those are returned via a special group endpoint @@ -538,7 +538,7 @@ impl UserOrganization { json!({ "id": self.uuid, "userId": self.user_uuid, - "name": if self.get_unrevoked_status() >= UserOrgStatus::Accepted as i32 { Some(user.name) } else { None }, + "name": if self.get_unrevoked_status() >= MembershipStatus::Accepted as i32 { Some(user.name) } else { None }, "email": user.email, "externalId": self.external_id, "avatarColor": user.avatar_color, @@ -590,8 +590,8 @@ impl UserOrganization { // Because BitWarden want the status to be -1 for revoked users we need to catch that here. // We subtract/add a number so we can restore/activate the user to it's previous state again. - let status = if self.status < UserOrgStatus::Revoked as i32 { - UserOrgStatus::Revoked as i32 + let status = if self.status < MembershipStatus::Revoked as i32 { + MembershipStatus::Revoked as i32 } else { self.status }; @@ -614,7 +614,7 @@ impl UserOrganization { db_run! { conn: sqlite, mysql { match diesel::replace_into(users_organizations::table) - .values(UserOrganizationDb::to_db(self)) + .values(MembershipDb::to_db(self)) .execute(conn) { Ok(_) => Ok(()), @@ -622,7 +622,7 @@ impl UserOrganization { Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => { diesel::update(users_organizations::table) .filter(users_organizations::uuid.eq(&self.uuid)) - .set(UserOrganizationDb::to_db(self)) + .set(MembershipDb::to_db(self)) .execute(conn) .map_res("Error adding user to organization") }, @@ -630,7 +630,7 @@ impl UserOrganization { }.map_res("Error adding user to organization") } postgresql { - let value = UserOrganizationDb::to_db(self); + let value = MembershipDb::to_db(self); diesel::insert_into(users_organizations::table) .values(&value) .on_conflict(users_organizations::uuid) @@ -646,7 +646,7 @@ impl UserOrganization { User::update_uuid_revision(&self.user_uuid, conn).await; CollectionUser::delete_all_by_user_and_org(&self.user_uuid, &self.org_uuid, conn).await?; - GroupUser::delete_all_by_user(&self.uuid, conn).await?; + GroupUser::delete_all_by_member(&self.uuid, conn).await?; db_run! { conn: { diesel::delete(users_organizations::table.filter(users_organizations::uuid.eq(self.uuid))) @@ -656,46 +656,46 @@ impl UserOrganization { } pub async fn delete_all_by_organization(org_uuid: &str, conn: &mut DbConn) -> EmptyResult { - for user_org in Self::find_by_org(org_uuid, conn).await { - user_org.delete(conn).await?; + for member in Self::find_by_org(org_uuid, conn).await { + member.delete(conn).await?; } Ok(()) } pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult { - for user_org in Self::find_any_state_by_user(user_uuid, conn).await { - user_org.delete(conn).await?; + for member in Self::find_any_state_by_user(user_uuid, conn).await { + member.delete(conn).await?; } Ok(()) } - pub async fn find_by_email_and_org(email: &str, org_id: &str, conn: &mut DbConn) -> Option { + pub async fn find_by_email_and_org(email: &str, org_id: &str, conn: &mut DbConn) -> Option { if let Some(user) = User::find_by_mail(email, conn).await { - if let Some(user_org) = UserOrganization::find_by_user_and_org(&user.uuid, org_id, conn).await { - return Some(user_org); + if let Some(member) = Membership::find_by_user_and_org(&user.uuid, org_id, conn).await { + return Some(member); } } None } - pub fn has_status(&self, status: UserOrgStatus) -> bool { + pub fn has_status(&self, status: MembershipStatus) -> bool { self.status == status as i32 } - pub fn has_type(&self, user_type: UserOrgType) -> bool { + pub fn has_type(&self, user_type: MembershipType) -> bool { self.atype == user_type as i32 } pub fn has_full_access(&self) -> bool { - (self.access_all || self.atype >= UserOrgType::Admin) && self.has_status(UserOrgStatus::Confirmed) + (self.access_all || self.atype >= MembershipType::Admin) && self.has_status(MembershipStatus::Confirmed) } pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option { db_run! { conn: { users_organizations::table .filter(users_organizations::uuid.eq(uuid)) - .first::(conn) + .first::(conn) .ok().from_db() }} } @@ -705,7 +705,7 @@ impl UserOrganization { users_organizations::table .filter(users_organizations::uuid.eq(uuid)) .filter(users_organizations::org_uuid.eq(org_uuid)) - .first::(conn) + .first::(conn) .ok().from_db() }} } @@ -714,8 +714,8 @@ impl UserOrganization { db_run! { conn: { users_organizations::table .filter(users_organizations::user_uuid.eq(user_uuid)) - .filter(users_organizations::status.eq(UserOrgStatus::Confirmed as i32)) - .load::(conn) + .filter(users_organizations::status.eq(MembershipStatus::Confirmed as i32)) + .load::(conn) .unwrap_or_default().from_db() }} } @@ -724,8 +724,8 @@ impl UserOrganization { db_run! { conn: { users_organizations::table .filter(users_organizations::user_uuid.eq(user_uuid)) - .filter(users_organizations::status.eq(UserOrgStatus::Invited as i32)) - .load::(conn) + .filter(users_organizations::status.eq(MembershipStatus::Invited as i32)) + .load::(conn) .unwrap_or_default().from_db() }} } @@ -734,7 +734,7 @@ impl UserOrganization { db_run! { conn: { users_organizations::table .filter(users_organizations::user_uuid.eq(user_uuid)) - .load::(conn) + .load::(conn) .unwrap_or_default().from_db() }} } @@ -743,7 +743,7 @@ impl UserOrganization { db_run! { conn: { users_organizations::table .filter(users_organizations::user_uuid.eq(user_uuid)) - .filter(users_organizations::status.eq(UserOrgStatus::Accepted as i32).or(users_organizations::status.eq(UserOrgStatus::Confirmed as i32))) + .filter(users_organizations::status.eq(MembershipStatus::Accepted as i32).or(users_organizations::status.eq(MembershipStatus::Confirmed as i32))) .count() .first::(conn) .unwrap_or(0) @@ -754,7 +754,7 @@ impl UserOrganization { db_run! { conn: { users_organizations::table .filter(users_organizations::org_uuid.eq(org_uuid)) - .load::(conn) + .load::(conn) .expect("Error loading user organizations").from_db() }} } @@ -763,8 +763,8 @@ impl UserOrganization { db_run! { conn: { users_organizations::table .filter(users_organizations::org_uuid.eq(org_uuid)) - .filter(users_organizations::status.eq(UserOrgStatus::Confirmed as i32)) - .load::(conn) + .filter(users_organizations::status.eq(MembershipStatus::Confirmed as i32)) + .load::(conn) .unwrap_or_default().from_db() }} } @@ -780,22 +780,22 @@ impl UserOrganization { }} } - pub async fn find_by_org_and_type(org_uuid: &str, atype: UserOrgType, conn: &mut DbConn) -> Vec { + pub async fn find_by_org_and_type(org_uuid: &str, atype: MembershipType, conn: &mut DbConn) -> Vec { db_run! { conn: { users_organizations::table .filter(users_organizations::org_uuid.eq(org_uuid)) .filter(users_organizations::atype.eq(atype as i32)) - .load::(conn) + .load::(conn) .expect("Error loading user organizations").from_db() }} } - pub async fn count_confirmed_by_org_and_type(org_uuid: &str, atype: UserOrgType, conn: &mut DbConn) -> i64 { + pub async fn count_confirmed_by_org_and_type(org_uuid: &str, atype: MembershipType, conn: &mut DbConn) -> i64 { db_run! { conn: { users_organizations::table .filter(users_organizations::org_uuid.eq(org_uuid)) .filter(users_organizations::atype.eq(atype as i32)) - .filter(users_organizations::status.eq(UserOrgStatus::Confirmed as i32)) + .filter(users_organizations::status.eq(MembershipStatus::Confirmed as i32)) .count() .first::(conn) .unwrap_or(0) @@ -807,7 +807,7 @@ impl UserOrganization { users_organizations::table .filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::org_uuid.eq(org_uuid)) - .first::(conn) + .first::(conn) .ok().from_db() }} } @@ -818,9 +818,9 @@ impl UserOrganization { .filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::org_uuid.eq(org_uuid)) .filter( - users_organizations::status.eq(UserOrgStatus::Confirmed as i32) + users_organizations::status.eq(MembershipStatus::Confirmed as i32) ) - .first::(conn) + .first::(conn) .ok().from_db() }} } @@ -829,12 +829,12 @@ impl UserOrganization { db_run! { conn: { users_organizations::table .filter(users_organizations::user_uuid.eq(user_uuid)) - .load::(conn) + .load::(conn) .expect("Error loading user organizations").from_db() }} } - pub async fn get_org_uuid_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { + pub async fn get_orgs_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { users_organizations::table .filter(users_organizations::user_uuid.eq(user_uuid)) @@ -855,10 +855,10 @@ impl UserOrganization { .and(org_policies::enabled.eq(true))) ) .filter( - users_organizations::status.eq(UserOrgStatus::Confirmed as i32) + users_organizations::status.eq(MembershipStatus::Confirmed as i32) ) .select(users_organizations::all_columns) - .load::(conn) + .load::(conn) .unwrap_or_default().from_db() }} } @@ -882,7 +882,7 @@ impl UserOrganization { ) .select(users_organizations::all_columns) .distinct() - .load::(conn).expect("Error loading user organizations").from_db() + .load::(conn).expect("Error loading user organizations").from_db() }} } @@ -908,7 +908,7 @@ impl UserOrganization { ) .select(users_organizations::all_columns) .distinct() - .load::(conn).expect("Error loading user organizations with groups").from_db() + .load::(conn).expect("Error loading user organizations with groups").from_db() }} } @@ -917,7 +917,7 @@ impl UserOrganization { users_organizations::table .inner_join(ciphers::table.on(ciphers::uuid.eq(cipher_uuid).and(ciphers::organization_uuid.eq(users_organizations::org_uuid.nullable())))) .filter(users_organizations::user_uuid.eq(user_uuid)) - .filter(users_organizations::atype.eq_any(vec![UserOrgType::Owner as i32, UserOrgType::Admin as i32])) + .filter(users_organizations::atype.eq_any(vec![MembershipType::Owner as i32, MembershipType::Admin as i32])) .count() .first::(conn) .ok().unwrap_or(0) != 0 @@ -937,7 +937,7 @@ impl UserOrganization { ) ) .select(users_organizations::all_columns) - .load::(conn).expect("Error loading user organizations").from_db() + .load::(conn).expect("Error loading user organizations").from_db() }} } @@ -948,7 +948,7 @@ impl UserOrganization { users_organizations::external_id.eq(ext_id) .and(users_organizations::org_uuid.eq(org_uuid)) ) - .first::(conn).ok().from_db() + .first::(conn).ok().from_db() }} } } @@ -1011,9 +1011,9 @@ mod tests { #[test] #[allow(non_snake_case)] - fn partial_cmp_UserOrgType() { - assert!(UserOrgType::Owner > UserOrgType::Admin); - assert!(UserOrgType::Admin > UserOrgType::Manager); - assert!(UserOrgType::Manager > UserOrgType::User); + fn partial_cmp_MembershipType() { + assert!(MembershipType::Owner > MembershipType::Admin); + assert!(MembershipType::Admin > MembershipType::Manager); + assert!(MembershipType::Manager > MembershipType::User); } } diff --git a/src/db/models/user.rs b/src/db/models/user.rs index 94f42c84..981a4605 100644 --- a/src/db/models/user.rs +++ b/src/db/models/user.rs @@ -215,8 +215,7 @@ impl User { } use super::{ - Cipher, Device, EmergencyAccess, Favorite, Folder, Send, TwoFactor, TwoFactorIncomplete, UserOrgType, - UserOrganization, + Cipher, Device, EmergencyAccess, Favorite, Folder, Membership, MembershipType, Send, TwoFactor, TwoFactorIncomplete, }; use crate::db::DbConn; @@ -227,7 +226,7 @@ use crate::error::MapResult; impl User { pub async fn to_json(&self, conn: &mut DbConn) -> Value { let mut orgs_json = Vec::new(); - for c in UserOrganization::find_confirmed_by_user(&self.uuid, conn).await { + for c in Membership::find_confirmed_by_user(&self.uuid, conn).await { orgs_json.push(c.to_json(conn).await); } @@ -304,10 +303,9 @@ impl User { } pub async fn delete(self, conn: &mut DbConn) -> EmptyResult { - for user_org in UserOrganization::find_confirmed_by_user(&self.uuid, conn).await { - if user_org.atype == UserOrgType::Owner - && UserOrganization::count_confirmed_by_org_and_type(&user_org.org_uuid, UserOrgType::Owner, conn).await - <= 1 + for member in Membership::find_confirmed_by_user(&self.uuid, conn).await { + if member.atype == MembershipType::Owner + && Membership::count_confirmed_by_org_and_type(&member.org_uuid, MembershipType::Owner, conn).await <= 1 { err!("Can't delete last owner") } @@ -316,7 +314,7 @@ impl User { Send::delete_all_by_user(&self.uuid, conn).await?; EmergencyAccess::delete_all_by_user(&self.uuid, conn).await?; EmergencyAccess::delete_all_by_grantee_email(&self.email, conn).await?; - UserOrganization::delete_all_by_user(&self.uuid, conn).await?; + Membership::delete_all_by_user(&self.uuid, conn).await?; Cipher::delete_all_by_user(&self.uuid, conn).await?; Favorite::delete_all_by_user(&self.uuid, conn).await?; Folder::delete_all_by_user(&self.uuid, conn).await?; diff --git a/src/mail.rs b/src/mail.rs index 1a32ae25..a8e250c8 100644 --- a/src/mail.rs +++ b/src/mail.rs @@ -260,7 +260,7 @@ pub async fn send_single_org_removed_from_org(address: &str, org_name: &str) -> pub async fn send_invite( user: &User, org_id: Option, - org_user_id: Option, + member_id: Option, org_name: &str, invited_by_email: Option, ) -> EmptyResult { @@ -268,7 +268,7 @@ pub async fn send_invite( user.uuid.clone(), user.email.clone(), org_id.clone(), - org_user_id.clone(), + member_id.clone(), invited_by_email, ); let invite_token = encode_jwt(&claims); @@ -279,7 +279,7 @@ pub async fn send_invite( .append_pair("email", &user.email) .append_pair("organizationName", org_name) .append_pair("organizationId", org_id.as_deref().unwrap_or("_")) - .append_pair("organizationUserId", org_user_id.as_deref().unwrap_or("_")) + .append_pair("organizationUserId", member_id.as_deref().unwrap_or("_")) .append_pair("token", &invite_token); if user.private_key.is_some() { query_params.append_pair("orgUserHasExistingUser", "true");