forked from Github-Mirrors/canaille
Added an option to disable password recovery
This commit is contained in:
parent
b1f21180df
commit
b7b6040a3e
10 changed files with 48 additions and 4 deletions
11
CHANGES.rst
11
CHANGES.rst
|
@ -3,7 +3,16 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on `Keep a Changelog <https://keepachangelog.com/en/1.0.0/>`_,
|
The format is based on `Keep a Changelog <https://keepachangelog.com/en/1.0.0/>`_,
|
||||||
and this project adheres to `Semantic Versioning <https://semver.org/spec/v2.0.0.html>`_.
|
and this project adheres to `Semantic Versioning <https://semver.org/spec/v2.0.0.html>`_.
|
||||||
|
|
||||||
[0.0.7] - 2022-03-15
|
[0.0.9] - 2022-xx-xx
|
||||||
|
====================
|
||||||
|
|
||||||
|
Added
|
||||||
|
*****
|
||||||
|
|
||||||
|
- ``DISABLE_PASSWORD_RESET`` configuration option to disable password recovery. :pr:`46`
|
||||||
|
|
||||||
|
|
||||||
|
[0.0.8] - 2022-03-15
|
||||||
====================
|
====================
|
||||||
|
|
||||||
Fixed
|
Fixed
|
||||||
|
|
|
@ -179,6 +179,9 @@ def create_app(config=None, validate=True):
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"has_smtp": "SMTP" in app.config,
|
"has_smtp": "SMTP" in app.config,
|
||||||
|
"has_password_recovery": app.config.get(
|
||||||
|
"ENABLE_PASSWORD_RECOVERY", True
|
||||||
|
),
|
||||||
"logo_url": app.config.get("LOGO"),
|
"logo_url": app.config.get("LOGO"),
|
||||||
"favicon_url": app.config.get("FAVICON", app.config.get("LOGO")),
|
"favicon_url": app.config.get("FAVICON", app.config.get("LOGO")),
|
||||||
"website_name": app.config.get("NAME", "Canaille"),
|
"website_name": app.config.get("NAME", "Canaille"),
|
||||||
|
|
|
@ -526,6 +526,9 @@ def impersonate(user, username):
|
||||||
@bp.route("/reset", methods=["GET", "POST"])
|
@bp.route("/reset", methods=["GET", "POST"])
|
||||||
@smtp_needed()
|
@smtp_needed()
|
||||||
def forgotten():
|
def forgotten():
|
||||||
|
if not current_app.config.get("ENABLE_PASSWORD_RECOVERY", True):
|
||||||
|
abort(404)
|
||||||
|
|
||||||
form = ForgottenPasswordForm(request.form)
|
form = ForgottenPasswordForm(request.form)
|
||||||
if not request.form:
|
if not request.form:
|
||||||
return render_template("forgotten-password.html", form=form)
|
return render_template("forgotten-password.html", form=form)
|
||||||
|
@ -562,6 +565,9 @@ def forgotten():
|
||||||
|
|
||||||
@bp.route("/reset/<uid>/<hash>", methods=["GET", "POST"])
|
@bp.route("/reset/<uid>/<hash>", methods=["GET", "POST"])
|
||||||
def reset(uid, hash):
|
def reset(uid, hash):
|
||||||
|
if not current_app.config.get("ENABLE_PASSWORD_RECOVERY", True):
|
||||||
|
abort(404)
|
||||||
|
|
||||||
form = PasswordResetForm(request.form)
|
form = PasswordResetForm(request.form)
|
||||||
user = User.get(uid)
|
user = User.get(uid)
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,10 @@ OIDC_METADATA_FILE = "canaille/conf/openid-configuration.json"
|
||||||
# wether the login exists or not.
|
# wether the login exists or not.
|
||||||
# HIDE_INVALID_LOGINS = false
|
# HIDE_INVALID_LOGINS = false
|
||||||
|
|
||||||
|
# If ENABLE_PASSWORD_RECOVERY is false, then users cannot ask for a password
|
||||||
|
# recovery link by email. This option is true by default.
|
||||||
|
# ENABLE_PASSWORD_RECOVERY = true
|
||||||
|
|
||||||
# The validity duration of registration invitations, in seconds.
|
# The validity duration of registration invitations, in seconds.
|
||||||
# Defaults to 2 days
|
# Defaults to 2 days
|
||||||
# INVITATION_EXPIRATION = 172800
|
# INVITATION_EXPIRATION = 172800
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
|
|
||||||
<div class="ui right aligned container">
|
<div class="ui right aligned container">
|
||||||
<div class="ui stackable buttons">
|
<div class="ui stackable buttons">
|
||||||
{% if has_smtp %}
|
{% if has_smtp and has_password_recovery %}
|
||||||
<a type="button" class="ui right floated button" href="{{ url_for('account.forgotten') }}">{{ _("Forgotten password") }}</a>
|
<a type="button" class="ui right floated button" href="{{ url_for('account.forgotten') }}">{{ _("Forgotten password") }}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<button type="submit" class="ui right floated primary button">{{ _("Continue") }}</button>
|
<button type="submit" class="ui right floated primary button">{{ _("Continue") }}</button>
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
<div class="ui right aligned container">
|
<div class="ui right aligned container">
|
||||||
<div class="ui stackable buttons">
|
<div class="ui stackable buttons">
|
||||||
<a type="button" class="ui right floated button" href="{{ url_for('account.login') }}">{{ _("I am not %(username)s", username=username) }}</a>
|
<a type="button" class="ui right floated button" href="{{ url_for('account.login') }}">{{ _("I am not %(username)s", username=username) }}</a>
|
||||||
{% if has_smtp %}
|
{% if has_smtp and has_password_recovery %}
|
||||||
<a type="button" class="ui right floated button" href="{{ url_for('account.forgotten') }}">{{ _("Forgotten password") }}</a>
|
<a type="button" class="ui right floated button" href="{{ url_for('account.forgotten') }}">{{ _("Forgotten password") }}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<button type="submit" class="ui right floated primary button">{{ _("Sign in") }}</button>
|
<button type="submit" class="ui right floated primary button">{{ _("Sign in") }}</button>
|
||||||
|
|
|
@ -40,6 +40,10 @@ OIDC_METADATA_FILE = "conf/openid-configuration.json"
|
||||||
# wether the login exists or not.
|
# wether the login exists or not.
|
||||||
# HIDE_INVALID_LOGINS = false
|
# HIDE_INVALID_LOGINS = false
|
||||||
|
|
||||||
|
# If ENABLE_PASSWORD_RECOVERY is false, then users cannot ask for a password
|
||||||
|
# recovery link by email. This option is true by default.
|
||||||
|
# ENABLE_PASSWORD_RECOVERY = true
|
||||||
|
|
||||||
# The validity duration of registration invitations, in seconds.
|
# The validity duration of registration invitations, in seconds.
|
||||||
# Defaults to 2 days
|
# Defaults to 2 days
|
||||||
# INVITATION_EXPIRATION = 172800
|
# INVITATION_EXPIRATION = 172800
|
||||||
|
|
|
@ -40,6 +40,10 @@ OIDC_METADATA_FILE = "conf/openid-configuration.json"
|
||||||
# wether the login exists or not.
|
# wether the login exists or not.
|
||||||
# HIDE_INVALID_LOGINS = false
|
# HIDE_INVALID_LOGINS = false
|
||||||
|
|
||||||
|
# If ENABLE_PASSWORD_RECOVERY is false, then users cannot ask for a password
|
||||||
|
# recovery link by email. This option is true by default.
|
||||||
|
# ENABLE_PASSWORD_RECOVERY = true
|
||||||
|
|
||||||
# The validity duration of registration invitations, in seconds.
|
# The validity duration of registration invitations, in seconds.
|
||||||
# Defaults to 2 days
|
# Defaults to 2 days
|
||||||
# INVITATION_EXPIRATION = 172800
|
# INVITATION_EXPIRATION = 172800
|
||||||
|
|
|
@ -49,7 +49,11 @@ Canaille is based on Flask, so any `flask configuration <https://flask.palletspr
|
||||||
|
|
||||||
:HIDE_INVALID_LOGINS:
|
:HIDE_INVALID_LOGINS:
|
||||||
*Optional.* Wether to tell the users if a username exists during failing login attempts.
|
*Optional.* Wether to tell the users if a username exists during failing login attempts.
|
||||||
Defaults to ``True``. This may be a security issue to disable this, as this give a way to malicious people to guess who has an account on this canaille instance.
|
Defaults to ``True``. This may be a security issue to disable this, as this give a way to malicious people to if an account exists on this canaille instance.
|
||||||
|
|
||||||
|
:ENABLE_PASSWORD_RECOVERY:
|
||||||
|
*Optional* Wether the password recovery feature is enabled or not.
|
||||||
|
Defaults to ``True``.
|
||||||
|
|
||||||
:INVITATION_EXPIRATION:
|
:INVITATION_EXPIRATION:
|
||||||
*Optional* The validity duration of registration invitations, in seconds.
|
*Optional* The validity duration of registration invitations, in seconds.
|
||||||
|
|
|
@ -1,3 +1,13 @@
|
||||||
|
def test_password_forgotten_disabled(smtpd, testclient, slapd_connection, user):
|
||||||
|
testclient.app.config["ENABLE_PASSWORD_RECOVERY"] = False
|
||||||
|
|
||||||
|
testclient.get("/reset", status=404)
|
||||||
|
testclient.get("/reset/uid/hash", status=404)
|
||||||
|
|
||||||
|
res = testclient.get("/login")
|
||||||
|
assert "Forgotten password" not in res.text
|
||||||
|
|
||||||
|
|
||||||
def test_password_forgotten(smtpd, testclient, slapd_connection, user):
|
def test_password_forgotten(smtpd, testclient, slapd_connection, user):
|
||||||
res = testclient.get("/reset", status=200)
|
res = testclient.get("/reset", status=200)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue