Commit 0a9a5706 authored by kaiyou's avatar kaiyou

Implement admin-driven password reset

parent bb89d429
Pipeline #1485 passed with stage
in 13 minutes and 39 seconds
......@@ -2,6 +2,7 @@ from hiboo import models, utils, security
from hiboo.account import blueprint, forms
from flask_babel import lazy_gettext as _
import datetime
import flask_login
import flask
......@@ -49,3 +50,24 @@ def signup():
return flask.redirect(utils.url_or_intent(".home"))
return flask.render_template("account_signup.html", form=form)
@blueprint.route("/reset/<token_uuid>", methods=["GET", "POST"])
def reset(token_uuid):
token = models.ResetToken.query.get(token_uuid) or flask.abort(403)
if token.updated_at is not None or token.expired_at <
flask.flash(_("Invalid or expired reset link"), "danger")
return flask.redirect(flask.url_for(".signin"))
form = forms.PasswordForm()
del form.old
if form.validate_on_submit():
token.expired_at =
auth = token.user.auths[0]
models.log(models.History.PASSWORD, user=token.user)
flask.flash(_("Successfully reset your password"), "success")
return flask.redirect(flask.url_for(".signin"))
return flask.render_template("account_reset.html", form=form)
\ No newline at end of file
{% extends "form.html" %}
{% block title %}{% trans %}Reset your password{% endtrans %}{% endblock %}
......@@ -221,6 +221,15 @@ class ClaimName(db.Model):
username = db.Column(db.String(255), nullable=False)
class ResetToken(db.Model):
""" A reset token is used to reset authentication for a given user.
__tablename__ = "resettoken"
user_uuid = db.Column(db.String(36), db.ForeignKey(User.uuid))
user = db.relationship(User)
expired_at = db.Column(db.DateTime, nullable=False)
class History(db.Model):
""" Records an even in an account's or profile's lifetime.
......@@ -56,3 +56,7 @@
{% endblock %}
{% block actions %}
<a href="{{ url_for(".password_reset", user_uuid=user.uuid) }}" class="btn btn-primary">{% trans %}Password reset{% endtrans %}</a>
{% endblock %}
from hiboo.user import blueprint, forms
from hiboo import models, utils, security
from flask_babel import lazy_gettext as _
import datetime
import flask
......@@ -24,3 +26,17 @@ def list():
def details(user_uuid):
user = models.User.query.get(user_uuid) or flask.abort(404)
return flask.render_template("user_details.html", user=user)
@blueprint.route("/reset/<user_uuid>", methods=["GET", "POST"])
@security.confirmation_required("generate a password reset link")
def password_reset(user_uuid):
user = models.User.query.get(user_uuid) or flask.abort(404)
expired = + datetime.timedelta(days=1)
token = models.ResetToken(user=user, expired_at=expired)
reset_link = flask.url_for("account.reset", token_uuid=token.uuid, _external=True)
flask.flash(_("Reset link: {}").format(reset_link), "success")
return flask.redirect(flask.url_for(".details", user_uuid=user.uuid))
\ No newline at end of file
""" Add reset tokens
Revision ID: 665cdee2f311
Revises: 29a70a960a97
Create Date: 2020-08-02 14:34:56.262198
from alembic import op
import sqlalchemy as sa
revision = '665cdee2f311'
down_revision = '29a70a960a97'
branch_labels = None
depends_on = None
def upgrade():
sa.Column('user_uuid', sa.String(length=36), nullable=True),
sa.Column('expired_at', sa.DateTime(), nullable=False),
sa.Column('uuid', sa.String(length=36), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('comment', sa.String(length=255), nullable=True),
sa.ForeignKeyConstraint(['user_uuid'], ['user.uuid'], ),
def downgrade():
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment