Can remove created factors
This commit is contained in:
		@@ -6,4 +6,4 @@ pub mod admin_controller;
 | 
				
			|||||||
pub mod admin_api;
 | 
					pub mod admin_api;
 | 
				
			||||||
pub mod openid_controller;
 | 
					pub mod openid_controller;
 | 
				
			||||||
pub mod two_factors_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::Addr;
 | 
				
			||||||
use actix_web::{HttpResponse, Responder, web};
 | 
					use actix_web::{HttpResponse, Responder, web};
 | 
				
			||||||
 | 
					use uuid::Uuid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::actors::users_actor;
 | 
					use crate::actors::users_actor;
 | 
				
			||||||
use crate::actors::users_actor::UsersActor;
 | 
					use crate::actors::users_actor::UsersActor;
 | 
				
			||||||
use crate::data::current_user::CurrentUser;
 | 
					use crate::data::current_user::CurrentUser;
 | 
				
			||||||
use crate::data::totp_key::TotpKey;
 | 
					use crate::data::totp_key::TotpKey;
 | 
				
			||||||
use crate::data::user::{SecondFactor, User};
 | 
					use crate::data::user::{FactorID, SecondFactor, SecondFactorType, User};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(serde::Deserialize)]
 | 
					#[derive(serde::Deserialize)]
 | 
				
			||||||
pub struct Request {
 | 
					pub struct AddTOTPRequest {
 | 
				
			||||||
    factor_name: String,
 | 
					    factor_name: String,
 | 
				
			||||||
    secret: String,
 | 
					    secret: String,
 | 
				
			||||||
    first_code: 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 {
 | 
					                              users: web::Data<Addr<UsersActor>>) -> impl Responder {
 | 
				
			||||||
    let key = TotpKey::from_encoded_secret(&form.secret);
 | 
					    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);
 | 
					    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;
 | 
					    let res = users.send(users_actor::UpdateUserRequest(user)).await.unwrap().0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if !res {
 | 
					    if !res {
 | 
				
			||||||
@@ -39,3 +44,22 @@ pub async fn save_totp_factor(user: CurrentUser, form: web::Json<Request>,
 | 
				
			|||||||
        HttpResponse::Ok().body("Added new factor!")
 | 
					        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;
 | 
					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)]
 | 
					#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
 | 
				
			||||||
pub enum SecondFactor {
 | 
					pub enum SecondFactorType {
 | 
				
			||||||
    TOTP(TotpKey)
 | 
					    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)]
 | 
					#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
 | 
				
			||||||
pub struct User {
 | 
					pub struct User {
 | 
				
			||||||
    pub uid: UserID,
 | 
					    pub uid: UserID,
 | 
				
			||||||
@@ -53,6 +71,12 @@ impl User {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        self.second_factors.as_mut().unwrap().push(factor);
 | 
					        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 {
 | 
					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))
 | 
					            .route("/settings/two_factors/add_totp", web::get().to(two_factors_controller::add_totp_factor_route))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // User API
 | 
					            // 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
 | 
					            // Admin routes
 | 
				
			||||||
            .route("/admin", web::get()
 | 
					            .route("/admin", web::get()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -75,7 +75,7 @@
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            try {
 | 
					            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",
 | 
					                    method: "post",
 | 
				
			||||||
                    headers: {
 | 
					                    headers: {
 | 
				
			||||||
                      'Content-Type': 'application/json',
 | 
					                      'Content-Type': 'application/json',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,52 @@
 | 
				
			|||||||
<p>
 | 
					<p>
 | 
				
			||||||
    <a href="/settings/two_factors/add_totp" type="button" class="btn btn-primary">Add Authenticator App</a>
 | 
					    <a href="/settings/two_factors/add_totp" type="button" class="btn btn-primary">Add Authenticator App</a>
 | 
				
			||||||
</p>
 | 
					</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 %}
 | 
					{% endblock content %}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user