From 3d42b66a1054c3b5336b92153717d6cce21c4446 Mon Sep 17 00:00:00 2001 From: Stefan Melmuk Date: Wed, 25 Dec 2024 02:53:22 +0100 Subject: [PATCH] add collection_membership type --- src/api/core/organizations.rs | 37 +++++++++++++++--------------- src/db/models/collection.rs | 43 +++++++++++++++++++++++++++-------- 2 files changed, 53 insertions(+), 27 deletions(-) diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs index 35297dcc..28eef5e0 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -43,13 +43,13 @@ pub fn routes() -> Vec { get_org_details, get_members, send_invite, - reinvite_user, - bulk_reinvite_user, + reinvite_member, + bulk_reinvite_member, confirm_invite, bulk_confirm_invite, accept_invite, get_user, - edit_user, + edit_membership, put_membership, delete_user, bulk_delete_user, @@ -340,7 +340,8 @@ async fn get_org_collections_details( }; // get all collection memberships for the current organization - let col_users = CollectionUser::find_by_organization(&org_id, &mut conn).await; + // NOTE: the loaded col_users have a MembershipId + let col_users = CollectionUser::find_by_organization_swap_user_uuid_with_member_uuid(&org_id, &mut conn).await; // check if current user has full access to the organization (either directly or via any group) let has_full_access_to_org = member.access_all @@ -688,9 +689,9 @@ async fn get_collection_users( err!("Collection not found in Organization") }; - let mut user_list = Vec::new(); + let mut member_list = Vec::new(); for col_user in CollectionUser::find_by_collection(&collection.uuid, &mut conn).await { - user_list.push( + member_list.push( Membership::find_by_user_and_org(&col_user.user_uuid, &org_id, &mut conn) .await .unwrap() @@ -698,7 +699,7 @@ async fn get_collection_users( ); } - Ok(Json(json!(user_list))) + Ok(Json(json!(member_list))) } #[put("/organizations//collections//users", data = "")] @@ -972,7 +973,7 @@ async fn send_invite( } #[post("/organizations//users/reinvite", data = "")] -async fn bulk_reinvite_user( +async fn bulk_reinvite_member( org_id: OrganizationId, data: Json, headers: AdminHeaders, @@ -982,7 +983,7 @@ async fn bulk_reinvite_user( let mut bulk_response = Vec::new(); for member_id in data.ids { - let err_msg = match _reinvite_user(&org_id, &member_id, &headers.user.email, &mut conn).await { + let err_msg = match _reinvite_member(&org_id, &member_id, &headers.user.email, &mut conn).await { Ok(_) => String::new(), Err(e) => format!("{e:?}"), }; @@ -1004,16 +1005,16 @@ async fn bulk_reinvite_user( } #[post("/organizations//users//reinvite")] -async fn reinvite_user( +async fn reinvite_member( org_id: OrganizationId, member_id: MembershipId, headers: AdminHeaders, mut conn: DbConn, ) -> EmptyResult { - _reinvite_user(&org_id, &member_id, &headers.user.email, &mut conn).await + _reinvite_member(&org_id, &member_id, &headers.user.email, &mut conn).await } -async fn _reinvite_user( +async fn _reinvite_member( org_id: &OrganizationId, member_id: &MembershipId, invited_by_email: &str, @@ -1104,7 +1105,7 @@ async fn accept_invite( err!("Reset password key is required, but not provided."); } - // This check is also done at accept_invite(), _confirm_invite, _activate_user(), edit_user(), admin::update_membership_type + // This check is also done at accept_invite(), _confirm_invite, _activate_membership(), edit_membership(), admin::update_membership_type // It returns different error messages per function. if member.atype < MembershipType::Admin { match OrgPolicy::is_user_allowed(&member.user_uuid, &org_id, false, &mut conn).await { @@ -1245,7 +1246,7 @@ async fn _confirm_invite( err!("User in invalid state") } - // This check is also done at accept_invite(), _confirm_invite, _activate_user(), edit_user(), admin::update_membership_type + // This check is also done at accept_invite(), _confirm_invite, _activate_membership(), edit_membership(), admin::update_membership_type // It returns different error messages per function. if member_to_confirm.atype < MembershipType::Admin { match OrgPolicy::is_user_allowed(&member_to_confirm.user_uuid, org_id, true, conn).await { @@ -1336,11 +1337,11 @@ async fn put_membership( headers: AdminHeaders, conn: DbConn, ) -> EmptyResult { - edit_user(org_id, member_id, data, headers, conn).await + edit_membership(org_id, member_id, data, headers, conn).await } #[post("/organizations//users/", data = "", rank = 1)] -async fn edit_user( +async fn edit_membership( org_id: OrganizationId, member_id: MembershipId, data: Json, @@ -1378,7 +1379,7 @@ async fn edit_user( } } - // This check is also done at accept_invite(), _confirm_invite, _activate_user(), edit_user(), admin::update_membership_type + // This check is also done at accept_invite(), _confirm_invite, _activate_membership(), edit_membership(), admin::update_membership_type // It returns different error messages per function. if new_type < MembershipType::Admin { match OrgPolicy::is_user_allowed(&member_to_edit.user_uuid, &org_id, true, &mut conn).await { @@ -2282,7 +2283,7 @@ async fn _restore_membership( err!("Only owners can restore other owners") } - // This check is also done at accept_invite(), _confirm_invite, _activate_user(), edit_user(), admin::update_membership_type + // This check is also done at accept_invite(), _confirm_invite, _activate_membership(), edit_membership(), admin::update_membership_type // It returns different error messages per function. if member.atype < MembershipType::Admin { match OrgPolicy::is_user_allowed(&member.user_uuid, org_id, false, conn).await { diff --git a/src/db/models/collection.rs b/src/db/models/collection.rs index 7d94f9d5..94d30fcc 100644 --- a/src/db/models/collection.rs +++ b/src/db/models/collection.rs @@ -3,7 +3,8 @@ use rocket::request::FromParam; use serde_json::Value; use super::{ - CipherId, CollectionGroup, GroupUser, Membership, MembershipStatus, MembershipType, OrganizationId, User, UserId, + CipherId, CollectionGroup, GroupUser, Membership, MembershipId, MembershipStatus, MembershipType, OrganizationId, + User, UserId, }; use crate::CONFIG; @@ -527,8 +528,11 @@ impl CollectionUser { }} } - pub async fn find_by_organization(org_uuid: &OrganizationId, conn: &mut DbConn) -> Vec { - db_run! { conn: { + pub async fn find_by_organization_swap_user_uuid_with_member_uuid( + org_uuid: &OrganizationId, + conn: &mut DbConn, + ) -> Vec { + let col_users = db_run! { conn: { users_collections::table .inner_join(collections::table.on(collections::uuid.eq(users_collections::collection_uuid))) .filter(collections::org_uuid.eq(org_uuid)) @@ -537,7 +541,8 @@ impl CollectionUser { .load::(conn) .expect("Error loading users_collections") .from_db() - }} + }}; + col_users.into_iter().map(|c| c.into()).collect() } pub async fn save( @@ -626,8 +631,8 @@ impl CollectionUser { pub async fn find_by_collection_swap_user_uuid_with_member_uuid( collection_uuid: &CollectionId, conn: &mut DbConn, - ) -> Vec { - db_run! { conn: { + ) -> Vec { + let col_users = db_run! { conn: { users_collections::table .filter(users_collections::collection_uuid.eq(collection_uuid)) .inner_join(users_organizations::table.on(users_organizations::user_uuid.eq(users_collections::user_uuid))) @@ -635,7 +640,8 @@ impl CollectionUser { .load::(conn) .expect("Error loading users_collections") .from_db() - }} + }}; + col_users.into_iter().map(|c| c.into()).collect() } pub async fn find_by_collection_and_user( @@ -775,10 +781,18 @@ impl CollectionCipher { } } -impl CollectionUser { +// Added in case we need the membership_uuid instead of the user_uuid +pub struct CollectionMembership { + pub membership_uuid: MembershipId, + pub collection_uuid: CollectionId, + pub read_only: bool, + pub hide_passwords: bool, +} + +impl CollectionMembership { pub fn to_json_details_for_user(&self) -> Value { json!({ - "id": self.user_uuid, + "id": self.membership_uuid, "readOnly": self.read_only, "hidePasswords": self.hide_passwords, "manage": false @@ -786,6 +800,17 @@ impl CollectionUser { } } +impl From for CollectionMembership { + fn from(c: CollectionUser) -> Self { + Self { + membership_uuid: c.user_uuid.to_string().into(), + collection_uuid: c.collection_uuid, + read_only: c.read_only, + hide_passwords: c.hide_passwords, + } + } +} + #[derive( Clone, Debug, AsRef, Deref, DieselNewType, Display, From, FromForm, Hash, PartialEq, Eq, Serialize, Deserialize, )]