create /recovery_code page

This commit is contained in:
Son NK 2020-05-17 10:04:54 +02:00
parent aaa1a869ea
commit 3f7842ed3e
5 changed files with 79 additions and 0 deletions

View File

@ -20,4 +20,5 @@ from .views import (
mailbox_detail,
refused_email,
referral,
recovery_code,
)

View File

@ -0,0 +1,40 @@
{% extends 'default.html' %}
{% set active_page = "setting" %}
{% block title %}
Recovery Codes
{% endblock %}
{% block default_content %}
<div class="card">
<div class="card-body">
<h1 class="h3">Recovery codes</h1>
<p>
If you lose access to your authentication device, you can use one of these backup codes to login to your
account. Each code may be used only once. Make a copy of these codes, and store it somewhere safe.
</p>
<ul>
{% for recovery_code in recovery_codes %}
{% if recovery_code.used %}
<li>
<span style="text-decoration: line-through">{{ recovery_code.code }}</span>.
Used {{ recovery_code.used_at | dt }}.
</li>
{% else %}
<li>
{{ recovery_code.code }}
</li>
{% endif %}
{% endfor %}
</ul>
<form method="post" class="mt-6">
<input type="submit" class="btn btn-outline-primary" value="Generate New Codes">
</form>
<div class="small-text">
Beware: Generating new codes invalidates all the previous ones, make sure to write down the new ones!
</div>
</div>
</div>
{% endblock %}

View File

@ -98,6 +98,7 @@
<a href="{{ url_for('dashboard.fido_setup') }}" class="btn btn-outline-primary">Setup WebAuthn</a>
{% else %}
<a href="{{ url_for('dashboard.fido_cancel') }}" class="btn btn-outline-danger">Disable WebAuthn</a>
<a href="{{ url_for('dashboard.recovery_code_route') }}" class="btn btn-outline-secondary">Recovery Codes</a>
{% endif %}
</div>
</div>
@ -114,6 +115,7 @@
<a href="{{ url_for('dashboard.mfa_setup') }}" class="btn btn-outline-primary">Setup TOTP</a>
{% else %}
<a href="{{ url_for('dashboard.mfa_cancel') }}" class="btn btn-outline-danger">Disable TOTP</a>
<a href="{{ url_for('dashboard.recovery_code_route') }}" class="btn btn-outline-secondary">Recovery Codes</a>
{% endif %}
</div>
</div>

View File

@ -0,0 +1,30 @@
from flask import render_template, flash, redirect, url_for, request
from flask_login import login_required, current_user
from app.dashboard.base import dashboard_bp
from app.log import LOG
from app.models import RecoveryCode
@dashboard_bp.route("/recovery_code", methods=["GET", "POST"])
@login_required
def recovery_code_route():
if not current_user.two_factor_authentication_enabled():
flash("you need to enable either TOTP or WebAuthn", "warning")
return redirect(url_for("dashboard.index"))
recovery_codes = RecoveryCode.query.filter_by(user_id=current_user.id).all()
if request.method == "GET" and not recovery_codes:
# user arrives at this page for the first time
LOG.d("%s has no recovery keys, generate", current_user)
RecoveryCode.generate(current_user)
recovery_codes = RecoveryCode.query.filter_by(user_id=current_user.id).all()
if request.method == "POST":
RecoveryCode.generate(current_user)
flash("New recovery codes generated", "success")
return redirect(url_for("dashboard.recovery_code_route"))
return render_template(
"dashboard/recovery_code.html", recovery_codes=recovery_codes
)

View File

@ -1381,3 +1381,9 @@ class RecoveryCode(db.Model, ModelMixin):
LOG.d("Create recovery codes for %s", user)
db.session.commit()
@classmethod
def empty(cls, user):
"""Delete all recovery codes for user"""
cls.query.filter_by(user_id=user.id).delete()
db.session.commit()