Can remove created factors
This commit is contained in:
		| @@ -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; | ||||
| pub mod two_factor_api; | ||||
| @@ -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<Request>, | ||||
| pub async fn save_totp_factor(user: CurrentUser, form: web::Json<AddTOTPRequest>, | ||||
|                               users: web::Data<Addr<UsersActor>>) -> 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<Request>, | ||||
|     } | ||||
| 
 | ||||
|     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 { | ||||
| @@ -39,3 +44,22 @@ pub async fn save_totp_factor(user: CurrentUser, form: web::Json<Request>, | ||||
|         HttpResponse::Ok().body("Added new factor!") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(serde::Deserialize)] | ||||
| pub struct DeleteFactorRequest { | ||||
|     id: FactorID, | ||||
| } | ||||
| 
 | ||||
| pub async fn delete_factor(user: CurrentUser, form: web::Json<DeleteFactorRequest>, | ||||
|                            users: web::Data<Addr<UsersActor>>) -> 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!") | ||||
|     } | ||||
| } | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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() | ||||
|   | ||||
| @@ -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', | ||||
|   | ||||
| @@ -13,6 +13,52 @@ | ||||
| <p> | ||||
|     <a href="/settings/two_factors/add_totp" type="button" class="btn btn-primary">Add Authenticator App</a> | ||||
| </p> | ||||
| TODO : show the list of currently registered 2 factors methods | ||||
|  | ||||
| <table class="table table-hover" style="max-width: 800px;" aria-describedby="Factors list"> | ||||
|     <thead> | ||||
|     <tr> | ||||
|         <th scope="col">Factor type</th> | ||||
|         <th scope="col">Name</th> | ||||
|         <th scope="col">Actions</th> | ||||
|     </tr> | ||||
|     </thead> | ||||
|     <tbody> | ||||
|     {% for f in user.second_factors.as_deref().unwrap_or_default() %} | ||||
|     <tr id="factor-{{ f.id.0 }}"> | ||||
|         <td>{{ f.type_str() }}</td> | ||||
|         <td>{{ f.name }}</td> | ||||
|         <td><a href="javascript:delete_factor('{{ f.id.0 }}');">Delete</a></td> | ||||
|     </tr> | ||||
|     {% endfor %} | ||||
|     </tbody> | ||||
| </table> | ||||
|  | ||||
| <script> | ||||
|     async function delete_factor(id) { | ||||
|         if (!confirm("Do you really want to remove this factor?")) | ||||
|             return; | ||||
|  | ||||
|         try { | ||||
|             const res = await fetch("/settings/api/two_factor/delete_factor", { | ||||
|                 method: "post", | ||||
|                 headers: { | ||||
|                   'Content-Type': 'application/json', | ||||
|                 }, | ||||
|                 body: JSON.stringify({ | ||||
|                     id: id, | ||||
|                 }) | ||||
|             }); | ||||
|  | ||||
|             let text = await res.text(); | ||||
|             alert(text); | ||||
|  | ||||
|             if (res.status == 200) | ||||
|                 document.getElementById("factor-" + id).remove(); | ||||
|         } catch(e) { | ||||
|             console.error(e); | ||||
|             alert("Failed to remove factor!"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| </script> | ||||
| {% endblock content %} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user