user.rs (15276B)
1 use crate::config; 2 use crate::crypto; 3 use crate::util; 4 use chrono::{NaiveDateTime, TimeDelta, Utc}; 5 use diesel::result::{self, DatabaseErrorKind}; 6 use serde_json::Value; 7 8 db_object! { 9 #[derive(Identifiable, Queryable, Insertable, AsChangeset)] 10 #[diesel(table_name = users)] 11 #[diesel(treat_none_as_null = true)] 12 #[diesel(primary_key(uuid))] 13 pub struct User { 14 pub uuid: String, 15 pub enabled: bool, 16 pub created_at: NaiveDateTime, 17 pub updated_at: NaiveDateTime, 18 pub verified_at: Option<NaiveDateTime>, 19 pub last_verifying_at: Option<NaiveDateTime>, 20 login_verify_count: i32, 21 pub email: String, 22 pub email_new: Option<String>, 23 pub email_new_token: Option<String>, 24 pub name: String, 25 pub password_hash: Vec<u8>, 26 pub salt: Vec<u8>, 27 password_iterations: i32, 28 pub password_hint: Option<String>, 29 pub akey: String, 30 pub private_key: Option<String>, 31 pub public_key: Option<String>, 32 #[diesel(column_name = "totp_secret")] // Note, this is only added to the UserDb structs, not to User 33 _totp_secret: Option<String>, 34 pub totp_recover: Option<String>, 35 pub security_stamp: String, 36 pub stamp_exception: Option<String>, 37 pub equivalent_domains: String, 38 pub excluded_globals: String, 39 pub client_kdf_type: i32, 40 client_kdf_iter: i32, 41 client_kdf_memory: Option<i32>, 42 client_kdf_parallelism: Option<i32>, 43 pub api_key: Option<String>, 44 pub avatar_color: Option<String>, 45 pub external_id: Option<String>, // Todo: Needs to be removed in the future, this is not used anymore. 46 } 47 } 48 49 pub enum UserKdfType { 50 Pbkdf2 = 0, 51 Argon2id = 1, 52 } 53 impl From<UserKdfType> for i32 { 54 fn from(value: UserKdfType) -> Self { 55 match value { 56 UserKdfType::Pbkdf2 => 0i32, 57 UserKdfType::Argon2id => 1i32, 58 } 59 } 60 } 61 62 enum UserStatus { 63 Enabled = 0, 64 Invited = 1, 65 _Disabled = 2, 66 } 67 impl From<UserStatus> for i32 { 68 fn from(value: UserStatus) -> Self { 69 match value { 70 UserStatus::Enabled => 0i32, 71 UserStatus::Invited => 1i32, 72 UserStatus::_Disabled => 2i32, 73 } 74 } 75 } 76 77 #[derive(Serialize, Deserialize)] 78 pub struct UserStampException { 79 pub routes: Vec<String>, 80 pub security_stamp: String, 81 pub expire: u64, 82 } 83 84 /// Local methods 85 impl User { 86 pub fn client_kdf_type_default() -> i32 { 87 i32::from(UserKdfType::Pbkdf2) 88 } 89 pub const CLIENT_KDF_ITER_DEFAULT: u32 = 600_000u32; 90 91 pub fn new(email: &str) -> Self { 92 let now = Utc::now().naive_utc(); 93 let email = email.to_lowercase(); 94 Self { 95 uuid: util::get_uuid(), 96 enabled: true, 97 created_at: now, 98 updated_at: now, 99 verified_at: None, 100 last_verifying_at: None, 101 login_verify_count: 0, 102 name: email.clone(), 103 email, 104 akey: String::new(), 105 email_new: None, 106 email_new_token: None, 107 password_hash: Vec::new(), 108 salt: crypto::get_random_bytes::<64>().to_vec(), 109 password_iterations: i32::try_from(config::get_config().password_iterations) 110 .expect("overflow"), 111 security_stamp: util::get_uuid(), 112 stamp_exception: None, 113 password_hint: None, 114 private_key: None, 115 public_key: None, 116 _totp_secret: None, 117 totp_recover: None, 118 equivalent_domains: "[]".to_owned(), 119 excluded_globals: "[]".to_owned(), 120 client_kdf_type: Self::client_kdf_type_default(), 121 client_kdf_iter: i32::try_from(Self::CLIENT_KDF_ITER_DEFAULT).expect("overflow"), 122 client_kdf_memory: None, 123 client_kdf_parallelism: None, 124 api_key: None, 125 avatar_color: None, 126 external_id: None, // Todo: Needs to be removed in the future, this is not used anymore. 127 } 128 } 129 pub fn login_verify_count(&self) -> u32 { 130 u32::try_from(self.login_verify_count).expect("underflow") 131 } 132 pub fn set_login_verify_count(&mut self, count: u32) { 133 self.login_verify_count = i32::try_from(count).expect("overflow"); 134 } 135 pub fn password_iterations(&self) -> u32 { 136 u32::try_from(self.password_iterations).expect("underflow") 137 } 138 pub fn set_password_iterations(&mut self, iter: u32) { 139 self.password_iterations = i32::try_from(iter).expect("overflow"); 140 } 141 pub fn client_kdf_iter(&self) -> u32 { 142 u32::try_from(self.client_kdf_iter).expect("underflow") 143 } 144 pub fn set_client_kdf_iter(&mut self, iter: u32) { 145 self.client_kdf_iter = i32::try_from(iter).expect("overflow"); 146 } 147 pub fn client_kdf_memory(&self) -> Option<u32> { 148 self.client_kdf_memory 149 .map(|mem| u32::try_from(mem).expect("underflow")) 150 } 151 pub fn set_client_kdf_memory(&mut self, mem: Option<u32>) { 152 self.client_kdf_memory = mem.map(|kdf| i32::try_from(kdf).expect("overflow")); 153 } 154 pub fn client_kdf_parallelism(&self) -> Option<u32> { 155 self.client_kdf_parallelism 156 .map(|mem| u32::try_from(mem).expect("underflow")) 157 } 158 pub fn set_client_kdf_parallelism(&mut self, par: Option<u32>) { 159 self.client_kdf_parallelism = par.map(|pll| i32::try_from(pll).expect("overflow")); 160 } 161 162 pub fn check_valid_password(&self, password: &str) -> bool { 163 crypto::verify_password_hash( 164 password.as_bytes(), 165 &self.salt, 166 &self.password_hash, 167 u32::try_from(self.password_iterations) 168 .expect("underflow converting password iterations into a u32"), 169 ) 170 } 171 172 pub fn check_valid_recovery_code(&self, recovery_code: &str) -> bool { 173 self.totp_recover.as_ref().map_or(false, |totp_recover| { 174 crypto::ct_eq(recovery_code, totp_recover.to_lowercase()) 175 }) 176 } 177 178 pub fn check_valid_api_key(&self, key: &str) -> bool { 179 matches!(self.api_key, Some(ref api_key) if crypto::ct_eq(api_key, key)) 180 } 181 182 /// Set the password hash generated 183 /// And resets the security_stamp. Based upon the allow_next_route the security_stamp will be different. 184 /// 185 /// # Arguments 186 /// 187 /// * `password` - A str which contains a hashed version of the users master password. 188 /// * `new_key` - A String which contains the new aKey value of the users master password. 189 /// * `allow_next_route` - A Option<Vec<String>> with the function names of the next allowed (rocket) routes. 190 /// These routes are able to use the previous stamp id for the next 2 minutes. 191 /// After these 2 minutes this stamp will expire. 192 /// 193 pub fn set_password( 194 &mut self, 195 password: &str, 196 new_key: Option<String>, 197 reset_security_stamp: bool, 198 allow_next_route: Option<Vec<String>>, 199 ) { 200 self.password_hash = crypto::hash_password( 201 password.as_bytes(), 202 &self.salt, 203 u32::try_from(self.password_iterations) 204 .expect("underflow converting password iterations into a u32"), 205 ); 206 if let Some(route) = allow_next_route { 207 self.set_stamp_exception(route); 208 } 209 if let Some(new_key) = new_key { 210 self.akey = new_key; 211 } 212 if reset_security_stamp { 213 self.reset_security_stamp(); 214 } 215 } 216 217 pub fn reset_security_stamp(&mut self) { 218 self.security_stamp = util::get_uuid(); 219 } 220 221 /// Set the stamp_exception to only allow a subsequent request matching a specific route using the current security-stamp. 222 /// 223 /// # Arguments 224 /// * `route_exception` - A Vec<String> with the function names of the next allowed (rocket) routes. 225 /// These routes are able to use the previous stamp id for the next 2 minutes. 226 /// After these 2 minutes this stamp will expire. 227 /// 228 pub fn set_stamp_exception(&mut self, route_exception: Vec<String>) { 229 let stamp_exception = UserStampException { 230 routes: route_exception, 231 security_stamp: self.security_stamp.clone(), 232 expire: u64::try_from( 233 Utc::now() 234 .checked_add_signed( 235 TimeDelta::try_minutes(2).expect("TimeDelta::try_minutes(2) to work"), 236 ) 237 .expect("overflow") 238 .timestamp(), 239 ) 240 .expect("underflow"), 241 }; 242 self.stamp_exception = Some(serde_json::to_string(&stamp_exception).unwrap_or_default()); 243 } 244 245 /// Resets the stamp_exception to prevent re-use of the previous security-stamp 246 pub fn reset_stamp_exception(&mut self) { 247 self.stamp_exception = None; 248 } 249 } 250 251 use super::{ 252 Cipher, Device, Favorite, Folder, TwoFactorType, UserOrgType, UserOrganization, 253 WebAuthnChallenge, 254 }; 255 use crate::api::EmptyResult; 256 use crate::db::DbConn; 257 use crate::error::MapResult; 258 259 /// Database methods 260 impl User { 261 pub async fn to_json(&self, conn: &DbConn) -> Value { 262 let mut orgs_json = Vec::new(); 263 for c in UserOrganization::find_confirmed_by_user(&self.uuid, conn).await { 264 orgs_json.push(c.to_json(conn).await); 265 } 266 let twofactor_enabled = TwoFactorType::has_twofactor(&self.uuid, conn) 267 .await 268 .expect("unable to get two factor info"); 269 // TODO: Might want to save the status field in the DB 270 let status = if self.password_hash.is_empty() { 271 UserStatus::Invited 272 } else { 273 UserStatus::Enabled 274 }; 275 json!({ 276 "_status": i32::from(status), 277 "id": self.uuid, 278 "name": self.name, 279 "email": self.email, 280 "emailVerified": true, 281 "premium": true, 282 "premiumFromOrganization": false, 283 "masterPasswordHint": self.password_hint, 284 "culture": "en-US", 285 "twoFactorEnabled": twofactor_enabled, 286 "key": self.akey, 287 "privateKey": self.private_key, 288 "securityStamp": self.security_stamp, 289 "organizations": orgs_json, 290 "providers": [], 291 "providerOrganizations": [], 292 "forcePasswordReset": false, 293 "avatarColor": self.avatar_color, 294 "usesKeyConnector": false, 295 "creationDate": util::format_date(&self.created_at), 296 "object": "profile", 297 }) 298 } 299 300 pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { 301 if self.email.trim().is_empty() { 302 err!("User email can't be empty") 303 } 304 self.updated_at = Utc::now().naive_utc(); 305 db_run! {conn: 306 { 307 match diesel::replace_into(users::table) 308 .values(UserDb::to_db(self)) 309 .execute(conn) 310 { 311 Ok(_) => Ok(()), 312 // Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first. 313 Err(result::Error::DatabaseError(DatabaseErrorKind::ForeignKeyViolation, _)) => { 314 diesel::update(users::table) 315 .filter(users::uuid.eq(&self.uuid)) 316 .set(UserDb::to_db(self)) 317 .execute(conn) 318 .map_res("Error saving user") 319 } 320 Err(e) => Err(e.into()), 321 }.map_res("Error saving user") 322 } 323 } 324 } 325 326 pub async fn delete(self, conn: &DbConn) -> EmptyResult { 327 for user_org in UserOrganization::find_confirmed_by_user(&self.uuid, conn).await { 328 if user_org.atype == UserOrgType::Owner 329 && UserOrganization::count_confirmed_by_org_and_type( 330 &user_org.org_uuid, 331 UserOrgType::Owner, 332 conn, 333 ) 334 .await 335 <= 1 336 { 337 err!("Can't delete last owner") 338 } 339 } 340 UserOrganization::delete_all_by_user(&self.uuid, conn).await?; 341 Cipher::delete_all_by_user(&self.uuid, conn).await?; 342 Favorite::delete_all_by_user(&self.uuid, conn).await?; 343 Folder::delete_all_by_user(&self.uuid, conn).await?; 344 Device::delete_all_by_user(&self.uuid, conn).await?; 345 TwoFactorType::delete_all_by_user(&self.uuid, conn).await?; 346 WebAuthnChallenge::delete_all(&self.uuid, conn).await?; 347 db_run! {conn: { 348 diesel::delete(users::table.filter(users::uuid.eq(self.uuid))) 349 .execute(conn) 350 .map_res("Error deleting user") 351 }} 352 } 353 354 pub async fn update_uuid_revision(uuid: &str, conn: &DbConn) { 355 if let Err(e) = Self::_update_revision(uuid, &Utc::now().naive_utc(), conn).await { 356 warn!("Failed to update revision for {}: {:#?}", uuid, e); 357 } 358 } 359 360 pub async fn update_all_revisions(conn: &DbConn) -> EmptyResult { 361 let updated_at = Utc::now().naive_utc(); 362 db_run! {conn: { 363 util::retry(|| { 364 diesel::update(users::table) 365 .set(users::updated_at.eq(updated_at)) 366 .execute(conn) 367 }, 10) 368 .map_res("Error updating revision date for all users") 369 }} 370 } 371 372 pub async fn update_revision(&mut self, conn: &DbConn) -> EmptyResult { 373 self.updated_at = Utc::now().naive_utc(); 374 Self::_update_revision(&self.uuid, &self.updated_at, conn).await 375 } 376 377 async fn _update_revision(uuid: &str, date: &NaiveDateTime, conn: &DbConn) -> EmptyResult { 378 db_run! {conn: { 379 util::retry(|| { 380 diesel::update(users::table.filter(users::uuid.eq(uuid))) 381 .set(users::updated_at.eq(date)) 382 .execute(conn) 383 }, 10) 384 .map_res("Error updating user revision") 385 }} 386 } 387 388 pub async fn find_by_mail(mail: &str, conn: &DbConn) -> Option<Self> { 389 let lower_mail = mail.to_lowercase(); 390 db_run! {conn: { 391 users::table 392 .filter(users::email.eq(lower_mail)) 393 .first::<UserDb>(conn) 394 .ok() 395 .from_db() 396 }} 397 } 398 399 pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> { 400 db_run! {conn: { 401 users::table.filter(users::uuid.eq(uuid)).first::<UserDb>(conn).ok().from_db() 402 }} 403 } 404 405 pub async fn get_all(conn: &DbConn) -> Vec<Self> { 406 db_run! {conn: { 407 users::table.load::<UserDb>(conn).expect("Error loading users").from_db() 408 }} 409 } 410 411 pub async fn last_active(&self, conn: &DbConn) -> Option<NaiveDateTime> { 412 match Device::find_latest_active_by_user(&self.uuid, conn).await { 413 Some(device) => Some(device.updated_at), 414 None => None, 415 } 416 } 417 }