Browse Source

Merge 00cc9f79b1 into d6a3d539ed

pull/7295/merge
Timshel 5 days ago
committed by GitHub
parent
commit
7fbedb8b31
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 113
      src/api/core/accounts.rs

113
src/api/core/accounts.rs

@ -97,14 +97,11 @@ pub struct RegisterData {
email: String, email: String,
#[serde(flatten)] #[serde(flatten)]
kdf: KDFData, compat: RegisterDataCompat,
#[serde(alias = "userSymmetricKey")]
key: String,
#[serde(alias = "userAsymmetricKeys")] #[serde(alias = "userAsymmetricKeys")]
keys: Option<KeysData>, keys: Option<KeysData>,
master_password_hash: String,
master_password_hint: Option<String>, master_password_hint: Option<String>,
name: Option<String>, name: Option<String>,
@ -119,17 +116,70 @@ pub struct RegisterData {
org_invite_token: Option<String>, org_invite_token: Option<String>,
} }
impl RegisterData {
fn hash(&self) -> String {
self.compat.fold(|rdc| &rdc.master_password_hash, |rdcu| &rdcu.master_password_authentication.hash).to_owned()
}
fn kdf(&self) -> &KDFData {
self.compat.fold(|rdc| &rdc.kdf, |rdcu| &rdcu.master_password_authentication.kdf)
}
fn key(&self) -> String {
self.compat.fold(|rdc| &rdc.key, |rdcu| &rdcu.master_password_unlock.key).to_owned()
}
fn unprocessable(&self) -> bool {
let mut unprocessable = false;
*self.compat.fold(
|_| &false,
|rdcu| {
unprocessable = rdcu.master_password_authentication.kdf != rdcu.master_password_unlock.kdf
|| rdcu.master_password_authentication.salt != self.email
|| rdcu.master_password_unlock.salt != self.email;
&unprocessable
},
)
}
}
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")] struct RegisterDataOld {
pub struct SetPasswordData {
#[serde(flatten)] #[serde(flatten)]
kdf: KDFData, kdf: KDFData,
#[serde(alias = "userSymmetricKey")]
key: String, key: String,
keys: Option<KeysData>,
#[serde(alias = "masterPasswordHash")]
master_password_hash: String, master_password_hash: String,
master_password_hint: Option<String>, }
org_identifier: Option<String>,
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct RegisterDataCur {
master_password_authentication: MasterPasswordAuthentication,
master_password_unlock: MasterPasswordUnlock,
}
#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum RegisterDataCompat {
RegisterDataOld(RegisterDataOld),
RegisterDataCur(RegisterDataCur),
}
impl RegisterDataCompat {
fn fold<'a, T>(
&'a self,
fct: impl FnOnce(&'a RegisterDataOld) -> &'a T,
fcu: impl FnOnce(&'a RegisterDataCur) -> &'a T,
) -> &'a T {
match self {
RegisterDataCompat::RegisterDataOld(rdc) => fct(rdc),
RegisterDataCompat::RegisterDataCur(rdcu) => fcu(rdcu),
}
}
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -139,6 +189,39 @@ struct KeysData {
public_key: String, public_key: String,
} }
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MasterPasswordAuthentication {
kdf: KDFData,
salt: String,
#[serde(alias = "masterPasswordAuthenticationHash")]
hash: String,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MasterPasswordUnlock {
kdf: KDFData,
salt: String,
#[serde(alias = "masterKeyWrappedUserKey")]
key: String,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SetPasswordData {
#[serde(flatten)]
kdf: KDFData,
key: String,
keys: Option<KeysData>,
master_password_hash: String,
master_password_hint: Option<String>,
org_identifier: Option<String>,
}
/// Trims whitespace from password hints, and converts blank password hints to `None`. /// Trims whitespace from password hints, and converts blank password hints to `None`.
fn clean_password_hint(password_hint: Option<&String>) -> Option<String> { fn clean_password_hint(password_hint: Option<&String>) -> Option<String> {
match password_hint { match password_hint {
@ -177,6 +260,10 @@ pub async fn register(data: Json<RegisterData>, email_verification: bool, conn:
let mut pending_emergency_access = None; let mut pending_emergency_access = None;
if data.unprocessable() {
err_code!("Unexpected RegisterData format", Status::UnprocessableEntity.code);
}
// First, validate the provided verification tokens // First, validate the provided verification tokens
if email_verification { if email_verification {
match ( match (
@ -257,8 +344,8 @@ pub async fn register(data: Json<RegisterData>, email_verification: bool, conn:
err!("Registration not allowed or user already exists") err!("Registration not allowed or user already exists")
} }
if let Some(token) = data.org_invite_token { if let Some(token) = data.org_invite_token.as_ref() {
let claims = decode_invite(&token)?; let claims = decode_invite(token)?;
if claims.email == email { if claims.email == email {
// Verify the email address when signing up via a valid invite token // Verify the email address when signing up via a valid invite token
email_verified = true; email_verified = true;
@ -296,9 +383,9 @@ pub async fn register(data: Json<RegisterData>, email_verification: bool, conn:
// Make sure we don't leave a lingering invitation. // Make sure we don't leave a lingering invitation.
Invitation::take(&email, &conn).await; Invitation::take(&email, &conn).await;
set_kdf_data(&mut user, &data.kdf)?; set_kdf_data(&mut user, data.kdf())?;
user.set_password(&data.master_password_hash, Some(data.key), true, None, &conn).await?; user.set_password(&data.hash(), Some(data.key()), true, None, &conn).await?;
user.password_hint = password_hint; user.password_hint = password_hint;
// Add extra fields if present // Add extra fields if present

Loading…
Cancel
Save