adds new tests for compromised_password_check_failure situation.

This commit is contained in:
sebastien 2024-11-07 15:51:21 +01:00
parent 9844818280
commit f173a66793
4 changed files with 109 additions and 24 deletions

View file

@ -86,6 +86,37 @@ def password_strength_calculator(password):
return strength_score
def check_if_send_mail_to_admins(form, api_url, hashed_password_suffix):
if current_app.features.has_smtp and not request_is_htmx():
flash(
_(
"Password compromise investigation failed. Please contact the administrators."
),
"error",
)
group_user = Backend.instance.query(models.User)
emails_of_admins = [
user.emails[0]
for user in group_user
if any(group.display_name == "admins" for group in user.groups)
]
if form.user is not None:
user_name = form.user.user_name
user_email = form.user.emails[0]
else:
user_name = form["user_name"].data
user_email = form["emails"].data[0]
for admin_email in emails_of_admins:
send_compromised_password_check_failure_mail(
api_url, user_name, user_email, hashed_password_suffix, admin_email
)
return
return
def compromised_password_validator(form, field):
try:
from hashlib import sha1
@ -94,13 +125,6 @@ def compromised_password_validator(form, field):
except ImportError:
return None
group_user = Backend.instance.query(models.User)
emails_of_admins = []
for user in group_user:
for group in user.groups:
if "admins" == group.display_name:
emails_of_admins.append(user.emails[0])
hashed_password = sha1(field.data.encode("utf-8")).hexdigest()
hashed_password_prefix, hashed_password_suffix = (
hashed_password[:5].upper(),
@ -113,23 +137,8 @@ def compromised_password_validator(form, field):
response = requests.api.get(api_url, timeout=10)
except Exception as e:
print("Error: " + str(e))
if current_app.features.has_smtp and not request_is_htmx():
flash(
_(
"Password compromise investigation failed. Please contact the administrators."
),
"error",
)
if form.user is not None:
user_name = form.user.user_name
user_email = form.user.emails[0]
else:
user_name = form["user_name"].data
user_email = form["emails"].data[0]
for admin_email in emails_of_admins:
send_compromised_password_check_failure_mail(
api_url, user_name, user_email, hashed_password_suffix, admin_email
)
check_if_send_mail_to_admins(form, api_url, hashed_password_suffix)
return None

View file

@ -7,6 +7,7 @@ from flask import current_app
from werkzeug.datastructures import ImmutableMultiDict
from canaille.app.forms import DateTimeUTCField
from canaille.app.forms import compromised_password_validator
from canaille.app.forms import password_length_validator
from canaille.app.forms import password_too_long_validator
from canaille.app.forms import phone_number
@ -333,3 +334,23 @@ def test_maximum_password_length_config(testclient):
password_too_long_validator(None, Field("a" * 4096))
with pytest.raises(wtforms.ValidationError):
password_too_long_validator(None, Field("a" * 4097))
def test_compromised_password_validator(testclient):
class Field:
def __init__(self, data):
self.data = data
compromised_password_validator(None, Field("i'm a little pea"))
compromised_password_validator(None, Field("i'm a little chickpea"))
compromised_password_validator(None, Field("i'm singing in the rain"))
with pytest.raises(wtforms.ValidationError):
compromised_password_validator(None, Field("password"))
with pytest.raises(wtforms.ValidationError):
compromised_password_validator(None, Field("987654321"))
with pytest.raises(wtforms.ValidationError):
compromised_password_validator(None, Field("correct horse battery staple"))
with pytest.raises(wtforms.ValidationError):
compromised_password_validator(None, Field("zxcvbn123"))
with pytest.raises(wtforms.ValidationError):
compromised_password_validator(None, Field("azertyuiop123"))

View file

@ -180,6 +180,31 @@ def test_profile_settings_compromised_password(testclient, logged_user):
with_different_values("i'm a little pea", 'data-percent="100"')
@mock.patch("requests.api.get")
def test_profile_settings_compromised_password_request_api_failed_but_password_updated(
api_get, testclient, user, logged_user, backend
):
api_get.side_effect = mock.Mock(side_effect=Exception())
res = testclient.get("/profile/user/settings", status=200)
res.form["password1"] = "123456789"
res.form["password2"] = "123456789"
res = res.form.submit(name="action", value="edit-settings")
assert (
"error",
"Password compromise investigation failed. Please contact the administrators.",
) in res.flashes
assert ("success", "Profile updated successfully.") in res.flashes
backend.reload(logged_user)
assert logged_user.user_name == "user"
assert backend.check_user_password(logged_user, "123456789")[0]
def test_edition_without_groups(
testclient,
logged_user,

View file

@ -169,3 +169,33 @@ def test_registration_with_compromised_password(testclient, backend, foo_group):
user = backend.get(models.User, user_name="newuser")
assert user is None
@mock.patch("requests.api.get")
def test_registration_with_compromised_password_request_api_failed_but_account_created(
api_get, testclient, backend
):
api_get.side_effect = mock.Mock(side_effect=Exception())
testclient.app.config["CANAILLE"]["ENABLE_REGISTRATION"] = True
testclient.app.config["CANAILLE"]["EMAIL_CONFIRMATION"] = False
assert not backend.query(models.User, user_name="newuser")
res = testclient.get(url_for("core.account.registration"), status=200)
res.form["user_name"] = "newuser"
res.form["password1"] = "123456789"
res.form["password2"] = "123456789"
res.form["family_name"] = "newuser"
res.form["emails-0"] = "newuser@example.com"
res = res.form.submit()
assert (
"error",
"Password compromise investigation failed. Please contact the administrators.",
) in res.flashes
assert ("success", "Your account has been created successfully.") in res.flashes
user = backend.get(models.User, user_name="newuser")
assert user
backend.delete(user)