Spiegel von
https://github.com/dani-garcia/vaultwarden.git
synchronisiert 2025-01-08 11:55:42 +01:00
rename membership
rename UserOrganization to Membership to clarify the relation and prevent confusion whether something refers to a member(ship) or user
Dieser Commit ist enthalten in:
Ursprung
ed4ad67e73
Commit
0b9e3bafd3
20 geänderte Dateien mit 568 neuen und 616 gelöschten Zeilen
|
@ -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.
|
||||||
|
|
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
|
@ -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?;
|
||||||
|
|
||||||
|
|
46
src/auth.rs
46
src/auth.rs
|
@ -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");
|
||||||
|
|
Laden …
In neuem Issue referenzieren