def test_is_crypt_context(self): "test is_crypt_context()" from passlib.utils import is_crypt_context from passlib.context import CryptContext cc = CryptContext(["des_crypt"]) self.assertTrue(is_crypt_context(cc)) self.assertFalse(not is_crypt_context(cc))
def patch(): #get config ctx = getattr(settings, "PASSLIB_CONTEXT", "passlib-default") catfunc = getattr(settings, "PASSLIB_GET_CATEGORY", get_category) #parse & validate input value if not ctx: # remove any patching that was already set, just in case. set_django_password_context(None) return if ctx == "passlib-default": ctx = DEFAULT_CTX if isinstance(ctx, (unicode, bytes)): ctx = CryptPolicy.from_string(ctx) if isinstance(ctx, CryptPolicy): ctx = CryptContext(policy=ctx) if not is_crypt_context(ctx): raise TypeError("django settings.PASSLIB_CONTEXT must be CryptContext instance or config string: %r" % (ctx,)) #monkeypatch django.contrib.auth.models:User set_django_password_context(ctx, get_category=catfunc)
def patch(): #get config ctx = getattr(settings, "PASSLIB_CONTEXT", "passlib-default") catfunc = getattr(settings, "PASSLIB_GET_CATEGORY", get_category) #parse & validate input value if not ctx: # remove any patching that was already set, just in case. set_django_password_context(None) return if ctx == "passlib-default": ctx = DEFAULT_CTX if isinstance(ctx, (unicode, bytes)): ctx = CryptPolicy.from_string(ctx) if isinstance(ctx, CryptPolicy): ctx = CryptContext(policy=ctx) if not is_crypt_context(ctx): raise TypeError( "django settings.PASSLIB_CONTEXT must be CryptContext instance or config string: %r" % (ctx, )) #monkeypatch django.contrib.auth.models:User set_django_password_context(ctx, get_category=catfunc)
def test_is_crypt_context(self): "test is_crypt_context()" cc = CryptContext(["des_crypt"]) self.assertTrue(utils.is_crypt_context(cc)) self.assertFalse(not utils.is_crypt_context(cc))
def set_django_password_context(context=None, get_category=get_category): """monkeypatches :mod:`!django.contrib.auth` to use specified password context. :arg context: Passlib context to use for Django password hashing. If ``None``, restores original Django functions. In order to support existing hashes, any context specified should include all the hashes in :data:`django_context` in addition to custom hashes. :param get_category: Optional function to use when mapping Django user -> CryptContext category. If a function, should have syntax ``catfunc(user) -> category|None``. If ``None``, no function is used. By default, uses a function which returns ``"superuser"`` for superusers, and ``"staff"`` for staff. This function monkeypatches the following parts of Django: * :func:`!django.contrib.auth.models.check_password` * :meth:`!django.contrib.auth.models.User.check_password` * :meth:`!django.contrib.auth.models.User.set_password` It also stores the provided context in :data:`!django.contrib.auth.models.User.password_context`, for easy access. """ global _django_patch_state, _dam, _has_django0 _import_django() state = _django_patch_state User = _dam.User # issue warning if something else monkeypatched User # while our patch was applied. if state is not None: if um(User.set_password) is not state['user_set_password']: warn("another library has patched " "django.contrib.auth.models:User.set_password") if um(User.check_password) is not state['user_check_password']: warn("another library has patched" "django.contrib.auth.models:User.check_password") if _dam.check_password is not state['models_check_password']: warn("another library has patched" "django.contrib.auth.models:check_password") #check if we should just restore original state if context is None: if state is not None: del User.password_context _dam.check_password = state['orig_models_check_password'] User.set_password = state['orig_user_set_password'] User.check_password = state['orig_user_check_password'] _django_patch_state = None return #validate inputs if not is_crypt_context(context): raise TypeError("context must be CryptContext instance or None: %r" % (type(context), )) #backup original state if this is first call if state is None: _django_patch_state = state = dict( orig_user_check_password=um(User.check_password), orig_user_set_password=um(User.set_password), orig_models_check_password=_dam.check_password, ) #prepare replacements if _has_django0: UNUSABLE_PASSWORD = "******" else: UNUSABLE_PASSWORD = _dam.UNUSABLE_PASSWORD def set_password(user, raw_password): "passlib replacement for User.set_password()" if raw_password is None: if _has_django0: # django 0.9 user.password = UNUSABLE_PASSWORD else: user.set_unusable_password() else: cat = get_category(user) if get_category else None user.password = context.encrypt(raw_password, category=cat) def check_password(user, raw_password): "passlib replacement for User.check_password()" if raw_password is None: return False hash = user.password if not hash or hash == UNUSABLE_PASSWORD: return False cat = get_category(user) if get_category else None ok, new_hash = context.verify_and_update(raw_password, hash, category=cat) if ok and new_hash is not None: user.password = new_hash user.save() return ok def raw_check_password(raw_password, enc_password): "passlib replacement for check_password()" if not enc_password or enc_password == UNUSABLE_PASSWORD: raise ValueError("no password hash specified") return context.verify(raw_password, enc_password) #set new state User.password_context = context User.set_password = state['user_set_password'] = set_password User.check_password = state['user_check_password'] = check_password _dam.check_password = state['models_check_password'] = raw_check_password state['context'] = context state['get_category'] = get_category
def set_django_password_context(context=None, get_category=get_category): """monkeypatches :mod:`!django.contrib.auth` to use specified password context. :arg context: Passlib context to use for Django password hashing. If ``None``, restores original Django functions. In order to support existing hashes, any context specified should include all the hashes in :data:`django_context` in addition to custom hashes. :param get_category: Optional function to use when mapping Django user -> CryptContext category. If a function, should have syntax ``catfunc(user) -> category|None``. If ``None``, no function is used. By default, uses a function which returns ``"superuser"`` for superusers, and ``"staff"`` for staff. This function monkeypatches the following parts of Django: * :func:`!django.contrib.auth.models.check_password` * :meth:`!django.contrib.auth.models.User.check_password` * :meth:`!django.contrib.auth.models.User.set_password` It also stores the provided context in :data:`!django.contrib.auth.models.User.password_context`, for easy access. """ global _django_patch_state, _dam, _has_django0 _import_django() state = _django_patch_state User = _dam.User # issue warning if something else monkeypatched User # while our patch was applied. if state is not None: if um(User.set_password) is not state['user_set_password']: warn("another library has patched " "django.contrib.auth.models:User.set_password") if um(User.check_password) is not state['user_check_password']: warn("another library has patched" "django.contrib.auth.models:User.check_password") if _dam.check_password is not state['models_check_password']: warn("another library has patched" "django.contrib.auth.models:check_password") #check if we should just restore original state if context is None: if state is not None: del User.password_context _dam.check_password = state['orig_models_check_password'] User.set_password = state['orig_user_set_password'] User.check_password = state['orig_user_check_password'] _django_patch_state = None return #validate inputs if not is_crypt_context(context): raise TypeError("context must be CryptContext instance or None: %r" % (type(context),)) #backup original state if this is first call if state is None: _django_patch_state = state = dict( orig_user_check_password = um(User.check_password), orig_user_set_password = um(User.set_password), orig_models_check_password = _dam.check_password, ) #prepare replacements if _has_django0: UNUSABLE_PASSWORD = "******" else: UNUSABLE_PASSWORD = _dam.UNUSABLE_PASSWORD def set_password(user, raw_password): "passlib replacement for User.set_password()" if raw_password is None: if _has_django0: # django 0.9 user.password = UNUSABLE_PASSWORD else: user.set_unusable_password() else: cat = get_category(user) if get_category else None user.password = context.encrypt(raw_password, category=cat) def check_password(user, raw_password): "passlib replacement for User.check_password()" if raw_password is None: return False hash = user.password if not hash or hash == UNUSABLE_PASSWORD: return False cat = get_category(user) if get_category else None ok, new_hash = context.verify_and_update(raw_password, hash, category=cat) if ok and new_hash is not None: user.password = new_hash user.save() return ok def raw_check_password(raw_password, enc_password): "passlib replacement for check_password()" if not enc_password or enc_password == UNUSABLE_PASSWORD: raise ValueError("no password hash specified") return context.verify(raw_password, enc_password) #set new state User.password_context = context User.set_password = state['user_set_password'] = set_password User.check_password = state['user_check_password'] = check_password _dam.check_password = state['models_check_password'] = raw_check_password state['context' ] = context state['get_category'] = get_category