Skip to content

Commit

Permalink
rename membership
Browse files Browse the repository at this point in the history
rename UserOrganization to Membership to clarify the relation
and prevent confusion whether something refers to a member(ship) or user
  • Loading branch information
stefan0xC committed Jan 5, 2025
1 parent ef4bff0 commit 90cfb6d
Show file tree
Hide file tree
Showing 20 changed files with 632 additions and 686 deletions.
43 changes: 21 additions & 22 deletions src/api/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub fn routes() -> Vec<Route> {
disable_user,
enable_user,
remove_2fa,
update_user_org_type,
update_membership_type,
update_revision_users,
post_config,
delete_config,
Expand Down Expand Up @@ -394,15 +394,15 @@ async fn get_user_json(uuid: &str, _token: AdminToken, mut conn: DbConn) -> Json
async fn delete_user(uuid: &str, token: AdminToken, mut conn: DbConn) -> EmptyResult {
let user = get_user_or_404(uuid, &mut conn).await?;

// Get the user_org records before deleting the actual user
let user_orgs = UserOrganization::find_any_state_by_user(uuid, &mut conn).await;
// Get the membership records before deleting the actual user
let memberships = Membership::find_any_state_by_user(uuid, &mut conn).await;
let res = user.delete(&mut conn).await;

for user_org in user_orgs {
for membership in memberships {
log_event(
EventType::OrganizationUserRemoved as i32,
&user_org.uuid,
&user_org.org_uuid,
&membership.uuid,
&membership.org_uuid,
ACTING_ADMIN_USER,
14, // Use UnknownBrowser type
&token.ip.ip,
Expand Down Expand Up @@ -485,42 +485,41 @@ async fn resend_user_invite(uuid: &str, _token: AdminToken, mut conn: DbConn) ->
}

#[derive(Debug, Deserialize)]
struct UserOrgTypeData {
struct MembershipTypeData {
user_type: NumberOrString,
user_uuid: String,
org_uuid: String,
}

#[post("/users/org_type", data = "<data>")]
async fn update_user_org_type(data: Json<UserOrgTypeData>, token: AdminToken, mut conn: DbConn) -> EmptyResult {
let data: UserOrgTypeData = data.into_inner();
async fn update_membership_type(data: Json<MembershipTypeData>, token: AdminToken, mut conn: DbConn) -> EmptyResult {
let data: MembershipTypeData = data.into_inner();

let Some(mut user_to_edit) =
UserOrganization::find_by_user_and_org(&data.user_uuid, &data.org_uuid, &mut conn).await
let Some(mut member_to_edit) = Membership::find_by_user_and_org(&data.user_uuid, &data.org_uuid, &mut conn).await
else {
err!("The specified user isn't member of the organization")
};

let new_type = match UserOrgType::from_str(&data.user_type.into_string()) {
let new_type = match MembershipType::from_str(&data.user_type.into_string()) {
Some(new_type) => new_type as i32,
None => err!("Invalid type"),
};

if user_to_edit.atype == UserOrgType::Owner && new_type != UserOrgType::Owner {
if member_to_edit.atype == MembershipType::Owner && new_type != MembershipType::Owner {
// Removing owner permission, check that there is at least one other confirmed owner
if UserOrganization::count_confirmed_by_org_and_type(&data.org_uuid, UserOrgType::Owner, &mut conn).await <= 1 {
if Membership::count_confirmed_by_org_and_type(&data.org_uuid, MembershipType::Owner, &mut conn).await <= 1 {
err!("Can't change the type of the last owner")
}
}

// This check is also done at api::organizations::{accept_invite(), _confirm_invite, _activate_user(), edit_user()}, update_user_org_type
// This check is also done at api::organizations::{accept_invite, _confirm_invite, _activate_member, edit_member}, update_membership_type
// It returns different error messages per function.
if new_type < UserOrgType::Admin {
match OrgPolicy::is_user_allowed(&user_to_edit.user_uuid, &user_to_edit.org_uuid, true, &mut conn).await {
if new_type < MembershipType::Admin {
match OrgPolicy::is_user_allowed(&member_to_edit.user_uuid, &member_to_edit.org_uuid, true, &mut conn).await {
Ok(_) => {}
Err(OrgPolicyErr::TwoFactorMissing) => {
if CONFIG.email_2fa_auto_fallback() {
two_factor::email::find_and_activate_email_2fa(&user_to_edit.user_uuid, &mut conn).await?;
two_factor::email::find_and_activate_email_2fa(&member_to_edit.user_uuid, &mut conn).await?;
} else {
err!("You cannot modify this user to this type because they have not setup 2FA");
}
Expand All @@ -533,7 +532,7 @@ async fn update_user_org_type(data: Json<UserOrgTypeData>, token: AdminToken, mu

log_event(
EventType::OrganizationUserUpdated as i32,
&user_to_edit.uuid,
&member_to_edit.uuid,
&data.org_uuid,
ACTING_ADMIN_USER,
14, // Use UnknownBrowser type
Expand All @@ -542,8 +541,8 @@ async fn update_user_org_type(data: Json<UserOrgTypeData>, token: AdminToken, mu
)
.await;

user_to_edit.atype = new_type;
user_to_edit.save(&mut conn).await
member_to_edit.atype = new_type;
member_to_edit.save(&mut conn).await
}

#[post("/users/update_revision")]
Expand All @@ -557,7 +556,7 @@ async fn organizations_overview(_token: AdminToken, mut conn: DbConn) -> ApiResu
let mut organizations_json = Vec::with_capacity(organizations.len());
for o in organizations {
let mut org = o.to_json();
org["user_count"] = json!(UserOrganization::count_by_org(&o.uuid, &mut conn).await);
org["user_count"] = json!(Membership::count_by_org(&o.uuid, &mut conn).await);
org["cipher_count"] = json!(Cipher::count_by_org(&o.uuid, &mut conn).await);
org["collection_count"] = json!(Collection::count_by_org(&o.uuid, &mut conn).await);
org["group_count"] = json!(Group::count_by_org(&o.uuid, &mut conn).await);
Expand Down
30 changes: 15 additions & 15 deletions src/api/core/accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,15 +106,15 @@ fn enforce_password_hint_setting(password_hint: &Option<String>) -> EmptyResult
}
Ok(())
}
async fn is_email_2fa_required(org_user_uuid: Option<String>, conn: &mut DbConn) -> bool {
async fn is_email_2fa_required(member_uuid: Option<String>, conn: &mut DbConn) -> bool {
if !CONFIG._enable_email_2fa() {
return false;
}
if CONFIG.email_2fa_enforce_on_verified_invite() {
return true;
}
if org_user_uuid.is_some() {
return OrgPolicy::is_enabled_for_member(&org_user_uuid.unwrap(), OrgPolicyType::TwoFactorAuthentication, conn)
if member_uuid.is_some() {
return OrgPolicy::is_enabled_for_member(&member_uuid.unwrap(), OrgPolicyType::TwoFactorAuthentication, conn)
.await;
}
false
Expand Down Expand Up @@ -161,9 +161,9 @@ pub async fn _register(data: Json<RegisterData>, mut conn: DbConn) -> JsonResult
err!("Registration email does not match invite email")
}
} else if Invitation::take(&email, &mut conn).await {
for user_org in UserOrganization::find_invited_by_user(&user.uuid, &mut conn).await.iter_mut() {
user_org.status = UserOrgStatus::Accepted as i32;
user_org.save(&mut conn).await?;
for membership in Membership::find_invited_by_user(&user.uuid, &mut conn).await.iter_mut() {
membership.status = MembershipStatus::Accepted as i32;
membership.save(&mut conn).await?;
}
user
} else if CONFIG.is_signup_allowed(&email)
Expand Down Expand Up @@ -489,7 +489,7 @@ fn validate_keydata(
existing_ciphers: &[Cipher],
existing_folders: &[Folder],
existing_emergency_access: &[EmergencyAccess],
existing_user_orgs: &[UserOrganization],
existing_memberships: &[Membership],
existing_sends: &[Send],
) -> EmptyResult {
// Check that we're correctly rotating all the user's ciphers
Expand Down Expand Up @@ -521,7 +521,7 @@ fn validate_keydata(
}

// Check that we're correctly rotating all the user's reset password keys
let existing_reset_password_ids = existing_user_orgs.iter().map(|uo| uo.org_uuid.as_str()).collect::<HashSet<_>>();
let existing_reset_password_ids = existing_memberships.iter().map(|m| m.org_uuid.as_str()).collect::<HashSet<_>>();
let provided_reset_password_ids =
data.reset_password_keys.iter().map(|rp| rp.organization_id.as_str()).collect::<HashSet<_>>();
if !provided_reset_password_ids.is_superset(&existing_reset_password_ids) {
Expand Down Expand Up @@ -560,17 +560,17 @@ async fn post_rotatekey(data: Json<KeyData>, headers: Headers, mut conn: DbConn,
let mut existing_ciphers = Cipher::find_owned_by_user(user_uuid, &mut conn).await;
let mut existing_folders = Folder::find_by_user(user_uuid, &mut conn).await;
let mut existing_emergency_access = EmergencyAccess::find_all_by_grantor_uuid(user_uuid, &mut conn).await;
let mut existing_user_orgs = UserOrganization::find_by_user(user_uuid, &mut conn).await;
let mut existing_memberships = Membership::find_by_user(user_uuid, &mut conn).await;
// We only rotate the reset password key if it is set.
existing_user_orgs.retain(|uo| uo.reset_password_key.is_some());
existing_memberships.retain(|m| m.reset_password_key.is_some());
let mut existing_sends = Send::find_by_user(user_uuid, &mut conn).await;

validate_keydata(
&data,
&existing_ciphers,
&existing_folders,
&existing_emergency_access,
&existing_user_orgs,
&existing_memberships,
&existing_sends,
)?;

Expand Down Expand Up @@ -602,14 +602,14 @@ async fn post_rotatekey(data: Json<KeyData>, headers: Headers, mut conn: DbConn,

// Update reset password data
for reset_password_data in data.reset_password_keys {
let Some(user_org) =
existing_user_orgs.iter_mut().find(|uo| uo.org_uuid == reset_password_data.organization_id)
let Some(membership) =
existing_memberships.iter_mut().find(|m| m.org_uuid == reset_password_data.organization_id)
else {
err!("Reset password doesn't exist")
};

user_org.reset_password_key = Some(reset_password_data.reset_password_key);
user_org.save(&mut conn).await?
membership.reset_password_key = Some(reset_password_data.reset_password_key);
membership.save(&mut conn).await?
}

// Update send data
Expand Down
33 changes: 15 additions & 18 deletions src/api/core/ciphers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,11 +405,11 @@ pub async fn update_cipher_from_data(
let transfer_cipher = cipher.organization_uuid.is_none() && data.organization_id.is_some();

if let Some(org_id) = data.organization_id {
match UserOrganization::find_by_user_and_org(&headers.user.uuid, &org_id, conn).await {
match Membership::find_by_user_and_org(&headers.user.uuid, &org_id, conn).await {
None => err!("You don't have permission to add item to organization"),
Some(org_user) => {
Some(member) => {
if shared_to_collections.is_some()
|| org_user.has_full_access()
|| member.has_full_access()
|| cipher.is_write_accessible_to_user(&headers.user.uuid, conn).await
{
cipher.organization_uuid = Some(org_id);
Expand Down Expand Up @@ -1593,10 +1593,10 @@ async fn delete_all(
match organization {
Some(org_data) => {
// Organization ID in query params, purging organization vault
match UserOrganization::find_by_user_and_org(&user.uuid, &org_data.org_id, &mut conn).await {
match Membership::find_by_user_and_org(&user.uuid, &org_data.org_id, &mut conn).await {
None => err!("You don't have permission to purge the organization vault"),
Some(user_org) => {
if user_org.atype == UserOrgType::Owner {
Some(member) => {
if member.atype == MembershipType::Owner {
Cipher::delete_all_by_organization(&org_data.org_id, &mut conn).await?;
nt.send_user_update(UpdateType::SyncVault, &user).await;

Expand Down Expand Up @@ -1835,7 +1835,7 @@ pub struct CipherSyncData {
pub cipher_folders: HashMap<String, String>,
pub cipher_favorites: HashSet<String>,
pub cipher_collections: HashMap<String, Vec<String>>,
pub user_organizations: HashMap<String, UserOrganization>,
pub members: HashMap<String, Membership>,
pub user_collections: HashMap<String, CollectionUser>,
pub user_collections_groups: HashMap<String, CollectionGroup>,
pub user_group_full_access_for_organizations: HashSet<String>,
Expand Down Expand Up @@ -1869,8 +1869,8 @@ impl CipherSyncData {
}

// Generate a list of Cipher UUID's containing a Vec with one or more Attachment records
let user_org_uuids = UserOrganization::get_org_uuid_by_user(user_uuid, conn).await;
let attachments = Attachment::find_all_by_user_and_orgs(user_uuid, &user_org_uuids, conn).await;
let orgs = Membership::get_orgs_by_user(user_uuid, conn).await;
let attachments = Attachment::find_all_by_user_and_orgs(user_uuid, &orgs, conn).await;
let mut cipher_attachments: HashMap<String, Vec<Attachment>> = HashMap::with_capacity(attachments.len());
for attachment in attachments {
cipher_attachments.entry(attachment.cipher_uuid.clone()).or_default().push(attachment);
Expand All @@ -1884,12 +1884,9 @@ impl CipherSyncData {
cipher_collections.entry(cipher).or_default().push(collection);
}

// Generate a HashMap with the Organization UUID as key and the UserOrganization record
let user_organizations: HashMap<String, UserOrganization> = UserOrganization::find_by_user(user_uuid, conn)
.await
.into_iter()
.map(|uo| (uo.org_uuid.clone(), uo))
.collect();
// Generate a HashMap with the Organization UUID as key and the Membership record
let members: HashMap<String, Membership> =
Membership::find_by_user(user_uuid, conn).await.into_iter().map(|m| (m.org_uuid.clone(), m)).collect();

// Generate a HashMap with the User_Collections UUID as key and the CollectionUser record
let user_collections: HashMap<String, CollectionUser> = CollectionUser::find_by_user(user_uuid, conn)
Expand All @@ -1909,9 +1906,9 @@ impl CipherSyncData {
HashMap::new()
};

// Get all organizations that the user has full access to via group assignment
// Get all organizations that the given user has full access to via group assignment
let user_group_full_access_for_organizations: HashSet<String> = if CONFIG.org_groups_enabled() {
Group::gather_user_organizations_full_access(user_uuid, conn).await.into_iter().collect()
Group::get_orgs_by_user_with_full_access(user_uuid, conn).await.into_iter().collect()
} else {
HashSet::new()
};
Expand All @@ -1921,7 +1918,7 @@ impl CipherSyncData {
cipher_folders,
cipher_favorites,
cipher_collections,
user_organizations,
members,
user_collections,
user_collections_groups,
user_group_full_access_for_organizations,
Expand Down
6 changes: 3 additions & 3 deletions src/api/core/emergency_access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -662,9 +662,9 @@ async fn password_emergency_access(
TwoFactor::delete_all_by_user(&grantor_user.uuid, &mut conn).await?;

// Remove grantor from all organisations unless Owner
for user_org in UserOrganization::find_any_state_by_user(&grantor_user.uuid, &mut conn).await {
if user_org.atype != UserOrgType::Owner as i32 {
user_org.delete(&mut conn).await?;
for member in Membership::find_any_state_by_user(&grantor_user.uuid, &mut conn).await {
if member.atype != MembershipType::Owner as i32 {
member.delete(&mut conn).await?;
}
}
Ok(())
Expand Down
12 changes: 6 additions & 6 deletions src/api/core/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
api::{EmptyResult, JsonResult},
auth::{AdminHeaders, Headers},
db::{
models::{Cipher, Event, UserOrganization},
models::{Cipher, Event, Membership},
DbConn, DbPool,
},
util::parse_date,
Expand Down Expand Up @@ -66,7 +66,7 @@ async fn get_cipher_events(cipher_id: &str, data: EventRange, headers: Headers,
Vec::with_capacity(0)
} else {
let mut events_json = Vec::with_capacity(0);
if UserOrganization::user_has_ge_admin_access_to_cipher(&headers.user.uuid, cipher_id, &mut conn).await {
if Membership::user_has_ge_admin_access_to_cipher(&headers.user.uuid, cipher_id, &mut conn).await {
let start_date = parse_date(&data.start);
let end_date = if let Some(before_date) = &data.continuation_token {
parse_date(before_date)
Expand All @@ -90,10 +90,10 @@ async fn get_cipher_events(cipher_id: &str, data: EventRange, headers: Headers,
})))
}

#[get("/organizations/<org_id>/users/<user_org_id>/events?<data..>")]
#[get("/organizations/<org_id>/users/<member_id>/events?<data..>")]
async fn get_user_events(
org_id: &str,
user_org_id: &str,
member_id: &str,
data: EventRange,
_headers: AdminHeaders,
mut conn: DbConn,
Expand All @@ -110,7 +110,7 @@ async fn get_user_events(
parse_date(&data.end)
};

Event::find_by_org_and_user_org(org_id, user_org_id, &start_date, &end_date, &mut conn)
Event::find_by_org_and_member(org_id, member_id, &start_date, &end_date, &mut conn)
.await
.iter()
.map(|e| e.to_json())
Expand Down Expand Up @@ -233,7 +233,7 @@ async fn _log_user_event(
ip: &IpAddr,
conn: &mut DbConn,
) {
let orgs = UserOrganization::get_org_uuid_by_user(user_uuid, conn).await;
let orgs = Membership::get_orgs_by_user(user_uuid, conn).await;
let mut events: Vec<Event> = Vec::with_capacity(orgs.len() + 1); // We need an event per org and one without an org

// Upstream saves the event also without any org_uuid.
Expand Down
Loading

0 comments on commit 90cfb6d

Please sign in to comment.