Browse Source

Support new account recovery password payload

pull/7747/head
tom27052006 1 week ago
parent
commit
3c11d8df45
  1. 76
      src/api/core/organizations.rs

76
src/api/core/organizations.rs

@ -2935,12 +2935,59 @@ struct OrganizationUserResetPasswordEnrollmentRequest {
otp: Option<String>,
}
#[derive(Deserialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
struct RecoverAccountKdfData {
#[serde(alias = "kdfType")]
kdf: i32,
#[serde(alias = "iterations")]
kdf_iterations: i32,
#[serde(alias = "memory")]
kdf_memory: Option<i32>,
#[serde(alias = "parallelism")]
kdf_parallelism: Option<i32>,
}
impl RecoverAccountKdfData {
fn matches_user(&self, user: &User) -> bool {
self.kdf == user.client_kdf_type
&& self.kdf_iterations == user.client_kdf_iter
&& self.kdf_memory == user.client_kdf_memory
&& self.kdf_parallelism == user.client_kdf_parallelism
}
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct RecoverAccountAuthenticationData {
salt: String,
kdf: RecoverAccountKdfData,
master_password_authentication_hash: String,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct RecoverAccountUnlockData {
salt: String,
kdf: RecoverAccountKdfData,
master_key_wrapped_user_key: String,
}
fn master_password_salt(user: &User) -> String {
user.email.trim().to_lowercase()
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct OrganizationUserRecoverAccountRequest {
// Legacy payload
new_master_password_hash: Option<String>,
key: Option<String>,
// Current payload
authentication_data: Option<RecoverAccountAuthenticationData>,
unlock_data: Option<RecoverAccountUnlockData>,
#[serde(default)]
reset_master_password: bool,
#[serde(default)]
@ -3053,13 +3100,33 @@ async fn recover_account(
}
if req.reset_master_password {
if let Some(key) = req.key
&& let Some(hash) = req.new_master_password_hash
let (new_master_password_hash, new_key) = if let (Some(authentication_data), Some(unlock_data)) =
(req.authentication_data, req.unlock_data)
{
user.set_password(hash.as_str(), Some(key), true, None, &conn).await?;
if authentication_data.kdf != unlock_data.kdf {
err!("KDF settings must be equal for authentication and unlock")
}
if authentication_data.salt != unlock_data.salt {
err!("Invalid master password salt")
}
if !authentication_data.kdf.matches_user(&user) {
err!("KDF settings do not match the user account")
}
if authentication_data.salt != master_password_salt(&user) {
err!("Invalid master password salt")
}
(authentication_data.master_password_authentication_hash, unlock_data.master_key_wrapped_user_key)
} else if let (Some(new_master_password_hash), Some(new_key)) = (req.new_master_password_hash, req.key) {
(new_master_password_hash, new_key)
} else {
err_code!("Unprocessable request", "Missing fields to reset password", Status::UnprocessableEntity.code);
}
};
user.set_password(&new_master_password_hash, Some(new_key), true, None, &conn).await?;
}
if req.reset_two_factor {
@ -3117,6 +3184,7 @@ async fn get_reset_password_details(
"kdfIterations": user.client_kdf_iter,
"kdfMemory": user.client_kdf_memory,
"kdfParallelism": user.client_kdf_parallelism,
"masterPasswordSalt": master_password_salt(&user),
"resetPasswordKey": member.reset_password_key,
"encryptedPrivateKey": org.private_key,
})))

Loading…
Cancel
Save