rename membership

rename UserOrganization to Membership to clarify the relation
and prevent confusion whether something refers to a member(ship) or user
Stefan Melmuk 2 weeks ago
parent ed4ad67e73
commit 0b9e3bafd3
No known key found for this signature in database
GPG Key ID: 817020C608FE9C09

@ -50,7 +50,7 @@ pub fn routes() -> Vec<Route> {
disable_user, disable_user,
enable_user, enable_user,
remove_2fa, remove_2fa,
update_user_org_type, update_membership_type,
update_revision_users, update_revision_users,
post_config, post_config,
delete_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 { async fn delete_user(uuid: &str, token: AdminToken, mut conn: DbConn) -> EmptyResult {
let user = get_user_or_404(uuid, &mut conn).await?; let user = get_user_or_404(uuid, &mut conn).await?;
// Get the user_org records before deleting the actual user // Get the membership records before deleting the actual user
let user_orgs = UserOrganization::find_any_state_by_user(uuid, &mut conn).await; let memberships = Membership::find_any_state_by_user(uuid, &mut conn).await;
let res = user.delete(&mut conn).await; let res = user.delete(&mut conn).await;
for user_org in user_orgs { for membership in memberships {
log_event( log_event(
EventType::OrganizationUserRemoved as i32, EventType::OrganizationUserRemoved as i32,
&user_org.uuid, &membership.uuid,
&user_org.org_uuid, &membership.org_uuid,
ACTING_ADMIN_USER, ACTING_ADMIN_USER,
14, // Use UnknownBrowser type 14, // Use UnknownBrowser type
&token.ip.ip, &token.ip.ip,
@ -485,42 +485,41 @@ async fn resend_user_invite(uuid: &str, _token: AdminToken, mut conn: DbConn) ->
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
struct UserOrgTypeData { struct MembershipTypeData {
user_type: NumberOrString, user_type: NumberOrString,
user_uuid: String, user_uuid: String,
org_uuid: String, org_uuid: String,
} }
#[post("/users/org_type", data = "<data>")] #[post("/users/org_type", data = "<data>")]
async fn update_user_org_type(data: Json<UserOrgTypeData>, token: AdminToken, mut conn: DbConn) -> EmptyResult { async fn update_membership_type(data: Json<MembershipTypeData>, token: AdminToken, mut conn: DbConn) -> EmptyResult {
let data: UserOrgTypeData = data.into_inner(); let data: MembershipTypeData = data.into_inner();
let Some(mut user_to_edit) = let Some(mut member_to_edit) = Membership::find_by_user_and_org(&data.user_uuid, &data.org_uuid, &mut conn).await
UserOrganization::find_by_user_and_org(&data.user_uuid, &data.org_uuid, &mut conn).await
else { else {
err!("The specified user isn't member of the organization") 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, Some(new_type) => new_type as i32,
None => err!("Invalid type"), 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 // 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") err!("Can't change the type of the last owner")
} }
} }
// This check is also done at api::organizations::{accept_invite(), _confirm_invite, _activate_user(), edit_user()}, update_user_org_type // This check is also done at api::organizations::{accept_invite(), _confirm_invite, _activate_user(), edit_user()}, update_membership_type
// It returns different error messages per function. // It returns different error messages per function.
if new_type < UserOrgType::Admin { if new_type < MembershipType::Admin {
match OrgPolicy::is_user_allowed(&user_to_edit.user_uuid, &user_to_edit.org_uuid, true, &mut conn).await { match OrgPolicy::is_user_allowed(&member_to_edit.user_uuid, &member_to_edit.org_uuid, true, &mut conn).await {
Ok(_) => {} Ok(_) => {}
Err(OrgPolicyErr::TwoFactorMissing) => { Err(OrgPolicyErr::TwoFactorMissing) => {
if CONFIG.email_2fa_auto_fallback() { 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 { } else {
err!("You cannot modify this user to this type because they have not setup 2FA"); 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<UserOrgTypeData>, token: AdminToken, mu
log_event( log_event(
EventType::OrganizationUserUpdated as i32, EventType::OrganizationUserUpdated as i32,
&user_to_edit.uuid, &member_to_edit.uuid,
&data.org_uuid, &data.org_uuid,
ACTING_ADMIN_USER, ACTING_ADMIN_USER,
14, // Use UnknownBrowser type 14, // Use UnknownBrowser type
@ -542,8 +541,8 @@ async fn update_user_org_type(data: Json<UserOrgTypeData>, token: AdminToken, mu
) )
.await; .await;
user_to_edit.atype = new_type; member_to_edit.atype = new_type;
user_to_edit.save(&mut conn).await member_to_edit.save(&mut conn).await
} }
#[post("/users/update_revision")] #[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()); let mut organizations_json = Vec::with_capacity(organizations.len());
for o in organizations { for o in organizations {
let mut org = o.to_json(); 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["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["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); org["group_count"] = json!(Group::count_by_org(&o.uuid, &mut conn).await);

@ -106,15 +106,15 @@ fn enforce_password_hint_setting(password_hint: &Option<String>) -> EmptyResult
} }
Ok(()) Ok(())
} }
async fn is_email_2fa_required(org_user_uuid: Option<String>, conn: &mut DbConn) -> bool { async fn is_email_2fa_required(member_uuid: Option<String>, conn: &mut DbConn) -> bool {
if !CONFIG._enable_email_2fa() { if !CONFIG._enable_email_2fa() {
return false; return false;
} }
if CONFIG.email_2fa_enforce_on_verified_invite() { if CONFIG.email_2fa_enforce_on_verified_invite() {
return true; return true;
} }
if org_user_uuid.is_some() { if member_uuid.is_some() {
return OrgPolicy::is_enabled_for_member(&org_user_uuid.unwrap(), OrgPolicyType::TwoFactorAuthentication, conn) return OrgPolicy::is_enabled_for_member(&member_uuid.unwrap(), OrgPolicyType::TwoFactorAuthentication, conn)
.await; .await;
} }
false false
@ -161,9 +161,9 @@ pub async fn _register(data: Json<RegisterData>, mut conn: DbConn) -> JsonResult
err!("Registration email does not match invite email") err!("Registration email does not match invite email")
} }
} else if Invitation::take(&email, &mut conn).await { } else if Invitation::take(&email, &mut conn).await {
for user_org in UserOrganization::find_invited_by_user(&user.uuid, &mut conn).await.iter_mut() { for membership in Membership::find_invited_by_user(&user.uuid, &mut conn).await.iter_mut() {
user_org.status = UserOrgStatus::Accepted as i32; membership.status = MembershipStatus::Accepted as i32;
user_org.save(&mut conn).await?; membership.save(&mut conn).await?;
} }
user user
} else if CONFIG.is_signup_allowed(&email) } else if CONFIG.is_signup_allowed(&email)
@ -484,7 +484,7 @@ fn validate_keydata(
existing_ciphers: &[Cipher], existing_ciphers: &[Cipher],
existing_folders: &[Folder], existing_folders: &[Folder],
existing_emergency_access: &[EmergencyAccess], existing_emergency_access: &[EmergencyAccess],
existing_user_orgs: &[UserOrganization], existing_memberships: &[Membership],
existing_sends: &[Send], existing_sends: &[Send],
) -> EmptyResult { ) -> EmptyResult {
// Check that we're correctly rotating all the user's ciphers // Check that we're correctly rotating all the user's ciphers
@ -516,7 +516,7 @@ fn validate_keydata(
} }
// Check that we're correctly rotating all the user's reset password keys // 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::<HashSet<_>>(); let existing_reset_password_ids = existing_memberships.iter().map(|m| m.org_uuid.as_str()).collect::<HashSet<_>>();
let provided_reset_password_ids = let provided_reset_password_ids =
data.reset_password_keys.iter().map(|rp| rp.organization_id.as_str()).collect::<HashSet<_>>(); data.reset_password_keys.iter().map(|rp| rp.organization_id.as_str()).collect::<HashSet<_>>();
if !provided_reset_password_ids.is_superset(&existing_reset_password_ids) { if !provided_reset_password_ids.is_superset(&existing_reset_password_ids) {
@ -555,9 +555,9 @@ async fn post_rotatekey(data: Json<KeyData>, headers: Headers, mut conn: DbConn,
let mut existing_ciphers = Cipher::find_owned_by_user(user_uuid, &mut conn).await; 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_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_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. // 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; let mut existing_sends = Send::find_by_user(user_uuid, &mut conn).await;
validate_keydata( validate_keydata(
@ -565,7 +565,7 @@ async fn post_rotatekey(data: Json<KeyData>, headers: Headers, mut conn: DbConn,
&existing_ciphers, &existing_ciphers,
&existing_folders, &existing_folders,
&existing_emergency_access, &existing_emergency_access,
&existing_user_orgs, &existing_memberships,
&existing_sends, &existing_sends,
)?; )?;
@ -597,14 +597,14 @@ async fn post_rotatekey(data: Json<KeyData>, headers: Headers, mut conn: DbConn,
// Update reset password data // Update reset password data
for reset_password_data in data.reset_password_keys { for reset_password_data in data.reset_password_keys {
let Some(user_org) = let Some(membership) =
existing_user_orgs.iter_mut().find(|uo| uo.org_uuid == reset_password_data.organization_id) existing_memberships.iter_mut().find(|m| m.org_uuid == reset_password_data.organization_id)
else { else {
err!("Reset password doesn't exist") err!("Reset password doesn't exist")
}; };
user_org.reset_password_key = Some(reset_password_data.reset_password_key); membership.reset_password_key = Some(reset_password_data.reset_password_key);
user_org.save(&mut conn).await? membership.save(&mut conn).await?
} }
// Update send data // Update send data

@ -405,11 +405,11 @@ pub async fn update_cipher_from_data(
let transfer_cipher = cipher.organization_uuid.is_none() && data.organization_id.is_some(); let transfer_cipher = cipher.organization_uuid.is_none() && data.organization_id.is_some();
if let Some(org_id) = data.organization_id { 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"), None => err!("You don't have permission to add item to organization"),
Some(org_user) => { Some(member) => {
if shared_to_collections.is_some() 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.is_write_accessible_to_user(&headers.user.uuid, conn).await
{ {
cipher.organization_uuid = Some(org_id); cipher.organization_uuid = Some(org_id);
@ -1593,10 +1593,10 @@ async fn delete_all(
match organization { match organization {
Some(org_data) => { Some(org_data) => {
// Organization ID in query params, purging organization vault // 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"), None => err!("You don't have permission to purge the organization vault"),
Some(user_org) => { Some(member) => {
if user_org.atype == UserOrgType::Owner { if member.atype == MembershipType::Owner {
Cipher::delete_all_by_organization(&org_data.org_id, &mut conn).await?; Cipher::delete_all_by_organization(&org_data.org_id, &mut conn).await?;
nt.send_user_update(UpdateType::SyncVault, &user).await; nt.send_user_update(UpdateType::SyncVault, &user).await;
@ -1835,7 +1835,7 @@ pub struct CipherSyncData {
pub cipher_folders: HashMap<String, String>, pub cipher_folders: HashMap<String, String>,
pub cipher_favorites: HashSet<String>, pub cipher_favorites: HashSet<String>,
pub cipher_collections: HashMap<String, Vec<String>>, pub cipher_collections: HashMap<String, Vec<String>>,
pub user_organizations: HashMap<String, UserOrganization>, pub members: HashMap<String, Membership>,
pub user_collections: HashMap<String, CollectionUser>, pub user_collections: HashMap<String, CollectionUser>,
pub user_collections_groups: HashMap<String, CollectionGroup>, pub user_collections_groups: HashMap<String, CollectionGroup>,
pub user_group_full_access_for_organizations: HashSet<String>, pub user_group_full_access_for_organizations: HashSet<String>,
@ -1869,8 +1869,8 @@ impl CipherSyncData {
} }
// Generate a list of Cipher UUID's containing a Vec with one or more Attachment records // 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 orgs = Membership::get_orgs_by_user(user_uuid, conn).await;
let attachments = Attachment::find_all_by_user_and_orgs(user_uuid, &user_org_uuids, conn).await; let attachments = Attachment::find_all_by_user_and_orgs(user_uuid, &orgs, conn).await;
let mut cipher_attachments: HashMap<String, Vec<Attachment>> = HashMap::with_capacity(attachments.len()); let mut cipher_attachments: HashMap<String, Vec<Attachment>> = HashMap::with_capacity(attachments.len());
for attachment in attachments { for attachment in attachments {
cipher_attachments.entry(attachment.cipher_uuid.clone()).or_default().push(attachment); 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); cipher_collections.entry(cipher).or_default().push(collection);
} }
// Generate a HashMap with the Organization UUID as key and the UserOrganization record // Generate a HashMap with the Organization UUID as key and the Membership record
let user_organizations: HashMap<String, UserOrganization> = UserOrganization::find_by_user(user_uuid, conn) let members: HashMap<String, Membership> =
.await Membership::find_by_user(user_uuid, conn).await.into_iter().map(|m| (m.org_uuid.clone(), m)).collect();
.into_iter()
.map(|uo| (uo.org_uuid.clone(), uo))
.collect();
// Generate a HashMap with the User_Collections UUID as key and the CollectionUser record // Generate a HashMap with the User_Collections UUID as key and the CollectionUser record
let user_collections: HashMap<String, CollectionUser> = CollectionUser::find_by_user(user_uuid, conn) let user_collections: HashMap<String, CollectionUser> = CollectionUser::find_by_user(user_uuid, conn)
@ -1909,9 +1906,9 @@ impl CipherSyncData {
HashMap::new() 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<String> = if CONFIG.org_groups_enabled() { let user_group_full_access_for_organizations: HashSet<String> = 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 { } else {
HashSet::new() HashSet::new()
}; };
@ -1921,7 +1918,7 @@ impl CipherSyncData {
cipher_folders, cipher_folders,
cipher_favorites, cipher_favorites,
cipher_collections, cipher_collections,
user_organizations, members,
user_collections, user_collections,
user_collections_groups, user_collections_groups,
user_group_full_access_for_organizations, user_group_full_access_for_organizations,

@ -662,9 +662,9 @@ async fn password_emergency_access(
TwoFactor::delete_all_by_user(&grantor_user.uuid, &mut conn).await?; TwoFactor::delete_all_by_user(&grantor_user.uuid, &mut conn).await?;
// Remove grantor from all organisations unless Owner // Remove grantor from all organisations unless Owner
for user_org in UserOrganization::find_any_state_by_user(&grantor_user.uuid, &mut conn).await { for member in Membership::find_any_state_by_user(&grantor_user.uuid, &mut conn).await {
if user_org.atype != UserOrgType::Owner as i32 { if member.atype != MembershipType::Owner as i32 {
user_org.delete(&mut conn).await?; member.delete(&mut conn).await?;
} }
} }
Ok(()) Ok(())

@ -8,7 +8,7 @@ use crate::{
api::{EmptyResult, JsonResult}, api::{EmptyResult, JsonResult},
auth::{AdminHeaders, Headers}, auth::{AdminHeaders, Headers},
db::{ db::{
models::{Cipher, Event, UserOrganization}, models::{Cipher, Event, Membership},
DbConn, DbPool, DbConn, DbPool,
}, },
util::parse_date, util::parse_date,
@ -66,7 +66,7 @@ async fn get_cipher_events(cipher_id: &str, data: EventRange, headers: Headers,
Vec::with_capacity(0) Vec::with_capacity(0)
} else { } else {
let mut events_json = Vec::with_capacity(0); 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 start_date = parse_date(&data.start);
let end_date = if let Some(before_date) = &data.continuation_token { let end_date = if let Some(before_date) = &data.continuation_token {
parse_date(before_date) parse_date(before_date)
@ -90,10 +90,10 @@ async fn get_cipher_events(cipher_id: &str, data: EventRange, headers: Headers,
}))) })))
} }
#[get("/organizations/<org_id>/users/<user_org_id>/events?<data..>")] #[get("/organizations/<org_id>/users/<member_id>/events?<data..>")]
async fn get_user_events( async fn get_user_events(
org_id: &str, org_id: &str,
user_org_id: &str, member_id: &str,
data: EventRange, data: EventRange,
_headers: AdminHeaders, _headers: AdminHeaders,
mut conn: DbConn, mut conn: DbConn,
@ -110,7 +110,7 @@ async fn get_user_events(
parse_date(&data.end) 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 .await
.iter() .iter()
.map(|e| e.to_json()) .map(|e| e.to_json())
@ -233,7 +233,7 @@ async fn _log_user_event(
ip: &IpAddr, ip: &IpAddr,
conn: &mut DbConn, 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<Event> = Vec::with_capacity(orgs.len() + 1); // We need an event per org and one without an org let mut events: Vec<Event> = 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. // Upstream saves the event also without any org_uuid.

File diff suppressed because it is too large Load Diff

@ -54,38 +54,33 @@ async fn ldap_import(data: Json<OrgImportData>, token: PublicToken, mut conn: Db
for user_data in &data.members { for user_data in &data.members {
if user_data.deleted { if user_data.deleted {
// If user is marked for deletion and it exists, revoke it // If user is marked for deletion and it exists, revoke it
if let Some(mut user_org) = if let Some(mut member) = Membership::find_by_email_and_org(&user_data.email, &org_id, &mut conn).await {
UserOrganization::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 // Only revoke a user if it is not the last confirmed owner
let revoked = if user_org.atype == UserOrgType::Owner let revoked = if member.atype == MembershipType::Owner
&& user_org.status == UserOrgStatus::Confirmed as i32 && member.status == MembershipStatus::Confirmed as i32
{ {
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
<= 1
{ {
warn!("Can't revoke the last owner"); warn!("Can't revoke the last owner");
false false
} else { } else {
user_org.revoke() member.revoke()
} }
} else { } 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 { if revoked || ext_modified {
user_org.save(&mut conn).await?; member.save(&mut conn).await?;
} }
} }
// If user is part of the organization, restore it // If user is part of the organization, restore it
} else if let Some(mut user_org) = } else if let Some(mut member) = Membership::find_by_email_and_org(&user_data.email, &org_id, &mut conn).await {
UserOrganization::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()));
let restored = user_org.restore();
let ext_modified = user_org.set_external_id(Some(user_data.external_id.clone()));
if restored || ext_modified { if restored || ext_modified {
user_org.save(&mut conn).await?; member.save(&mut conn).await?;
} }
} else { } else {
// If user is not part of the organization // If user is not part of the organization
@ -103,19 +98,19 @@ async fn ldap_import(data: Json<OrgImportData>, token: PublicToken, mut conn: Db
new_user new_user
} }
}; };
let user_org_status = if CONFIG.mail_enabled() || user.password_hash.is_empty() { let member_status = if CONFIG.mail_enabled() || user.password_hash.is_empty() {
UserOrgStatus::Invited as i32 MembershipStatus::Invited as i32
} else { } 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()); let mut new_member = Membership::new(user.uuid.clone(), org_id.clone());
new_org_user.set_external_id(Some(user_data.external_id.clone())); new_member.set_external_id(Some(user_data.external_id.clone()));
new_org_user.access_all = false; new_member.access_all = false;
new_org_user.atype = UserOrgType::User as i32; new_member.atype = MembershipType::User as i32;
new_org_user.status = user_org_status; new_member.status = member_status;
new_org_user.save(&mut conn).await?; new_member.save(&mut conn).await?;
if CONFIG.mail_enabled() { if CONFIG.mail_enabled() {
let (org_name, org_email) = match Organization::find_by_uuid(&org_id, &mut conn).await { 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<OrgImportData>, token: PublicToken, mut conn: Db
None => err!("Error looking up organization"), 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?; .await?;
} }
} }
@ -149,9 +144,8 @@ async fn ldap_import(data: Json<OrgImportData>, token: PublicToken, mut conn: Db
GroupUser::delete_all_by_group(&group_uuid, &mut conn).await?; GroupUser::delete_all_by_group(&group_uuid, &mut conn).await?;
for ext_id in &group_data.member_external_ids { 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 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());
let mut group_user = GroupUser::new(group_uuid.clone(), user_org.uuid.clone());
group_user.save(&mut conn).await?; group_user.save(&mut conn).await?;
} }
} }
@ -164,20 +158,19 @@ async fn ldap_import(data: Json<OrgImportData>, token: PublicToken, mut conn: Db
if data.overwrite_existing { if data.overwrite_existing {
// Generate a HashSet to quickly verify if a member is listed or not. // Generate a HashSet to quickly verify if a member is listed or not.
let sync_members: HashSet<String> = data.members.into_iter().map(|m| m.external_id).collect(); let sync_members: HashSet<String> = data.members.into_iter().map(|m| m.external_id).collect();
for user_org in UserOrganization::find_by_org(&org_id, &mut conn).await { for member in Membership::find_by_org(&org_id, &mut conn).await {
if let Some(ref user_external_id) = user_org.external_id { if let Some(ref user_external_id) = member.external_id {
if !sync_members.contains(user_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 // 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) if Membership::count_confirmed_by_org_and_type(&org_id, MembershipType::Owner, &mut conn).await
.await
<= 1 <= 1
{ {
warn!("Can't delete the last owner"); warn!("Can't delete the last owner");
continue; continue;
} }
} }
user_org.delete(&mut conn).await?; member.delete(&mut conn).await?;
} }
} }
} }

@ -178,12 +178,11 @@ pub async fn enforce_2fa_policy(
ip: &std::net::IpAddr, ip: &std::net::IpAddr,
conn: &mut DbConn, conn: &mut DbConn,
) -> EmptyResult { ) -> EmptyResult {
for member in UserOrganization::find_by_user_and_policy(&user.uuid, OrgPolicyType::TwoFactorAuthentication, conn) for member in
.await Membership::find_by_user_and_policy(&user.uuid, OrgPolicyType::TwoFactorAuthentication, conn).await.into_iter()
.into_iter()
{ {
// Policy only applies to non-Owner/non-Admin members who have accepted joining the org // 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() { if CONFIG.mail_enabled() {
let org = Organization::find_by_uuid(&member.org_uuid, conn).await.unwrap(); let org = Organization::find_by_uuid(&member.org_uuid, conn).await.unwrap();
mail::send_2fa_removed_from_org(&user.email, &org.name).await?; 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, conn: &mut DbConn,
) -> EmptyResult { ) -> EmptyResult {
let org = Organization::find_by_uuid(org_uuid, conn).await.unwrap(); 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. // 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() { if CONFIG.mail_enabled() {
let user = User::find_by_uuid(&member.user_uuid, conn).await.unwrap(); let user = User::find_by_uuid(&member.user_uuid, conn).await.unwrap();
mail::send_2fa_removed_from_org(&user.email, &org.name).await?; mail::send_2fa_removed_from_org(&user.email, &org.name).await?;

@ -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 // 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 // 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); let (access_token, expires_in) = device.refresh_tokens(&user, scope_vec);
device.save(conn).await?; 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 // 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 // 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); let (access_token, expires_in) = device.refresh_tokens(&user, scope_vec);
device.save(conn).await?; 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 // 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 // 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); let (access_token, expires_in) = device.refresh_tokens(&user, scope_vec);
device.save(conn).await?; device.save(conn).await?;

@ -191,7 +191,7 @@ pub struct InviteJwtClaims {
pub email: String, pub email: String,
pub org_id: Option<String>, pub org_id: Option<String>,
pub user_org_id: Option<String>, pub member_id: Option<String>,
pub invited_by_email: Option<String>, pub invited_by_email: Option<String>,
} }
@ -199,7 +199,7 @@ pub fn generate_invite_claims(
uuid: String, uuid: String,
email: String, email: String,
org_id: Option<String>, org_id: Option<String>,
user_org_id: Option<String>, member_id: Option<String>,
invited_by_email: Option<String>, invited_by_email: Option<String>,
) -> InviteJwtClaims { ) -> InviteJwtClaims {
let time_now = Utc::now(); let time_now = Utc::now();
@ -211,7 +211,7 @@ pub fn generate_invite_claims(
sub: uuid, sub: uuid,
email, email,
org_id, org_id,
user_org_id, member_id,
invited_by_email, invited_by_email,
} }
} }
@ -371,7 +371,7 @@ use rocket::{
}; };
use crate::db::{ use crate::db::{
models::{Collection, Device, User, UserOrgStatus, UserOrgType, UserOrganization, UserStampException}, models::{Collection, Device, Membership, MembershipStatus, MembershipType, User, UserStampException},
DbConn, DbConn,
}; };
@ -534,8 +534,8 @@ pub struct OrgHeaders {
pub host: String, pub host: String,
pub device: Device, pub device: Device,
pub user: User, pub user: User,
pub org_user_type: UserOrgType, pub membership_type: MembershipType,
pub org_user: UserOrganization, pub membership: Membership,
pub ip: ClientIp, pub ip: ClientIp,
} }
@ -574,10 +574,10 @@ impl<'r> FromRequest<'r> for OrgHeaders {
}; };
let user = headers.user; let user = headers.user;
let org_user = match UserOrganization::find_by_user_and_org(&user.uuid, org_id, &mut conn).await { let membership = match Membership::find_by_user_and_org(&user.uuid, org_id, &mut conn).await {
Some(user) => { Some(member) => {
if user.status == UserOrgStatus::Confirmed as i32 { if member.status == MembershipStatus::Confirmed as i32 {
user member
} else { } else {
err_handler!("The current user isn't confirmed member of the organization") 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, host: headers.host,
device: headers.device, device: headers.device,
user, user,
org_user_type: { membership_type: {
if let Some(org_usr_type) = UserOrgType::from_i32(org_user.atype) { if let Some(org_usr_type) = MembershipType::from_i32(membership.atype) {
org_usr_type org_usr_type
} else { } else {
// This should only happen if the DB is corrupted // This should only happen if the DB is corrupted
err_handler!("Unknown user type in the database") err_handler!("Unknown user type in the database")
} }
}, },
org_user, membership,
ip: headers.ip, ip: headers.ip,
}) })
} }
@ -610,7 +610,7 @@ pub struct AdminHeaders {
pub host: String, pub host: String,
pub device: Device, pub device: Device,
pub user: User, pub user: User,
pub org_user_type: UserOrgType, pub membership_type: MembershipType,
pub ip: ClientIp, pub ip: ClientIp,
} }
@ -620,12 +620,12 @@ impl<'r> FromRequest<'r> for AdminHeaders {
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> { async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
let headers = try_outcome!(OrgHeaders::from_request(request).await); 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 { Outcome::Success(Self {
host: headers.host, host: headers.host,
device: headers.device, device: headers.device,
user: headers.user, user: headers.user,
org_user_type: headers.org_user_type, membership_type: headers.membership_type,
ip: headers.ip, ip: headers.ip,
}) })
} else { } else {
@ -680,7 +680,7 @@ impl<'r> FromRequest<'r> for ManagerHeaders {
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> { async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
let headers = try_outcome!(OrgHeaders::from_request(request).await); 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) { match get_col_id(request) {
Some(col_id) => { Some(col_id) => {
let mut conn = match DbConn::from_request(request).await { let mut conn = match DbConn::from_request(request).await {
@ -688,7 +688,7 @@ impl<'r> FromRequest<'r> for ManagerHeaders {
_ => err_handler!("Error getting DB"), _ => 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") err_handler!("The current user isn't a manager for this collection")
} }
} }
@ -724,7 +724,7 @@ pub struct ManagerHeadersLoose {
pub host: String, pub host: String,
pub device: Device, pub device: Device,
pub user: User, pub user: User,
pub org_user: UserOrganization, pub membership: Membership,
pub ip: ClientIp, pub ip: ClientIp,
} }
@ -734,12 +734,12 @@ impl<'r> FromRequest<'r> for ManagerHeadersLoose {
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> { async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
let headers = try_outcome!(OrgHeaders::from_request(request).await); 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 { Outcome::Success(Self {
host: headers.host, host: headers.host,
device: headers.device, device: headers.device,
user: headers.user, user: headers.user,
org_user: headers.org_user, membership: headers.membership,
ip: headers.ip, ip: headers.ip,
}) })
} else { } else {
@ -769,7 +769,7 @@ impl ManagerHeaders {
if uuid::Uuid::parse_str(col_id).is_err() { if uuid::Uuid::parse_str(col_id).is_err() {
err!("Collection Id is malformed!"); 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!"); 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<Self, Self::Error> { async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
let headers = try_outcome!(OrgHeaders::from_request(request).await); 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 { Outcome::Success(Self {
device: headers.device, device: headers.device,
user: headers.user, user: headers.user,

@ -4,7 +4,7 @@ use chrono::{NaiveDateTime, TimeDelta, Utc};
use serde_json::Value; use serde_json::Value;
use super::{ 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}; use crate::api::core::{CipherData, CipherSyncData, CipherSyncType};
@ -367,17 +367,16 @@ impl Cipher {
// Belongs to Organization, need to update affected users // Belongs to Organization, need to update affected users
if let Some(ref org_uuid) = self.organization_uuid { if let Some(ref org_uuid) = self.organization_uuid {
// users having access to the collection // users having access to the collection
let mut collection_users = let mut collection_users = Membership::find_by_cipher_and_org(&self.uuid, org_uuid, conn).await;
UserOrganization::find_by_cipher_and_org(&self.uuid, org_uuid, conn).await;
if CONFIG.org_groups_enabled() { if CONFIG.org_groups_enabled() {
// members of a group having access to the collection // members of a group having access to the collection
let group_users = 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); collection_users.extend(group_users);
} }
for user_org in collection_users { for member in collection_users {
User::update_uuid_revision(&user_org.user_uuid, conn).await; User::update_uuid_revision(&member.user_uuid, conn).await;
user_uuids.push(user_org.user_uuid.clone()) user_uuids.push(member.user_uuid.clone())
} }
} }
} }
@ -502,11 +501,11 @@ impl Cipher {
) -> bool { ) -> bool {
if let Some(ref org_uuid) = self.organization_uuid { if let Some(ref org_uuid) = self.organization_uuid {
if let Some(cipher_sync_data) = cipher_sync_data { if let Some(cipher_sync_data) = cipher_sync_data {
if let Some(cached_user_org) = cipher_sync_data.user_organizations.get(org_uuid) { if let Some(cached_member) = cipher_sync_data.members.get(org_uuid) {
return cached_user_org.has_full_access(); return cached_member.has_full_access();
} }
} else if let Some(user_org) = UserOrganization::find_by_user_and_org(user_uuid, org_uuid, conn).await { } else if let Some(member) = Membership::find_by_user_and_org(user_uuid, org_uuid, conn).await {
return user_org.has_full_access(); return member.has_full_access();
} }
} }
false false
@ -721,7 +720,7 @@ impl Cipher {
.left_join(users_organizations::table.on( .left_join(users_organizations::table.on(
ciphers::organization_uuid.eq(users_organizations::org_uuid.nullable()) ciphers::organization_uuid.eq(users_organizations::org_uuid.nullable())
.and(users_organizations::user_uuid.eq(user_uuid)) .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( .left_join(users_collections::table.on(
ciphers_collections::collection_uuid.eq(users_collections::collection_uuid) ciphers_collections::collection_uuid.eq(users_collections::collection_uuid)
@ -748,7 +747,7 @@ impl Cipher {
if !visible_only { if !visible_only {
query = query.or_filter( query = query.or_filter(
users_organizations::atype.le(UserOrgType::Admin as i32) // Org admin/owner users_organizations::atype.le(MembershipType::Admin as i32) // Org admin/owner
); );
} }
@ -766,7 +765,7 @@ impl Cipher {
.left_join(users_organizations::table.on( .left_join(users_organizations::table.on(
ciphers::organization_uuid.eq(users_organizations::org_uuid.nullable()) ciphers::organization_uuid.eq(users_organizations::org_uuid.nullable())
.and(users_organizations::user_uuid.eq(user_uuid)) .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( .left_join(users_collections::table.on(
ciphers_collections::collection_uuid.eq(users_collections::collection_uuid) ciphers_collections::collection_uuid.eq(users_collections::collection_uuid)
@ -780,7 +779,7 @@ impl Cipher {
if !visible_only { if !visible_only {
query = query.or_filter( query = query.or_filter(
users_organizations::atype.le(UserOrgType::Admin as i32) // Org admin/owner users_organizations::atype.le(MembershipType::Admin as i32) // Org admin/owner
); );
} }
@ -946,7 +945,7 @@ impl Cipher {
.or(groups::access_all.eq(true)) // Access via groups .or(groups::access_all.eq(true)) // Access via groups
.or(collections_groups::collections_uuid.is_not_null() // Access via groups .or(collections_groups::collections_uuid.is_not_null() // Access via groups
.and(collections_groups::read_only.eq(false))) .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) .select(ciphers_collections::collection_uuid)
.load::<String>(conn).unwrap_or_default() .load::<String>(conn).unwrap_or_default()
@ -969,7 +968,7 @@ impl Cipher {
.filter(users_organizations::access_all.eq(true) // User has access all .filter(users_organizations::access_all.eq(true) // User has access all
.or(users_collections::user_uuid.eq(user_id) // User has access to collection .or(users_collections::user_uuid.eq(user_id) // User has access to collection
.and(users_collections::read_only.eq(false))) .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) .select(ciphers_collections::collection_uuid)
.load::<String>(conn).unwrap_or_default() .load::<String>(conn).unwrap_or_default()
@ -1008,7 +1007,7 @@ impl Cipher {
)) ))
.or_filter(users_collections::user_uuid.eq(user_id)) // User has access to collection .or_filter(users_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::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(groups::access_all.eq(true)) //Access via group
.or_filter(collections_groups::collections_uuid.is_not_null()) //Access via group .or_filter(collections_groups::collections_uuid.is_not_null()) //Access via group
.select(ciphers_collections::all_columns) .select(ciphers_collections::all_columns)

@ -1,6 +1,6 @@
use serde_json::Value; use serde_json::Value;
use super::{CollectionGroup, GroupUser, User, UserOrgStatus, UserOrgType, UserOrganization}; use super::{CollectionGroup, GroupUser, Membership, MembershipStatus, MembershipType, User};
use crate::CONFIG; use crate::CONFIG;
db_object! { db_object! {
@ -79,13 +79,13 @@ impl Collection {
conn: &mut DbConn, conn: &mut DbConn,
) -> Value { ) -> Value {
let (read_only, hide_passwords, can_manage) = if let Some(cipher_sync_data) = cipher_sync_data { 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 // Only for Manager types Bitwarden returns true for the can_manage option
// Owners and Admins always have true // Owners and Admins always have true
Some(uo) if uo.has_full_access() => (false, false, uo.atype >= UserOrgType::Manager), Some(m) if m.has_full_access() => (false, false, m.atype >= MembershipType::Manager),
Some(uo) => { Some(m) => {
// Only let a manager manage collections when the have full read/write access // 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) { 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) (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) { } else if let Some(cg) = cipher_sync_data.user_collections_groups.get(&self.uuid) {
@ -97,10 +97,10 @@ impl Collection {
_ => (true, true, false), _ => (true, true, false),
} }
} else { } else {
match UserOrganization::find_confirmed_by_user_and_org(user_uuid, &self.org_uuid, conn).await { match Membership::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(m) if m.has_full_access() => (false, false, m.atype >= MembershipType::Manager),
Some(ou) => { Some(m) => {
let is_manager = ou.atype == UserOrgType::Manager; let is_manager = m.atype == MembershipType::Manager;
let read_only = !self.is_writable_by_user(user_uuid, conn).await; let read_only = !self.is_writable_by_user(user_uuid, conn).await;
let hide_passwords = self.hide_passwords_for_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) (read_only, hide_passwords, is_manager && !read_only && !hide_passwords)
@ -121,13 +121,13 @@ impl Collection {
json_object json_object
} }
pub async fn can_access_collection(org_user: &UserOrganization, col_id: &str, conn: &mut DbConn) -> bool { pub async fn can_access_collection(member: &Membership, col_id: &str, conn: &mut DbConn) -> bool {
org_user.has_status(UserOrgStatus::Confirmed) member.has_status(MembershipStatus::Confirmed)
&& (org_user.has_full_access() && (member.has_full_access()
|| CollectionUser::has_access_to_collection_by_user(col_id, &org_user.user_uuid, conn).await || CollectionUser::has_access_to_collection_by_user(col_id, &member.user_uuid, conn).await
|| (CONFIG.org_groups_enabled() || (CONFIG.org_groups_enabled()
&& (GroupUser::has_full_access_by_member(&org_user.org_uuid, &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, &org_user.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) { 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() { for member in Membership::find_by_collection_and_org(&self.uuid, &self.org_uuid, conn).await.iter() {
User::update_uuid_revision(&user_org.user_uuid, conn).await; User::update_uuid_revision(&member.user_uuid, conn).await;
} }
} }
@ -234,7 +234,7 @@ impl Collection {
) )
)) ))
.filter( .filter(
users_organizations::status.eq(UserOrgStatus::Confirmed as i32) users_organizations::status.eq(MembershipStatus::Confirmed as i32)
) )
.filter( .filter(
users_collections::user_uuid.eq(user_uuid).or( // Directly accessed collection users_collections::user_uuid.eq(user_uuid).or( // Directly accessed collection
@ -265,7 +265,7 @@ impl Collection {
) )
)) ))
.filter( .filter(
users_organizations::status.eq(UserOrgStatus::Confirmed as i32) users_organizations::status.eq(MembershipStatus::Confirmed as i32)
) )
.filter( .filter(
users_collections::user_uuid.eq(user_uuid).or( // Directly accessed collection users_collections::user_uuid.eq(user_uuid).or( // Directly accessed collection
@ -349,7 +349,7 @@ impl Collection {
.filter( .filter(
users_collections::collection_uuid.eq(uuid).or( // Directly accessed collection users_collections::collection_uuid.eq(uuid).or( // Directly accessed collection
users_organizations::access_all.eq(true).or( // access_all in Organization 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( )).or(
groups::access_all.eq(true) // access_all in groups groups::access_all.eq(true) // access_all in groups
).or( // access via groups ).or( // access via groups
@ -378,7 +378,7 @@ impl Collection {
.filter( .filter(
users_collections::collection_uuid.eq(uuid).or( // Directly accessed collection users_collections::collection_uuid.eq(uuid).or( // Directly accessed collection
users_organizations::access_all.eq(true).or( // access_all in Organization 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) ).select(collections::all_columns)
.first::<CollectionDb>(conn).ok() .first::<CollectionDb>(conn).ok()
@ -411,7 +411,7 @@ impl Collection {
collections_groups::groups_uuid.eq(groups_users::groups_uuid) collections_groups::groups_uuid.eq(groups_users::groups_uuid)
.and(collections_groups::collections_uuid.eq(collections::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_organizations::access_all.eq(true)) // access_all via membership
.or(users_collections::collection_uuid.eq(&self.uuid) // write access given to collection .or(users_collections::collection_uuid.eq(&self.uuid) // write access given to collection
.and(users_collections::read_only.eq(false))) .and(users_collections::read_only.eq(false)))
@ -436,7 +436,7 @@ impl Collection {
users_collections::collection_uuid.eq(collections::uuid) users_collections::collection_uuid.eq(collections::uuid)
.and(users_collections::user_uuid.eq(user_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_organizations::access_all.eq(true)) // access_all via membership
.or(users_collections::collection_uuid.eq(&self.uuid) // write access given to collection .or(users_collections::collection_uuid.eq(&self.uuid) // write access given to collection
.and(users_collections::read_only.eq(false))) .and(users_collections::read_only.eq(false)))
@ -478,7 +478,7 @@ impl Collection {
.filter( .filter(
users_collections::collection_uuid.eq(&self.uuid).and(users_collections::hide_passwords.eq(true)).or(// Directly accessed collection 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::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( )).or(
groups::access_all.eq(true) // access_all in groups groups::access_all.eq(true) // access_all in groups
).or( // access via groups ).or( // access via groups
@ -607,7 +607,7 @@ impl CollectionUser {
}} }}
} }
pub async fn find_by_collection_swap_user_uuid_with_org_user_uuid( pub async fn find_by_collection_swap_user_uuid_with_member_uuid(
collection_uuid: &str, collection_uuid: &str,
conn: &mut DbConn, conn: &mut DbConn,
) -> Vec<Self> { ) -> Vec<Self> {

@ -75,12 +75,12 @@ impl Device {
// Also These key/value pairs are not used anywhere by either Vaultwarden or Bitwarden Clients // 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 // 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<super::UserOrganization>, // fn arg: members: Vec<super::Membership>,
// --- // ---
// let orgowner: Vec<_> = orgs.iter().filter(|o| o.atype == 0).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<_> = orgs.iter().filter(|o| o.atype == 1).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<_> = orgs.iter().filter(|o| o.atype == 2).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<_> = orgs.iter().filter(|o| o.atype == 3).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 // Create the JWT claims struct, to send to the client
use crate::auth::{encode_jwt, LoginJwtClaims, DEFAULT_VALIDITY, JWT_LOGIN_ISSUER}; use crate::auth::{encode_jwt, LoginJwtClaims, DEFAULT_VALIDITY, JWT_LOGIN_ISSUER};

@ -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, org_uuid: &str,
user_org_uuid: &str, member_uuid: &str,
start: &NaiveDateTime, start: &NaiveDateTime,
end: &NaiveDateTime, end: &NaiveDateTime,
conn: &mut DbConn, conn: &mut DbConn,
) -> Vec<Self> { ) -> Vec<Self> {
db_run! { conn: { db_run! { conn: {
event::table 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::org_uuid.eq(org_uuid))
.filter(event::event_date.between(start, end)) .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()))) .filter(event::user_uuid.eq(users_organizations::user_uuid.nullable()).or(event::act_user_uuid.eq(users_organizations::user_uuid.nullable())))

@ -1,4 +1,4 @@
use super::{User, UserOrganization}; use super::{Membership, User};
use crate::api::EmptyResult; use crate::api::EmptyResult;
use crate::db::DbConn; use crate::db::DbConn;
use crate::error::MapResult; use crate::error::MapResult;
@ -213,7 +213,7 @@ impl Group {
}} }}
} }
//Returns all organizations the user has full access to //Returns all organizations the user has full access to
pub async fn gather_user_organizations_full_access(user_uuid: &str, conn: &mut DbConn) -> Vec<String> { pub async fn get_orgs_by_user_with_full_access(user_uuid: &str, conn: &mut DbConn) -> Vec<String> {
db_run! { conn: { db_run! { conn: {
groups_users::table groups_users::table
.inner_join(users_organizations::table.on( .inner_join(users_organizations::table.on(
@ -520,9 +520,9 @@ impl GroupUser {
} }
pub async fn update_user_revision(&self, conn: &mut DbConn) { pub async fn update_user_revision(&self, conn: &mut DbConn) {
match UserOrganization::find_by_uuid(&self.users_organizations_uuid, conn).await { match Membership::find_by_uuid(&self.users_organizations_uuid, conn).await {
Some(user) => User::update_uuid_revision(&user.user_uuid, conn).await, Some(member) => User::update_uuid_revision(&member.user_uuid, conn).await,
None => warn!("User could not be found!"), None => warn!("Member could not be found!"),
} }
} }
@ -531,9 +531,9 @@ impl GroupUser {
users_organizations_uuid: &str, users_organizations_uuid: &str,
conn: &mut DbConn, conn: &mut DbConn,
) -> EmptyResult { ) -> EmptyResult {
match UserOrganization::find_by_uuid(users_organizations_uuid, conn).await { match Membership::find_by_uuid(users_organizations_uuid, conn).await {
Some(user) => User::update_uuid_revision(&user.user_uuid, conn).await, Some(member) => User::update_uuid_revision(&member.user_uuid, conn).await,
None => warn!("User could not be found!"), None => warn!("Member could not be found!"),
}; };
db_run! { conn: { db_run! { conn: {
@ -559,15 +559,15 @@ impl GroupUser {
}} }}
} }
pub async fn delete_all_by_user(users_organizations_uuid: &str, conn: &mut DbConn) -> EmptyResult { pub async fn delete_all_by_member(member_uuid: &str, conn: &mut DbConn) -> EmptyResult {
match UserOrganization::find_by_uuid(users_organizations_uuid, conn).await { match Membership::find_by_uuid(member_uuid, conn).await {
Some(user) => User::update_uuid_revision(&user.user_uuid, conn).await, Some(member) => User::update_uuid_revision(&member.user_uuid, conn).await,
None => warn!("User could not be found!"), None => warn!("Member could not be found!"),
} }
db_run! { conn: { db_run! { conn: {
diesel::delete(groups_users::table) 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) .execute(conn)
.map_res("Error deleting user groups") .map_res("Error deleting user groups")
}} }}

@ -27,7 +27,7 @@ pub use self::favorite::Favorite;
pub use self::folder::{Folder, FolderCipher}; pub use self::folder::{Folder, FolderCipher};
pub use self::group::{CollectionGroup, Group, GroupUser}; pub use self::group::{CollectionGroup, Group, GroupUser};
pub use self::org_policy::{OrgPolicy, OrgPolicyErr, OrgPolicyType}; 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::send::{Send, SendType};
pub use self::two_factor::{TwoFactor, TwoFactorType}; pub use self::two_factor::{TwoFactor, TwoFactorType};
pub use self::two_factor_duo_context::TwoFactorDuoContext; pub use self::two_factor_duo_context::TwoFactorDuoContext;

@ -5,7 +5,7 @@ use crate::api::EmptyResult;
use crate::db::DbConn; use crate::db::DbConn;
use crate::error::MapResult; use crate::error::MapResult;
use super::{TwoFactor, UserOrgStatus, UserOrgType, UserOrganization}; use super::{Membership, MembershipStatus, MembershipType, TwoFactor};
db_object! { db_object! {
#[derive(Identifiable, Queryable, Insertable, AsChangeset)] #[derive(Identifiable, Queryable, Insertable, AsChangeset)]
@ -161,7 +161,7 @@ impl OrgPolicy {
.and(users_organizations::user_uuid.eq(user_uuid))) .and(users_organizations::user_uuid.eq(user_uuid)))
) )
.filter( .filter(
users_organizations::status.eq(UserOrgStatus::Confirmed as i32) users_organizations::status.eq(MembershipStatus::Confirmed as i32)
) )
.select(org_policies::all_columns) .select(org_policies::all_columns)
.load::<OrgPolicyDb>(conn) .load::<OrgPolicyDb>(conn)
@ -202,10 +202,10 @@ impl OrgPolicy {
.and(users_organizations::user_uuid.eq(user_uuid))) .and(users_organizations::user_uuid.eq(user_uuid)))
) )
.filter( .filter(
users_organizations::status.eq(UserOrgStatus::Accepted as i32) users_organizations::status.eq(MembershipStatus::Accepted as i32)
) )
.or_filter( .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::atype.eq(policy_type as i32))
.filter(org_policies::enabled.eq(true)) .filter(org_policies::enabled.eq(true))
@ -229,7 +229,7 @@ impl OrgPolicy {
.and(users_organizations::user_uuid.eq(user_uuid))) .and(users_organizations::user_uuid.eq(user_uuid)))
) )
.filter( .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::atype.eq(policy_type as i32))
.filter(org_policies::enabled.eq(true)) .filter(org_policies::enabled.eq(true))
@ -257,8 +257,8 @@ impl OrgPolicy {
continue; continue;
} }
if let Some(user) = UserOrganization::find_by_user_and_org(user_uuid, &policy.org_uuid, conn).await { if let Some(user) = Membership::find_by_user_and_org(user_uuid, &policy.org_uuid, conn).await {
if user.atype < UserOrgType::Admin { if user.atype < MembershipType::Admin {
return true; return true;
} }
} }
@ -316,8 +316,8 @@ impl OrgPolicy {
for policy in for policy in
OrgPolicy::find_confirmed_by_user_and_active_policy(user_uuid, OrgPolicyType::SendOptions, conn).await 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 let Some(user) = Membership::find_by_user_and_org(user_uuid, &policy.org_uuid, conn).await {
if user.atype < UserOrgType::Admin { if user.atype < MembershipType::Admin {
match serde_json::from_str::<SendOptionsPolicyData>(&policy.data) { match serde_json::from_str::<SendOptionsPolicyData>(&policy.data) {
Ok(opts) => { Ok(opts) => {
if opts.disable_hide_email { if opts.disable_hide_email {
@ -332,9 +332,9 @@ impl OrgPolicy {
false false
} }
pub async fn is_enabled_for_member(org_user_uuid: &str, policy_type: OrgPolicyType, conn: &mut DbConn) -> bool { pub async fn is_enabled_for_member(member_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(member) = Membership::find_by_uuid(member_uuid, conn).await {
if let Some(policy) = OrgPolicy::find_by_org_and_type(&membership.org_uuid, policy_type, conn).await { if let Some(policy) = OrgPolicy::find_by_org_and_type(&member.org_uuid, policy_type, conn).await {
return policy.enabled; return policy.enabled;
} }
} }

@ -25,7 +25,7 @@ db_object! {
#[derive(Identifiable, Queryable, Insertable, AsChangeset)] #[derive(Identifiable, Queryable, Insertable, AsChangeset)]
#[diesel(table_name = users_organizations)] #[diesel(table_name = users_organizations)]
#[diesel(primary_key(uuid))] #[diesel(primary_key(uuid))]
pub struct UserOrganization { pub struct Membership {
pub uuid: String, pub uuid: String,
pub user_uuid: String, pub user_uuid: String,
pub org_uuid: String, pub org_uuid: String,
@ -51,7 +51,7 @@ db_object! {
} }
// https://github.com/bitwarden/server/blob/b86a04cef9f1e1b82cf18e49fc94e017c641130c/src/Core/Enums/OrganizationUserStatusType.cs // https://github.com/bitwarden/server/blob/b86a04cef9f1e1b82cf18e49fc94e017c641130c/src/Core/Enums/OrganizationUserStatusType.cs
pub enum UserOrgStatus { pub enum MembershipStatus {
Revoked = -1, Revoked = -1,
Invited = 0, Invited = 0,
Accepted = 1, Accepted = 1,
@ -59,27 +59,27 @@ pub enum UserOrgStatus {
} }
#[derive(Copy, Clone, PartialEq, Eq, num_derive::FromPrimitive)] #[derive(Copy, Clone, PartialEq, Eq, num_derive::FromPrimitive)]
pub enum UserOrgType { pub enum MembershipType {
Owner = 0, Owner = 0,
Admin = 1, Admin = 1,
User = 2, User = 2,
Manager = 3, Manager = 3,
} }
impl UserOrgType { impl MembershipType {
pub fn from_str(s: &str) -> Option<Self> { pub fn from_str(s: &str) -> Option<Self> {
match s { match s {
"0" | "Owner" => Some(UserOrgType::Owner), "0" | "Owner" => Some(MembershipType::Owner),
"1" | "Admin" => Some(UserOrgType::Admin), "1" | "Admin" => Some(MembershipType::Admin),
"2" | "User" => Some(UserOrgType::User), "2" | "User" => Some(MembershipType::User),
"3" | "Manager" => Some(UserOrgType::Manager), "3" | "Manager" => Some(MembershipType::Manager),
_ => None, _ => None,
} }
} }
} }
impl Ord for UserOrgType { impl Ord for MembershipType {
fn cmp(&self, other: &UserOrgType) -> Ordering { fn cmp(&self, other: &MembershipType) -> Ordering {
// For easy comparison, map each variant to an access level (where 0 is lowest). // For easy comparison, map each variant to an access level (where 0 is lowest).
static ACCESS_LEVEL: [i32; 4] = [ static ACCESS_LEVEL: [i32; 4] = [
3, // Owner 3, // Owner
@ -91,19 +91,19 @@ impl Ord for UserOrgType {
} }
} }
impl PartialOrd for UserOrgType { impl PartialOrd for MembershipType {
fn partial_cmp(&self, other: &UserOrgType) -> Option<Ordering> { fn partial_cmp(&self, other: &MembershipType) -> Option<Ordering> {
Some(self.cmp(other)) Some(self.cmp(other))
} }
} }
impl PartialEq<i32> for UserOrgType { impl PartialEq<i32> for MembershipType {
fn eq(&self, other: &i32) -> bool { fn eq(&self, other: &i32) -> bool {
*other == *self as i32 *other == *self as i32
} }
} }
impl PartialOrd<i32> for UserOrgType { impl PartialOrd<i32> for MembershipType {
fn partial_cmp(&self, other: &i32) -> Option<Ordering> { fn partial_cmp(&self, other: &i32) -> Option<Ordering> {
if let Some(other) = Self::from_i32(*other) { if let Some(other) = Self::from_i32(*other) {
return Some(self.cmp(&other)); return Some(self.cmp(&other));
@ -120,25 +120,25 @@ impl PartialOrd<i32> for UserOrgType {
} }
} }
impl PartialEq<UserOrgType> for i32 { impl PartialEq<MembershipType> for i32 {
fn eq(&self, other: &UserOrgType) -> bool { fn eq(&self, other: &MembershipType) -> bool {
*self == *other as i32 *self == *other as i32
} }
} }
impl PartialOrd<UserOrgType> for i32 { impl PartialOrd<MembershipType> for i32 {
fn partial_cmp(&self, other: &UserOrgType) -> Option<Ordering> { fn partial_cmp(&self, other: &MembershipType) -> Option<Ordering> {
if let Some(self_type) = UserOrgType::from_i32(*self) { if let Some(self_type) = MembershipType::from_i32(*self) {
return Some(self_type.cmp(other)); return Some(self_type.cmp(other));
} }
None None
} }
fn lt(&self, other: &UserOrgType) -> bool { fn lt(&self, other: &MembershipType) -> bool {
matches!(self.partial_cmp(other), Some(Ordering::Less) | None) 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) matches!(self.partial_cmp(other), Some(Ordering::Less | Ordering::Equal) | None)
} }
} }
@ -199,7 +199,7 @@ impl Organization {
// It should also provide enough room for 100+ types, which i doubt will ever happen. // It should also provide enough room for 100+ types, which i doubt will ever happen.
static ACTIVATE_REVOKE_DIFF: i32 = 128; static ACTIVATE_REVOKE_DIFF: i32 = 128;
impl UserOrganization { impl Membership {
pub fn new(user_uuid: String, org_uuid: String) -> Self { pub fn new(user_uuid: String, org_uuid: String) -> Self {
Self { Self {
uuid: crate::util::get_uuid(), uuid: crate::util::get_uuid(),
@ -209,15 +209,15 @@ impl UserOrganization {
access_all: false, access_all: false,
akey: String::new(), akey: String::new(),
status: UserOrgStatus::Accepted as i32, status: MembershipStatus::Accepted as i32,
atype: UserOrgType::User as i32, atype: MembershipType::User as i32,
reset_password_key: None, reset_password_key: None,
external_id: None, external_id: None,
} }
} }
pub fn restore(&mut self) -> bool { 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; self.status += ACTIVATE_REVOKE_DIFF;
return true; return true;
} }
@ -225,7 +225,7 @@ impl UserOrganization {
} }
pub fn revoke(&mut self) -> bool { 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; self.status -= ACTIVATE_REVOKE_DIFF;
return true; return true;
} }
@ -234,7 +234,7 @@ impl UserOrganization {
/// Return the status of the user in an unrevoked state /// Return the status of the user in an unrevoked state
pub fn get_unrevoked_status(&self) -> i32 { 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; return self.status + ACTIVATE_REVOKE_DIFF;
} }
self.status self.status
@ -283,8 +283,8 @@ impl Organization {
err!(format!("BillingEmail {} is not a valid email address", self.billing_email.trim())) 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() { for member in Membership::find_by_org(&self.uuid, conn).await.iter() {
User::update_uuid_revision(&user_org.user_uuid, conn).await; User::update_uuid_revision(&member.user_uuid, conn).await;
} }
db_run! { conn: db_run! { conn:
@ -324,7 +324,7 @@ impl Organization {
Cipher::delete_all_by_organization(&self.uuid, conn).await?; Cipher::delete_all_by_organization(&self.uuid, conn).await?;
Collection::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?; OrgPolicy::delete_all_by_organization(&self.uuid, conn).await?;
Group::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?; OrganizationApiKey::delete_all_by_organization(&self.uuid, conn).await?;
@ -352,7 +352,7 @@ impl Organization {
} }
} }
impl UserOrganization { impl Membership {
pub async fn to_json(&self, conn: &mut DbConn) -> Value { pub async fn to_json(&self, conn: &mut DbConn) -> Value {
let org = Organization::find_by_uuid(&self.org_uuid, conn).await.unwrap(); let org = Organization::find_by_uuid(&self.org_uuid, conn).await.unwrap();
@ -446,8 +446,8 @@ impl UserOrganization {
// Because BitWarden want the status to be -1 for revoked users we need to catch that here. // 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. // 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 { let status = if self.status < MembershipStatus::Revoked as i32 {
UserOrgStatus::Revoked as i32 MembershipStatus::Revoked as i32
} else { } else {
self.status self.status
}; };
@ -489,12 +489,12 @@ impl UserOrganization {
.into_iter() .into_iter()
.filter_map(|c| { .filter_map(|c| {
let (read_only, hide_passwords, can_manage) = if self.has_full_access() { 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) { } else if let Some(cu) = cu.get(&c.uuid) {
( (
cu.read_only, cu.read_only,
cu.hide_passwords, 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 // 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 // Those are returned via a special group endpoint
@ -538,7 +538,7 @@ impl UserOrganization {
json!({ json!({
"id": self.uuid, "id": self.uuid,
"userId": self.user_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, "email": user.email,
"externalId": self.external_id, "externalId": self.external_id,
"avatarColor": user.avatar_color, "avatarColor": user.avatar_color,
@ -590,8 +590,8 @@ impl UserOrganization {
// Because BitWarden want the status to be -1 for revoked users we need to catch that here. // 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. // 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 { let status = if self.status < MembershipStatus::Revoked as i32 {
UserOrgStatus::Revoked as i32 MembershipStatus::Revoked as i32
} else { } else {
self.status self.status
}; };
@ -614,7 +614,7 @@ impl UserOrganization {
db_run! { conn: db_run! { conn:
sqlite, mysql { sqlite, mysql {
match diesel::replace_into(users_organizations::table) match diesel::replace_into(users_organizations::table)
.values(UserOrganizationDb::to_db(self)) .values(MembershipDb::to_db(self))
.execute(conn) .execute(conn)
{ {
Ok(_) => Ok(()), Ok(_) => Ok(()),
@ -622,7 +622,7 @@ impl UserOrganization {
Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => { Err(diesel::result::Error::DatabaseError(diesel::result::DatabaseErrorKind::ForeignKeyViolation, _)) => {
diesel::update(users_organizations::table) diesel::update(users_organizations::table)
.filter(users_organizations::uuid.eq(&self.uuid)) .filter(users_organizations::uuid.eq(&self.uuid))
.set(UserOrganizationDb::to_db(self)) .set(MembershipDb::to_db(self))
.execute(conn) .execute(conn)
.map_res("Error adding user to organization") .map_res("Error adding user to organization")
}, },
@ -630,7 +630,7 @@ impl UserOrganization {
}.map_res("Error adding user to organization") }.map_res("Error adding user to organization")
} }
postgresql { postgresql {
let value = UserOrganizationDb::to_db(self); let value = MembershipDb::to_db(self);
diesel::insert_into(users_organizations::table) diesel::insert_into(users_organizations::table)
.values(&value) .values(&value)
.on_conflict(users_organizations::uuid) .on_conflict(users_organizations::uuid)
@ -646,7 +646,7 @@ impl UserOrganization {
User::update_uuid_revision(&self.user_uuid, conn).await; User::update_uuid_revision(&self.user_uuid, conn).await;
CollectionUser::delete_all_by_user_and_org(&self.user_uuid, &self.org_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: { db_run! { conn: {
diesel::delete(users_organizations::table.filter(users_organizations::uuid.eq(self.uuid))) diesel::delete(users_organizations::table.filter(users_organizations::uuid.eq(self.uuid)))
@ -656,46 +656,46 @@ impl UserOrganization {
} }
pub async fn delete_all_by_organization(org_uuid: &str, conn: &mut DbConn) -> EmptyResult { 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 { for member in Self::find_by_org(org_uuid, conn).await {
user_org.delete(conn).await?; member.delete(conn).await?;
} }
Ok(()) Ok(())
} }
pub async fn delete_all_by_user(user_uuid: &str, conn: &mut DbConn) -> EmptyResult { 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 { for member in Self::find_any_state_by_user(user_uuid, conn).await {
user_org.delete(conn).await?; member.delete(conn).await?;
} }
Ok(()) Ok(())
} }
pub async fn find_by_email_and_org(email: &str, org_id: &str, conn: &mut DbConn) -> Option<UserOrganization> { pub async fn find_by_email_and_org(email: &str, org_id: &str, conn: &mut DbConn) -> Option<Membership> {
if let Some(user) = User::find_by_mail(email, conn).await { 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 { if let Some(member) = Membership::find_by_user_and_org(&user.uuid, org_id, conn).await {
return Some(user_org); return Some(member);
} }
} }
None None
} }
pub fn has_status(&self, status: UserOrgStatus) -> bool { pub fn has_status(&self, status: MembershipStatus) -> bool {
self.status == status as i32 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 self.atype == user_type as i32
} }
pub fn has_full_access(&self) -> bool { 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<Self> { pub async fn find_by_uuid(uuid: &str, conn: &mut DbConn) -> Option<Self> {
db_run! { conn: { db_run! { conn: {
users_organizations::table users_organizations::table
.filter(users_organizations::uuid.eq(uuid)) .filter(users_organizations::uuid.eq(uuid))
.first::<UserOrganizationDb>(conn) .first::<MembershipDb>(conn)
.ok().from_db() .ok().from_db()
}} }}
} }
@ -705,7 +705,7 @@ impl UserOrganization {
users_organizations::table users_organizations::table
.filter(users_organizations::uuid.eq(uuid)) .filter(users_organizations::uuid.eq(uuid))
.filter(users_organizations::org_uuid.eq(org_uuid)) .filter(users_organizations::org_uuid.eq(org_uuid))
.first::<UserOrganizationDb>(conn) .first::<MembershipDb>(conn)
.ok().from_db() .ok().from_db()
}} }}
} }
@ -714,8 +714,8 @@ impl UserOrganization {
db_run! { conn: { db_run! { conn: {
users_organizations::table users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::user_uuid.eq(user_uuid))
.filter(users_organizations::status.eq(UserOrgStatus::Confirmed as i32)) .filter(users_organizations::status.eq(MembershipStatus::Confirmed as i32))
.load::<UserOrganizationDb>(conn) .load::<MembershipDb>(conn)
.unwrap_or_default().from_db() .unwrap_or_default().from_db()
}} }}
} }
@ -724,8 +724,8 @@ impl UserOrganization {
db_run! { conn: { db_run! { conn: {
users_organizations::table users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::user_uuid.eq(user_uuid))
.filter(users_organizations::status.eq(UserOrgStatus::Invited as i32)) .filter(users_organizations::status.eq(MembershipStatus::Invited as i32))
.load::<UserOrganizationDb>(conn) .load::<MembershipDb>(conn)
.unwrap_or_default().from_db() .unwrap_or_default().from_db()
}} }}
} }
@ -734,7 +734,7 @@ impl UserOrganization {
db_run! { conn: { db_run! { conn: {
users_organizations::table users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::user_uuid.eq(user_uuid))
.load::<UserOrganizationDb>(conn) .load::<MembershipDb>(conn)
.unwrap_or_default().from_db() .unwrap_or_default().from_db()
}} }}
} }
@ -743,7 +743,7 @@ impl UserOrganization {
db_run! { conn: { db_run! { conn: {
users_organizations::table users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid)) .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() .count()
.first::<i64>(conn) .first::<i64>(conn)
.unwrap_or(0) .unwrap_or(0)
@ -754,7 +754,7 @@ impl UserOrganization {
db_run! { conn: { db_run! { conn: {
users_organizations::table users_organizations::table
.filter(users_organizations::org_uuid.eq(org_uuid)) .filter(users_organizations::org_uuid.eq(org_uuid))
.load::<UserOrganizationDb>(conn) .load::<MembershipDb>(conn)
.expect("Error loading user organizations").from_db() .expect("Error loading user organizations").from_db()
}} }}
} }
@ -763,8 +763,8 @@ impl UserOrganization {
db_run! { conn: { db_run! { conn: {
users_organizations::table users_organizations::table
.filter(users_organizations::org_uuid.eq(org_uuid)) .filter(users_organizations::org_uuid.eq(org_uuid))
.filter(users_organizations::status.eq(UserOrgStatus::Confirmed as i32)) .filter(users_organizations::status.eq(MembershipStatus::Confirmed as i32))
.load::<UserOrganizationDb>(conn) .load::<MembershipDb>(conn)
.unwrap_or_default().from_db() .unwrap_or_default().from_db()
}} }}
} }
@ -780,22 +780,22 @@ impl UserOrganization {
}} }}
} }
pub async fn find_by_org_and_type(org_uuid: &str, atype: UserOrgType, conn: &mut DbConn) -> Vec<Self> { pub async fn find_by_org_and_type(org_uuid: &str, atype: MembershipType, conn: &mut DbConn) -> Vec<Self> {
db_run! { conn: { db_run! { conn: {
users_organizations::table users_organizations::table
.filter(users_organizations::org_uuid.eq(org_uuid)) .filter(users_organizations::org_uuid.eq(org_uuid))
.filter(users_organizations::atype.eq(atype as i32)) .filter(users_organizations::atype.eq(atype as i32))
.load::<UserOrganizationDb>(conn) .load::<MembershipDb>(conn)
.expect("Error loading user organizations").from_db() .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: { db_run! { conn: {
users_organizations::table users_organizations::table
.filter(users_organizations::org_uuid.eq(org_uuid)) .filter(users_organizations::org_uuid.eq(org_uuid))
.filter(users_organizations::atype.eq(atype as i32)) .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() .count()
.first::<i64>(conn) .first::<i64>(conn)
.unwrap_or(0) .unwrap_or(0)
@ -807,7 +807,7 @@ impl UserOrganization {
users_organizations::table users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::user_uuid.eq(user_uuid))
.filter(users_organizations::org_uuid.eq(org_uuid)) .filter(users_organizations::org_uuid.eq(org_uuid))
.first::<UserOrganizationDb>(conn) .first::<MembershipDb>(conn)
.ok().from_db() .ok().from_db()
}} }}
} }
@ -818,9 +818,9 @@ impl UserOrganization {
.filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::user_uuid.eq(user_uuid))
.filter(users_organizations::org_uuid.eq(org_uuid)) .filter(users_organizations::org_uuid.eq(org_uuid))
.filter( .filter(
users_organizations::status.eq(UserOrgStatus::Confirmed as i32) users_organizations::status.eq(MembershipStatus::Confirmed as i32)
) )
.first::<UserOrganizationDb>(conn) .first::<MembershipDb>(conn)
.ok().from_db() .ok().from_db()
}} }}
} }
@ -829,12 +829,12 @@ impl UserOrganization {
db_run! { conn: { db_run! { conn: {
users_organizations::table users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::user_uuid.eq(user_uuid))
.load::<UserOrganizationDb>(conn) .load::<MembershipDb>(conn)
.expect("Error loading user organizations").from_db() .expect("Error loading user organizations").from_db()
}} }}
} }
pub async fn get_org_uuid_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<String> { pub async fn get_orgs_by_user(user_uuid: &str, conn: &mut DbConn) -> Vec<String> {
db_run! { conn: { db_run! { conn: {
users_organizations::table users_organizations::table
.filter(users_organizations::user_uuid.eq(user_uuid)) .filter(users_organizations::user_uuid.eq(user_uuid))
@ -855,10 +855,10 @@ impl UserOrganization {
.and(org_policies::enabled.eq(true))) .and(org_policies::enabled.eq(true)))
) )
.filter( .filter(
users_organizations::status.eq(UserOrgStatus::Confirmed as i32) users_organizations::status.eq(MembershipStatus::Confirmed as i32)
) )
.select(users_organizations::all_columns) .select(users_organizations::all_columns)
.load::<UserOrganizationDb>(conn) .load::<MembershipDb>(conn)
.unwrap_or_default().from_db() .unwrap_or_default().from_db()
}} }}
} }
@ -882,7 +882,7 @@ impl UserOrganization {
) )
.select(users_organizations::all_columns) .select(users_organizations::all_columns)
.distinct() .distinct()
.load::<UserOrganizationDb>(conn).expect("Error loading user organizations").from_db() .load::<MembershipDb>(conn).expect("Error loading user organizations").from_db()
}} }}
} }
@ -908,7 +908,7 @@ impl UserOrganization {
) )
.select(users_organizations::all_columns) .select(users_organizations::all_columns)
.distinct() .distinct()
.load::<UserOrganizationDb>(conn).expect("Error loading user organizations with groups").from_db() .load::<MembershipDb>(conn).expect("Error loading user organizations with groups").from_db()
}} }}
} }
@ -917,7 +917,7 @@ impl UserOrganization {
users_organizations::table users_organizations::table
.inner_join(ciphers::table.on(ciphers::uuid.eq(cipher_uuid).and(ciphers::organization_uuid.eq(users_organizations::org_uuid.nullable())))) .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::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() .count()
.first::<i64>(conn) .first::<i64>(conn)
.ok().unwrap_or(0) != 0 .ok().unwrap_or(0) != 0
@ -937,7 +937,7 @@ impl UserOrganization {
) )
) )
.select(users_organizations::all_columns) .select(users_organizations::all_columns)
.load::<UserOrganizationDb>(conn).expect("Error loading user organizations").from_db() .load::<MembershipDb>(conn).expect("Error loading user organizations").from_db()
}} }}
} }
@ -948,7 +948,7 @@ impl UserOrganization {
users_organizations::external_id.eq(ext_id) users_organizations::external_id.eq(ext_id)
.and(users_organizations::org_uuid.eq(org_uuid)) .and(users_organizations::org_uuid.eq(org_uuid))
) )
.first::<UserOrganizationDb>(conn).ok().from_db() .first::<MembershipDb>(conn).ok().from_db()
}} }}
} }
} }
@ -1011,9 +1011,9 @@ mod tests {
#[test] #[test]
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn partial_cmp_UserOrgType() { fn partial_cmp_MembershipType() {
assert!(UserOrgType::Owner > UserOrgType::Admin); assert!(MembershipType::Owner > MembershipType::Admin);
assert!(UserOrgType::Admin > UserOrgType::Manager); assert!(MembershipType::Admin > MembershipType::Manager);
assert!(UserOrgType::Manager > UserOrgType::User); assert!(MembershipType::Manager > MembershipType::User);
} }
} }

@ -215,8 +215,7 @@ impl User {
} }
use super::{ use super::{
Cipher, Device, EmergencyAccess, Favorite, Folder, Send, TwoFactor, TwoFactorIncomplete, UserOrgType, Cipher, Device, EmergencyAccess, Favorite, Folder, Membership, MembershipType, Send, TwoFactor, TwoFactorIncomplete,
UserOrganization,
}; };
use crate::db::DbConn; use crate::db::DbConn;
@ -227,7 +226,7 @@ use crate::error::MapResult;
impl User { impl User {
pub async fn to_json(&self, conn: &mut DbConn) -> Value { pub async fn to_json(&self, conn: &mut DbConn) -> Value {
let mut orgs_json = Vec::new(); 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); orgs_json.push(c.to_json(conn).await);
} }
@ -304,10 +303,9 @@ impl User {
} }
pub async fn delete(self, conn: &mut DbConn) -> EmptyResult { pub async fn delete(self, conn: &mut DbConn) -> EmptyResult {
for user_org in UserOrganization::find_confirmed_by_user(&self.uuid, conn).await { for member in Membership::find_confirmed_by_user(&self.uuid, conn).await {
if user_org.atype == UserOrgType::Owner if member.atype == MembershipType::Owner
&& UserOrganization::count_confirmed_by_org_and_type(&user_org.org_uuid, UserOrgType::Owner, conn).await && Membership::count_confirmed_by_org_and_type(&member.org_uuid, MembershipType::Owner, conn).await <= 1
<= 1
{ {
err!("Can't delete last owner") err!("Can't delete last owner")
} }
@ -316,7 +314,7 @@ impl User {
Send::delete_all_by_user(&self.uuid, conn).await?; Send::delete_all_by_user(&self.uuid, conn).await?;
EmergencyAccess::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?; 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?; Cipher::delete_all_by_user(&self.uuid, conn).await?;
Favorite::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?; Folder::delete_all_by_user(&self.uuid, conn).await?;

@ -260,7 +260,7 @@ pub async fn send_single_org_removed_from_org(address: &str, org_name: &str) ->
pub async fn send_invite( pub async fn send_invite(
user: &User, user: &User,
org_id: Option<String>, org_id: Option<String>,
org_user_id: Option<String>, member_id: Option<String>,
org_name: &str, org_name: &str,
invited_by_email: Option<String>, invited_by_email: Option<String>,
) -> EmptyResult { ) -> EmptyResult {
@ -268,7 +268,7 @@ pub async fn send_invite(
user.uuid.clone(), user.uuid.clone(),
user.email.clone(), user.email.clone(),
org_id.clone(), org_id.clone(),
org_user_id.clone(), member_id.clone(),
invited_by_email, invited_by_email,
); );
let invite_token = encode_jwt(&claims); let invite_token = encode_jwt(&claims);
@ -279,7 +279,7 @@ pub async fn send_invite(
.append_pair("email", &user.email) .append_pair("email", &user.email)
.append_pair("organizationName", org_name) .append_pair("organizationName", org_name)
.append_pair("organizationId", org_id.as_deref().unwrap_or("_")) .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); .append_pair("token", &invite_token);
if user.private_key.is_some() { if user.private_key.is_some() {
query_params.append_pair("orgUserHasExistingUser", "true"); query_params.append_pair("orgUserHasExistingUser", "true");

Loading…
Cancel
Save