diff --git a/src/controllers/mod.rs b/src/controllers/mod.rs index 8efd15e..a94576d 100644 --- a/src/controllers/mod.rs +++ b/src/controllers/mod.rs @@ -6,4 +6,4 @@ pub mod admin_controller; pub mod admin_api; pub mod openid_controller; pub mod two_factors_controller; -pub mod two_factors_api; \ No newline at end of file +pub mod two_factor_api; \ No newline at end of file diff --git a/src/controllers/two_factors_api.rs b/src/controllers/two_factor_api.rs similarity index 58% rename from src/controllers/two_factors_api.rs rename to src/controllers/two_factor_api.rs index 17376cb..158634b 100644 --- a/src/controllers/two_factors_api.rs +++ b/src/controllers/two_factor_api.rs @@ -1,20 +1,21 @@ use actix::Addr; use actix_web::{HttpResponse, Responder, web}; +use uuid::Uuid; use crate::actors::users_actor; use crate::actors::users_actor::UsersActor; use crate::data::current_user::CurrentUser; use crate::data::totp_key::TotpKey; -use crate::data::user::{SecondFactor, User}; +use crate::data::user::{FactorID, SecondFactor, SecondFactorType, User}; #[derive(serde::Deserialize)] -pub struct Request { +pub struct AddTOTPRequest { factor_name: String, secret: String, first_code: String, } -pub async fn save_totp_factor(user: CurrentUser, form: web::Json, +pub async fn save_totp_factor(user: CurrentUser, form: web::Json, users: web::Data>) -> impl Responder { let key = TotpKey::from_encoded_secret(&form.secret); @@ -30,7 +31,11 @@ pub async fn save_totp_factor(user: CurrentUser, form: web::Json, } let mut user = User::from(user); - user.add_factor(SecondFactor::TOTP(key)); + user.add_factor(SecondFactor { + id: FactorID(Uuid::new_v4().to_string()), + name: form.0.factor_name, + kind: SecondFactorType::TOTP(key), + }); let res = users.send(users_actor::UpdateUserRequest(user)).await.unwrap().0; if !res { @@ -38,4 +43,23 @@ pub async fn save_totp_factor(user: CurrentUser, form: web::Json, } else { HttpResponse::Ok().body("Added new factor!") } +} + +#[derive(serde::Deserialize)] +pub struct DeleteFactorRequest { + id: FactorID, +} + +pub async fn delete_factor(user: CurrentUser, form: web::Json, + users: web::Data>) -> impl Responder { + let mut user = User::from(user); + user.remove_factor(form.0.id); + + let res = users.send(users_actor::UpdateUserRequest(user)).await.unwrap().0; + + if !res { + HttpResponse::InternalServerError().body("Failed to update user information!") + } else { + HttpResponse::Ok().body("Removed factor!") + } } \ No newline at end of file diff --git a/src/data/user.rs b/src/data/user.rs index eed6534..a0f0edf 100644 --- a/src/data/user.rs +++ b/src/data/user.rs @@ -5,11 +5,29 @@ use crate::utils::err::Res; pub type UserID = String; +#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct FactorID(pub String); + #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] -pub enum SecondFactor { +pub enum SecondFactorType { TOTP(TotpKey) } +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +pub struct SecondFactor { + pub id: FactorID, + pub name: String, + pub kind: SecondFactorType, +} + +impl SecondFactor { + pub fn type_str(&self) -> &'static str { + match self.kind { + SecondFactorType::TOTP(_) => "Authenticator app" + } + } +} + #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] pub struct User { pub uid: UserID, @@ -53,6 +71,12 @@ impl User { self.second_factors.as_mut().unwrap().push(factor); } + + pub fn remove_factor(&mut self, factor_id: FactorID) { + if let Some(f) = self.second_factors.as_mut() { + f.retain(|f| f.id != factor_id); + } + } } impl PartialEq for User { diff --git a/src/main.rs b/src/main.rs index e4eb9ec..b6f3c9a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -122,7 +122,8 @@ async fn main() -> std::io::Result<()> { .route("/settings/two_factors/add_totp", web::get().to(two_factors_controller::add_totp_factor_route)) // User API - .route("/settings/api/two_factors/save_totp_factor", web::post().to(two_factors_api::save_totp_factor)) + .route("/settings/api/two_factor/save_totp_factor", web::post().to(two_factor_api::save_totp_factor)) + .route("/settings/api/two_factor/delete_factor", web::post().to(two_factor_api::delete_factor)) // Admin routes .route("/admin", web::get() diff --git a/templates/settings/add_2fa_totp_page.html b/templates/settings/add_2fa_totp_page.html index 0ecb0c9..3d15c6f 100644 --- a/templates/settings/add_2fa_totp_page.html +++ b/templates/settings/add_2fa_totp_page.html @@ -75,7 +75,7 @@ return; try { - const res = await fetch("/settings/api/two_factors/save_totp_factor", { + const res = await fetch("/settings/api/two_factor/save_totp_factor", { method: "post", headers: { 'Content-Type': 'application/json', diff --git a/templates/settings/two_factors_page.html b/templates/settings/two_factors_page.html index cc7de08..69a6a47 100644 --- a/templates/settings/two_factors_page.html +++ b/templates/settings/two_factors_page.html @@ -13,6 +13,52 @@

Add Authenticator App

-TODO : show the list of currently registered 2 factors methods + + + + + + + + + + {% for f in user.second_factors.as_deref().unwrap_or_default() %} + + + + + + {% endfor %} + +
Factor typeNameActions
{{ f.type_str() }}{{ f.name }}Delete
+ + {% endblock content %}