Can change user password
This commit is contained in:
		@@ -1,7 +1,7 @@
 | 
				
			|||||||
use actix::{Actor, Context, Handler, Message, MessageResult};
 | 
					use actix::{Actor, Context, Handler, Message, MessageResult};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::data::entity_manager::EntityManager;
 | 
					use crate::data::entity_manager::EntityManager;
 | 
				
			||||||
use crate::data::user::{User, UserID, verify_password};
 | 
					use crate::data::user::{User, UserID};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug)]
 | 
				
			||||||
pub enum LoginResult {
 | 
					pub enum LoginResult {
 | 
				
			||||||
@@ -57,7 +57,7 @@ impl Handler<LoginRequest> for UsersActor {
 | 
				
			|||||||
        match self.manager.find_by_username_or_email(&msg.login) {
 | 
					        match self.manager.find_by_username_or_email(&msg.login) {
 | 
				
			||||||
            None => MessageResult(LoginResult::AccountNotFound),
 | 
					            None => MessageResult(LoginResult::AccountNotFound),
 | 
				
			||||||
            Some(user) => {
 | 
					            Some(user) => {
 | 
				
			||||||
                if !verify_password(msg.password, &user.password) {
 | 
					                if !user.verify_password(&msg.password) {
 | 
				
			||||||
                    return MessageResult(LoginResult::InvalidPassword);
 | 
					                    return MessageResult(LoginResult::InvalidPassword);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,9 +3,11 @@ use actix_identity::Identity;
 | 
				
			|||||||
use actix_web::{HttpResponse, Responder, web};
 | 
					use actix_web::{HttpResponse, Responder, web};
 | 
				
			||||||
use askama::Template;
 | 
					use askama::Template;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::actors::users_actor;
 | 
					use crate::actors::{bruteforce_actor, users_actor};
 | 
				
			||||||
 | 
					use crate::actors::bruteforce_actor::BruteForceActor;
 | 
				
			||||||
use crate::actors::users_actor::UsersActor;
 | 
					use crate::actors::users_actor::UsersActor;
 | 
				
			||||||
use crate::constants::APP_NAME;
 | 
					use crate::constants::{APP_NAME, MAX_FAILED_LOGIN_ATTEMPTS, MIN_PASS_LEN};
 | 
				
			||||||
 | 
					use crate::data::remote_ip::RemoteIP;
 | 
				
			||||||
use crate::data::session_identity::SessionIdentity;
 | 
					use crate::data::session_identity::SessionIdentity;
 | 
				
			||||||
use crate::data::user::User;
 | 
					use crate::data::user::User;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -21,11 +23,12 @@ struct BaseSettingsPage {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl BaseSettingsPage {
 | 
					impl BaseSettingsPage {
 | 
				
			||||||
    async fn get(user: &User) -> BaseSettingsPage {
 | 
					    async fn get(page_title: &'static str, user: &User,
 | 
				
			||||||
 | 
					                 danger_message: Option<String>, success_message: Option<String>) -> BaseSettingsPage {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            danger_message: None,
 | 
					            danger_message,
 | 
				
			||||||
            success_message: None,
 | 
					            success_message,
 | 
				
			||||||
            page_title: "Account details",
 | 
					            page_title,
 | 
				
			||||||
            app_name: APP_NAME,
 | 
					            app_name: APP_NAME,
 | 
				
			||||||
            is_admin: user.admin,
 | 
					            is_admin: user.admin,
 | 
				
			||||||
            user_name: user.username.to_string(),
 | 
					            user_name: user.username.to_string(),
 | 
				
			||||||
@@ -44,15 +47,22 @@ struct AccountDetailsPage {
 | 
				
			|||||||
    email: String,
 | 
					    email: String,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Template)]
 | 
				
			||||||
 | 
					#[template(path = "settings/change_password.html")]
 | 
				
			||||||
 | 
					struct ChangePasswordPage {
 | 
				
			||||||
 | 
					    _parent: BaseSettingsPage,
 | 
				
			||||||
 | 
					    min_pwd_len: usize,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Account details page
 | 
					/// Account details page
 | 
				
			||||||
pub async fn account_settings_details_route(id: Identity, user_actor: web::Data<Addr<UsersActor>>) -> impl Responder {
 | 
					pub async fn account_settings_details_route(id: Identity, users: web::Data<Addr<UsersActor>>) -> impl Responder {
 | 
				
			||||||
    let user: User = user_actor.send(
 | 
					    let user: User = users.send(
 | 
				
			||||||
        users_actor::GetUserRequest(SessionIdentity(&id).user_id())
 | 
					        users_actor::GetUserRequest(SessionIdentity(&id).user_id())
 | 
				
			||||||
    ).await.unwrap().0.unwrap();
 | 
					    ).await.unwrap().0.unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    HttpResponse::Ok()
 | 
					    HttpResponse::Ok()
 | 
				
			||||||
        .body(AccountDetailsPage {
 | 
					        .body(AccountDetailsPage {
 | 
				
			||||||
            _parent: BaseSettingsPage::get(&user).await,
 | 
					            _parent: BaseSettingsPage::get("Account details", &user, None, None).await,
 | 
				
			||||||
            user_id: user.uid,
 | 
					            user_id: user.uid,
 | 
				
			||||||
            first_name: user.first_name,
 | 
					            first_name: user.first_name,
 | 
				
			||||||
            last_name: user.last_last,
 | 
					            last_name: user.last_last,
 | 
				
			||||||
@@ -60,3 +70,64 @@ pub async fn account_settings_details_route(id: Identity, user_actor: web::Data<
 | 
				
			|||||||
            email: user.email,
 | 
					            email: user.email,
 | 
				
			||||||
        }.render().unwrap())
 | 
					        }.render().unwrap())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(serde::Deserialize)]
 | 
				
			||||||
 | 
					pub struct PassChangeRequest {
 | 
				
			||||||
 | 
					    pub old_pass: String,
 | 
				
			||||||
 | 
					    pub new_pass: String,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Change password route
 | 
				
			||||||
 | 
					pub async fn change_password_route(id: Identity,
 | 
				
			||||||
 | 
					                                   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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let user: User = users.send(
 | 
				
			||||||
 | 
					        users_actor::GetUserRequest(SessionIdentity(&id).user_id())
 | 
				
			||||||
 | 
					    ).await.unwrap().0.unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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 {
 | 
				
			||||||
 | 
					            _parent: BaseSettingsPage::get("Change password", &user, danger, success).await,
 | 
				
			||||||
 | 
					            min_pwd_len: MIN_PASS_LEN,
 | 
				
			||||||
 | 
					        }.render().unwrap())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -3,3 +3,4 @@ pub mod entity_manager;
 | 
				
			|||||||
pub mod service;
 | 
					pub mod service;
 | 
				
			||||||
pub mod session_identity;
 | 
					pub mod session_identity;
 | 
				
			||||||
pub mod user;
 | 
					pub mod user;
 | 
				
			||||||
 | 
					pub mod remote_ip;
 | 
				
			||||||
							
								
								
									
										28
									
								
								src/data/remote_ip.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/data/remote_ip.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					use std::net::IpAddr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use actix_web::{Error, FromRequest, HttpRequest, web};
 | 
				
			||||||
 | 
					use actix_web::dev::Payload;
 | 
				
			||||||
 | 
					use futures_util::future::{Ready, ready};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::data::app_config::AppConfig;
 | 
				
			||||||
 | 
					use crate::utils::network_utils::get_remote_ip;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Copy, Clone, Debug, Eq, PartialEq)]
 | 
				
			||||||
 | 
					pub struct RemoteIP(pub IpAddr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Into<IpAddr> for RemoteIP {
 | 
				
			||||||
 | 
					    fn into(self) -> IpAddr {
 | 
				
			||||||
 | 
					        self.0
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl FromRequest for RemoteIP {
 | 
				
			||||||
 | 
					    type Error = Error;
 | 
				
			||||||
 | 
					    type Future = Ready<Result<RemoteIP, Error>>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[inline]
 | 
				
			||||||
 | 
					    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
 | 
				
			||||||
 | 
					        let config: &web::Data<AppConfig> = req.app_data().expect("AppData undefined!");
 | 
				
			||||||
 | 
					        ready(Ok(RemoteIP(get_remote_ip(req, config.proxy_ip.as_deref()))))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -21,6 +21,12 @@ pub struct User {
 | 
				
			|||||||
    pub authorized_services: Option<Vec<ServiceID>>,
 | 
					    pub authorized_services: Option<Vec<ServiceID>>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl User {
 | 
				
			||||||
 | 
					    pub fn verify_password<P: AsRef<[u8]>>(&self, pass: P) -> bool {
 | 
				
			||||||
 | 
					        verify_password(pass, &self.password)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl PartialEq for User {
 | 
					impl PartialEq for User {
 | 
				
			||||||
    fn eq(&self, other: &Self) -> bool {
 | 
					    fn eq(&self, other: &Self) -> bool {
 | 
				
			||||||
        self.uid.eq(&other.uid)
 | 
					        self.uid.eq(&other.uid)
 | 
				
			||||||
@@ -81,8 +87,8 @@ impl EntityManager<User> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /// Update user information
 | 
					    /// Update user information
 | 
				
			||||||
    fn update_user<F>(&mut self, id: &UserID, update: F) -> bool
 | 
					    fn update_user<F>(&mut self, id: &UserID, update: F) -> bool
 | 
				
			||||||
    where
 | 
					        where
 | 
				
			||||||
        F: FnOnce(User) -> User,
 | 
					            F: FnOnce(User) -> User,
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        let user = match self.find_by_user_id(id) {
 | 
					        let user = match self.find_by_user_id(id) {
 | 
				
			||||||
            None => return false,
 | 
					            None => return false,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -106,6 +106,8 @@ async fn main() -> std::io::Result<()> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            // Settings routes
 | 
					            // Settings routes
 | 
				
			||||||
            .route("/settings", web::get().to(settings_controller::account_settings_details_route))
 | 
					            .route("/settings", web::get().to(settings_controller::account_settings_details_route))
 | 
				
			||||||
 | 
					            .route("/settings/change_password", web::get().to(settings_controller::change_password_route))
 | 
				
			||||||
 | 
					            .route("/settings/change_password", web::post().to(settings_controller::change_password_route))
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
        .bind(listen_address)?
 | 
					        .bind(listen_address)?
 | 
				
			||||||
        .run()
 | 
					        .run()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,69 +1,81 @@
 | 
				
			|||||||
<!DOCTYPE html>
 | 
					<!DOCTYPE html>
 | 
				
			||||||
<html lang="en">
 | 
					<html lang="en">
 | 
				
			||||||
<head>
 | 
					<head>
 | 
				
			||||||
  <meta charset="UTF-8">
 | 
					    <meta charset="UTF-8">
 | 
				
			||||||
  <title>{{ page_title }} - {{ app_name }}</title>
 | 
					    <title>{{ page_title }} - {{ app_name }}</title>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <!-- Bootstrap core CSS -->
 | 
					    <!-- Bootstrap core CSS -->
 | 
				
			||||||
  <link href="/assets/css/bootstrap.css" rel="stylesheet" crossorigin="anonymous"/>
 | 
					    <link href="/assets/css/bootstrap.css" rel="stylesheet" crossorigin="anonymous"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <link rel="stylesheet" href="/assets/css/base_settings_page.css">
 | 
					    <link rel="stylesheet" href="/assets/css/base_settings_page.css">
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
  <div class="d-flex flex-column flex-shrink-0 p-3 bg-light" style="width: 280px;">
 | 
					<div class="d-flex flex-column flex-shrink-0 p-3 bg-light" style="width: 280px;">
 | 
				
			||||||
    <a href="/" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto link-dark text-decoration-none">
 | 
					    <a href="/" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto link-dark text-decoration-none">
 | 
				
			||||||
      <span class="fs-4">{{ app_name }}</span>
 | 
					        <span class="fs-4">{{ app_name }}</span>
 | 
				
			||||||
    </a>
 | 
					    </a>
 | 
				
			||||||
    <hr>
 | 
					    <hr>
 | 
				
			||||||
    <ul class="nav nav-pills flex-column mb-auto">
 | 
					    <ul class="nav nav-pills flex-column mb-auto">
 | 
				
			||||||
      <li class="nav-item">
 | 
					        <li class="nav-item">
 | 
				
			||||||
        <a href="/settings" class="nav-link active" aria-current="page">
 | 
					            <a href="/settings" class="nav-link link-dark">
 | 
				
			||||||
          Account details
 | 
					                Account details
 | 
				
			||||||
        </a>
 | 
					            </a>
 | 
				
			||||||
      </li>
 | 
					        </li>
 | 
				
			||||||
      <li>
 | 
					        <li>
 | 
				
			||||||
        <a href="/settings/change_password" class="nav-link link-dark">
 | 
					            <a href="/settings/change_password" class="nav-link link-dark">
 | 
				
			||||||
          Change password
 | 
					                Change password
 | 
				
			||||||
        </a>
 | 
					            </a>
 | 
				
			||||||
      </li>
 | 
					        </li>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <hr />
 | 
					        <hr/>
 | 
				
			||||||
      {% if is_admin %}
 | 
					        {% if is_admin %}
 | 
				
			||||||
      <li>
 | 
					        <li>
 | 
				
			||||||
        <a href="/admin/apps" class="nav-link link-dark">
 | 
					            <a href="/admin/apps" class="nav-link link-dark">
 | 
				
			||||||
          Applications
 | 
					                Applications
 | 
				
			||||||
        </a>
 | 
					            </a>
 | 
				
			||||||
      </li>
 | 
					        </li>
 | 
				
			||||||
      <li>
 | 
					        <li>
 | 
				
			||||||
        <a href="/admin/users" class="nav-link link-dark">
 | 
					            <a href="/admin/users" class="nav-link link-dark">
 | 
				
			||||||
          Users
 | 
					                Users
 | 
				
			||||||
        </a>
 | 
					            </a>
 | 
				
			||||||
      </li>
 | 
					        </li>
 | 
				
			||||||
      {% endif %}
 | 
					        {% endif %}
 | 
				
			||||||
    </ul>
 | 
					    </ul>
 | 
				
			||||||
    <hr>
 | 
					    <hr>
 | 
				
			||||||
    <div class="dropdown">
 | 
					    <div class="dropdown">
 | 
				
			||||||
      <a href="#" class="d-flex align-items-center link-dark text-decoration-none dropdown-toggle" id="dropdownUser" data-bs-toggle="dropdown" aria-expanded="false">
 | 
					        <a href="#" class="d-flex align-items-center link-dark text-decoration-none dropdown-toggle" id="dropdownUser"
 | 
				
			||||||
        <img src="/assets/img/account.png" alt="" width="32" height="32" class="rounded-circle me-2">
 | 
					           data-bs-toggle="dropdown" aria-expanded="false">
 | 
				
			||||||
        <strong>{{ user_name }}</strong>
 | 
					            <img src="/assets/img/account.png" alt="" width="32" height="32" class="rounded-circle me-2">
 | 
				
			||||||
      </a>
 | 
					            <strong>{{ user_name }}</strong>
 | 
				
			||||||
      <ul class="dropdown-menu text-small shadow" aria-labelledby="dropdownUser">
 | 
					        </a>
 | 
				
			||||||
        <li><a class="dropdown-item" href="/logout">Sign out</a></li>
 | 
					        <ul class="dropdown-menu text-small shadow" aria-labelledby="dropdownUser">
 | 
				
			||||||
      </ul>
 | 
					            <li><a class="dropdown-item" href="/logout">Sign out</a></li>
 | 
				
			||||||
 | 
					        </ul>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  </div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <div class="page_body" style="flex: 1">
 | 
					<div class="page_body" style="flex: 1">
 | 
				
			||||||
    {% if let Some(msg) = danger_message %}<div class="alert alert-danger">{{ msg }}</div>{% endif %}
 | 
					    {% if let Some(msg) = danger_message %}
 | 
				
			||||||
    {% if let Some(msg) = success_message %}<div class="alert alert-success">{{ msg }}</div>{% endif %}
 | 
					    <div class="alert alert-danger">{{ msg }}</div>
 | 
				
			||||||
 | 
					    {% endif %}
 | 
				
			||||||
 | 
					    {% if let Some(msg) = success_message %}
 | 
				
			||||||
 | 
					    <div class="alert alert-success">{{ msg }}</div>
 | 
				
			||||||
 | 
					    {% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <h2 class="bd-title mt-0" style="margin-bottom: 40px;">{{ page_title }}</h2>
 | 
					    <h2 class="bd-title mt-0" style="margin-bottom: 40px;">{{ page_title }}</h2>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    {% block content %}
 | 
					    {% block content %}
 | 
				
			||||||
    TO_REPLACE
 | 
					    TO_REPLACE
 | 
				
			||||||
    {% endblock content %}
 | 
					    {% endblock content %}
 | 
				
			||||||
  </div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script src="/assets/js/bootstrap.bundle.min.js"></script>
 | 
					<script src="/assets/js/bootstrap.bundle.min.js"></script>
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					  document.querySelectorAll(".nav-link").forEach(el => {
 | 
				
			||||||
 | 
					    if(el.href === location.href) el.classList.add("active");
 | 
				
			||||||
 | 
					    else el.classList.remove("active")
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
</body>
 | 
					</body>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
							
								
								
									
										57
									
								
								templates/settings/change_password.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								templates/settings/change_password.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					{% extends "base_settings_page.html" %}
 | 
				
			||||||
 | 
					{% block content %}
 | 
				
			||||||
 | 
					<form id="change_password_form" action="/settings/change_password" method="post">
 | 
				
			||||||
 | 
					    <div class="form-group">
 | 
				
			||||||
 | 
					        <label for="currPassword" class="form-label mt-4">Current password</label>
 | 
				
			||||||
 | 
					        <input type="password" name="old_pass" class="form-control"
 | 
				
			||||||
 | 
					               id="currPassword" placeholder="Your current password" required />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div> </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div class="form-group">
 | 
				
			||||||
 | 
					        <label for="newPassword" class="form-label mt-4">New password</label>
 | 
				
			||||||
 | 
					        <input type="password" name="new_pass" class="form-control" id="newPassword"
 | 
				
			||||||
 | 
					               placeholder="New password" minlength="{{ min_pwd_len }}" />
 | 
				
			||||||
 | 
					        <div class="invalid-feedback" id="errNewPass"></div>
 | 
				
			||||||
 | 
					        <small class="form-text text-muted">Please choose a password of at least {{ min_pwd_len }} characters.</small>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div class="form-group">
 | 
				
			||||||
 | 
					        <label for="confirmNewPassword" class="form-label mt-4">Confirm new password</label>
 | 
				
			||||||
 | 
					        <input type="password" class="form-control" id="confirmNewPassword" placeholder="Confirm new password">
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div> </div>
 | 
				
			||||||
 | 
					    <div> </div>
 | 
				
			||||||
 | 
					    <div> </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <button type="submit" class="btn btn-primary">Change password</button>
 | 
				
			||||||
 | 
					</form>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					    const form = document.getElementById("change_password_form");
 | 
				
			||||||
 | 
					    const errPass1 = document.getElementById("errNewPass");
 | 
				
			||||||
 | 
					    form.addEventListener("submit", (e) => {
 | 
				
			||||||
 | 
					        e.preventDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const pass1 = document.getElementById("newPassword");
 | 
				
			||||||
 | 
					        const pass2 = document.getElementById("confirmNewPassword");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        errPass1.innerHTML = "";
 | 
				
			||||||
 | 
					        pass1.classList.remove("is-invalid");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (pass1.value.length < {{ min_pwd_len }}) {
 | 
				
			||||||
 | 
					            errPass1.innerHTML = "Your password must have at least {{ min_pwd_len }} characters!";
 | 
				
			||||||
 | 
					            pass1.classList.add("is-invalid");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if (pass1.value !== pass2.value) {
 | 
				
			||||||
 | 
					            errPass1.innerHTML = "The password and its confirmation are not the same !";
 | 
				
			||||||
 | 
					            pass1.classList.add("is-invalid");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            form.submit();
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% endblock content %}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user