user.rs (15182B)
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::{Cipher, Device, Favorite, Folder, TwoFactorType, UserOrgType, UserOrganization}; 252 use crate::api::EmptyResult; 253 use crate::db::DbConn; 254 use crate::error::MapResult; 255 256 /// Database methods 257 impl User { 258 pub async fn to_json(&self, conn: &DbConn) -> Value { 259 let mut orgs_json = Vec::new(); 260 for c in UserOrganization::find_confirmed_by_user(&self.uuid, conn).await { 261 orgs_json.push(c.to_json(conn).await); 262 } 263 let twofactor_enabled = TwoFactorType::has_twofactor(&self.uuid, conn) 264 .await 265 .expect("unable to get two factor info"); 266 // TODO: Might want to save the status field in the DB 267 let status = if self.password_hash.is_empty() { 268 UserStatus::Invited 269 } else { 270 UserStatus::Enabled 271 }; 272 json!({ 273 "_status": i32::from(status), 274 "id": self.uuid, 275 "name": self.name, 276 "email": self.email, 277 "emailVerified": true, 278 "premium": true, 279 "premiumFromOrganization": false, 280 "masterPasswordHint": self.password_hint, 281 "culture": "en-US", 282 "twoFactorEnabled": twofactor_enabled, 283 "key": self.akey, 284 "privateKey": self.private_key, 285 "securityStamp": self.security_stamp, 286 "organizations": orgs_json, 287 "providers": [], 288 "providerOrganizations": [], 289 "forcePasswordReset": false, 290 "avatarColor": self.avatar_color, 291 "usesKeyConnector": false, 292 "creationDate": util::format_date(&self.created_at), 293 "object": "profile", 294 }) 295 } 296 297 pub async fn save(&mut self, conn: &DbConn) -> EmptyResult { 298 if self.email.trim().is_empty() { 299 err!("User email can't be empty") 300 } 301 self.updated_at = Utc::now().naive_utc(); 302 db_run! {conn: 303 { 304 match diesel::replace_into(users::table) 305 .values(UserDb::to_db(self)) 306 .execute(conn) 307 { 308 Ok(_) => Ok(()), 309 // Record already exists and causes a Foreign Key Violation because replace_into() wants to delete the record first. 310 Err(result::Error::DatabaseError(DatabaseErrorKind::ForeignKeyViolation, _)) => { 311 diesel::update(users::table) 312 .filter(users::uuid.eq(&self.uuid)) 313 .set(UserDb::to_db(self)) 314 .execute(conn) 315 .map_res("Error saving user") 316 } 317 Err(e) => Err(e.into()), 318 }.map_res("Error saving user") 319 } 320 } 321 } 322 323 pub async fn delete(self, conn: &DbConn) -> EmptyResult { 324 for user_org in UserOrganization::find_confirmed_by_user(&self.uuid, conn).await { 325 if user_org.atype == UserOrgType::Owner 326 && UserOrganization::count_confirmed_by_org_and_type( 327 &user_org.org_uuid, 328 UserOrgType::Owner, 329 conn, 330 ) 331 .await 332 <= 1 333 { 334 err!("Can't delete last owner") 335 } 336 } 337 UserOrganization::delete_all_by_user(&self.uuid, conn).await?; 338 Cipher::delete_all_by_user(&self.uuid, conn).await?; 339 Favorite::delete_all_by_user(&self.uuid, conn).await?; 340 Folder::delete_all_by_user(&self.uuid, conn).await?; 341 Device::delete_all_by_user(&self.uuid, conn).await?; 342 TwoFactorType::delete_all_by_user(&self.uuid, conn).await?; 343 db_run! {conn: { 344 diesel::delete(users::table.filter(users::uuid.eq(self.uuid))) 345 .execute(conn) 346 .map_res("Error deleting user") 347 }} 348 } 349 350 pub async fn update_uuid_revision(uuid: &str, conn: &DbConn) { 351 if let Err(e) = Self::_update_revision(uuid, &Utc::now().naive_utc(), conn).await { 352 warn!("Failed to update revision for {}: {:#?}", uuid, e); 353 } 354 } 355 356 pub async fn update_all_revisions(conn: &DbConn) -> EmptyResult { 357 let updated_at = Utc::now().naive_utc(); 358 db_run! {conn: { 359 util::retry(|| { 360 diesel::update(users::table) 361 .set(users::updated_at.eq(updated_at)) 362 .execute(conn) 363 }, 10) 364 .map_res("Error updating revision date for all users") 365 }} 366 } 367 368 pub async fn update_revision(&mut self, conn: &DbConn) -> EmptyResult { 369 self.updated_at = Utc::now().naive_utc(); 370 Self::_update_revision(&self.uuid, &self.updated_at, conn).await 371 } 372 373 async fn _update_revision(uuid: &str, date: &NaiveDateTime, conn: &DbConn) -> EmptyResult { 374 db_run! {conn: { 375 util::retry(|| { 376 diesel::update(users::table.filter(users::uuid.eq(uuid))) 377 .set(users::updated_at.eq(date)) 378 .execute(conn) 379 }, 10) 380 .map_res("Error updating user revision") 381 }} 382 } 383 384 pub async fn find_by_mail(mail: &str, conn: &DbConn) -> Option<Self> { 385 let lower_mail = mail.to_lowercase(); 386 db_run! {conn: { 387 users::table 388 .filter(users::email.eq(lower_mail)) 389 .first::<UserDb>(conn) 390 .ok() 391 .from_db() 392 }} 393 } 394 395 pub async fn find_by_uuid(uuid: &str, conn: &DbConn) -> Option<Self> { 396 db_run! {conn: { 397 users::table.filter(users::uuid.eq(uuid)).first::<UserDb>(conn).ok().from_db() 398 }} 399 } 400 401 pub async fn get_all(conn: &DbConn) -> Vec<Self> { 402 db_run! {conn: { 403 users::table.load::<UserDb>(conn).expect("Error loading users").from_db() 404 }} 405 } 406 407 pub async fn last_active(&self, conn: &DbConn) -> Option<NaiveDateTime> { 408 match Device::find_latest_active_by_user(&self.uuid, conn).await { 409 Some(device) => Some(device.updated_at), 410 None => None, 411 } 412 } 413 }