Implement password reset
This commit is contained in:
parent
7fcbac2e83
commit
da0dd066d3
|
@ -0,0 +1,13 @@
|
||||||
|
Someone, probably you, wants to reset your donor password.
|
||||||
|
|
||||||
|
To proceed, click this link:
|
||||||
|
|
||||||
|
{{root}}/password-reset/{{user.password_reset}}
|
||||||
|
|
||||||
|
This link expires in 24 hours. If you don't want to change your password or you
|
||||||
|
weren't expecting this email, just ignore it.
|
||||||
|
|
||||||
|
If you have questions, send an email to {{your_email}}.
|
||||||
|
|
||||||
|
--
|
||||||
|
{{your_name}}
|
|
@ -5,7 +5,7 @@ from fosspay.objects import *
|
||||||
from fosspay.database import db
|
from fosspay.database import db
|
||||||
from fosspay.common import *
|
from fosspay.common import *
|
||||||
from fosspay.config import _cfg, load_config
|
from fosspay.config import _cfg, load_config
|
||||||
from fosspay.email import send_thank_you
|
from fosspay.email import send_thank_you, send_password_reset
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import locale
|
import locale
|
||||||
|
@ -40,13 +40,13 @@ def setup():
|
||||||
email = request.form.get("email")
|
email = request.form.get("email")
|
||||||
password = request.form.get("password")
|
password = request.form.get("password")
|
||||||
if not email or not password:
|
if not email or not password:
|
||||||
return redirect("/") # TODO: Tell them what they did wrong (i.e. being stupid)
|
return redirect("..") # TODO: Tell them what they did wrong (i.e. being stupid)
|
||||||
user = User(email, password)
|
user = User(email, password)
|
||||||
user.admin = True
|
user.admin = True
|
||||||
db.add(user)
|
db.add(user)
|
||||||
db.commit()
|
db.commit()
|
||||||
login_user(user)
|
login_user(user)
|
||||||
return redirect("/admin?first-run=1")
|
return redirect("admin?first-run=1")
|
||||||
|
|
||||||
@html.route("/admin")
|
@html.route("/admin")
|
||||||
@adminrequired
|
@adminrequired
|
||||||
|
@ -72,14 +72,14 @@ def create_project():
|
||||||
project = Project(name)
|
project = Project(name)
|
||||||
db.add(project)
|
db.add(project)
|
||||||
db.commit()
|
db.commit()
|
||||||
return redirect("/admin")
|
return redirect("admin")
|
||||||
|
|
||||||
@html.route("/login", methods=["GET", "POST"])
|
@html.route("/login", methods=["GET", "POST"])
|
||||||
def login():
|
def login():
|
||||||
if current_user:
|
if current_user:
|
||||||
if current_user.admin:
|
if current_user.admin:
|
||||||
return redirect("/admin")
|
return redirect("admin")
|
||||||
return redirect("/panel")
|
return redirect("panel")
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
return render_template("login.html")
|
return render_template("login.html")
|
||||||
email = request.form.get("email")
|
email = request.form.get("email")
|
||||||
|
@ -93,14 +93,14 @@ def login():
|
||||||
return render_template("login.html", errors=True)
|
return render_template("login.html", errors=True)
|
||||||
login_user(user)
|
login_user(user)
|
||||||
if user.admin:
|
if user.admin:
|
||||||
return redirect("/admin")
|
return redirect("admin")
|
||||||
return redirect("/panel")
|
return redirect("panel")
|
||||||
|
|
||||||
@html.route("/logout")
|
@html.route("/logout")
|
||||||
@loginrequired
|
@loginrequired
|
||||||
def logout():
|
def logout():
|
||||||
logout_user()
|
logout_user()
|
||||||
return redirect("/")
|
return redirect("..")
|
||||||
|
|
||||||
@html.route("/donate", methods=["POST"])
|
@html.route("/donate", methods=["POST"])
|
||||||
@json_output
|
@json_output
|
||||||
|
@ -171,23 +171,41 @@ def donate():
|
||||||
else:
|
else:
|
||||||
return { "success": True, "new_account": new_account }
|
return { "success": True, "new_account": new_account }
|
||||||
|
|
||||||
|
def issue_password_reset(email):
|
||||||
|
user = User.query.filter(User.email == email).first()
|
||||||
|
if not user:
|
||||||
|
return render_template("reset.html", errors="No one with that email found.")
|
||||||
|
user.password_reset = binascii.b2a_hex(os.urandom(20)).decode("utf-8")
|
||||||
|
user.password_reset_expires = datetime.now() + timedelta(days=1)
|
||||||
|
send_password_reset(user)
|
||||||
|
db.commit()
|
||||||
|
return render_template("reset.html", done=True)
|
||||||
|
|
||||||
@html.route("/password-reset", methods=['GET', 'POST'], defaults={'token': None})
|
@html.route("/password-reset", methods=['GET', 'POST'], defaults={'token': None})
|
||||||
@html.route("/password-reset/<token>", methods=['GET', 'POST'])
|
@html.route("/password-reset/<token>", methods=['GET', 'POST'])
|
||||||
def reset_password(token):
|
def reset_password(token):
|
||||||
if not token and request.method == "POST":
|
if request.method == "GET" and not token:
|
||||||
|
return render_template("reset.html")
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
token = request.form.get("token")
|
token = request.form.get("token")
|
||||||
|
email = request.form.get("email")
|
||||||
|
|
||||||
|
if email:
|
||||||
|
return issue_password_reset(email)
|
||||||
|
|
||||||
if not token:
|
if not token:
|
||||||
redirect("/")
|
return redirect("..")
|
||||||
else:
|
|
||||||
redirect("/")
|
|
||||||
user = User.query.filter(User.password_reset == token).first()
|
user = User.query.filter(User.password_reset == token).first()
|
||||||
if not user:
|
if not user:
|
||||||
redirect("/")
|
return render_template("reset.html", errors="This link has expired.")
|
||||||
|
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
if user.password_reset_expires == None or user.password_reset_expires < datetime.now():
|
if user.password_reset_expires == None or user.password_reset_expires < datetime.now():
|
||||||
return render_template("reset.html", expired=True)
|
return render_template("reset.html", errors="This link has expired.")
|
||||||
if user.password_reset != token:
|
if user.password_reset != token:
|
||||||
redirect("/")
|
redirect("..")
|
||||||
return render_template("reset.html", token=token)
|
return render_template("reset.html", token=token)
|
||||||
else:
|
else:
|
||||||
if user.password_reset_expires == None or user.password_reset_expires < datetime.now():
|
if user.password_reset_expires == None or user.password_reset_expires < datetime.now():
|
||||||
|
@ -202,7 +220,7 @@ def reset_password(token):
|
||||||
user.password_reset_expires = None
|
user.password_reset_expires = None
|
||||||
db.commit()
|
db.commit()
|
||||||
login_user(user)
|
login_user(user)
|
||||||
return redirect("/panel")
|
return redirect("panel")
|
||||||
|
|
||||||
@html.route("/panel")
|
@html.route("/panel")
|
||||||
@loginrequired
|
@loginrequired
|
||||||
|
|
|
@ -31,3 +31,23 @@ def send_thank_you(user, amount, monthly):
|
||||||
message['To'] = user.email
|
message['To'] = user.email
|
||||||
smtp.sendmail(_cfg("smtp-from"), [ user.email ], message.as_string())
|
smtp.sendmail(_cfg("smtp-from"), [ user.email ], message.as_string())
|
||||||
smtp.quit()
|
smtp.quit()
|
||||||
|
|
||||||
|
def send_password_reset(user):
|
||||||
|
if _cfg("smtp-host") == "":
|
||||||
|
return
|
||||||
|
smtp = smtplib.SMTP(_cfg("smtp-host"), _cfgi("smtp-port"))
|
||||||
|
smtp.login(_cfg("smtp-user"), _cfg("smtp-password"))
|
||||||
|
with open("emails/reset-password") as f:
|
||||||
|
message = MIMEText(html.parser.HTMLParser().unescape(\
|
||||||
|
pystache.render(f.read(), {
|
||||||
|
"user": user,
|
||||||
|
"root": _cfg("protocol") + "://" + _cfg("domain"),
|
||||||
|
"your_name": _cfg("your-name"),
|
||||||
|
"your_email": _cfg("your-email")
|
||||||
|
})))
|
||||||
|
message['X-MC-PreserveRecipients'] = "false"
|
||||||
|
message['Subject'] = "Reset your donor password"
|
||||||
|
message['From'] = _cfg("smtp-from")
|
||||||
|
message['To'] = user.email
|
||||||
|
smtp.sendmail(_cfg("smtp-from"), [ user.email ], message.as_string())
|
||||||
|
smtp.quit()
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
{% extends "layout.html" %}
|
||||||
|
{% block body %}
|
||||||
|
<div class="well">
|
||||||
|
<div class="container">
|
||||||
|
<h1>Donate to {{ _cfg("your-name") }}</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 col-md-offset-3">
|
||||||
|
<h1>Reset Password</h1>
|
||||||
|
{% if errors %}
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<p>
|
||||||
|
{{ errors }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if done %}
|
||||||
|
<p>
|
||||||
|
An email should arrive shortly. If you need help, contact
|
||||||
|
<a href="mailto:{{_cfg("your-email")}}">{{_cfg("your-email")}}</a>.
|
||||||
|
</p>
|
||||||
|
{% elif token %}
|
||||||
|
<form action="{{root}}/password-reset" method="POST">
|
||||||
|
<div class="form-group">
|
||||||
|
<input class="form-control" type="password" name="password" placeholder="New password" />
|
||||||
|
<input type="hidden" name="token" value="{{ token }}" />
|
||||||
|
</div>
|
||||||
|
<input type="submit" value="Submit" class="btn btn-primary" />
|
||||||
|
</form>
|
||||||
|
{% else %}
|
||||||
|
<form action="{{root}}/password-reset" method="POST">
|
||||||
|
<div class="form-group">
|
||||||
|
<input class="form-control" type="text" name="email" placeholder="your@email.com" />
|
||||||
|
</div>
|
||||||
|
<input type="submit" value="Submit" class="btn btn-primary" />
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
Loading…
Reference in New Issue