2022-04-04 15:39:23 +00:00
|
|
|
use actix::Addr;
|
|
|
|
use actix_web::{HttpResponse, Responder, web};
|
|
|
|
use askama::Template;
|
|
|
|
|
2022-04-05 15:17:34 +00:00
|
|
|
use crate::actors::{bruteforce_actor, users_actor};
|
|
|
|
use crate::actors::bruteforce_actor::BruteForceActor;
|
2022-04-04 15:39:23 +00:00
|
|
|
use crate::actors::users_actor::UsersActor;
|
2022-04-05 15:17:34 +00:00
|
|
|
use crate::constants::{APP_NAME, MAX_FAILED_LOGIN_ATTEMPTS, MIN_PASS_LEN};
|
2022-04-05 15:40:15 +00:00
|
|
|
use crate::data::current_user::CurrentUser;
|
2022-04-05 15:17:34 +00:00
|
|
|
use crate::data::remote_ip::RemoteIP;
|
2022-04-04 15:39:23 +00:00
|
|
|
use crate::data::user::User;
|
|
|
|
|
|
|
|
#[derive(Template)]
|
|
|
|
#[template(path = "settings/base_settings_page.html")]
|
2022-04-06 15:18:06 +00:00
|
|
|
pub(crate) struct BaseSettingsPage {
|
|
|
|
pub danger_message: Option<String>,
|
|
|
|
pub success_message: Option<String>,
|
|
|
|
pub page_title: &'static str,
|
|
|
|
pub app_name: &'static str,
|
|
|
|
pub is_admin: bool,
|
|
|
|
pub user_name: String,
|
2022-04-04 15:39:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl BaseSettingsPage {
|
2022-04-06 15:18:06 +00:00
|
|
|
pub fn get(page_title: &'static str, user: &User,
|
2022-04-05 15:17:34 +00:00
|
|
|
danger_message: Option<String>, success_message: Option<String>) -> BaseSettingsPage {
|
2022-04-04 15:39:23 +00:00
|
|
|
Self {
|
2022-04-05 15:17:34 +00:00
|
|
|
danger_message,
|
|
|
|
success_message,
|
|
|
|
page_title,
|
2022-04-04 15:39:23 +00:00
|
|
|
app_name: APP_NAME,
|
|
|
|
is_admin: user.admin,
|
|
|
|
user_name: user.username.to_string(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Template)]
|
|
|
|
#[template(path = "settings/account_details.html")]
|
|
|
|
struct AccountDetailsPage {
|
|
|
|
_parent: BaseSettingsPage,
|
|
|
|
user_id: String,
|
|
|
|
first_name: String,
|
|
|
|
last_name: String,
|
|
|
|
username: String,
|
|
|
|
email: String,
|
|
|
|
}
|
|
|
|
|
2022-04-05 15:17:34 +00:00
|
|
|
#[derive(Template)]
|
|
|
|
#[template(path = "settings/change_password.html")]
|
|
|
|
struct ChangePasswordPage {
|
|
|
|
_parent: BaseSettingsPage,
|
|
|
|
min_pwd_len: usize,
|
|
|
|
}
|
|
|
|
|
2022-04-04 15:39:23 +00:00
|
|
|
/// Account details page
|
2022-04-05 15:40:15 +00:00
|
|
|
pub async fn account_settings_details_route(user: CurrentUser) -> impl Responder {
|
|
|
|
let user = user.into();
|
2022-04-04 15:39:23 +00:00
|
|
|
HttpResponse::Ok()
|
|
|
|
.body(AccountDetailsPage {
|
2022-04-06 15:18:06 +00:00
|
|
|
_parent: BaseSettingsPage::get("Account details", &user, None, None),
|
2022-04-04 15:39:23 +00:00
|
|
|
user_id: user.uid,
|
|
|
|
first_name: user.first_name,
|
2022-04-06 16:03:00 +00:00
|
|
|
last_name: user.last_name,
|
2022-04-04 15:39:23 +00:00
|
|
|
username: user.username,
|
|
|
|
email: user.email,
|
|
|
|
}.render().unwrap())
|
2022-04-05 15:17:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(serde::Deserialize)]
|
|
|
|
pub struct PassChangeRequest {
|
|
|
|
pub old_pass: String,
|
|
|
|
pub new_pass: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Change password route
|
2022-04-05 15:40:15 +00:00
|
|
|
pub async fn change_password_route(user: CurrentUser,
|
2022-04-05 15:17:34 +00:00
|
|
|
users: web::Data<Addr<UsersActor>>,
|
|
|
|
req: Option<web::Form<PassChangeRequest>>,
|
|
|
|
bruteforce: web::Data<Addr<BruteForceActor>>,
|
|
|
|
remote_ip: RemoteIP) -> impl Responder {
|
|
|
|
let mut danger = None;
|
|
|
|
let mut success = None;
|
|
|
|
|
2022-04-05 15:40:15 +00:00
|
|
|
let user: User = user.into();
|
2022-04-05 15:17:34 +00:00
|
|
|
|
|
|
|
let failed_attempts = bruteforce.send(bruteforce_actor::CountFailedAttempt { ip: remote_ip.into() }).await.unwrap();
|
|
|
|
if failed_attempts > MAX_FAILED_LOGIN_ATTEMPTS {
|
|
|
|
danger = Some("Too many invalid password attempts. Please try to change your password later.".to_string());
|
|
|
|
} else if let Some(req) = req {
|
|
|
|
|
|
|
|
// Invalid password
|
|
|
|
if !user.verify_password(&req.old_pass) {
|
|
|
|
danger = Some("Old password is invalid!".to_string());
|
|
|
|
bruteforce.send(bruteforce_actor::RecordFailedAttempt { ip: remote_ip.into() }).await.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Password too short
|
|
|
|
else if req.new_pass.len() < MIN_PASS_LEN {
|
|
|
|
danger = Some("New password is too short!".to_string());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Change password
|
|
|
|
else {
|
|
|
|
let res = users.send(
|
|
|
|
users_actor::ChangePasswordRequest {
|
|
|
|
user_id: user.uid.clone(),
|
|
|
|
new_password: req.new_pass.to_string(),
|
|
|
|
temporary: false,
|
|
|
|
}
|
|
|
|
).await.unwrap().0;
|
|
|
|
|
|
|
|
if !res {
|
|
|
|
danger = Some("An error occurred while trying to change your password!".to_string());
|
|
|
|
} else {
|
|
|
|
success = Some("Your password was successfully changed!".to_string());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HttpResponse::Ok()
|
|
|
|
.body(ChangePasswordPage {
|
2022-04-06 15:18:06 +00:00
|
|
|
_parent: BaseSettingsPage::get("Change password", &user, danger, success),
|
2022-04-05 15:17:34 +00:00
|
|
|
min_pwd_len: MIN_PASS_LEN,
|
|
|
|
}.render().unwrap())
|
2022-04-04 15:39:23 +00:00
|
|
|
}
|