diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index e0869c63..a8b9b22e 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -376,14 +376,12 @@ async fn post_set_password(data: Json, headers: Headers, conn: if let Some(identifier) = data.org_identifier { if identifier != crate::sso::FAKE_IDENTIFIER && identifier != crate::api::admin::FAKE_ADMIN_UUID { - let org = match Organization::find_by_uuid(&identifier.into(), &conn).await { - None => err!("Failed to retrieve the associated organization"), - Some(org) => org, + let Some(org) = Organization::find_by_uuid(&identifier.into(), &conn).await else { + err!("Failed to retrieve the associated organization") }; - let membership = match Membership::find_by_user_and_org(&user.uuid, &org.uuid, &conn).await { - None => err!("Failed to retrieve the invitation"), - Some(org) => org, + let Some(membership) = Membership::find_by_user_and_org(&user.uuid, &org.uuid, &conn).await else { + err!("Failed to retrieve the invitation") }; accept_org_invite(&user, membership, None, &conn).await?; diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs index 8696b50a..8a8d9838 100644 --- a/src/api/core/ciphers.rs +++ b/src/api/core/ciphers.rs @@ -426,7 +426,7 @@ 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 Membership::find_by_user_and_org(&headers.user.uuid, &org_id, conn).await { + match Membership::find_confirmed_by_user_and_org(&headers.user.uuid, &org_id, conn).await { None => err!("You don't have permission to add item to organization"), Some(member) => { if shared_to_collections.is_some() @@ -1989,8 +1989,11 @@ impl CipherSyncData { } // Generate a HashMap with the Organization UUID as key and the Membership record - let members: HashMap = - Membership::find_by_user(user_id, conn).await.into_iter().map(|m| (m.org_uuid.clone(), m)).collect(); + let members: HashMap = Membership::find_confirmed_by_user(user_id, conn) + .await + .into_iter() + .map(|m| (m.org_uuid.clone(), m)) + .collect(); // Generate a HashMap with the User_Collections UUID as key and the CollectionUser record let user_collections: HashMap = CollectionUser::find_by_user(user_id, conn) diff --git a/src/api/core/events.rs b/src/api/core/events.rs index 2f33a407..d1612255 100644 --- a/src/api/core/events.rs +++ b/src/api/core/events.rs @@ -240,7 +240,7 @@ async fn _log_user_event( ip: &IpAddr, conn: &DbConn, ) { - let memberships = Membership::find_by_user(user_id, conn).await; + let memberships = Membership::find_confirmed_by_user(user_id, conn).await; let mut events: Vec = Vec::with_capacity(memberships.len() + 1); // We need an event per org and one without an org // Upstream saves the event also without any org_id. diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs index 0beb7036..6b352e34 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -234,7 +234,7 @@ async fn post_delete_organization( #[post("/organizations//leave")] async fn leave_organization(org_id: OrganizationId, headers: Headers, conn: DbConn) -> EmptyResult { - match Membership::find_by_user_and_org(&headers.user.uuid, &org_id, &conn).await { + match Membership::find_confirmed_by_user_and_org(&headers.user.uuid, &org_id, &conn).await { None => err!("User not part of organization"), Some(member) => { if member.atype == MembershipType::Owner @@ -2951,15 +2951,19 @@ async fn check_reset_password_applicable(org_id: &OrganizationId, conn: &DbConn) Ok(()) } -#[put("/organizations//users//reset-password-enrollment", data = "")] +#[put("/organizations//users//reset-password-enrollment", data = "")] async fn put_reset_password_enrollment( org_id: OrganizationId, - member_id: MembershipId, - headers: Headers, + user_id: UserId, + headers: OrgMemberHeaders, data: Json, conn: DbConn, ) -> EmptyResult { - let Some(mut member) = Membership::find_by_user_and_org(&headers.user.uuid, &org_id, &conn).await else { + if user_id != headers.user.uuid { + err!("User to enroll isn't member of required organization", "The user_id and acting user do not match"); + } + + let Some(mut membership) = Membership::find_confirmed_by_user_and_org(&headers.user.uuid, &org_id, &conn).await else { err!("User to enroll isn't member of required organization") }; @@ -2986,16 +2990,16 @@ async fn put_reset_password_enrollment( .await?; } - member.reset_password_key = reset_password_key; - member.save(&conn).await?; + membership.reset_password_key = reset_password_key; + membership.save(&conn).await?; - let log_id = if member.reset_password_key.is_some() { + let event_type = if membership.reset_password_key.is_some() { EventType::OrganizationUserResetPasswordEnroll as i32 } else { EventType::OrganizationUserResetPasswordWithdraw as i32 }; - log_event(log_id, &member_id, &org_id, &headers.user.uuid, headers.device.atype, &headers.ip.ip, &conn).await; + log_event(event_type, &membership.uuid, &org_id, &headers.user.uuid, headers.device.atype, &headers.ip.ip, &conn).await; Ok(()) } diff --git a/src/auth.rs b/src/auth.rs index 224bcba5..1728bef7 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -704,10 +704,9 @@ pub struct OrgHeaders { impl OrgHeaders { fn is_member(&self) -> bool { - // NOTE: we don't care about MembershipStatus at the moment because this is only used - // where an invited, accepted or confirmed user is expected if this ever changes or - // if from_i32 is changed to return Some(Revoked) this check needs to be changed accordingly - self.membership_type >= MembershipType::User + // Only allow not revoked members, we can not use the Confirmed status here + // as some endpoints can be triggered by invited users during joining + self.membership_status != MembershipStatus::Revoked && self.membership_type >= MembershipType::User } fn is_confirmed_and_admin(&self) -> bool { self.membership_status == MembershipStatus::Confirmed && self.membership_type >= MembershipType::Admin @@ -768,7 +767,8 @@ impl<'r> FromRequest<'r> for OrgHeaders { }; let user = headers.user; - let Some(membership) = Membership::find_by_user_and_org(&user.uuid, &org_id, &conn).await else { + let Some(membership) = Membership::find_by_user_and_org(&user.uuid, &org_id, &conn).await + else { err_handler!("The current user isn't member of the organization"); }; diff --git a/src/db/models/cipher.rs b/src/db/models/cipher.rs index b28a25cd..2d95c226 100644 --- a/src/db/models/cipher.rs +++ b/src/db/models/cipher.rs @@ -559,7 +559,7 @@ impl Cipher { if let Some(cached_member) = cipher_sync_data.members.get(org_uuid) { return cached_member.has_full_access(); } - } else if let Some(member) = Membership::find_by_user_and_org(user_uuid, org_uuid, conn).await { + } else if let Some(member) = Membership::find_confirmed_by_user_and_org(user_uuid, org_uuid, conn).await { return member.has_full_access(); } } diff --git a/src/db/models/org_policy.rs b/src/db/models/org_policy.rs index 96811a2b..7e922f35 100644 --- a/src/db/models/org_policy.rs +++ b/src/db/models/org_policy.rs @@ -332,7 +332,7 @@ impl OrgPolicy { for policy in OrgPolicy::find_confirmed_by_user_and_active_policy(user_uuid, OrgPolicyType::SendOptions, conn).await { - if let Some(user) = Membership::find_by_user_and_org(user_uuid, &policy.org_uuid, conn).await { + if let Some(user) = Membership::find_confirmed_by_user_and_org(user_uuid, &policy.org_uuid, conn).await { if user.atype < MembershipType::Admin { match serde_json::from_str::(&policy.data) { Ok(opts) => {