rename membership

rename UserOrganization to Membership to clarify the relation
and prevent confusion whether something refers to a member(ship) or user
pull/5320/head
Stefan Melmuk 1 week 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,
enable_user,
remove_2fa,
update_user_org_type,
update_membership_type,
update_revision_users,
post_config,
delete_config,
@ -394,15 +394,15 @@ async fn get_user_json(uuid: &str, _token: AdminToken, mut conn: DbConn) -> Json
async fn delete_user(uuid: &str, token: AdminToken, mut conn: DbConn) -> EmptyResult {
let user = get_user_or_404(uuid, &mut conn).await?;
// Get the user_org records before deleting the actual user
let user_orgs = UserOrganization::find_any_state_by_user(uuid, &mut conn).await;
// Get the membership records before deleting the actual user
let memberships = Membership::find_any_state_by_user(uuid, &mut conn).await;
let res = user.delete(&mut conn).await;
for user_org in user_orgs {
for membership in memberships {
log_event(
EventType::OrganizationUserRemoved as i32,
&user_org.uuid,
&user_org.org_uuid,
&membership.uuid,
&membership.org_uuid,
ACTING_ADMIN_USER,
14, // Use UnknownBrowser type
&token.ip.ip,
@ -485,42 +485,41 @@ async fn resend_user_invite(uuid: &str, _token: AdminToken, mut conn: DbConn) ->
}
#[derive(Debug, Deserialize)]
struct UserOrgTypeData {
struct MembershipTypeData {
user_type: NumberOrString,
user_uuid: String,
org_uuid: String,
}
#[post("/users/org_type", data = "<data>")]
async fn update_user_org_type(data: Json<UserOrgTypeData>, token: AdminToken, mut conn: DbConn) -> EmptyResult {
let data: UserOrgTypeData = data.into_inner();
async fn update_membership_type(data: Json<MembershipTypeData>, token: AdminToken, mut conn: DbConn) -> EmptyResult {
let data: MembershipTypeData = data.into_inner();
let Some(mut user_to_edit) =
UserOrganization::find_by_user_and_org(&data.user_uuid, &data.org_uuid, &mut conn).await
let Some(mut member_to_edit) = Membership::find_by_user_and_org(&data.user_uuid, &data.org_uuid, &mut conn).await
else {
err!("The specified user isn't member of the organization")
};
let new_type = match UserOrgType::from_str(&data.user_type.into_string()) {
let new_type = match MembershipType::from_str(&data.user_type.into_string()) {
Some(new_type) => new_type as i32,
None => err!("Invalid type"),
};
if user_to_edit.atype == UserOrgType::Owner && new_type != UserOrgType::Owner {
if member_to_edit.atype == MembershipType::Owner && new_type != MembershipType::Owner {
// Removing owner permission, check that there is at least one other confirmed owner
if UserOrganization::count_confirmed_by_org_and_type(&data.org_uuid, UserOrgType::Owner, &mut conn).await <= 1 {
if Membership::count_confirmed_by_org_and_type(&data.org_uuid, MembershipType::Owner, &mut conn).await <= 1 {
err!("Can't change the type of the last owner")
}
}
// This check is also done at api::organizations::{accept_invite(), _confirm_invite, _activate_user(), edit_user()}, update_user_org_type
// This check is also done at api::organizations::{accept_invite(), _confirm_invite, _activate_user(), edit_user()}, update_membership_type
// It returns different error messages per function.
if new_type < UserOrgType::Admin {
match OrgPolicy::is_user_allowed(&user_to_edit.user_uuid, &user_to_edit.org_uuid, true, &mut conn).await {
if new_type < MembershipType::Admin {
match OrgPolicy::is_user_allowed(&member_to_edit.user_uuid, &member_to_edit.org_uuid, true, &mut conn).await {
Ok(_) => {}
Err(OrgPolicyErr::TwoFactorMissing) => {
if CONFIG.email_2fa_auto_fallback() {
two_factor::email::find_and_activate_email_2fa(&user_to_edit.user_uuid, &mut conn).await?;
two_factor::email::find_and_activate_email_2fa(&member_to_edit.user_uuid, &mut conn).await?;
} else {
err!("You cannot modify this user to this type because they have not setup 2FA");
}
@ -533,7 +532,7 @@ async fn update_user_org_type(data: Json<UserOrgTypeData>, token: AdminToken, mu
log_event(
EventType::OrganizationUserUpdated as i32,
&user_to_edit.uuid,
&member_to_edit.uuid,
&data.org_uuid,
ACTING_ADMIN_USER,
14, // Use UnknownBrowser type
@ -542,8 +541,8 @@ async fn update_user_org_type(data: Json<UserOrgTypeData>, token: AdminToken, mu
)
.await;
user_to_edit.atype = new_type;
user_to_edit.save(&mut conn).await
member_to_edit.atype = new_type;
member_to_edit.save(&mut conn).await
}
#[post("/users/update_revision")]
@ -557,7 +556,7 @@ async fn organizations_overview(_token: AdminToken, mut conn: DbConn) -> ApiResu
let mut organizations_json = Vec::with_capacity(organizations.len());
for o in organizations {
let mut org = o.to_json();
org["user_count"] = json!(UserOrganization::count_by_org(&o.uuid, &mut conn).await);
org["user_count"] = json!(Membership::count_by_org(&o.uuid, &mut conn).await);
org["cipher_count"] = json!(Cipher::count_by_org(&o.uuid, &mut conn).await);
org["collection_count"] = json!(Collection::count_by_org(&o.uuid, &mut conn).await);
org["group_count"] = json!(Group::count_by_org(&o.uuid, &mut conn).await);

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

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

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

@ -8,7 +8,7 @@ use crate::{
api::{EmptyResult, JsonResult},
auth::{AdminHeaders, Headers},
db::{
models::{Cipher, Event, UserOrganization},
models::{Cipher, Event, Membership},
DbConn, DbPool,
},
util::parse_date,
@ -66,7 +66,7 @@ async fn get_cipher_events(cipher_id: &str, data: EventRange, headers: Headers,
Vec::with_capacity(0)
} else {
let mut events_json = Vec::with_capacity(0);
if UserOrganization::user_has_ge_admin_access_to_cipher(&headers.user.uuid, cipher_id, &mut conn).await {
if Membership::user_has_ge_admin_access_to_cipher(&headers.user.uuid, cipher_id, &mut conn).await {
let start_date = parse_date(&data.start);
let end_date = if let Some(before_date) = &data.continuation_token {
parse_date(before_date)
@ -90,10 +90,10 @@ async fn get_cipher_events(cipher_id: &str, data: EventRange, headers: Headers,
})))
}
#[get("/organizations/<org_id>/users/<user_org_id>/events?<data..>")]
#[get("/organizations/<org_id>/users/<member_id>/events?<data..>")]
async fn get_user_events(
org_id: &str,
user_org_id: &str,
member_id: &str,
data: EventRange,
_headers: AdminHeaders,
mut conn: DbConn,
@ -110,7 +110,7 @@ async fn get_user_events(
parse_date(&data.end)
};
Event::find_by_org_and_user_org(org_id, user_org_id, &start_date, &end_date, &mut conn)
Event::find_by_org_and_member(org_id, member_id, &start_date, &end_date, &mut conn)
.await
.iter()
.map(|e| e.to_json())
@ -233,7 +233,7 @@ async fn _log_user_event(
ip: &IpAddr,
conn: &mut DbConn,
) {
let orgs = UserOrganization::get_org_uuid_by_user(user_uuid, conn).await;
let orgs = Membership::get_orgs_by_user(user_uuid, conn).await;
let mut events: Vec<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.

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

@ -178,12 +178,11 @@ pub async fn enforce_2fa_policy(
ip: &std::net::IpAddr,
conn: &mut DbConn,
) -> EmptyResult {
for member in UserOrganization::find_by_user_and_policy(&user.uuid, OrgPolicyType::TwoFactorAuthentication, conn)
.await
.into_iter()
for member in
Membership::find_by_user_and_policy(&user.uuid, OrgPolicyType::TwoFactorAuthentication, conn).await.into_iter()
{
// Policy only applies to non-Owner/non-Admin members who have accepted joining the org
if member.atype < UserOrgType::Admin {
if member.atype < MembershipType::Admin {
if CONFIG.mail_enabled() {
let org = Organization::find_by_uuid(&member.org_uuid, conn).await.unwrap();
mail::send_2fa_removed_from_org(&user.email, &org.name).await?;
@ -216,9 +215,9 @@ pub async fn enforce_2fa_policy_for_org(
conn: &mut DbConn,
) -> EmptyResult {
let org = Organization::find_by_uuid(org_uuid, conn).await.unwrap();
for member in UserOrganization::find_confirmed_by_org(org_uuid, conn).await.into_iter() {
for member in Membership::find_confirmed_by_org(org_uuid, conn).await.into_iter() {
// Don't enforce the policy for Admins and Owners.
if member.atype < UserOrgType::Admin && TwoFactor::find_by_user(&member.user_uuid, conn).await.is_empty() {
if member.atype < MembershipType::Admin && TwoFactor::find_by_user(&member.user_uuid, conn).await.is_empty() {
if CONFIG.mail_enabled() {
let user = User::find_by_uuid(&member.user_uuid, conn).await.unwrap();
mail::send_2fa_removed_from_org(&user.email, &org.name).await?;

@ -111,7 +111,7 @@ async fn _refresh_login(data: ConnectData, conn: &mut DbConn) -> JsonResult {
// Because this might get used in the future, and is add by the Bitwarden Server, lets keep it, but then commented out
// See: https://github.com/dani-garcia/vaultwarden/issues/4156
// ---
// let orgs = UserOrganization::find_confirmed_by_user(&user.uuid, conn).await;
// let members = Membership::find_confirmed_by_user(&user.uuid, conn).await;
let (access_token, expires_in) = device.refresh_tokens(&user, scope_vec);
device.save(conn).await?;
@ -291,7 +291,7 @@ async fn _password_login(
// Because this might get used in the future, and is add by the Bitwarden Server, lets keep it, but then commented out
// See: https://github.com/dani-garcia/vaultwarden/issues/4156
// ---
// let orgs = UserOrganization::find_confirmed_by_user(&user.uuid, conn).await;
// let members = Membership::find_confirmed_by_user(&user.uuid, conn).await;
let (access_token, expires_in) = device.refresh_tokens(&user, scope_vec);
device.save(conn).await?;
@ -440,7 +440,7 @@ async fn _user_api_key_login(
// Because this might get used in the future, and is add by the Bitwarden Server, lets keep it, but then commented out
// See: https://github.com/dani-garcia/vaultwarden/issues/4156
// ---
// let orgs = UserOrganization::find_confirmed_by_user(&user.uuid, conn).await;
// let members = Membership::find_confirmed_by_user(&user.uuid, conn).await;
let (access_token, expires_in) = device.refresh_tokens(&user, scope_vec);
device.save(conn).await?;

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

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

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

@ -75,12 +75,12 @@ impl Device {
// Also These key/value pairs are not used anywhere by either Vaultwarden or Bitwarden Clients
// Because these might get used in the future, and they are added by the Bitwarden Server, lets keep it, but then commented out
// ---
// fn arg: orgs: Vec<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 orgadmin: Vec<_> = orgs.iter().filter(|o| o.atype == 1).map(|o| o.org_uuid.clone()).collect();
// let orguser: Vec<_> = orgs.iter().filter(|o| o.atype == 2).map(|o| o.org_uuid.clone()).collect();
// let orgmanager: Vec<_> = orgs.iter().filter(|o| o.atype == 3).map(|o| o.org_uuid.clone()).collect();
// let orgowner: Vec<_> = members.iter().filter(|m| m.atype == 0).map(|o| o.org_uuid.clone()).collect();
// let orgadmin: Vec<_> = members.iter().filter(|m| m.atype == 1).map(|o| o.org_uuid.clone()).collect();
// let orguser: Vec<_> = members.iter().filter(|m| m.atype == 2).map(|o| o.org_uuid.clone()).collect();
// let orgmanager: Vec<_> = members.iter().filter(|m| m.atype == 3).map(|o| o.org_uuid.clone()).collect();
// Create the JWT claims struct, to send to the client
use crate::auth::{encode_jwt, LoginJwtClaims, DEFAULT_VALIDITY, JWT_LOGIN_ISSUER};

@ -274,16 +274,16 @@ impl Event {
}}
}
pub async fn find_by_org_and_user_org(
pub async fn find_by_org_and_member(
org_uuid: &str,
user_org_uuid: &str,
member_uuid: &str,
start: &NaiveDateTime,
end: &NaiveDateTime,
conn: &mut DbConn,
) -> Vec<Self> {
db_run! { conn: {
event::table
.inner_join(users_organizations::table.on(users_organizations::uuid.eq(user_org_uuid)))
.inner_join(users_organizations::table.on(users_organizations::uuid.eq(member_uuid)))
.filter(event::org_uuid.eq(org_uuid))
.filter(event::event_date.between(start, end))
.filter(event::user_uuid.eq(users_organizations::user_uuid.nullable()).or(event::act_user_uuid.eq(users_organizations::user_uuid.nullable())))

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

@ -27,7 +27,7 @@ pub use self::favorite::Favorite;
pub use self::folder::{Folder, FolderCipher};
pub use self::group::{CollectionGroup, Group, GroupUser};
pub use self::org_policy::{OrgPolicy, OrgPolicyErr, OrgPolicyType};
pub use self::organization::{Organization, OrganizationApiKey, UserOrgStatus, UserOrgType, UserOrganization};
pub use self::organization::{Membership, MembershipStatus, MembershipType, Organization, OrganizationApiKey};
pub use self::send::{Send, SendType};
pub use self::two_factor::{TwoFactor, TwoFactorType};
pub use self::two_factor_duo_context::TwoFactorDuoContext;

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

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

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

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

Loading…
Cancel
Save