From 3ff5bf7dd20949e9247cc36b66b9c635b4176b55 Mon Sep 17 00:00:00 2001 From: BlackDex Date: Sun, 5 Jan 2025 15:20:40 +0100 Subject: [PATCH] WIP: Defer creating users until after mail WIP Prevent saving user/org-member to database until after mail has been sent Signed-off-by: BlackDex --- src/api/core/organizations.rs | 93 ++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 41 deletions(-) diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs index 902ab25a12..c2c1ca69f8 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -895,6 +895,8 @@ async fn send_invite(org_id: &str, data: Json, headers: AdminHeaders data.access_all = true; } + let mut invitation: Option = None; + let mut user_created: bool = false; for email in data.emails.iter() { let mut user_org_status = UserOrgStatus::Invited as i32; let user = match User::find_by_mail(email, &mut conn).await { @@ -908,13 +910,11 @@ async fn send_invite(org_id: &str, data: Json, headers: AdminHeaders } if !CONFIG.mail_enabled() { - let invitation = Invitation::new(email); - invitation.save(&mut conn).await?; + invitation = Some(Invitation::new(email)); } - let mut user = User::new(email.clone()); - user.save(&mut conn).await?; - user + user_created = true; + User::new(email.clone()) } Some(user) => { if UserOrganization::find_by_user_and_org(&user.uuid, org_id, &mut conn).await.is_some() { @@ -929,11 +929,11 @@ async fn send_invite(org_id: &str, data: Json, headers: AdminHeaders } }; - let mut new_user = UserOrganization::new(user.uuid.clone(), String::from(org_id)); + let mut new_member = UserOrganization::new(user.uuid.clone(), String::from(org_id)); let access_all = data.access_all; - new_user.access_all = access_all; - new_user.atype = new_type; - new_user.status = user_org_status; + new_member.access_all = access_all; + new_member.atype = new_type; + new_member.status = user_org_status; // If no accessAll, add the collections received if !access_all { @@ -954,16 +954,25 @@ async fn send_invite(org_id: &str, data: Json, headers: AdminHeaders } } - new_user.save(&mut conn).await?; + if CONFIG.mail_enabled() { + let org_name = match Organization::find_by_uuid(org_id, &mut conn).await { + Some(org) => org.name, + None => err!("Error looking up organization"), + }; - for group in data.groups.iter() { - let mut group_entry = GroupUser::new(String::from(group), new_user.uuid.clone()); - group_entry.save(&mut conn).await?; + mail::send_invite( + &user, + Some(String::from(org_id)), + Some(new_member.uuid.clone()), + &org_name, + Some(headers.user.email.clone()), + ) + .await?; } log_event( EventType::OrganizationUserInvited as i32, - &new_user.uuid, + &new_member.uuid, org_id, &headers.user.uuid, headers.device.atype, @@ -972,20 +981,20 @@ async fn send_invite(org_id: &str, data: Json, headers: AdminHeaders ) .await; - if CONFIG.mail_enabled() { - let org_name = match Organization::find_by_uuid(org_id, &mut conn).await { - Some(org) => org.name, - None => err!("Error looking up organization"), - }; + // Save all database objects if everything went ok + // This prevents creating a user if sending the invite mail fails for example + if let Some(ref invite) = invitation { + invite.save(&mut conn).await?; + } + if user_created { + let mut user = user; + user.save(&mut conn).await?; + } + new_member.save(&mut conn).await?; - mail::send_invite( - &user, - Some(String::from(org_id)), - Some(new_user.uuid), - &org_name, - Some(headers.user.email.clone()), - ) - .await?; + for group in data.groups.iter() { + let mut group_entry = GroupUser::new(String::from(group), new_member.uuid.clone()); + group_entry.save(&mut conn).await?; } } @@ -2074,19 +2083,6 @@ async fn import(org_id: &str, data: Json, headers: Headers, mut c new_org_user.atype = UserOrgType::User as i32; new_org_user.status = user_org_status; - new_org_user.save(&mut conn).await?; - - log_event( - EventType::OrganizationUserInvited as i32, - &new_org_user.uuid, - org_id, - &headers.user.uuid, - headers.device.atype, - &headers.ip.ip, - &mut conn, - ) - .await; - if CONFIG.mail_enabled() { let org_name = match Organization::find_by_uuid(org_id, &mut conn).await { Some(org) => org.name, @@ -2096,12 +2092,27 @@ async fn import(org_id: &str, data: Json, headers: Headers, mut c mail::send_invite( &user, Some(String::from(org_id)), - Some(new_org_user.uuid), + Some(new_org_user.uuid.clone()), &org_name, Some(headers.user.email.clone()), ) .await?; } + + // Save the user after sending an email + // If sending fails the user will not be saved to the database, and will not result in the admin needing to reinvite the users manually + new_org_user.save(&mut conn).await?; + + log_event( + EventType::OrganizationUserInvited as i32, + &new_org_user.uuid, + org_id, + &headers.user.uuid, + headers.device.atype, + &headers.ip.ip, + &mut conn, + ) + .await; } } }