diff --git a/src/api/admin.rs b/src/api/admin.rs index f49f6ed34b..c7c11b0cca 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_member, edit_member}, 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 ae78c00ac9..f6c2048e67 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) @@ -489,7 +489,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 @@ -521,7 +521,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) { @@ -560,9 +560,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( @@ -570,7 +570,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, )?; @@ -602,14 +602,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 136b890a7e..8e1c4f0e13 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 8ed9e87a7b..6d3d881dd4 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 484094f52e..49ff083f41 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 902ab25a12..b21ae6b99d 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -32,8 +32,8 @@ pub fn routes() -> Vec { put_organization, post_organization, post_organization_collections, - delete_organization_collection_user, - post_organization_collection_delete_user, + delete_organization_collection_member, + post_organization_collection_delete_member, post_organization_collection_update, put_organization_collection_update, delete_organization_collection, @@ -41,20 +41,20 @@ 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, + reinvite_member, + bulk_reinvite_members, confirm_invite, bulk_confirm_invite, accept_invite, get_org_user_mini_details, get_user, - edit_user, - put_organization_user, - delete_user, - bulk_delete_user, - post_delete_user, + edit_member, + put_member, + delete_member, + bulk_delete_member, + post_delete_member, post_org_import, list_policies, list_policies_token, @@ -69,14 +69,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_member, + bulk_deactivate_members, + revoke_member, + bulk_revoke_members, + activate_member, + bulk_activate_members, + restore_member, + bulk_restore_members, get_groups, get_groups_details, post_groups, @@ -87,13 +87,13 @@ pub fn routes() -> Vec { delete_group, post_delete_group, bulk_delete_groups, - get_group_users, - put_group_users, + get_group_members, + put_group_members, get_user_groups, post_user_groups, put_user_groups, - delete_group_user, - post_delete_group_user, + delete_group_member, + post_delete_group_member, put_reset_password_enrollment, get_reset_password_details, put_reset_password, @@ -174,16 +174,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())) @@ -218,18 +218,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, @@ -238,7 +238,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 } } } @@ -320,31 +320,27 @@ 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") }; // get all collection memberships for the current organization - let coll_users = CollectionUser::find_by_organization_swap_user_uuid_with_org_user_uuid(org_id, &mut conn).await; - // Generate a HashMap to get the correct UserOrgType per user to determine the manage permission + let coll_users = CollectionUser::find_by_organization_swap_user_uuid_with_member_uuid(org_id, &mut conn).await; + // Generate a HashMap to get the correct MembershipType per user to determine the manage permission // We use the uuid instead of the user_uuid here, since that is what is used in CollectionUser - let users_org_type: HashMap = UserOrganization::find_confirmed_by_org(org_id, &mut conn) - .await - .into_iter() - .map(|uo| (uo.uuid, uo.atype)) - .collect(); + let membership_type: HashMap = + Membership::find_confirmed_by_org(org_id, &mut conn).await.into_iter().map(|m| (m.uuid, m.atype)).collect(); // 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); // Not assigned collections should not be returned if !assigned { @@ -358,7 +354,7 @@ async fn get_org_collections_details(org_id: &str, headers: ManagerHeadersLoose, .map(|collection_user| { SelectionReadOnly::to_collection_user_details_read_only( collection_user, - *users_org_type.get(&collection_user.user_uuid).unwrap_or(&(UserOrgType::User as i32)), + *membership_type.get(&collection_user.user_uuid).unwrap_or(&(MembershipType::User as i32)), ) .to_json() }) @@ -431,20 +427,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())) @@ -509,25 +505,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/")] -async fn delete_organization_collection_user( +#[delete("/organizations//collections//user/")] +async fn delete_organization_collection_member( org_id: &str, col_id: &str, - org_user_id: &str, + member_id: &str, _headers: AdminHeaders, mut conn: DbConn, ) -> EmptyResult { @@ -535,10 +531,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, } @@ -546,15 +542,15 @@ async fn delete_organization_collection_user( } } -#[post("/organizations//collections//delete-user/")] -async fn post_organization_collection_delete_user( +#[post("/organizations//collections//delete-user/")] +async fn post_organization_collection_delete_member( 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_member(org_id, col_id, member_id, headers, conn).await } async fn _delete_organization_collection( @@ -647,8 +643,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") }; @@ -666,28 +661,28 @@ async fn get_org_collection_detail( Vec::with_capacity(0) }; - // Generate a HashMap to get the correct UserOrgType per user to determine the manage permission + // Generate a HashMap to get the correct MembershipType per user to determine the manage permission // We use the uuid instead of the user_uuid here, since that is what is used in CollectionUser - let users_org_type: HashMap = UserOrganization::find_confirmed_by_org(org_id, &mut conn) + let membership_type: HashMap = Membership::find_confirmed_by_org(org_id, &mut conn) .await .into_iter() - .map(|uo| (uo.uuid, uo.atype)) + .map(|m| (m.uuid, m.atype)) .collect(); 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| { SelectionReadOnly::to_collection_user_details_read_only( collection_user, - *users_org_type.get(&collection_user.user_uuid).unwrap_or(&(UserOrgType::User as i32)), + *membership_type.get(&collection_user.user_uuid).unwrap_or(&(MembershipType::User as i32)), ) .to_json() }) .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); @@ -710,7 +705,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), @@ -738,7 +733,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") }; @@ -760,9 +755,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); } @@ -795,14 +788,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), @@ -874,13 +867,13 @@ async fn send_invite(org_id: &str, data: Json, headers: AdminHeaders // HACK: We need the raw user-type to be sure custom role is selected to determine the access_all permission // The from_str() will convert the custom role type into a manager role type let raw_type = &data.r#type.into_string(); - // UserOrgType::from_str will convert custom (4) to manager (3) - let new_type = match UserOrgType::from_str(raw_type) { + // Membership::from_str will convert custom (4) to manager (3) + let new_type = match MembershipType::from_str(raw_type) { 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") } @@ -896,7 +889,7 @@ async fn send_invite(org_id: &str, data: Json, headers: AdminHeaders } 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() { @@ -917,23 +910,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_member = 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_member.access_all = access_all; + new_member.atype = new_type; + new_member.status = member_status; // If no accessAll, add the collections received if !access_all { @@ -954,16 +947,16 @@ async fn send_invite(org_id: &str, data: Json, headers: AdminHeaders } } - new_user.save(&mut conn).await?; + new_member.save(&mut conn).await?; for group in data.groups.iter() { - let mut group_entry = GroupUser::new(String::from(group), new_user.uuid.clone()); + let mut group_entry = GroupUser::new(String::from(group), new_member.uuid.clone()); group_entry.save(&mut conn).await?; } log_event( EventType::OrganizationUserInvited as i32, - &new_user.uuid, + &new_member.uuid, org_id, &headers.user.uuid, headers.device.atype, @@ -981,7 +974,7 @@ async fn send_invite(org_id: &str, data: Json, headers: AdminHeaders mail::send_invite( &user, Some(String::from(org_id)), - Some(new_user.uuid), + Some(new_member.uuid), &org_name, Some(headers.user.email.clone()), ) @@ -993,7 +986,7 @@ async fn send_invite(org_id: &str, data: Json, headers: AdminHeaders } #[post("/organizations//users/reinvite", data = "")] -async fn bulk_reinvite_user( +async fn bulk_reinvite_members( org_id: &str, data: Json, headers: AdminHeaders, @@ -1002,8 +995,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_member(org_id, &member_id, &headers.user.email, &mut conn).await { Ok(_) => String::new(), Err(e) => format!("{e:?}"), }; @@ -1011,7 +1004,7 @@ async fn bulk_reinvite_user( bulk_response.push(json!( { "object": "OrganizationBulkConfirmResponseModel", - "id": org_user_id, + "id": member_id, "error": err_msg } )) @@ -1024,21 +1017,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_member(org_id: &str, member: &str, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult { + _reinvite_member(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_member(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.") }; @@ -1055,7 +1048,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()), ) @@ -1065,9 +1058,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(()) @@ -1080,28 +1073,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") } @@ -1110,10 +1103,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_member, edit_member, 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() { @@ -1128,13 +1121,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"), @@ -1187,9 +1180,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:?}"), }; @@ -1197,7 +1190,7 @@ async fn bulk_confirm_invite( bulk_response.push(json!( { "object": "OrganizationBulkConfirmResponseModel", - "id": org_user_id, + "id": member_id, "error": err_msg } )); @@ -1213,10 +1206,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, @@ -1224,41 +1217,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_member, edit_member, 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"); } @@ -1269,12 +1262,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, @@ -1288,16 +1281,16 @@ 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; } @@ -1306,27 +1299,27 @@ async fn _confirm_invite( #[get("/organizations//users/mini-details", rank = 1)] async fn get_org_user_mini_details(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 { - users_json.push(u.to_json_mini_details(&mut conn).await); + let mut members_json = Vec::new(); + for m in Membership::find_by_org(org_id, &mut conn).await { + members_json.push(m.to_json_mini_details(&mut conn).await); } Json(json!({ - "data": users_json, + "data": members_json, "object": "list", "continuationToken": null, })) } -#[get("/organizations//users/?", rank = 2)] +#[get("/organizations//users/?", rank = 2)] 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") }; @@ -1350,21 +1343,21 @@ struct EditUserData { permissions: HashMap, } -#[put("/organizations//users/", data = "", rank = 1)] -async fn put_organization_user( +#[put("/organizations//users/", data = "", rank = 1)] +async fn put_member( 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_member(org_id, member_id, data, headers, conn).await } -#[post("/organizations//users/", data = "", rank = 1)] -async fn edit_user( +#[post("/organizations//users/", data = "", rank = 1)] +async fn edit_member( org_id: &str, - org_user_id: &str, + member_id: &str, data: Json, headers: AdminHeaders, mut conn: DbConn, @@ -1374,8 +1367,8 @@ async fn edit_user( // HACK: We need the raw user-type to be sure custom role is selected to determine the access_all permission // The from_str() will convert the custom role type into a manager role type let raw_type = &data.r#type.into_string(); - // UserOrgType::from_str will convert custom (4) to manager (3) - let Some(new_type) = UserOrgType::from_str(raw_type) else { + // MembershipTyp::from_str will convert custom (4) to manager (3) + let Some(new_type) = MembershipType::from_str(raw_type) else { err!("Invalid type") }; @@ -1390,40 +1383,40 @@ async fn edit_user( data.access_all = true; } - let mut user_to_edit = match UserOrganization::find_by_uuid_and_org(org_user_id, org_id, &mut conn).await { - Some(user) => user, + let mut member_to_edit = match Membership::find_by_uuid_and_org(member_id, org_id, &mut conn).await { + Some(member) => member, None => 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_member, edit_member, 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"); } @@ -1434,11 +1427,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?; } @@ -1449,7 +1442,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, @@ -1461,16 +1454,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, @@ -1479,11 +1472,11 @@ async fn edit_user( ) .await; - user_to_edit.save(&mut conn).await + member_to_edit.save(&mut conn).await } #[delete("/organizations//users", data = "")] -async fn bulk_delete_user( +async fn bulk_delete_member( org_id: &str, data: Json, headers: AdminHeaders, @@ -1493,8 +1486,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_member(org_id, &member_id, &headers, &mut conn, &nt).await { Ok(_) => String::new(), Err(e) => format!("{e:?}"), }; @@ -1502,7 +1495,7 @@ async fn bulk_delete_user( bulk_response.push(json!( { "object": "OrganizationBulkConfirmResponseModel", - "id": org_user_id, + "id": member_id, "error": err_msg } )) @@ -1515,53 +1508,54 @@ async fn bulk_delete_user( })) } -#[delete("/organizations//users/")] -async fn delete_user( +#[delete("/organizations//users/")] +async fn delete_member( 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_member(org_id, member_id, &headers, &mut conn, &nt).await } -#[post("/organizations//users//delete")] -async fn post_delete_user( +#[post("/organizations//users//delete")] +async fn post_delete_member( 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_member(org_id, member_id, &headers, &mut conn, &nt).await } -async fn _delete_user( +async fn _delete_member( 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, @@ -1570,11 +1564,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 = "")] @@ -1587,23 +1581,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"), } } @@ -1879,14 +1873,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(); @@ -2036,8 +2030,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"), }; @@ -2045,10 +2039,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, @@ -2057,28 +2051,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, @@ -2096,7 +2090,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()), ) @@ -2108,12 +2102,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, @@ -2122,7 +2116,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?; } } } @@ -2132,35 +2126,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_member(org_id: &str, member_id: &str, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult { + _revoke_member(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_members( org_id: &str, data: Json, headers: AdminHeaders, conn: DbConn, ) -> Json { - bulk_revoke_organization_user(org_id, data, headers, conn).await + bulk_revoke_members(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_member(org_id: &str, member_id: &str, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult { + _revoke_member(org_id, member_id, &headers, &mut conn).await } #[derive(Deserialize, Debug)] @@ -2170,7 +2154,7 @@ struct OrgBulkRevokeData { } #[put("/organizations//users/revoke", data = "")] -async fn bulk_revoke_organization_user( +async fn bulk_revoke_members( org_id: &str, data: Json, headers: AdminHeaders, @@ -2180,9 +2164,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_member(org_id, &member_id, &headers, &mut conn).await { Ok(_) => String::new(), Err(e) => format!("{e:?}"), }; @@ -2190,7 +2174,7 @@ async fn bulk_revoke_organization_user( bulk_response.push(json!( { "object": "OrganizationUserBulkResponseModel", - "id": org_user_id, + "id": member_id, "error": err_msg } )); @@ -2206,32 +2190,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_member(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, @@ -2247,39 +2226,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_member(org_id: &str, member_id: &str, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult { + _restore_member(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_members( org_id: &str, data: Json, headers: AdminHeaders, conn: DbConn, ) -> Json { - bulk_restore_organization_user(org_id, data, headers, conn).await + bulk_restore_members(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_member(org_id: &str, member_id: &str, headers: AdminHeaders, mut conn: DbConn) -> EmptyResult { + _restore_member(org_id, member_id, &headers, &mut conn).await } #[put("/organizations//users/restore", data = "")] -async fn bulk_restore_organization_user( +async fn bulk_restore_members( org_id: &str, data: Json, headers: AdminHeaders, @@ -2288,8 +2257,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_member(org_id, &member_id, &headers, &mut conn).await { Ok(_) => String::new(), Err(e) => format!("{e:?}"), }; @@ -2297,7 +2266,7 @@ async fn bulk_restore_organization_user( bulk_response.push(json!( { "object": "OrganizationUserBulkResponseModel", - "id": org_user_id, + "id": member_id, "error": err_msg } )); @@ -2310,29 +2279,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_member(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_member, edit_member, 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"); } @@ -2343,12 +2307,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, @@ -2446,7 +2410,7 @@ impl SelectionReadOnly { pub fn to_collection_user_details_read_only( collection_user: &CollectionUser, - user_org_type: i32, + membership_type: i32, ) -> SelectionReadOnly { // Vaultwarden allows manage access for Admins and Owners by default // For managers (Or custom role) it depends if they have read_ony or hide_passwords set to true or not @@ -2454,8 +2418,8 @@ impl SelectionReadOnly { id: collection_user.user_uuid.clone(), read_only: collection_user.read_only, hide_passwords: collection_user.hide_passwords, - manage: user_org_type >= UserOrgType::Admin - || (user_org_type == UserOrgType::Manager + manage: membership_type >= MembershipType::Admin + || (membership_type == MembershipType::Manager && !collection_user.read_only && !collection_user.hide_passwords), } @@ -2655,7 +2619,7 @@ async fn get_group(org_id: &str, group_id: &str, _headers: AdminHeaders, mut con } #[get("/organizations//groups//users")] -async fn get_group_users(org_id: &str, group_id: &str, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult { +async fn get_group_members(org_id: &str, group_id: &str, _headers: AdminHeaders, mut conn: DbConn) -> JsonResult { if !CONFIG.org_groups_enabled() { err!("Group support is disabled"); } @@ -2664,17 +2628,17 @@ async fn get_group_users(org_id: &str, group_id: &str, _headers: AdminHeaders, m err!("Group could not be found!", "Group uuid is invalid or does not belong to the organization") }; - let group_users: Vec = GroupUser::find_by_group(group_id, &mut conn) + let group_members: Vec = GroupUser::find_by_group(group_id, &mut conn) .await .iter() .map(|entry| entry.users_organizations_uuid.clone()) .collect(); - Ok(Json(json!(group_users))) + Ok(Json(json!(group_members))) } #[put("/organizations//groups//users", data = "")] -async fn put_group_users( +async fn put_group_members( org_id: &str, group_id: &str, headers: AdminHeaders, @@ -2717,7 +2681,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!") }; @@ -2733,21 +2697,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, @@ -2756,21 +2720,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, @@ -2782,22 +2746,22 @@ async fn put_user_groups( Ok(()) } -#[post("/organizations//groups//delete-user/")] -async fn post_delete_group_user( +#[post("/organizations//groups//delete-user/")] +async fn post_delete_group_member( 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_member(org_id, group_id, member_id, headers, conn).await } -#[delete("/organizations//groups//users/")] -async fn delete_group_user( +#[delete("/organizations//groups//users/")] +async fn delete_group_member( org_id: &str, group_id: &str, - org_user_id: &str, + member_id: &str, headers: AdminHeaders, mut conn: DbConn, ) -> EmptyResult { @@ -2805,7 +2769,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."); } @@ -2815,7 +2779,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, @@ -2824,7 +2788,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)] @@ -2864,10 +2828,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, @@ -2877,20 +2841,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"); } @@ -2910,7 +2874,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, @@ -2922,10 +2886,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 { @@ -2933,15 +2897,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!({ @@ -2950,7 +2914,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, }))) @@ -2958,20 +2922,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"), } } @@ -2992,15 +2956,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") }; @@ -3023,16 +2987,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 3b3e74cb94..1480cef098 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 486b526a0a..756ae83697 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 13248f0ad7..1d1119875c 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 15910679a9..fda4cb0224 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 782ae69983..04d3b4048c 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}; @@ -391,17 +391,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()) } } } @@ -526,11 +525,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 @@ -745,7 +744,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) @@ -772,7 +771,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 ); } @@ -790,7 +789,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) @@ -804,7 +803,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 ); } @@ -970,7 +969,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() @@ -993,7 +992,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() @@ -1032,7 +1031,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 907aebf7a8..59967c9688 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 @@ -511,10 +511,7 @@ impl CollectionUser { }} } - pub async fn find_by_organization_swap_user_uuid_with_org_user_uuid( - org_uuid: &str, - conn: &mut DbConn, - ) -> Vec { + pub async fn find_by_organization_swap_user_uuid_with_member_uuid(org_uuid: &str, conn: &mut DbConn) -> Vec { db_run! { conn: { users_collections::table .inner_join(collections::table.on(collections::uuid.eq(users_collections::collection_uuid))) @@ -610,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 8feab49dd4..efb3380d93 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 22d8fb00e8..0f9e0e107f 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 84c2727a2d..526ceb6ec8 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; @@ -216,7 +216,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( @@ -523,9 +523,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!"), } } @@ -534,9 +534,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: { @@ -562,15 +562,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 43595bee56..b42ed6058c 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 14447c05dd..d5cb372c30 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 c8c1d3a459..80e0d674ea 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,29 +59,29 @@ 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), // HACK: We convert the custom role to a manager role - "4" | "Custom" => Some(UserOrgType::Manager), + "4" | "Custom" => 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 @@ -93,19 +93,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)); @@ -122,25 +122,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) } } @@ -214,7 +214,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(), @@ -224,15 +224,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; } @@ -240,7 +240,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; } @@ -249,7 +249,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 @@ -307,8 +307,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: @@ -348,7 +348,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?; @@ -376,13 +376,13 @@ 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(); // HACK: Convert the manager type to a custom type // It will be converted back on other locations - let user_org_type = self.type_manager_as_custom(); + let membership_type = self.type_manager_as_custom(); let permissions = json!({ // TODO: Add full support for Custom User Roles @@ -392,9 +392,9 @@ impl UserOrganization { "accessImportExport": false, "accessReports": false, // If the following 3 Collection roles are set to true a custom user has access all permission - "createNewCollections": user_org_type == 4 && self.access_all, - "editAnyCollection": user_org_type == 4 && self.access_all, - "deleteAnyCollection": user_org_type == 4 && self.access_all, + "createNewCollections": membership_type == 4 && self.access_all, + "editAnyCollection": membership_type == 4 && self.access_all, + "deleteAnyCollection": membership_type == 4 && self.access_all, "manageGroups": false, "managePolicies": false, "manageSso": false, // Not supported @@ -459,7 +459,7 @@ impl UserOrganization { "userId": self.user_uuid, "key": self.akey, "status": self.status, - "type": user_org_type, + "type": membership_type, "enabled": true, "object": "profileOrganization", @@ -476,8 +476,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 }; @@ -519,12 +519,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 @@ -548,11 +548,11 @@ impl UserOrganization { // HACK: Convert the manager type to a custom type // It will be converted back on other locations - let user_org_type = self.type_manager_as_custom(); + let membership_type = self.type_manager_as_custom(); // HACK: Only return permissions if the user is of type custom and has access_all // Else Bitwarden will assume the defaults of all false - let permissions = if user_org_type == 4 && self.access_all { + let permissions = if membership_type == 4 && self.access_all { json!({ // TODO: Add full support for Custom User Roles // See: https://bitwarden.com/help/article/user-types-access-control/#custom-role @@ -578,7 +578,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, @@ -586,7 +586,7 @@ impl UserOrganization { "collections": collections, "status": status, - "type": user_org_type, + "type": membership_type, "accessAll": self.access_all, "twoFactorEnabled": twofactor_enabled, "resetPasswordEnrolled": self.reset_password_key.is_some(), @@ -630,8 +630,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 }; @@ -654,8 +654,8 @@ impl UserOrganization { // Because Bitwarden wants 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 }; @@ -677,7 +677,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(()), @@ -685,7 +685,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") }, @@ -693,7 +693,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) @@ -709,7 +709,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))) @@ -719,46 +719,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() }} } @@ -768,7 +768,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() }} } @@ -777,8 +777,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() }} } @@ -787,8 +787,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() }} } @@ -797,7 +797,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() }} } @@ -806,7 +806,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) @@ -817,7 +817,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() }} } @@ -826,8 +826,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() }} } @@ -843,22 +843,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) @@ -870,7 +870,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() }} } @@ -881,9 +881,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() }} } @@ -892,12 +892,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)) @@ -918,10 +918,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() }} } @@ -945,7 +945,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() }} } @@ -971,7 +971,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() }} } @@ -980,7 +980,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 @@ -1000,7 +1000,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() }} } @@ -1011,7 +1011,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() }} } } @@ -1074,10 +1074,10 @@ 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); - assert!(UserOrgType::Manager == UserOrgType::from_str("4").unwrap()); + fn partial_cmp_MembershipType() { + assert!(MembershipType::Owner > MembershipType::Admin); + assert!(MembershipType::Admin > MembershipType::Manager); + assert!(MembershipType::Manager > MembershipType::User); + assert!(MembershipType::Manager == MembershipType::from_str("4").unwrap()); } } diff --git a/src/db/models/user.rs b/src/db/models/user.rs index 94f42c8420..981a46056f 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 1a32ae259c..a8e250c8a4 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");