165 lines
4.7 KiB
Rust
165 lines
4.7 KiB
Rust
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<AddTOTPRequest>,
|
|
users: web::Data<Addr<UsersActor>>,
|
|
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<AddWebauthnRequest>,
|
|
users: web::Data<Addr<UsersActor>>,
|
|
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<DeleteFactorRequest>,
|
|
users: web::Data<Addr<UsersActor>>,
|
|
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<Addr<UsersActor>>,
|
|
logger: ActionLogger,
|
|
) -> impl Responder {
|
|
users
|
|
.send(users_actor::Clear2FALoginHistory(user.uid.clone()))
|
|
.await
|
|
.unwrap();
|
|
|
|
logger.log(Action::ClearedHisLoginHistory);
|
|
HttpResponse::Ok().body("History successfully cleared")
|
|
}
|