Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent new users/members to be stored in db when invite fails #5350

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 73 additions & 58 deletions src/api/core/organizations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,7 @@ async fn send_invite(org_id: &str, data: Json<InviteData>, headers: AdminHeaders
data.access_all = true;
}

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 {
Expand All @@ -908,13 +909,13 @@ async fn send_invite(org_id: &str, data: Json<InviteData>, headers: AdminHeaders
}

if !CONFIG.mail_enabled() {
let invitation = Invitation::new(email);
invitation.save(&mut conn).await?;
Invitation::new(email).save(&mut conn).await?;
}

let mut user = User::new(email.clone());
user.save(&mut conn).await?;
user
let mut new_user = User::new(email.clone());
new_user.save(&mut conn).await?;
user_created = true;
new_user
}
Some(user) => {
if UserOrganization::find_by_user_and_org(&user.uuid, org_id, &mut conn).await.is_some() {
Expand All @@ -929,11 +930,49 @@ async fn send_invite(org_id: &str, data: Json<InviteData>, 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;
new_member.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"),
};

if let Err(e) = mail::send_invite(
&user,
Some(String::from(org_id)),
Some(new_member.uuid.clone()),
&org_name,
Some(headers.user.email.clone()),
)
.await
{
// Upon error delete the user, invite and org member records when needed
if user_created {
user.delete(&mut conn).await?;
} else {
new_member.delete(&mut conn).await?;
}

err!(format!("Error sending invite: {e:?} "));
};
}

log_event(
EventType::OrganizationUserInvited as i32,
&new_member.uuid.clone(),
org_id,
&headers.user.uuid,
headers.device.atype,
&headers.ip.ip,
&mut conn,
)
.await;

// If no accessAll, add the collections received
if !access_all {
Expand All @@ -954,39 +993,10 @@ async fn send_invite(org_id: &str, data: Json<InviteData>, headers: AdminHeaders
}
}

new_user.save(&mut conn).await?;

for group in data.groups.iter() {
let mut group_entry = GroupUser::new(String::from(group), new_user.uuid.clone());
let mut group_entry = GroupUser::new(String::from(group), new_member.uuid.clone());
group_entry.save(&mut conn).await?;
}

log_event(
EventType::OrganizationUserInvited as i32,
&new_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,
None => err!("Error looking up organization"),
};

mail::send_invite(
&user,
Some(String::from(org_id)),
Some(new_user.uuid),
&org_name,
Some(headers.user.email.clone()),
)
.await?;
}
}

Ok(())
Expand Down Expand Up @@ -1064,7 +1074,7 @@ async fn _reinvite_user(org_id: &str, user_org: &str, invited_by_email: &str, co
let invitation = Invitation::new(&user.email);
invitation.save(conn).await?;
} else {
let _ = Invitation::take(&user.email, conn).await;
Invitation::take(&user.email, conn).await;
let mut user_org = user_org;
user_org.status = UserOrgStatus::Accepted as i32;
user_org.save(conn).await?;
Expand Down Expand Up @@ -2026,6 +2036,9 @@ struct OrgImportData {
users: Vec<OrgImportUserData>,
}

/// This function seems to be deprected
/// It is only used with older directory connectors
/// TODO: Cleanup Tech debt
#[post("/organizations/<org_id>/import", data = "<data>")]
async fn import(org_id: &str, data: Json<OrgImportData>, headers: Headers, mut conn: DbConn) -> EmptyResult {
let data = data.into_inner();
Expand Down Expand Up @@ -2069,23 +2082,10 @@ async fn import(org_id: &str, data: Json<OrgImportData>, headers: Headers, mut c
UserOrgStatus::Accepted as i32 // Automatically mark user as accepted if no email invites
};

let mut new_org_user = UserOrganization::new(user.uuid.clone(), String::from(org_id));
new_org_user.access_all = false;
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;
let mut new_member = UserOrganization::new(user.uuid.clone(), String::from(org_id));
new_member.access_all = false;
new_member.atype = UserOrgType::User as i32;
new_member.status = user_org_status;

if CONFIG.mail_enabled() {
let org_name = match Organization::find_by_uuid(org_id, &mut conn).await {
Expand All @@ -2096,12 +2096,27 @@ async fn import(org_id: &str, data: Json<OrgImportData>, headers: Headers, mut c
mail::send_invite(
&user,
Some(String::from(org_id)),
Some(new_org_user.uuid),
Some(new_member.uuid.clone()),
&org_name,
Some(headers.user.email.clone()),
)
.await?;
}

// Save the member after sending an email
// If sending fails the member will not be saved to the database, and will not result in the admin needing to reinvite the users manually
new_member.save(&mut conn).await?;

log_event(
EventType::OrganizationUserInvited as i32,
&new_member.uuid,
org_id,
&headers.user.uuid,
headers.device.atype,
&headers.ip.ip,
&mut conn,
)
.await;
}
}
}
Expand Down
37 changes: 27 additions & 10 deletions src/api/core/public.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ async fn ldap_import(data: Json<OrgImportData>, token: PublicToken, mut conn: Db
let data = data.into_inner();

for user_data in &data.members {
let mut user_created: bool = false;
if user_data.deleted {
// If user is marked for deletion and it exists, revoke it
if let Some(mut user_org) =
Expand Down Expand Up @@ -97,9 +98,9 @@ async fn ldap_import(data: Json<OrgImportData>, token: PublicToken, mut conn: Db
new_user.save(&mut conn).await?;

if !CONFIG.mail_enabled() {
let invitation = Invitation::new(&new_user.email);
invitation.save(&mut conn).await?;
Invitation::new(&new_user.email).save(&mut conn).await?;
}
user_created = true;
new_user
}
};
Expand All @@ -109,22 +110,38 @@ async fn ldap_import(data: Json<OrgImportData>, token: PublicToken, mut conn: Db
UserOrgStatus::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());
new_org_user.set_external_id(Some(user_data.external_id.clone()));
new_org_user.access_all = false;
new_org_user.atype = UserOrgType::User as i32;
new_org_user.status = user_org_status;
let mut new_member = UserOrganization::new(user.uuid.clone(), org_id.clone());
new_member.set_external_id(Some(user_data.external_id.clone()));
new_member.access_all = false;
new_member.atype = UserOrgType::User as i32;
new_member.status = user_org_status;

new_org_user.save(&mut conn).await?;
new_member.save(&mut conn).await?;

if CONFIG.mail_enabled() {
let (org_name, org_email) = match Organization::find_by_uuid(&org_id, &mut conn).await {
Some(org) => (org.name, org.billing_email),
None => err!("Error looking up organization"),
};

mail::send_invite(&user, Some(org_id.clone()), Some(new_org_user.uuid), &org_name, Some(org_email))
.await?;
if let Err(e) = mail::send_invite(
&user,
Some(org_id.clone()),
Some(new_member.uuid.clone()),
&org_name,
Some(org_email),
)
.await
{
// Upon error delete the user, invite and org member records when needed
if user_created {
user.delete(&mut conn).await?;
} else {
new_member.delete(&mut conn).await?;
}

err!(format!("Error sending invite: {e:?} "));
}
}
}
}
Expand Down