11from flask import Flask , render_template , request , redirect , url_for , jsonify , flash , json
22import firebase_admin
33from firebase_admin import credentials , firestore
4- import bcrypt
4+ import bcrypt , secrets
55from firebase .config import Config
66from db_utils import upload_file_to_db , connect_to_database
77from flask_mail import Mail , Message
88import email_credentials
9+ from datetime import datetime , timedelta , timezone
910
1011app = Flask (__name__ )
1112app .config .from_object (Config )
@@ -108,13 +109,133 @@ def pre_approved_access_code():
108109 return redirect (url_for ('homepage' ))
109110 return render_template ('pre_approved_access_code.html' )
110111
112+ @app .route ('/reset_password' , methods = ['GET' , 'POST' ])
113+ def reset_password ():
114+ if request .method == 'POST' :
115+ email = request .form .get ('email' ).strip ().lower ()
116+ user_ref = db .collection ('users' ).document (email )
117+ user_doc = user_ref .get ()
118+
119+ if user_doc .exists :
120+ verification_secret = secrets .token_hex (3 )
121+ expiration_time = datetime .now (timezone .utc ) + timedelta (minutes = 15 )
122+
123+
124+ user_ref .update ({
125+ 'verification_code' : verification_secret ,
126+ 'verification_expiry' : expiration_time
127+ })
128+
129+ subject = "HUA Password Reset"
130+ body = f"""Hi,
131+
132+ You recently requested a password reset for HUA. Your verification code is below.
133+
134+ { verification_secret }
135+
136+ This code will expire in 15 minutes.
137+
138+ If you didn't request a password reset, you can safely disregard this email.
139+
140+ This account is not monitored, please do not reply to this email.
141+ """
142+ try :
143+ msg = Message (subject , sender = "Huaverso@gmail.com" , recipients = [email ], body = body )
144+ mail .send (msg )
145+
146+ flash ("A verification code has been sent to your email." , "success" )
147+ return redirect (url_for ('enter_code' , email = email ))
148+ except Exception :
149+ flash ("Failed to send verification email. Please try again later." , "danger" )
150+ return redirect (url_for ('reset_password' ))
151+ else :
152+ flash ("No account with this email exists." , "danger" )
153+ return redirect (url_for ('reset_password' ))
154+
155+ return render_template ('reset_password.html' )
156+
157+ @app .route ('/enter_code' , methods = ['GET' , 'POST' ])
158+ def enter_code ():
159+ email = request .args .get ('email' )
160+ if not email :
161+ flash ("Invalid request." , "danger" )
162+ return redirect (url_for ('reset_password' ))
163+
164+ if request .method == 'POST' :
165+ entered_code = request .form .get ('verification_code' ).strip ()
166+ user_ref = db .collection ('users' ).document (email )
167+ user_doc = user_ref .get ()
168+
169+ if not user_doc .exists :
170+ flash ("Invalid request." , "danger" )
171+ return redirect (url_for ('reset_password' ))
172+
173+ stored_code = user_doc .to_dict ().get ('verification_code' )
174+ expiry_time = user_doc .to_dict ().get ('verification_expiry' )
175+
176+ if not stored_code or not expiry_time :
177+ flash ("No verification code found. Please initiate the password reset again." , "danger" )
178+ return redirect (url_for ('reset_password' ))
179+
180+ if entered_code != stored_code :
181+ flash ("Invalid verification code." , "danger" )
182+ return redirect (url_for ('enter_code' , email = email ))
183+
184+ if datetime .now (timezone .utc ) > expiry_time :
185+ flash ("The verification code has expired. Please request a new one." , "danger" )
186+ user_ref .update ({
187+ 'verification_code' : firestore .DELETE_FIELD ,
188+ 'verification_expiry' : firestore .DELETE_FIELD
189+ })
190+ return redirect (url_for ('reset_password' ))
191+
192+ return redirect (url_for ('new_password' , email = email ))
193+
194+ return render_template ('enter_code.html' , email = email )
195+
196+ @app .route ('/new_password' , methods = ['GET' , 'POST' ])
197+ def new_password ():
198+ email = request .args .get ('email' )
199+ if not email :
200+ flash ("Invalid request." , "danger" )
201+ return redirect (url_for ('reset_password' ))
202+
203+ if request .method == 'POST' :
204+ new_password = request .form .get ('new_password' )
205+ confirm_password = request .form .get ('confirm_password' )
206+
207+ if new_password != confirm_password :
208+ flash ("Passwords do not match." , "danger" )
209+ return redirect (url_for ('new_password' , email = email ))
210+
211+ if len (new_password ) < 6 :
212+ flash ("Password must be at least 6 characters long." , "danger" )
213+ return redirect (url_for ('new_password' , email = email ))
214+
215+ # Hash the new password and decode to store as string
216+ hashed_password = bcrypt .hashpw (new_password .encode ('utf-8' ), bcrypt .gensalt ()).decode ('utf-8' )
217+ user_ref = db .collection ('users' ).document (email )
218+ user_ref .update ({'password' : hashed_password })
219+
220+ user_ref .update ({
221+ 'verification_code' : firestore .DELETE_FIELD ,
222+ 'verification_expiry' : firestore .DELETE_FIELD
223+ })
224+
225+ flash ("Your password has been successfully reset. You can now log in." , "success" )
226+ return redirect (url_for ('home' ))
227+
228+ return render_template ('new_password.html' , email = email )
229+
230+
231+
111232
112233@app .route ('/login' , methods = ['GET' , 'POST' ])
113234def login ():
114- email = request .form .get ('email' )
115- password = request .form .get ('password' )
116-
117235 if request .method == 'POST' :
236+ print ("Form Data:" , request .form )
237+ email = request .form .get ('email' )
238+ password = request .form .get ('password' )
118239 try :
119240 # Retrieve user data from Firestore
120241 user_ref = db .collection ('users' ).document (email )
@@ -123,11 +244,21 @@ def login():
123244 if user_doc .exists :
124245 stored_hashed_password = user_doc .to_dict ().get ('password' )
125246
126- # Check if the password matches
127- if bcrypt .checkpw (password .encode ('utf-8' ), stored_hashed_password .encode ('utf-8' )):
128- return redirect (url_for ('homepage' , email = email ))
247+ # Ensure the stored hashed password exists
248+ if stored_hashed_password :
249+ # Encode the stored hashed password to bytes
250+ stored_hashed_password_bytes = stored_hashed_password .encode ('utf-8' )
251+
252+ # Check if the password matches
253+ if bcrypt .checkpw (password .encode ('utf-8' ), stored_hashed_password_bytes ):
254+ flash ("Logged in successfully." , "success" )
255+ return redirect (url_for ('homepage' , email = email ))
256+ else :
257+ flash ("Invalid password" , "danger" )
258+ return redirect (url_for ('login' ))
259+
129260 else :
130- flash ("Invalid password " , "danger" )
261+ flash ("Password not set for this account. " , "danger" )
131262 return redirect (url_for ('login' ))
132263 else :
133264 flash ("User not found" , "danger" )
@@ -136,8 +267,10 @@ def login():
136267 except Exception as e :
137268 flash (f"An error occurred: { str (e )} " , "danger" )
138269 return redirect (url_for ('login' ))
139- if request .method == "GET" :
140- return render_template ('login.html' )
270+
271+ # Handle GET requests
272+ return render_template ('login.html' )
273+
141274
142275@app .route ("/dashboard" )
143276def dashboard ():
0 commit comments