Skip to content

Commit

Permalink
feat(auth): use django-zxcvbn-password-validator for zxcvbn integration
Browse files Browse the repository at this point in the history
django-zxcvbn-password is no longer maitained, so better switch to a
maintained alternative.
  • Loading branch information
nijel committed Feb 25, 2025
1 parent e9cbddd commit 50ab644
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 43 deletions.
15 changes: 4 additions & 11 deletions docs/admin/auth.rst
Original file line number Diff line number Diff line change
Expand Up @@ -455,24 +455,17 @@ Password authentication
-----------------------

The default :file:`settings.py` comes with a reasonable set of
:setting:`django:AUTH_PASSWORD_VALIDATORS`:

* Passwords can't be too similar to your other personal info.
* Passwords must contain at least 10 characters.
* Passwords can't be a commonly used password.
* Passwords can't be entirely numeric.
* Passwords can't consist of a single character or only whitespace.
* Passwords can't match a password you have used in the past.

You can customize this setting to match your password policy.
:setting:`django:AUTH_PASSWORD_VALIDATORS` that ensures that weak passwords are
not allowed. You can customize this setting to match your password policy.

Additionally you can also install
`django-zxcvbn-password <https://pypi.org/project/django-zxcvbn-password/>`_
`django-zxcvbn-password-validator <https://github.com/Pierre-Sassoulas/django-zxcvbn-password-validator>`_
which gives quite realistic estimates of password difficulty and allows rejecting
passwords below a certain threshold.

.. seealso::

:setting:`PASSWORD_MINIMAL_STRENGTH`,
:envvar:`WEBLATE_MIN_PASSWORD_SCORE`

.. _saml-auth:
Expand Down
17 changes: 17 additions & 0 deletions docs/admin/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1357,6 +1357,23 @@ List for credentials for Pagure servers.
:ref:`vcs-pagure`,
`Pagure API <https://pagure.io/api/0/>`_

.. setting:: PASSWORD_MINIMAL_STRENGTH

PASSWORD_MINIMAL_STRENGTH
-------------------------

.. versionadded:: 5.10.2

Minimal password score as evaluated by the `zxcvbn
<https://github.com/dwolfhub/zxcvbn-python>`_ password strength estimator.

Defaults to 0, which means strength checking is disabled.

.. seealso::

:ref:`password-authentication`,
:envvar:`WEBLATE_MIN_PASSWORD_SCORE`


.. setting:: PRIVACY_URL

Expand Down
7 changes: 6 additions & 1 deletion docs/admin/install/docker.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1437,9 +1437,14 @@ Other authentication settings
.. envvar:: WEBLATE_MIN_PASSWORD_SCORE

Minimal password score as evaluated by the `zxcvbn
<https://github.com/dropbox/zxcvbn>`_ password strength estimator.
<https://github.com/dwolfhub/zxcvbn-python>`_ password strength estimator.
Defaults to 3, set to 0 to disable strength checking.

.. seealso::

:ref:`password-authentication`,
:setting:`PASSWORD_MINIMAL_STRENGTH`


PostgreSQL database setup
+++++++++++++++++++++++++
Expand Down
4 changes: 3 additions & 1 deletion docs/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ Weblate 5.10.2

.. rubric:: Compatibility

* Weblate has switched to a different library for zxcvbn integration, as the old one is no longer maintained, see :ref:`password-authentication`.

.. rubric:: Upgrading

Please follow :ref:`generic-upgrade-instructions` in order to perform update.

* There are several changes in :file:`settings_example.py`, most notable are changed settings ``AUTH_PASSWORD_VALIDATORS``; please adjust your settings accordingly.
* There are several changes in :file:`settings_example.py`, most notable are changed settings ``AUTH_PASSWORD_VALIDATORS`` and ``INSTALLED_APPS``; please adjust your settings accordingly.

.. rubric:: Contributors

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ wsgi = [
"gunicorn==23.0.0"
]
zxcvbn = [
"django-zxcvbn-password==2.1.1"
"django-zxcvbn-password-validator>=1.4.5,<1.5"
]

[project.readme]
Expand Down
15 changes: 8 additions & 7 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 19 additions & 11 deletions weblate/settings_docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,9 @@
SOCIAL_AUTH_SLUGIFY_USERNAMES = True
SOCIAL_AUTH_SLUGIFY_FUNCTION = "weblate.accounts.pipeline.slugify_username"

# Value higher than 0 enables validation using zxcvbn
PASSWORD_MINIMAL_STRENGTH = get_env_int("WEBLATE_MIN_PASSWORD_SCORE", 3)

# Password validation configuration
AUTH_PASSWORD_VALIDATORS = [
{
Expand All @@ -645,22 +648,23 @@
"OPTIONS": {"min_length": 10},
},
{"NAME": "weblate.accounts.password_validation.MaximalLengthValidator"},
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
{"NAME": "weblate.accounts.password_validation.CharsPasswordValidator"},
{"NAME": "weblate.accounts.password_validation.PastPasswordsValidator"},
]

# Optional password strength validation by django-zxcvbn-password
MIN_PASSWORD_SCORE = get_env_int("WEBLATE_MIN_PASSWORD_SCORE", 3)
if MIN_PASSWORD_SCORE:
if PASSWORD_MINIMAL_STRENGTH > 0:
AUTH_PASSWORD_VALIDATORS.append(
{
"NAME": "zxcvbn_password.ZXCVBNValidator",
"OPTIONS": {
"min_score": MIN_PASSWORD_SCORE,
"user_attributes": ("username", "email", "full_name"),
{"NAME": "django_zxcvbn_password_validator.ZxcvbnPasswordValidator"}
)
else:
AUTH_PASSWORD_VALIDATORS.extend(
[
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"
},
}
{"NAME": "weblate.accounts.password_validation.CharsPasswordValidator"},
]
)

# Password hashing (prefer Argon)
Expand Down Expand Up @@ -791,6 +795,10 @@
"drf_standardized_errors",
]

# django_zxcvbn_password_validator integration
if PASSWORD_MINIMAL_STRENGTH > 0:
INSTALLED_APPS.append("django_zxcvbn_password_validator")

# Legal integration
LEGAL_INTEGRATION = get_env_str("WEBLATE_LEGAL_INTEGRATION")
if LEGAL_INTEGRATION:
Expand Down
34 changes: 23 additions & 11 deletions weblate/settings_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,9 @@
SOCIAL_AUTH_SLUGIFY_USERNAMES = True
SOCIAL_AUTH_SLUGIFY_FUNCTION = "weblate.accounts.pipeline.slugify_username"

# Value higher than 0 enables validation using zxcvbn
PASSWORD_MINIMAL_STRENGTH = 0

# Password validation configuration
AUTH_PASSWORD_VALIDATORS = [
{
Expand All @@ -349,20 +352,25 @@
"OPTIONS": {"min_length": 10},
},
{"NAME": "weblate.accounts.password_validation.MaximalLengthValidator"},
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
{"NAME": "weblate.accounts.password_validation.CharsPasswordValidator"},
{"NAME": "weblate.accounts.password_validation.PastPasswordsValidator"},
# Optional password strength validation by django-zxcvbn-password
# {
# "NAME": "zxcvbn_password.ZXCVBNValidator",
# "OPTIONS": {
# "min_score": 3,
# "user_attributes": ("username", "email", "full_name")
# }
# },
]

# Optional password strength validation by django-zxcvbn-password-validator
if PASSWORD_MINIMAL_STRENGTH > 0:
AUTH_PASSWORD_VALIDATORS.append(
{"NAME": "django_zxcvbn_password_validator.ZxcvbnPasswordValidator"}
)
else:
AUTH_PASSWORD_VALIDATORS.extend(
[
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"
},
{"NAME": "weblate.accounts.password_validation.CharsPasswordValidator"},
]
)

# Password hashing (prefer Argon)
PASSWORD_HASHERS = [
"django.contrib.auth.hashers.Argon2PasswordHasher",
Expand Down Expand Up @@ -450,6 +458,10 @@
"drf_standardized_errors",
]

# django_zxcvbn_password_validator integration
if PASSWORD_MINIMAL_STRENGTH > 0:
INSTALLED_APPS.append("django_zxcvbn_password_validator")

# Custom exception reporter to include some details
DEFAULT_EXCEPTION_REPORTER_FILTER = "weblate.trans.debug.WeblateExceptionReporterFilter"

Expand Down

0 comments on commit 50ab644

Please sign in to comment.