mirror of
				https://github.com/StefBuwalda/cal_counter.git
				synced 2025-10-30 11:19:59 +00:00 
			
		
		
		
	Add change password functionality for users
Introduces a change password route, form, and template, allowing authenticated users to update their password. Updates the User model with a method to set the must_change_password flag. Adjusts login and navigation logic to support the new flow and ensures users are redirected to change their password if required.
This commit is contained in:
		| @@ -1,8 +1,9 @@ | |||||||
| from flask import Blueprint, request, render_template | from flask import Blueprint, request, render_template, redirect, url_for | ||||||
| from flask_login import current_user, login_user | from flask_login import current_user, login_user | ||||||
| from forms import LoginForm | from forms import LoginForm, ChangePasswordForm | ||||||
| from models import User | from models import User | ||||||
| from application.utils import default_return | from application.utils import default_return | ||||||
|  | from application import db | ||||||
|  |  | ||||||
| bp = Blueprint( | bp = Blueprint( | ||||||
|     "auth", |     "auth", | ||||||
| @@ -28,3 +29,22 @@ def login(): | |||||||
|             pass |             pass | ||||||
|             # invalid user |             # invalid user | ||||||
|     return render_template("login.html", form=form) |     return render_template("login.html", form=form) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @bp.route("/change_password", methods=["GET", "POST"]) | ||||||
|  | def change_password(): | ||||||
|  |     if not current_user.is_authenticated: | ||||||
|  |         return redirect(url_for("auth.login")) | ||||||
|  |  | ||||||
|  |     form = ChangePasswordForm() | ||||||
|  |     if form.validate_on_submit(): | ||||||
|  |         cur_check = current_user.check_password( | ||||||
|  |             password=form.current_password.data | ||||||
|  |         ) | ||||||
|  |         eq_check = form.new_password.data == form.confirm_password.data | ||||||
|  |         if cur_check and eq_check: | ||||||
|  |             current_user.change_password(form.new_password.data) | ||||||
|  |             current_user.set_pw_change(False) | ||||||
|  |             db.session.commit() | ||||||
|  |             return default_return() | ||||||
|  |     return render_template("change_password.html", form=form) | ||||||
|   | |||||||
							
								
								
									
										46
									
								
								application/auth/templates/change_password.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								application/auth/templates/change_password.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | |||||||
|  | {% extends "base.html" %} | ||||||
|  |  | ||||||
|  | {% block content %} | ||||||
|  | <div class="container d-flex justify-content-center align-items-center"> | ||||||
|  |     <div class="card shadow-sm p-4" style="width: 100%; max-width: 400px;"> | ||||||
|  |         <h3 class="mb-4 text-center">Login</h3> | ||||||
|  |         <form method="post"> | ||||||
|  |             {{ form.hidden_tag() }} | ||||||
|  |  | ||||||
|  |             <div class="mb-3"> | ||||||
|  |                 {{ form.current_password.label(class="form-label") }} | ||||||
|  |                 {{ form.current_password(class="form-control", placeholder="") }} | ||||||
|  |                 {% if form.current_password.errors %} | ||||||
|  |                 <div class="text-danger small"> | ||||||
|  |                     {{ form.current_password.errors[0] }} | ||||||
|  |                 </div> | ||||||
|  |                 {% endif %} | ||||||
|  |             </div> | ||||||
|  |  | ||||||
|  |             <div class="mb-3"> | ||||||
|  |                 {{ form.new_password.label(class="form-label") }} | ||||||
|  |                 {{ form.new_password(class="form-control", placeholder="Enter password") }} | ||||||
|  |                 {% if form.new_password.errors %} | ||||||
|  |                 <div class="text-danger small"> | ||||||
|  |                     {{ form.new_password.errors[0] }} | ||||||
|  |                 </div> | ||||||
|  |                 {% endif %} | ||||||
|  |             </div> | ||||||
|  |  | ||||||
|  |             <div class="mb-3"> | ||||||
|  |                 {{ form.confirm_password.label(class="form-label") }} | ||||||
|  |                 {{ form.confirm_password(class="form-control", placeholder="Enter password") }} | ||||||
|  |                 {% if form.confirm_password.errors %} | ||||||
|  |                 <div class="text-danger small"> | ||||||
|  |                     {{ form.confirm_password.errors[0] }} | ||||||
|  |                 </div> | ||||||
|  |                 {% endif %} | ||||||
|  |             </div> | ||||||
|  |  | ||||||
|  |             <div class="d-grid"> | ||||||
|  |                 {{ form.submit(class="btn btn-primary btn-lg") }} | ||||||
|  |             </div> | ||||||
|  |         </form> | ||||||
|  |     </div> | ||||||
|  | </div> | ||||||
|  | {% endblock%} | ||||||
| @@ -4,7 +4,7 @@ | |||||||
| <div class="container d-flex justify-content-center align-items-center"> | <div class="container d-flex justify-content-center align-items-center"> | ||||||
|     <div class="card shadow-sm p-4" style="width: 100%; max-width: 400px;"> |     <div class="card shadow-sm p-4" style="width: 100%; max-width: 400px;"> | ||||||
|         <h3 class="mb-4 text-center">Login</h3> |         <h3 class="mb-4 text-center">Login</h3> | ||||||
|         <form method="post" novalidate> |         <form method="post"> | ||||||
|             {{ form.hidden_tag() }} |             {{ form.hidden_tag() }} | ||||||
|  |  | ||||||
|             <div class="mb-3"> |             <div class="mb-3"> | ||||||
|   | |||||||
| @@ -36,7 +36,7 @@ | |||||||
|                         </li> |                         </li> | ||||||
|                         {% else %} |                         {% else %} | ||||||
|                         <li class="nav-item"> |                         <li class="nav-item"> | ||||||
|                             <a class="nav-link" href="{{ url_for('login') }}">Login</a> |                             <a class="nav-link" href="{{ url_for('auth.login') }}">Login</a> | ||||||
|                         </li> |                         </li> | ||||||
|                         {% endif %} |                         {% endif %} | ||||||
|                         <li class="nav-item"> |                         <li class="nav-item"> | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ from typing import Optional | |||||||
| def login_required(): | def login_required(): | ||||||
|     if not current_user.is_authenticated: |     if not current_user.is_authenticated: | ||||||
|         return redirect(url_for("auth.login")) |         return redirect(url_for("auth.login")) | ||||||
|         # if current_user.must_change_password: |     if current_user.must_change_password: | ||||||
|         flash("You have to change your password") |         flash("You have to change your password") | ||||||
|         return redirect(url_for("auth.change_password")) |         return redirect(url_for("auth.change_password")) | ||||||
|     return |     return | ||||||
|   | |||||||
							
								
								
									
										13
									
								
								forms.py
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								forms.py
									
									
									
									
									
								
							| @@ -11,7 +11,18 @@ from wtforms.validators import DataRequired, InputRequired, Optional | |||||||
| class LoginForm(FlaskForm): | class LoginForm(FlaskForm): | ||||||
|     username = StringField("Username", validators=[DataRequired()]) |     username = StringField("Username", validators=[DataRequired()]) | ||||||
|     password = PasswordField("Password", validators=[DataRequired()]) |     password = PasswordField("Password", validators=[DataRequired()]) | ||||||
|     submit = SubmitField("auth.login") |     submit = SubmitField("Log in") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class ChangePasswordForm(FlaskForm): | ||||||
|  |     current_password = PasswordField( | ||||||
|  |         "Current password", validators=[DataRequired()] | ||||||
|  |     ) | ||||||
|  |     new_password = PasswordField("New password", validators=[DataRequired()]) | ||||||
|  |     confirm_password = PasswordField( | ||||||
|  |         "Confirm new password", validators=[DataRequired()] | ||||||
|  |     ) | ||||||
|  |     submit = SubmitField("Change password") | ||||||
|  |  | ||||||
|  |  | ||||||
| class FoodItemForm(FlaskForm): | class FoodItemForm(FlaskForm): | ||||||
|   | |||||||
| @@ -36,6 +36,9 @@ class User(UserMixin, db.Model): | |||||||
|     def change_password(self, password: str) -> None: |     def change_password(self, password: str) -> None: | ||||||
|         self.password = generate_password_hash(password=password) |         self.password = generate_password_hash(password=password) | ||||||
|  |  | ||||||
|  |     def set_pw_change(self, change: bool) -> None: | ||||||
|  |         self.must_change_password = change | ||||||
|  |  | ||||||
|  |  | ||||||
| class Unit(db.Model): | class Unit(db.Model): | ||||||
|     __tablename__ = "unit" |     __tablename__ = "unit" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user