diff --git a/Lab4/__pycache__/database_helper.cpython-310.pyc b/Lab4/__pycache__/database_helper.cpython-310.pyc index 39a86975cf1d2709547c7edc3fcfc7d630ba111c..f635570f53710411968225763425148b7abec8f0 100644 Binary files a/Lab4/__pycache__/database_helper.cpython-310.pyc and b/Lab4/__pycache__/database_helper.cpython-310.pyc differ diff --git a/Lab4/__pycache__/server.cpython-310.pyc b/Lab4/__pycache__/server.cpython-310.pyc index 32747256fc1acb196f39de31516988c4e65704be..8923e6ded4ef87e497f3fa990279cda3c78dcc4d 100644 Binary files a/Lab4/__pycache__/server.cpython-310.pyc and b/Lab4/__pycache__/server.cpython-310.pyc differ diff --git a/Lab4/database.db b/Lab4/database.db index 7fd372c02c76c6a5d6a8824bc576312f87bdef86..4d522693d6546cc621213a78e6a9558f282423d2 100644 Binary files a/Lab4/database.db and b/Lab4/database.db differ diff --git a/Lab4/database_helper.py b/Lab4/database_helper.py index 2e52d2f10967c104be861b9067c90d8ff74afe04..2b9eb1344fa8996cde47c7e1238d46e1138e45b7 100644 --- a/Lab4/database_helper.py +++ b/Lab4/database_helper.py @@ -173,10 +173,16 @@ def check_user_exists(email): def change_password(email, newpassword): try: - sql = "UPDATE USERS SET PASSWORD = ? WHERE EMAIL = ?;" - get_db().execute(sql, (newpassword, email)) - get_db().commit() - return True + cursor = get_db().cursor() + cursor.execute("SELECT EMAIL FROM USERS WHERE EMAIL=?", [email]) + exists = cursor.fetchone() + if exists: + sql = "UPDATE USERS SET PASSWORD = ? WHERE EMAIL = ?;" + get_db().execute(sql, (newpassword, email)) + get_db().commit() + return True + else: + return False except: return False diff --git a/Lab4/server.py b/Lab4/server.py index c915e2314368c31391893cb1615e4dab4b8c3def..88f37dd51b7b930ffce393c1c7290af830a1ee18 100644 --- a/Lab4/server.py +++ b/Lab4/server.py @@ -1,5 +1,7 @@ -from flask import Flask, request, jsonify +from flask import Flask, request, jsonify, render_template from flask_sock import Sock +from datetime import datetime, timedelta +from flask_mail import Mail, Message import waitress import database_helper @@ -8,11 +10,25 @@ import string import json import re import io +import os +import secrets app = Flask(__name__, template_folder='static') +app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'devkey') +app.config['MAIL_SERVER']='sandbox.smtp.mailtrap.io' +app.config['MAIL_PORT'] = 2525 +app.config['MAIL_USERNAME'] = 'c1159416d31517' +app.config['MAIL_PASSWORD'] = '9971f0f667254a' +app.config['MAIL_USE_TLS'] = True +app.config['MAIL_USE_SSL'] = False +app.config['RECOVERY_TOKEN_LIFETIME'] = timedelta(minutes=1) +app.config['RECOVERY_TOKEN_LENGTH'] = 24 + sockets = Sock(app) +mail = Mail(app) loggedIn_for_ws = {} +tokens = {} @sockets.route("/profil_view") def socket_connection(ws): @@ -32,6 +48,57 @@ def hello_world(): return app.send_static_file("client.html"), 200 +@app.route('/password-recovery', methods=['GET', 'POST']) +def password_recovery(): + if request.method == 'POST': + email = request.form['email'] + if database_helper.check_user_exists(email): + token = generate_recovery_token(email) + send_recovery_link(email, token) + return render_template('password_recovery_sent.html') + else: + return render_template('password_recovery_form.html', message_error="This email doesn't exist") + else: + return render_template('password_recovery_form.html') + +def generate_recovery_token(email): + token = secrets.token_hex(app.config['RECOVERY_TOKEN_LENGTH']) + expiration_date = datetime.utcnow() + app.config['RECOVERY_TOKEN_LIFETIME'] + tokens[token] = {'email': email, 'expiration_date': expiration_date} + return token + +def send_recovery_link(email, token): + msg = Message('Twidder Password Recovery', sender='twidder@mailtrap.io', recipients=[email]) + recovery_link = request.url_root + 'reset-password/' + token + msg.body = f'Click the following link to reset your Twidder password: {recovery_link}' + mail.send(msg) + +@app.route('/reset-password/<token>', methods=['GET', 'POST']) +def reset_password(token): + if token not in tokens: + return render_template('password_recovery_form.html') + + expiration_date = tokens[token]['expiration_date'] + if datetime.utcnow() > expiration_date: + del tokens[token] + return render_template('password_recovery_form.html') + + if token in tokens and request.method == 'GET': + return render_template('recover_password.html') + + if request.method == 'POST': + pass1 = request.form['password1'] + pass2 = request.form['password2'] + email = tokens[token]['email'] + if(len(pass1) > 5 and len(pass2) > 5) and (pass1 == pass2): + del tokens[token] + database_helper.change_password(email, pass1) + return render_template('client.html') + else: + return render_template('recover_password.html', message_error = "Please enter a password of 6 characters at least and enter the same passwords.") + else: + return render_template('password_recovery_form.html') + @app.route("/users/sign_up", methods = ['POST']) def sign_up(): data = request.get_json() @@ -84,7 +151,10 @@ def sign_in(): def sign_out(): token = get_token_from_header() if(token != 1): + email = database_helper.tokenToEmail(token) if database_helper.removeFromLoggedInUsers(token): + del loggedIn_for_ws[email] + print("loggedIn_for_ws : ", loggedIn_for_ws) return "", 200 else: return "", 401 diff --git a/Lab4/static/client.css b/Lab4/static/client.css index 340dedfac6308a89436d7e28a4e2753f1d891836..879a877d07e677808d5759c87aba614d65bbee96 100644 --- a/Lab4/static/client.css +++ b/Lab4/static/client.css @@ -34,6 +34,12 @@ body { width: 600px; } +.forgot-password { + position: absolute; + margin-top: 5px; + margin-left: 60px; +} + h2 { text-align: center; } @@ -91,6 +97,11 @@ img { margin-top: 20px; } +#merror { + color: red; + text-align: center; +} + #panel-default { background-color: darkgrey; border: solid black 1px; diff --git a/Lab4/static/client.html b/Lab4/static/client.html index dc7af93fc34f24fc170610d70eb1e48741d3f9b4..d1b693d2739491af49d02490961a2cfed5daf782 100644 --- a/Lab4/static/client.html +++ b/Lab4/static/client.html @@ -14,6 +14,7 @@ <form id="login" onsubmit="signIn(this); return false;"> <div><label for="email">Email</label><input class="logintexts" type="email" id="l_email" required></div> <div><label for="password">Password</label><input class="logintexts" type="password" id="l_password" required></div> + <div><a href="/password-recovery" class="forgot-password" ">Forgot password?</a></div> <input value="Login" class="lbutton" type="submit"> </form> <form id="signup" onsubmit="signUp(this); return false;"> diff --git a/Lab4/static/client.js b/Lab4/static/client.js index 3bf22126ce679293c07619adb5f5eb8b334b2c69..6a21092085b0ebeffbe87d5128e831aedc59280e 100644 --- a/Lab4/static/client.js +++ b/Lab4/static/client.js @@ -211,6 +211,13 @@ function signIn(formData) { } +function checkpass(formData) { + let passwords = { + password1 : formData.password1.value, + password2 : formData.password2.value + } + window.alert('test'); +} //---------------------------------------SIGN OUT--------------------------------------- function signOut() { @@ -511,7 +518,7 @@ function drop(e) { document.getElementById("drop-zone").classList.remove("hover"); var files = e.dataTransfer.files; var file = files[0]; - + if (file.type.endsWith('png') || file.type.endsWith('jpeg')) { const formData = new FormData(); formData.append('image', file); @@ -542,6 +549,7 @@ function load_profilepicture() { var xhr = new XMLHttpRequest(); xhr.open('GET', '/users/account/getpp/' + localStorage.getItem('reloadHome')); xhr.responseType = 'blob'; + xhr.send(); xhr.onload = function() { if (xhr.status === 200) { // Create a new image object from the blob data @@ -550,5 +558,5 @@ function load_profilepicture() { img.src = window.URL.createObjectURL(blob); } }; - xhr.send(); + } diff --git a/Lab4/static/password_recovery.css b/Lab4/static/password_recovery.css new file mode 100644 index 0000000000000000000000000000000000000000..2d724327f3a5e1e82ada45563ac92c88fa51297a --- /dev/null +++ b/Lab4/static/password_recovery.css @@ -0,0 +1,34 @@ +body { + background-color: #0F98B7; +} +.container { + max-width: 300px; + margin: auto; +} +form { + background-color: #fff; + padding: 20px; + border-radius: 5px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); +} +label { + font-weight: bold; +} + +.form-group { + display: flex; + flex-direction: column; + width: 250px; +} + +.form-group label { + text-align: left; +} + +button { + margin-top: 5px; +} + +#error { + color: red; +} diff --git a/Lab4/static/password_recovery_form.html b/Lab4/static/password_recovery_form.html new file mode 100644 index 0000000000000000000000000000000000000000..f4da50f02c6c06e79a3faa759302787baaa60693 --- /dev/null +++ b/Lab4/static/password_recovery_form.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html> + <head> + <title>Twidder - Password Recovery</title> + <link href="/static/password_recovery.css" type="text/css" rel="stylesheet"> + </head> + <body> + <div class="container"> + <h1 class="mt-5 mb-3">Password Recovery</h1> + <form method="post" action="/password-recovery"> + <div class="form-group"> + <label for="email">Enter your email</label> + <input type="email" class="form-control" id="email" name="email" required> + </div> + <button type="submit" class="btn btn-primary">Send Recovery Link</button> + <p id="error">{{message_error}}<p> + </form> + </div> + </body> +</html> diff --git a/Lab4/static/password_recovery_sent.html b/Lab4/static/password_recovery_sent.html new file mode 100644 index 0000000000000000000000000000000000000000..fd9e290627f1c77294b9bf2d00f9a11da91730ad --- /dev/null +++ b/Lab4/static/password_recovery_sent.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html> + <head> + <title>Twidder - Password Recovery Sent</title> + <link href="/static/password_recovery.css" type="text/css" rel="stylesheet"> + </head> + <body> + <div class="container"> + <h1 class="mt-5 mb-3">Password Recovery Sent</h1> + <p>An email has been sent to your email with a link to reset your password. Please check your inbox.</p> + </div> + </body> +</html> diff --git a/Lab4/static/recover_password.html b/Lab4/static/recover_password.html new file mode 100644 index 0000000000000000000000000000000000000000..54e027082e9d75f1a2e7067ad356e7a1fc58d137 --- /dev/null +++ b/Lab4/static/recover_password.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html> + <head> + <title>Twidder - Change Password</title> + <link href="/static/password_recovery.css" type="text/css" rel="stylesheet"> + </head> + <body> + <div class="container"> + <h1 class="mt-5 mb-3">Change password</h1> + <form method="post"> + <div class="form-group"> + <label for="password1">New password</label> + <input type="password" class="form-control" id="password1" name="password1" required> + <label for="password2" id="test">Write it again</label> + <input type="password" class="form-control" id="password2" name="password2" required> + <button type="submit" class="btn btn-primary">Change</button> + <p id="error">{{message_error}}<p> + </div> + </form> + </div> + </body> +</html>