forked from Github-Mirrors/canaille
fix: crash for passwordless users at login when no SMTP server was configured
This commit is contained in:
parent
3fb5d0149d
commit
b46102bb75
4 changed files with 138 additions and 111 deletions
|
@ -15,6 +15,7 @@ Fixed
|
|||
^^^^^
|
||||
|
||||
- Dark theme colors for better readability
|
||||
- Crash for passwordless users at login when no SMTP server was configured.
|
||||
|
||||
[0.0.53] - 2024-04-22
|
||||
---------------------
|
||||
|
|
|
@ -49,7 +49,7 @@ def login():
|
|||
return render_template("login.html", form=form)
|
||||
|
||||
user = Backend.instance.get_user_from_login(form.login.data)
|
||||
if user and not user.has_password():
|
||||
if user and not user.has_password() and current_app.features.has_smtp:
|
||||
return redirect(url_for("core.auth.firstlogin", user=user))
|
||||
|
||||
if not form.validate():
|
||||
|
@ -81,7 +81,7 @@ def password():
|
|||
)
|
||||
|
||||
user = Backend.instance.get_user_from_login(session["attempt_login"])
|
||||
if user and not user.has_password():
|
||||
if user and not user.has_password() and current_app.features.has_smtp:
|
||||
return redirect(url_for("core.auth.firstlogin", user=user))
|
||||
|
||||
if not form.validate() or not user:
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
import datetime
|
||||
import logging
|
||||
from unittest import mock
|
||||
|
||||
from canaille.app import models
|
||||
|
||||
|
||||
def test_signin_and_out(testclient, user, caplog):
|
||||
|
@ -146,112 +143,6 @@ def test_password_page_already_logged_in(testclient, logged_user):
|
|||
assert res.location == "/profile/user"
|
||||
|
||||
|
||||
def test_user_without_password_first_login(testclient, backend, smtpd):
|
||||
assert len(smtpd.messages) == 0
|
||||
u = models.User(
|
||||
formatted_name="Temp User",
|
||||
family_name="Temp",
|
||||
user_name="temp",
|
||||
emails=["john@doe.com", "johhny@doe.com"],
|
||||
)
|
||||
backend.save(u)
|
||||
|
||||
res = testclient.get("/login", status=200)
|
||||
res.form["login"] = "temp"
|
||||
res = res.form.submit(status=302)
|
||||
|
||||
assert res.location == "/firstlogin/temp"
|
||||
res = res.follow(status=200)
|
||||
res.mustcontain("First login")
|
||||
|
||||
res = res.form.submit(name="action", value="sendmail")
|
||||
assert (
|
||||
"success",
|
||||
"A password initialization link has been sent at your email address. "
|
||||
"You should receive it within a few minutes.",
|
||||
) in res.flashes
|
||||
assert len(smtpd.messages) == 2
|
||||
assert [message["X-RcptTo"] for message in smtpd.messages] == u.emails
|
||||
assert "Password initialization" in smtpd.messages[0].get("Subject")
|
||||
backend.delete(u)
|
||||
|
||||
|
||||
@mock.patch("smtplib.SMTP")
|
||||
def test_first_login_account_initialization_mail_sending_failed(
|
||||
SMTP, testclient, backend, smtpd
|
||||
):
|
||||
SMTP.side_effect = mock.Mock(side_effect=OSError("unit test mail error"))
|
||||
assert len(smtpd.messages) == 0
|
||||
|
||||
u = models.User(
|
||||
formatted_name="Temp User",
|
||||
family_name="Temp",
|
||||
user_name="temp",
|
||||
emails=["john@doe.com"],
|
||||
)
|
||||
backend.save(u)
|
||||
|
||||
res = testclient.get("/firstlogin/temp")
|
||||
res = res.form.submit(name="action", value="sendmail", expect_errors=True)
|
||||
assert (
|
||||
"success",
|
||||
"A password initialization link has been sent at your email address. "
|
||||
"You should receive it within a few minutes.",
|
||||
) not in res.flashes
|
||||
assert ("error", "Could not send the password initialization email") in res.flashes
|
||||
assert len(smtpd.messages) == 0
|
||||
backend.delete(u)
|
||||
|
||||
|
||||
def test_first_login_form_error(testclient, backend, smtpd):
|
||||
assert len(smtpd.messages) == 0
|
||||
u = models.User(
|
||||
formatted_name="Temp User",
|
||||
family_name="Temp",
|
||||
user_name="temp",
|
||||
emails=["john@doe.com"],
|
||||
)
|
||||
backend.save(u)
|
||||
|
||||
res = testclient.get("/firstlogin/temp", status=200)
|
||||
res.form["csrf_token"] = "invalid"
|
||||
res = res.form.submit(
|
||||
name="action", value="sendmail", status=400, expect_errors=True
|
||||
)
|
||||
assert len(smtpd.messages) == 0
|
||||
backend.delete(u)
|
||||
|
||||
|
||||
def test_first_login_page_unavailable_for_users_with_password(
|
||||
testclient, backend, user
|
||||
):
|
||||
testclient.get("/firstlogin/user", status=404)
|
||||
|
||||
|
||||
def test_user_password_deleted_during_login(testclient, backend):
|
||||
u = models.User(
|
||||
formatted_name="Temp User",
|
||||
family_name="Temp",
|
||||
user_name="temp",
|
||||
emails=["john@doe.com"],
|
||||
password="correct horse battery staple",
|
||||
)
|
||||
backend.save(u)
|
||||
|
||||
res = testclient.get("/login")
|
||||
res.form["login"] = "temp"
|
||||
res = res.form.submit().follow()
|
||||
res.form["password"] = "correct horse battery staple"
|
||||
|
||||
u.password = None
|
||||
backend.save(u)
|
||||
|
||||
res = res.form.submit(status=302)
|
||||
assert res.location == "/firstlogin/temp"
|
||||
|
||||
backend.delete(u)
|
||||
|
||||
|
||||
def test_wrong_login(testclient, user):
|
||||
testclient.app.config["CANAILLE"]["HIDE_INVALID_LOGINS"] = True
|
||||
|
||||
|
|
135
tests/core/test_firstlogin.py
Normal file
135
tests/core/test_firstlogin.py
Normal file
|
@ -0,0 +1,135 @@
|
|||
from unittest import mock
|
||||
|
||||
from canaille.app import models
|
||||
|
||||
|
||||
def test_user_without_password_first_login(testclient, backend, smtpd):
|
||||
assert len(smtpd.messages) == 0
|
||||
u = models.User(
|
||||
formatted_name="Temp User",
|
||||
family_name="Temp",
|
||||
user_name="temp",
|
||||
emails=["john@doe.com", "johhny@doe.com"],
|
||||
)
|
||||
backend.save(u)
|
||||
|
||||
res = testclient.get("/login", status=200)
|
||||
res.form["login"] = "temp"
|
||||
res = res.form.submit(status=302)
|
||||
|
||||
assert res.location == "/firstlogin/temp"
|
||||
res = res.follow(status=200)
|
||||
res.mustcontain("First login")
|
||||
|
||||
res = res.form.submit(name="action", value="sendmail")
|
||||
assert (
|
||||
"success",
|
||||
"A password initialization link has been sent at your email address. "
|
||||
"You should receive it within a few minutes.",
|
||||
) in res.flashes
|
||||
assert len(smtpd.messages) == 2
|
||||
assert [message["X-RcptTo"] for message in smtpd.messages] == u.emails
|
||||
assert "Password initialization" in smtpd.messages[0].get("Subject")
|
||||
backend.delete(u)
|
||||
|
||||
|
||||
@mock.patch("smtplib.SMTP")
|
||||
def test_first_login_account_initialization_mail_sending_failed(
|
||||
SMTP, testclient, backend, smtpd
|
||||
):
|
||||
SMTP.side_effect = mock.Mock(side_effect=OSError("unit test mail error"))
|
||||
assert len(smtpd.messages) == 0
|
||||
|
||||
u = models.User(
|
||||
formatted_name="Temp User",
|
||||
family_name="Temp",
|
||||
user_name="temp",
|
||||
emails=["john@doe.com"],
|
||||
)
|
||||
backend.save(u)
|
||||
|
||||
res = testclient.get("/firstlogin/temp")
|
||||
res = res.form.submit(name="action", value="sendmail", expect_errors=True)
|
||||
assert (
|
||||
"success",
|
||||
"A password initialization link has been sent at your email address. "
|
||||
"You should receive it within a few minutes.",
|
||||
) not in res.flashes
|
||||
assert ("error", "Could not send the password initialization email") in res.flashes
|
||||
assert len(smtpd.messages) == 0
|
||||
backend.delete(u)
|
||||
|
||||
|
||||
def test_first_login_form_error(testclient, backend, smtpd):
|
||||
assert len(smtpd.messages) == 0
|
||||
u = models.User(
|
||||
formatted_name="Temp User",
|
||||
family_name="Temp",
|
||||
user_name="temp",
|
||||
emails=["john@doe.com"],
|
||||
)
|
||||
backend.save(u)
|
||||
|
||||
res = testclient.get("/firstlogin/temp", status=200)
|
||||
res.form["csrf_token"] = "invalid"
|
||||
res = res.form.submit(
|
||||
name="action", value="sendmail", status=400, expect_errors=True
|
||||
)
|
||||
assert len(smtpd.messages) == 0
|
||||
backend.delete(u)
|
||||
|
||||
|
||||
def test_first_login_page_unavailable_for_users_with_password(
|
||||
testclient, backend, user
|
||||
):
|
||||
testclient.get("/firstlogin/user", status=404)
|
||||
|
||||
|
||||
def test_user_password_deleted_during_login(testclient, backend):
|
||||
u = models.User(
|
||||
formatted_name="Temp User",
|
||||
family_name="Temp",
|
||||
user_name="temp",
|
||||
emails=["john@doe.com"],
|
||||
password="correct horse battery staple",
|
||||
)
|
||||
backend.save(u)
|
||||
|
||||
res = testclient.get("/login")
|
||||
res.form["login"] = "temp"
|
||||
res = res.form.submit().follow()
|
||||
res.form["password"] = "correct horse battery staple"
|
||||
|
||||
u.password = None
|
||||
backend.save(u)
|
||||
|
||||
res = res.form.submit(status=302)
|
||||
assert res.location == "/firstlogin/temp"
|
||||
|
||||
backend.delete(u)
|
||||
|
||||
|
||||
def test_smtp_disabled(testclient, backend, smtpd):
|
||||
testclient.app.config["CANAILLE"]["SMTP"] = None
|
||||
|
||||
assert len(smtpd.messages) == 0
|
||||
u = models.User(
|
||||
formatted_name="Temp User",
|
||||
family_name="Temp",
|
||||
user_name="temp",
|
||||
emails=["john@doe.com", "johhny@doe.com"],
|
||||
)
|
||||
backend.save(u)
|
||||
|
||||
res = testclient.get("/login", status=200)
|
||||
res.form["login"] = "temp"
|
||||
res = res.form.submit()
|
||||
res = res.follow()
|
||||
res.form["password"] = "incorrect horse"
|
||||
res = res.form.submit()
|
||||
assert ("error", "Login failed, please check your information") in res.flashes
|
||||
res.form["password"] = ""
|
||||
res = res.form.submit()
|
||||
assert ("error", "Login failed, please check your information") in res.flashes
|
||||
|
||||
backend.delete(u)
|
Loading…
Reference in a new issue