use actix::Addr; use actix_web::{HttpResponse, Responder, web}; use uuid::Uuid; use webauthn_rs::prelude::RegisterPublicKeyCredential; use crate::actors::users_actor; use crate::actors::users_actor::UsersActor; use crate::constants::MAX_SECOND_FACTOR_NAME_LEN; use crate::data::action_logger::{Action, ActionLogger}; use crate::data::critical_route::CriticalRoute; use crate::data::current_user::CurrentUser; use crate::data::totp_key::TotpKey; use crate::data::user::{FactorID, TwoFactor, TwoFactorType}; use crate::data::webauthn_manager::WebAuthManagerReq; fn preprocess_factor_name(name: &str) -> String { name.replace('<', "<") .replace('>', ">") .chars() .take(MAX_SECOND_FACTOR_NAME_LEN) .filter(|c| *c != '\n' && *c != '\t' && *c != '\r' && c.is_ascii()) .collect() } #[derive(serde::Deserialize)] pub struct AddTOTPRequest { factor_name: String, secret: String, first_code: String, } pub async fn save_totp_factor( _critical: CriticalRoute, user: CurrentUser, form: web::Json, users: web::Data>, logger: ActionLogger, ) -> impl Responder { let key = TotpKey::from_encoded_secret(&form.secret); if !key.check_code(&form.first_code).unwrap_or(false) { return HttpResponse::BadRequest().body(format!( "Given code is invalid (expected {}, {} or {})!", key.previous_code().unwrap_or_default(), key.current_code().unwrap_or_default(), key.following_code().unwrap_or_default(), )); } let factor_name = preprocess_factor_name(&form.factor_name); if factor_name.is_empty() { return HttpResponse::BadRequest().body("Please give a valid name to the factor!"); } let factor = TwoFactor { id: FactorID(Uuid::new_v4().to_string()), name: factor_name, kind: TwoFactorType::TOTP(key), }; logger.log(Action::AddNewFactor(&factor)); let res = users .send(users_actor::Add2FAFactor(user.uid.clone(), factor)) .await .unwrap(); if !res { HttpResponse::InternalServerError().body("Failed to update user information!") } else { HttpResponse::Ok().body("Added new factor!") } } #[derive(serde::Deserialize)] pub struct AddWebauthnRequest { opaque_state: String, factor_name: String, credential: RegisterPublicKeyCredential, } pub async fn save_webauthn_factor( _critical: CriticalRoute, user: CurrentUser, form: web::Json, users: web::Data>, manager: WebAuthManagerReq, logger: ActionLogger, ) -> impl Responder { let factor_name = preprocess_factor_name(&form.factor_name); if factor_name.is_empty() { return HttpResponse::BadRequest().body("Please give a valid name to the factor!"); } let key = match manager.finish_registration(&user, &form.0.opaque_state, form.0.credential) { Ok(k) => k, Err(e) => { log::error!("Failed to register security key! {:?}", e); return HttpResponse::InternalServerError().body("Failed to register key!"); } }; let factor = TwoFactor { id: FactorID(Uuid::new_v4().to_string()), name: factor_name, kind: TwoFactorType::WEBAUTHN(Box::new(key)), }; logger.log(Action::AddNewFactor(&factor)); let res = users .send(users_actor::Add2FAFactor(user.uid.clone(), factor)) .await .unwrap(); if !res { HttpResponse::InternalServerError().body("Failed to update user information!") } else { HttpResponse::Ok().body("Added new factor!") } } #[derive(serde::Deserialize)] pub struct DeleteFactorRequest { id: FactorID, } pub async fn delete_factor( _critical: CriticalRoute, user: CurrentUser, form: web::Json, users: web::Data>, logger: ActionLogger, ) -> impl Responder { let res = users .send(users_actor::Remove2FAFactor( user.uid.clone(), form.id.clone(), )) .await .unwrap(); if !res { HttpResponse::InternalServerError().body("Failed to update user information!") } else { logger.log(Action::Removed2FAFactor { factor_id: &form.0.id, }); HttpResponse::Ok().body("Removed factor!") } } pub async fn clear_login_history( _critical: CriticalRoute, user: CurrentUser, users: web::Data>, logger: ActionLogger, ) -> impl Responder { users .send(users_actor::Clear2FALoginHistory(user.uid.clone())) .await .unwrap(); logger.log(Action::ClearedHisLoginHistory); HttpResponse::Ok().body("History successfully cleared") }