From 4309df833406022ce410acb91092a3946bfc9ed0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= Date: Tue, 8 Jan 2019 15:11:16 +0100 Subject: [PATCH] Only create invitations when SMTP is disabled, and ignore invitations if we have a token. Disallow users from accepting invitation twice --- src/api/admin.rs | 8 ++-- src/api/core/accounts.rs | 42 +++++++++--------- src/api/core/organizations.rs | 81 +++++++++++++++++++---------------- 3 files changed, 67 insertions(+), 64 deletions(-) diff --git a/src/api/admin.rs b/src/api/admin.rs index b5e3ef8d..2d68d565 100644 --- a/src/api/admin.rs +++ b/src/api/admin.rs @@ -4,9 +4,9 @@ use serde_json::Value; use crate::api::{JsonResult, JsonUpcase}; use crate::CONFIG; -use crate::mail; use crate::db::models::*; use crate::db::DbConn; +use crate::mail; use rocket::request::{self, FromRequest, Request}; use rocket::{Outcome, Route}; @@ -41,14 +41,14 @@ fn invite_user(data: JsonUpcase, _token: AdminToken, conn: DbConn) - err!("Invitations are not allowed") } - let mut invitation = Invitation::new(data.Email); - invitation.save(&conn)?; - if let Some(ref mail_config) = CONFIG.mail { let mut user = User::new(email); user.save(&conn)?; let org_name = "bitwarden_rs"; mail::send_invite(&user.email, &user.uuid, None, None, &org_name, None, mail_config)?; + } else { + let mut invitation = Invitation::new(data.Email); + invitation.save(&conn)?; } Ok(Json(json!({}))) diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index 9db46a0b..ae76734f 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -61,29 +61,24 @@ fn register(data: JsonUpcase, conn: DbConn) -> EmptyResult { let mut user = match User::find_by_mail(&data.Email, &conn) { Some(user) => { - if Invitation::find_by_mail(&data.Email, &conn).is_some() { - if CONFIG.mail.is_none() { - for mut user_org in UserOrganization::find_invited_by_user(&user.uuid, &conn).iter_mut() { - user_org.status = UserOrgStatus::Accepted as i32; - user_org.save(&conn)?; - } - if !Invitation::take(&data.Email, &conn) { - err!("Error accepting invitation") - } + if !user.password_hash.is_empty() { + err!("User already exists") + } + + if let Some(token) = data.Token { + let claims: InviteJWTClaims = decode_invite_jwt(&token)?; + if claims.email == data.Email { user } else { - let token = match &data.Token { - Some(token) => token, - None => err!("No valid invite token"), - }; - - let claims: InviteJWTClaims = decode_invite_jwt(&token)?; - if claims.email == data.Email { - user - } else { - err!("Registration email does not match invite email") - } + err!("Registration email does not match invite email") } + } else if Invitation::take(&data.Email, &conn) { + for mut user_org in UserOrganization::find_invited_by_user(&user.uuid, &conn).iter_mut() { + user_org.status = UserOrgStatus::Accepted as i32; + user_org.save(&conn)?; + } + + user } else if CONFIG.signups_allowed { err!("Account with this email already exists") } else { @@ -91,14 +86,17 @@ fn register(data: JsonUpcase, conn: DbConn) -> EmptyResult { } } None => { - if CONFIG.signups_allowed || (CONFIG.mail.is_none() && Invitation::take(&data.Email, &conn)) { - User::new(data.Email) + if CONFIG.signups_allowed || Invitation::take(&data.Email, &conn) { + User::new(data.Email.clone()) } else { err!("Registration not allowed") } } }; + // Make sure we don't leave a lingering invitation. + Invitation::take(&data.Email, &conn); + if let Some(client_kdf_iter) = data.KdfIterations { user.client_kdf_iter = client_kdf_iter; } diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs index 38bb6021..6e278907 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -460,17 +460,19 @@ fn send_invite(org_id: String, data: JsonUpcase, headers: AdminHeade }; let user = match User::find_by_mail(&email, &conn) { None => { - if CONFIG.invitations_allowed { - // Invite user if that's enabled - let mut invitation = Invitation::new(email.clone()); - invitation.save(&conn)?; - let mut user = User::new(email.clone()); - user.save(&conn)?; - user_org_status = UserOrgStatus::Invited as i32; - user - } else { + if !CONFIG.invitations_allowed { err!(format!("User email does not exist: {}", email)) } + + if CONFIG.mail.is_none() { + let mut invitation = Invitation::new(email.clone()); + invitation.save(&conn)?; + } + + let mut user = User::new(email.clone()); + user.save(&conn)?; + user_org_status = UserOrgStatus::Invited as i32; + user } Some(user) => { if UserOrganization::find_by_user_and_org(&user.uuid, &org_id, &conn).is_some() { @@ -506,16 +508,16 @@ fn send_invite(org_id: String, data: JsonUpcase, headers: AdminHeade Some(org) => org.name, None => err!("Error looking up organization"), }; - + mail::send_invite( - &email, - &user.uuid, - Some(org_id.clone()), - Some(new_user.uuid), - &org_name, - Some(headers.user.email.clone()), - mail_config - )?; + &email, + &user.uuid, + Some(org_id.clone()), + Some(new_user.uuid), + &org_name, + Some(headers.user.email.clone()), + mail_config, + )?; } } @@ -546,9 +548,6 @@ fn reinvite_user(org_id: String, user_org: String, headers: AdminHeaders, conn: None => err!("User not found."), }; - let mut invitation = Invitation::new(user.email.clone()); - invitation.save(&conn)?; - let org_name = match Organization::find_by_uuid(&org_id, &conn) { Some(org) => org.name, None => err!("Error looking up organization."), @@ -556,14 +555,17 @@ fn reinvite_user(org_id: String, user_org: String, headers: AdminHeaders, conn: if let Some(ref mail_config) = CONFIG.mail { mail::send_invite( - &user.email, - &user.uuid, - Some(org_id), - Some(user_org.uuid), - &org_name, - Some(headers.user.email), - mail_config, + &user.email, + &user.uuid, + Some(org_id), + Some(user_org.uuid), + &org_name, + Some(headers.user.email), + mail_config, )?; + } else { + let mut invitation = Invitation::new(user.email.clone()); + invitation.save(&conn)?; } Ok(()) @@ -585,16 +587,19 @@ fn accept_invite(_org_id: String, _org_user_id: String, data: JsonUpcase { Invitation::take(&claims.email, &conn); - if claims.user_org_id.is_some() && claims.org_id.is_some() { - let mut user_org = - match UserOrganization::find_by_uuid_and_org(&claims.user_org_id.unwrap(), &claims.org_id.clone().unwrap(), &conn) { - Some(user_org) => user_org, - None => err!("Error accepting the invitation"), - }; - user_org.status = UserOrgStatus::Accepted as i32; - if user_org.save(&conn).is_err() { - err!("Failed to accept user to organization") + + if let (Some(user_org), Some(org)) = (&claims.user_org_id, &claims.org_id) { + let mut user_org = match UserOrganization::find_by_uuid_and_org(user_org, org, &conn) { + Some(user_org) => user_org, + None => err!("Error accepting the invitation"), + }; + + if user_org.status != UserOrgStatus::Invited as i32 { + err!("User already accepted the invitation") } + + user_org.status = UserOrgStatus::Accepted as i32; + user_org.save(&conn)?; } } None => err!("Invited user not found"), @@ -605,7 +610,7 @@ fn accept_invite(_org_id: String, _org_user_id: String, data: JsonUpcase org.name, - None => err!("Organization not found.") + None => err!("Organization not found."), }; }; if let Some(invited_by_email) = &claims.invited_by_email {