addd password recovery

This commit is contained in:
starnakin 2023-02-12 12:29:18 +01:00
parent 39574a2c06
commit f7d2e8563b
13 changed files with 362 additions and 19 deletions

Binary file not shown.

Binary file not shown.

View File

@ -1 +1,14 @@
{"users": {"1": {"camille@chauvet.pro": "b'$2b$12$AMuNu9CU/lUyaQjDmyWypeVg8beyRA795lrldMGAmHMXaeyfNnke.'"}}} {
"users": {
"1": {
"email": "camille@chauvet.pro",
"password": "b'$2b$12$FVOKVF9p/SlSv9ANVNDW.eOmv1f3qtB0WU86g4ED4B8J2fMBPZu7.'"
},
"2": {
"email": "spam@camille.chauvet.pro",
"password": "b'$2b$12$D2DexgunhMTbYWkXmEQKw.Aa13W9QZE4CjsNUyaU5bSaZ8ZpdJ2hO'"
}
},
"resets": {},
"_default": {}
}

View File

@ -1,27 +1,59 @@
from tinydb import TinyDB, Query from tinydb import TinyDB, where, Query
import uuid
import hasher import hasher
db = TinyDB("./database.json") db = TinyDB("./database.json", indent=4)
query = Query()
users = db.table("users"); users = db.table("users");
def get_users(): def get_users():
return (users.all()) return (users.all())
def get_user_by_email(email: str): def get_user_by_email(email: str):
for user in get_users(): user_lst = users.search(query.email == email)
if (list(user.keys())[0] == email): if (user_lst == []):
return (user); return (None)
return (user_lst[0])
def email_exist(email: str) -> bool:
return (get_user_by_email(email) != None)
def user_exist(email: str): def user_exist(email: str):
return (get_user_by_email(email) != None) return (get_user_by_email(email) != None)
def add_user(email: str, password: str): def add_user(email: str, password: str):
password_hashed = hasher.hash_text(password) password_hashed = hasher.hash_text(password)
users.insert({email: str(password_hashed)}); users.insert({"email": email, "password": str(password_hashed)});
def check_password(email: str, password: str): def check_password(email: str, password: str):
password_hashed = get_user_by_email(email).get(email) password_hashed = get_user_by_email(email).get("password")
password_hashed = bytes(password_hashed[2:-1], "utf-8") password_hashed = bytes(password_hashed[2:-1], "utf-8")
return (hasher.is_same(password, password_hashed)) return (hasher.is_same(password, password_hashed))
def change_user_password(email: str, password: str):
password_hashed = hasher.hash_text(password)
db.update({"password": password_hashed}, query.email == email)
resets = db.table("resets") resets = db.table("resets")
def get_email_by_reset_code(code: str):
user_lst = resets.search(query.code == code)
if (user_lst == []):
return (None)
return (user_lst[0])
def reset_code_exist(code: str) -> bool:
return (get_email_by_reset_code(code) != None)
def remove_reset_code_by_email(email: str):
resets.remove(query.email == email)
def remove_reset_code_by_code(code: str):
resets.remove(query.code == code)
def create_reset_code_by_email(email: str):
code = str(uuid.uuid4());
remove_reset_code_by_email(email);
resets.insert({"email": email, "code": code})
return (code)

View File

@ -1,6 +1,7 @@
import ssl import ssl
import smtplib import smtplib
from email.message import EmailMessage from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
config = { config = {
"server": "ssl0.ovh.net", "server": "ssl0.ovh.net",
@ -11,11 +12,11 @@ config = {
} }
def send_mail(mail_add:str, subject:str, mail_content:str): def send_mail(mail_add:str, subject:str, mail_content:str):
email = EmailMessage() email = MIMEMultipart()
email['From'] = config["display_name"] email['From'] = config["display_name"]
email["To"] = mail_add; email["To"] = mail_add;
email["subject"] = subject; email["subject"] = subject;
email.set_content(mail_content); email.attach(MIMEText(mail_content, "html"))
context = ssl.create_default_context(); context = ssl.create_default_context();
@ -23,4 +24,3 @@ def send_mail(mail_add:str, subject:str, mail_content:str):
smtp.login(config["email"], config["password"]); smtp.login(config["email"], config["password"]);
smtp.sendmail(config["email"], mail_add, email.as_string()); smtp.sendmail(config["email"], mail_add, email.as_string());
send_mail("camille@chauvet.pro", "test", "text")

40
main.py
View File

@ -2,6 +2,7 @@ from flask import Flask, render_template, request, redirect, session
from flask_session import Session from flask_session import Session
import utils import utils
import database import database
import mail
app = Flask(__name__); app = Flask(__name__);
app.config["SESSION_PERMANENT"] = False app.config["SESSION_PERMANENT"] = False
@ -12,7 +13,6 @@ Session(app)
def home(): def home():
return (render_template("home.html")); return (render_template("home.html"));
@app.route("/connected") @app.route("/connected")
def connected(): def connected():
if (not session.get("email")): if (not session.get("email")):
@ -21,12 +21,12 @@ def connected():
@app.route("/login") @app.route("/login")
def login(): def login():
if (session.get("email")):
return (redirect("/connected"))
return (render_template("login.html")); return (render_template("login.html"));
@app.route('/login', methods=['POST']) @app.route('/login', methods=['POST'])
def login_post(): def login_post():
if (not session.get("email")):
return (redirect("/connected"))
email = request.form.get('email') email = request.form.get('email')
password = request.form.get('password') password = request.form.get('password')
if (not database.user_exist(email)): if (not database.user_exist(email)):
@ -43,12 +43,12 @@ def logout():
@app.route("/signin") @app.route("/signin")
def signin(): def signin():
if (session.get("email")):
return (redirect("/connected"))
return (render_template("signin.html")); return (render_template("signin.html"));
@app.route('/signin', methods=['POST']) @app.route('/signin', methods=['POST'])
def signup_post(): def signin_post():
if (not session.get("email")):
return (redirect("/connected"))
email = request.form.get('email') email = request.form.get('email')
password = request.form.get('password') password = request.form.get('password')
repassword = request.form.get('repassword') repassword = request.form.get('repassword')
@ -65,9 +65,35 @@ def signup_post():
def forgot(): def forgot():
return (render_template("forgot.html")); return (render_template("forgot.html"));
@app.route("/forgot", methods=["POST"])
def forgot_post():
email = request.form.get('email')
if (not database.email_exist(email)):
return (render_template("forgot.html", error="Ce compte n'existe pas'"))
uuid = database.create_reset_code_by_email(email);
mail.send_mail(email, "Password recovery", render_template("mails/password_recovery.html", code=uuid))
return (redirect(f"/reset/{uuid}"))
@app.route("/reset/<uuid>") @app.route("/reset/<uuid>")
def reset(uuid): def reset(uuid):
return ("bozo") if (not database.reset_code_exist(uuid)):
return (render_template("error.html", error="code inconnu"))
email = database.get_email_by_reset_code(uuid)
email = email["email"]
return (render_template("reset.html", email=email))
@app.route("/reset/<uuid>", methods=['POST'])
def reset_post(uuid):
if (not database.reset_code_exist(uuid)):
return (render_template("error.html", error="code inconnu"))
email = database.get_email_by_reset_code(uuid)
password = request.form.get('password')
repassword = request.form.get('repassword')
if (password != repassword):
return (render_template("reset.html", error="Les deux mots de passe sont differents"))
database.change_user_password(email, password);
database.remove_reset_code_by_code(uuid);
return (redirect("/login"))
@app.route("/join/<uuid>") @app.route("/join/<uuid>")
def join(uuid): def join(uuid):

14
templates/connected.html Normal file
View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>PyMenu</title>
</head>
<body>
<h1>connected</h1>
<h2>Welcome to FlaskApp!</h2>
<a href="/logout">
<input type="button" value="logout">
</a>
</body>
</html>

16
templates/error.html Normal file
View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>PyMenu</title>
</head>
<body>
<h1>Error</h1>
{% if error %}
<h2>{{error}}</h2>
{% endif %}
<a href="/">
<input type="button" value="Revenir a l'acceuil">
</a>
</body>
</html>

83
templates/forgot.html Normal file
View File

@ -0,0 +1,83 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="index.css" />
<title>Beyond School</title>
</head>
<body>
<div id="container">
<form method="post">
<h2>Mot de passe oublié ?</h2>
<label><b>Email :</b></label>
<input type="email" name="email" placeholder="Insérez votre email" required>
<br>
{% if error %}
<p>{{error}}</p>
{% endif %}
<input type="submit" value="Valider">
<a href="/login">
<input type="button" value="connection">
</a>
</form>
</div>
<style>
body{
background: #67BE4B;
}
#container{
width:400px;
margin:0 auto;
margin-top:10%;
}
/* Bordered form */
form {
width:100%;
padding: 40px;
border: 1px solid #f1f1f1;
background: #fff;
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);
}
#container h2{
width: 38%;
margin: 0 auto;
padding-bottom: 10px;
text-align: center;
}
/* Full-width inputs */
input[type=email], input[type=password], input[type=number] {
width: 100%;
padding: 12px 20px;
margin: 8px 0;
display: inline-block;
border: 1px solid #ccc;
box-sizing: border-box;
}
/* Set a style for all buttons */
input[type=submit], input[type=button] {
background-color: #53af57;
color: white;
padding: 14px 20px;
margin: 8px 0;
border: none;
cursor: pointer;
width: 100%;
}
input[type=submit]:hover {
background-color: white;
color: #53af57;
border: 1px solid #53af57;
}
h6 {
float: right;
}
p {
color: red;
}
</style>
</body>
</html>

View File

@ -0,0 +1,76 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="index.css" />
<title>Beyond School</title>
</head>
<body>
<div id="container">
<form method="post">
<h2>Mot de passe oublié ?</h2>
<a href="http://localhost:5000/reset/{{code}}">
<input type="button" value="connection">
</a>
</form>
</div>
<style>
body{
background: #67BE4B;
}
#container{
width:400px;
margin:0 auto;
margin-top:10%;
}
/* Bordered form */
form {
width:100%;
padding: 40px;
border: 1px solid #f1f1f1;
background: #fff;
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);
}
#container h2{
width: 38%;
margin: 0 auto;
padding-bottom: 10px;
text-align: center;
}
/* Full-width inputs */
input[type=email], input[type=password], input[type=number] {
width: 100%;
padding: 12px 20px;
margin: 8px 0;
display: inline-block;
border: 1px solid #ccc;
box-sizing: border-box;
}
/* Set a style for all buttons */
input[type=submit], input[type=button] {
background-color: #53af57;
color: white;
padding: 14px 20px;
margin: 8px 0;
border: none;
cursor: pointer;
width: 100%;
}
input[type=submit]:hover {
background-color: white;
color: #53af57;
border: 1px solid #53af57;
}
h6 {
float: right;
}
p {
color: red;
}
</style>
</body>
</html>

83
templates/reset.html Normal file
View File

@ -0,0 +1,83 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="index.css" />
<title>Beyond School</title>
</head>
<body>
<div id="container">
<form name="signin" method="post">
<h2>Connection</h2>
<label><b>Email :</b></label>
<input type="email" name="email" placeholder={{email}} disabled=true>
<br>
<label><b>Mot de passe :</b></label>
<input type="password" name="password" placeholder="Insérez votre mot de passe" required>
<br>
<label><b>Mot de passe :</b></label>
<input type="password" name="repassword" placeholder="Insérez votre mot de passe" required>
<br>
<input type="submit" value="Changer de mot de passe">
</form>
</div>
<style>
body{
background: #67BE4B;
}
#container{
width:400px;
margin:0 auto;
margin-top:10%;
}
/* Bordered form */
form {
width:100%;
padding: 40px;
border: 1px solid #f1f1f1;
background: #fff;
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);
}
#container h2{
width: 38%;
margin: 0 auto;
padding-bottom: 10px;
text-align: center;
}
/* Full-width inputs */
input[type=email], input[type=password], input[type=number] {
width: 100%;
padding: 12px 20px;
margin: 8px 0;
display: inline-block;
border: 1px solid #ccc;
box-sizing: border-box;
}
/* Set a style for all buttons */
input[type=submit], input[type=button] {
background-color: #53af57;
color: white;
padding: 14px 20px;
margin: 8px 0;
border: none;
cursor: pointer;
width: 100%;
}
input[type=submit]:hover {
background-color: white;
color: #53af57;
border: 1px solid #53af57;
}
h6 {
float: right;
}
p {
color: red
}
</style>
</body>
</html>