Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Share a patch for Django 4.2 #16

Open
yarin-zhang opened this issue Mar 26, 2024 · 2 comments
Open

Share a patch for Django 4.2 #16

yarin-zhang opened this issue Mar 26, 2024 · 2 comments

Comments

@yarin-zhang
Copy link

I've been using this module to add pbkdf2_sha256 support for my Django project.

However, when I upgraded Django from 3.2 to 4.2, I encountered a problem due to the deprecation of ugettext_noop in Django 4.2, which caused errors in the project. Aside from this, Passlib worked well without any other issues.

Since both this project and its dependency, passlib, invoked ugettext_noop, it seemed that simply updating the reference to ugettext_noop (actually just replacing ugettext_noop with gettext_noop) might solve the problem.

But I found that both libraries haven't been updated for a while, and just submitting a PR here wouldn't fix the issues in both libraries. So, I tried using a patching approach to address this issue and am sharing it for others who might encounter the same problem.


My Approach

You'll need to create a new app, for example, passlib_patch, and then add a patches.py file.

from django.utils.translation import gettext_noop as _

# This file is a patch for django-hashers-passlib==0.4 and passlib==1.7.4 with Django 4.2. 
# These packages have not been updated for a long time and do not support Django 4.2.

def apply_patches():
    # Patch for passlib.ext.django.utils
    try:
        from passlib.ext.django.utils import _PasslibHasherWrapper

        # Patched safe_summary method to use the patched _ function instead of the original ugettext_noop
        def patched_safe_summary(self, encoded):
            from collections import OrderedDict
            from django.contrib.auth.hashers import mask_hash
            handler = self.passlib_handler
            items = [
                (_('algorithm'), handler.name),
            ]
            if hasattr(handler, "parsehash"):
                kwds = handler.parsehash(encoded, sanitize=mask_hash)
                for key, value in kwds.items():
                    key = self._translate_kwds.get(key, key)
                    items.append((_(key), value))
            return OrderedDict(items)

        _PasslibHasherWrapper.safe_summary = patched_safe_summary
    except ImportError as e:
        # Handle the case where passlib is not installed
        print(f"Failed to apply patch for passlib: {e}")

    # Patch for hashers_passlib
    try:
        # Check if ugettext_noop already exists, if not, add it
        from django.utils.translation import ugettext_noop
    except ImportError:
        # Dynamically add ugettext_noop pointing to gettext_noop in the django.utils.translation module
        import django.utils.translation
        django.utils.translation.ugettext_noop = _

        # Now safely import hashers_passlib since ugettext_noop has been added
        import hashers_passlib

        # Since we've successfully imported hashers_passlib, this indicates the patch has taken effect,
        # If necessary, original state can be restored here
        # However, typically, if ugettext_noop is not directly used elsewhere in the Django project, restoration may not be needed

    except Exception as e:
        print(f"Unexpected error while applying patch for hashers_passlib: {e}")

Then add the following code in apps.py:

from django.apps import AppConfig

class PasslibPatchConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'passlib_patch'

    def ready(self):
        from .patches import apply_patches
        apply_patches()

And add in __init__.py:

from . import patches

default_app_config = 'passlib_patch.apps.PasslibPatchConfig'

I wrote a tests.py file and passed the tests.

from django.test import TestCase
from django.contrib.auth import get_user_model
from passlib.hash import django_pbkdf2_sha256 as handler
from wp_user.patches import apply_patches

apply_patches()

class PasslibHashersTestCase(TestCase):
    def test_passlib_functionality(self):
        hashed = handler.hash("s3cr3t")
        self.assertTrue(handler.verify("s3cr3t", hashed), "Password verification failed!")

    def test_hashers_passlib_integration(self):
        User = get_user_model()
        user = User(username='testuser')
        user.set_password('my_password')
        user.save()

        self.assertTrue(user.check_password('my_password'), "User password verification failed!")

I can't guarantee that this code will work for everyone, but this patch did fix the error for me.

If there are any questions, feel free to discuss.

@n2o
Copy link

n2o commented May 10, 2024

Same problem here with an old project...

We are currently migrating to Poetry, which supports git dependencies. I'll go with this solution for now:

[tool.poetry.dependencies]
# ...
django-hashers-passlib = { git = "https://github.com/mathiasertl/django-hashers-passlib.git" }

@bartTC
Copy link

bartTC commented Jul 16, 2024

I forked the project and named it django-hashers-passlib-revived. It has support for up to Python 3.12 and Django 5.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants